]> jfr.im git - irc/evilnet/x3.git/blame - src/chanserv.c
some stuff by wasted from whatnet, see the changelog for more info
[irc/evilnet/x3.git] / src / chanserv.c
CommitLineData
d76ed9a9
AS
1/* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
3 *
83ff05c3 4 * This file is part of x3.
d76ed9a9 5 *
d0f04f71 6 * x3 is free software; you can redistribute it and/or modify
d76ed9a9
AS
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 opserv_bad_channel() */
26#include "saxdb.h"
27#include "timeq.h"
28
29#define CHANSERV_CONF_NAME "services/chanserv"
30
31/* ChanServ options */
32#define KEY_SUPPORT_CHANNEL "support_channel"
33#define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34#define KEY_DB_BACKUP_FREQ "db_backup_freq"
35#define KEY_INFO_DELAY "info_delay"
36#define KEY_MAX_GREETLEN "max_greetlen"
37#define KEY_ADJUST_THRESHOLD "adjust_threshold"
38#define KEY_ADJUST_DELAY "adjust_delay"
39#define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40#define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
31f23f13 41#define KEY_BAN_TIMEOUT_FREQ "ban_timeout_freq"
d76ed9a9
AS
42#define KEY_MAX_CHAN_USERS "max_chan_users"
43#define KEY_MAX_CHAN_BANS "max_chan_bans"
44#define KEY_NICK "nick"
45#define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
d76ed9a9
AS
46#define KEY_8BALL_RESPONSES "8ball"
47#define KEY_OLD_BAN_NAMES "old_ban_names"
48#define KEY_REFRESH_PERIOD "refresh_period"
49#define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50#define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51#define KEY_MAX_OWNED "max_owned"
52#define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53#define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54#define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55#define KEY_NODELETE_LEVEL "nodelete_level"
56#define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
a32da4c7 57#define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
d76ed9a9
AS
58
59/* ChanServ database */
c8ca69a0 60#define KEY_VERSION_CONTROL "version_control"
d76ed9a9
AS
61#define KEY_CHANNELS "channels"
62#define KEY_NOTE_TYPES "note_types"
63
c8ca69a0
AS
64/* version control paramiter */
65#define KEY_VERSION_NUMBER "version_number"
66
d76ed9a9
AS
67/* Note type parameters */
68#define KEY_NOTE_OPSERV_ACCESS "opserv_access"
69#define KEY_NOTE_CHANNEL_ACCESS "channel_access"
70#define KEY_NOTE_SETTER_ACCESS "setter_access"
71#define KEY_NOTE_VISIBILITY "visibility"
72#define KEY_NOTE_VIS_PRIVILEGED "privileged"
73#define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
74#define KEY_NOTE_VIS_ALL "all"
75#define KEY_NOTE_MAX_LENGTH "max_length"
76#define KEY_NOTE_SETTER "setter"
77#define KEY_NOTE_NOTE "note"
78
79/* Do-not-register channels */
80#define KEY_DNR "dnr"
81#define KEY_DNR_SET "set"
82#define KEY_DNR_SETTER "setter"
83#define KEY_DNR_REASON "reason"
84
85/* Channel data */
86#define KEY_REGISTERED "registered"
87#define KEY_REGISTRAR "registrar"
88#define KEY_SUSPENDED "suspended"
89#define KEY_PREVIOUS "previous"
90#define KEY_SUSPENDER "suspender"
91#define KEY_ISSUED "issued"
92#define KEY_REVOKED "revoked"
93#define KEY_SUSPEND_EXPIRES "suspend_expires"
94#define KEY_SUSPEND_REASON "suspend_reason"
82f37c08 95#define KEY_GIVEOWNERSHIP "giveownership"
96#define KEY_STAFF_ISSUER "staff_issuer"
97#define KEY_OLD_OWNER "old_owner"
98#define KEY_TARGET "target"
99#define KEY_TARGET_ACCESS "target_access"
d76ed9a9
AS
100#define KEY_VISITED "visited"
101#define KEY_TOPIC "topic"
102#define KEY_GREETING "greeting"
103#define KEY_USER_GREETING "user_greeting"
104#define KEY_MODES "modes"
105#define KEY_FLAGS "flags"
106#define KEY_OPTIONS "options"
107#define KEY_USERS "users"
c8273589 108#define KEY_BANS "bans" /* for lamers */
d76ed9a9
AS
109#define KEY_MAX "max"
110#define KEY_NOTES "notes"
111#define KEY_TOPIC_MASK "topic_mask"
a32da4c7 112#define KEY_OWNER_TRANSFER "owner_transfer"
d76ed9a9
AS
113
114/* User data */
115#define KEY_LEVEL "level"
116#define KEY_INFO "info"
117#define KEY_SEEN "seen"
118
119/* Ban data */
120#define KEY_OWNER "owner"
121#define KEY_REASON "reason"
122#define KEY_SET "set"
123#define KEY_DURATION "duration"
124#define KEY_EXPIRES "expires"
125#define KEY_TRIGGERED "triggered"
126
7637f48f 127#define KEY_GOD_TIMEOUT "god_timeout"
128
d76ed9a9
AS
129#define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
130#define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
131
132/* Administrative messages */
133static const struct message_entry msgtab[] = {
134 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
135
136/* Channel registration */
137 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
138 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
139 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
f8f30e75 140 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator (+o) in $b%s$b to register it." },
d76ed9a9 141 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
f8f30e75
AS
142 { "CSMSG_OWN_TOO_MANY", "%s already owns more than the limit of %d channels. Use FORCE to override." },
143 { "CSMSG_YOU_OWN_TOO_MANY", "You already own more than the limit of %d channels. Ask a staff member for help." },
144 { "CSMSG_ANOTHER_SERVICE", "Another service bot is in that channel already. Ask a staff member for help." },
d76ed9a9
AS
145
146/* Do-not-register channels */
147 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
de9510bc
AS
148 { "CSMSG_DNR_SEARCH_RESULTS", "$bDo-Not-Register Channels$b" },
149 { "CSMSG_DNR_INFO", "$b%s$b (set by $b%s$b): %s" },
150 { "CSMSG_DNR_INFO_SET", "$b%s$b (set %s ago by $b%s$b): %s" },
d76ed9a9
AS
151 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
152 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
153 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
154 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
155 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
156 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
157 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
158
159/* Channel unregistration */
160 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
161 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
162 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
fa0fac3e 163 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s %s'." },
d76ed9a9
AS
164
165/* Channel moving */
166 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
167 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
168
169/* Channel merging */
170 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
171 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
172 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
173 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
174 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
175
176/* Handle unregistration */
177 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
178
179/* Error messages */
180 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
181 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
182 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
183 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
c8273589 184 { "CSMSG_MAXIMUM_LAMERS", "This channel has reached the lamer count limit of $b%d$b." },
d76ed9a9
AS
185 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
186 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
187 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
188 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
55342ce8 189 { "CSMSG_ALREADY_HALFOPPED", "You are already halfopped in $b%s$b." },
d76ed9a9 190 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
55342ce8 191 { "CSMSG_ALREADY_DOWN", "You are not opped, halfopped, or voiced in $b%s$b." },
d76ed9a9
AS
192 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
193 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
194
195/* Removing yourself from a channel. */
196 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
197 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
198 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
199
200/* User management */
de9510bc 201 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %s (%d)." },
d76ed9a9
AS
202 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
203 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
204 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
205 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
a915f7d4
AS
206 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%s$b, not %s." },
207 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with %s access)." },
5ca901c3 208 { "CSMSG_ADDUSER_PENDING", "I have sent him/her a message letting them know, and if they auth or register soon, I will finish adding them automatically." },
ac3bdc8d 209 { "CSMSG_ADDUSER_PENDING_ALREADY", "He or she is already pending addition to %s once he/she auths with $b$N$b." },
1d957482
AS
210 { "CSMSG_ADDUSER_PENDING_HEADER", "Users to add to channels pending logins:" }, /* Remove after testing? */
211 { "CSMSG_ADDUSER_PENDING_LIST", "Channel %s user %s" }, /* Remove after testing? */
1bcd5f19 212 { "CSMSG_ADDUSER_PENDING_FOOTER", "--------- End of pending list ----------" }, /* Remove after testing? */
ac3bdc8d 213 /*{ "CSMSG_ADDUSER_PENDING_NOTINCHAN", "That user is not in %s, and is not auth'd." }, */
1bcd5f19 214 { "CSMSG_ADDUSER_PENDING_TARGET", "Channel Services bot here: %s would like to add you to my userlist in channel %s, but you are not authenticated to $b$N$b. Please authenticate now and you will be added. If you do not have an account, type /msg $N help register" },
d76ed9a9
AS
215 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
216
217 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
218 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
219 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
a32da4c7 220 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
d76ed9a9
AS
221 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
222 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
223
224/* Ban management */
c8273589
AS
225 { "CSMSG_LAMER_ADDED", "Added $b%s$b to %s LAMERs." },
226 { "CSMSG_TIMED_LAMER_ADDED", "LAMERed $b%s$b on %s for %s." },
d76ed9a9
AS
227 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
228 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
c8273589
AS
229 { "CSMSG_REASON_CHANGE", "Reason for LAMER $b%s$b changed." },
230 { "CSMSG_LAMER_EXTENDED", "Extended LAMER for $b%s$b, now expires in %s." },
231 { "CSMSG_BAN_REMOVED", "Matching ban(s) and LAMER(s) in $b%s$b were removed." },
232 { "CSMSG_TRIMMED_LAMERS", "Trimmed $b%d LAMERs$b from the %s LAMER list that were inactive for at least %s." },
d76ed9a9 233 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
c8273589 234 { "CSMSG_REDUNDANT_LAMER", "$b%s$b is already LAMER'd in %s." },
d76ed9a9
AS
235 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
236 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
237 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
238 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
239 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
c8273589 240 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban or LAMER found: $b%s$b." },
d76ed9a9
AS
241 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
242
243 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
244
245/* Channel management */
246 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
247 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
248 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
249
250 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
251 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
252 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
a915f7d4 253 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
d76ed9a9
AS
254 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
255 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
256 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
257
258 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
259 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
260 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
261 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
262 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
263 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
264 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
265 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
266 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
d76ed9a9
AS
267 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
268 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
269 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
270 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
271 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
272 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
273 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
274 { "CSMSG_SET_MODES", "$bModes $b %s" },
275 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
35caf917 276 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s - +l joinflood protection." },
d76ed9a9 277 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
35caf917 278 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d - and above userinfos are shown." },
d76ed9a9 279 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
35caf917
AS
280 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d - Userlevel required to invite self." },
281 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d - level and above can op unknown users." },
282 { "CSMSG_SET_ENFHALFOPS", "$bEnfHalfOps $b %d - level and above can hop unknown users." },
35caf917
AS
283 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d - and above can change channel modes." },
284 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d - and above can set the topic." },
285 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d - and above can use public commands." },
286 { "CSMSG_SET_SETTERS", "$bSetters $b %d - and above can change these settings." },
4b6129c0 287 { "CSMSG_SET_AUTOMODE", "$bAutoMode $b %d - %s" },
d76ed9a9
AS
288 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
289 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
290 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
291 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
7637f48f 292 { "CSMSG_SET_RESYNC", "$bResync $b %d - %s" },
31f23f13 293 { "CSMSG_SET_BANTIMEOUT", "$bBanTimeout $b %d - %s" },
4b6129c0 294
c8ca69a0
AS
295 { "CSMSG_USET_AUTOOP", "$bAutoOp $b %s" },
296 { "CSMSG_USET_AUTOVOICE", "$bAutoVoice $b %s" },
d76ed9a9
AS
297 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
298 { "CSMSG_USET_INFO", "$bInfo $b %s" },
299
300 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
31f23f13 301 { "CSMSG_USER_PROTECTED_KICK", "That user is protected." }, /* No $ or %s replacements! */
d76ed9a9 302 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
55342ce8 303 { "CSMSG_HOPBY_LOCKED", "You may not halfop users who lack halfop or greater access." },
d76ed9a9
AS
304 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
305 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
55342ce8 306 { "CSMSG_HALFOPPED_USERS", "Halfopped users in $b%s$b." },
d76ed9a9 307 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
55342ce8 308 { "CSMSG_DEHALFOPPED_USERS", "DeHalfopped users in $b%s$b." },
d76ed9a9
AS
309 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
310 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
4b6129c0
AS
311
312 { "CSMSG_AUTOMODE_NONE", "Noone will be automatically oped, half-oped, or voiced." },
313 { "CSMSG_AUTOMODE_NORMAL", "Give voice to peons, half-op to halfops, and op to ops." },
639bdb1d
AS
314 { "CSMSG_AUTOMODE_VOICE", "#1 plus give voice to everyone." },
315 { "CSMSG_AUTOMODE_HOP", "#1 plus give halfops to everyone." },
316 { "CSMSG_AUTOMODE_OP", "#1 plus give ops to everyone (not advised)" },
317 { "CSMSG_AUTOMODE_MUTE", "Give half-op to halfops, and op to ops only." },
4b6129c0 318
d76ed9a9
AS
319 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
320 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
321 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
322 { "CSMSG_PROTECT_NONE", "No users will be protected." },
323 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
324 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
325 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
31f23f13 326
d76ed9a9
AS
327 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
328 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
329 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
330 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
331 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
31f23f13 332
7637f48f 333 { "CSMSG_RESYNC_NEVER", "Never automaticly resync userlist." },
334 { "CSMSG_RESYNC_3_HOURS", "Resync userlist every 3 hours." },
335 { "CSMSG_RESYNC_6_HOURS", "Resync userlist every 6 hours." },
336 { "CSMSG_RESYNC_12_HOURS", "Resync userlist every 12 hours." },
337 { "CSMSG_RESYNC_24_HOURS", "Resync userlist every 24 hours." },
338
35caf917 339 { "CSMSG_CTCPREACTION_NONE", "CTCPs are allowed" },
d76ed9a9
AS
340 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
341 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
342 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
343 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
31f23f13
AS
344
345 { "CSMSG_BANTIMEOUT_NONE", "Bans will not be removed automatically."},
346 { "CSMSG_BANTIMEOUT_10M", "Bans will be removed after 10 minutes."},
347 { "CSMSG_BANTIMEOUT_2H", "Bans will be removed after 2 hours."},
348 { "CSMSG_BANTIMEOUT_4H", "Bans will be removed after 4 hours."},
349 { "CSMSG_BANTIMEOUT_1D", "Bans will be removed after 24 hours."},
350 { "CSMSG_BANTIMEOUT_1W", "Bans will be removed after 1 week."},
d76ed9a9
AS
351
352 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
353 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
354 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
355 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
356 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
357 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
358 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
359 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
360
361 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
c8273589 362 { "CSMSG_NO_BANS", "No bans found on $b%s$b." },
d76ed9a9
AS
363 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
364
365/* Channel userlist */
338a82b5
AS
366 { "CSMSG_ACCESS_ALL_HEADER_NORMAL", "$b%s Users From Level %s To %s$b" },
367 { "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", "$b%s Users From Level %s To %s Matching %s$b" },
368 /* uncomment if needed to adujust styles (and change code below)
369 { "CSMSG_ACCESS_ALL_HEADER_CLEAN", "$b%s Users From Level %s To %s$b" },
370 { "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", "$b%s Users From Level %s To %s Matching %s$b" },
371 { "CSMSG_ACCESS_ALL_HEADER_ADVANCED", "$b%s Users From Level %s To %s$b" },
372 { "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", "$b%s Users From Level %s To %s Matching %s$b" },
d9896a83
AS
373 { "CSMSG_ACCESS_ALL_HEADER_CLASSIC", "$b%s Users From Level %s To %s$b" },
374 { "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", "$b%s Users From Level %s To %s Matching %s$b" },
338a82b5 375 */
d76ed9a9 376 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
de9510bc 377 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%s$b (%u) in %s." },
c8273589 378 { "CSMSG_LAMERS_HEADER", "$bLamers in %s$b" },
d76ed9a9
AS
379
380/* Channel note list */
381 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
382 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
383 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
384 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
385 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
386 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
387 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
388 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
389 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
390 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
391 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
392 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
393 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
394 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
395 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
396
397/* Channel [un]suspension */
398 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
399 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
400 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
401 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
402 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
403 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
404 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
405
406/* Access information */
407 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
408 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
409 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
410 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
411 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
de9510bc 412 { "CSMSG_USER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s." },
d76ed9a9 413 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
de9510bc 414 { "CSMSG_HELPER_HAS_ACCESS", "%s has $b%s$b access (%d) in %s and has $bsecurity override$b enabled." },
d76ed9a9
AS
415 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
416 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
417 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
418
419/* Seen information */
420 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
421 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
422 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
423 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
424
425/* Names information */
426 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
427 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
428
429/* Channel information */
de9510bc
AS
430 { "CSMSG_CHANNEL_INFO", "$bInformation About %s$b" },
431 { "CSMSG_BAR", "----------------------------------------"},
d76ed9a9
AS
432 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
433 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
434 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
435 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
c8273589
AS
436 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
437 { "CSMSG_CHANNEL_LAMERS", "$bLamer Count: $b%i" },
d76ed9a9
AS
438 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
439 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
440 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
441 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
442 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
443 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
444 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
445 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
446 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
447 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
448 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
449 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
450 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
451 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
82f37c08 452 { "CSMSG_CHANNEL_OWNERSHIP_HISTORY", "Ownership transfer history for $b%s$b" },
453 { "CSMSG_CHANNEL_OWNERSHIP_NORMAL", "from %s to %s (%d access) on %s" },
454 { "CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", "from %s to %s (%d access) by %s on %s reason: %s" },
455 { "CSMSG_CHANNEL_OWNERSHIP_STAFF", "from %s to %s (%d access) by %s on %s" },
de9510bc 456 { "CSMSG_CHANNEL_END", "---------------End of Info--------------"},
cde20a3f 457 { "CSMSG_CHANNEL_END_CLEAN", "End of Info"},
d76ed9a9 458
de9510bc 459 { "CSMSG_PEEK_INFO", "$bStatus of %s$b" },
d76ed9a9
AS
460 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
461 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
462 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
463 { "CSMSG_PEEK_OPS", "$bOps:$b" },
464 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
de9510bc 465 { "CSMSG_PEEK_END", "-------------End of Status--------------" },
d76ed9a9
AS
466
467/* Network information */
468 { "CSMSG_NETWORK_INFO", "Network Information:" },
469 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
470 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
c8273589 471 { "CSMSG_NETWORK_LAMERS", "$bTotal Lamer Count: $b%i" },
d76ed9a9
AS
472 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
473 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
474 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
475 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
476 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
477
478/* Staff list */
479 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
480 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
481 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
482
483/* Channel searches */
484 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
485 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
486 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
de9510bc 487 { "CSMSG_CHANNEL_SEARCH_RESULTS", "$bChannels Found Matching Search$b" },
d76ed9a9
AS
488
489/* Channel configuration */
490 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
de9510bc
AS
491 { "CSMSG_CHANNEL_OPTIONS", "$bChannel Options for %s$b" },
492 { "CSMSG_CHANNEL_OPTIONS_END", "-------------End of Options-------------" },
d76ed9a9
AS
493 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
494
495/* User settings */
496 { "CSMSG_USER_OPTIONS", "User Options:" },
35caf917 497// { "CSMSG_USER_PROTECTED", "That user is protected." },
d76ed9a9
AS
498
499/* Toys */
500 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
501 { "CSMSG_PING_RESPONSE", "Pong!" },
502 { "CSMSG_WUT_RESPONSE", "wut" },
503 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
504 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
505 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
506 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
507 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
508 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
509 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
510
511/* Other things */
de9510bc 512 { "CSMSG_EVENT_SEARCH_RESULTS", "$bChannel Events for %s$b" },
23475fc6 513 { "CSMSG_LAST_INVALID", "Invalid argument. must be 1-200" },
d76ed9a9
AS
514 { NULL, NULL }
515};
516
517/* eject_user and unban_user flags */
518#define ACTION_KICK 0x0001
519#define ACTION_BAN 0x0002
c8273589
AS
520#define ACTION_ADD_LAMER 0x0004
521#define ACTION_ADD_TIMED_LAMER 0x0008
d76ed9a9 522#define ACTION_UNBAN 0x0010
c8273589 523#define ACTION_DEL_LAMER 0x0020
d76ed9a9
AS
524
525/* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
526#define MODELEN 40 + KEYLEN
527#define PADLEN 21
528#define ACCESSLEN 10
529
530#define CSFUNC_ARGS user, channel, argc, argv, cmd
531
532#define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
567a5f26 533#define CHANSERV_SYNTAX() svccmd_send_help_brief(user, chanserv, cmd)
d76ed9a9
AS
534#define REQUIRE_PARAMS(N) if(argc < (N)) { \
535 reply("MSG_MISSING_PARAMS", argv[0]); \
536 CHANSERV_SYNTAX(); \
537 return 0; }
538
539DECLARE_LIST(dnrList, struct do_not_register *);
540DEFINE_LIST(dnrList, struct do_not_register *);
541
542static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
543
544struct userNode *chanserv;
545dict_t note_types;
546int off_channel;
7637f48f 547extern struct string_list *autojoin_channels;
d76ed9a9
AS
548static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
549static struct log_type *CS_LOG;
ac3bdc8d
AS
550struct adduserPending* adduser_pendings = NULL;
551unsigned int adduser_pendings_count = 0;
7637f48f 552unsigned long god_timeout;
d76ed9a9
AS
553
554static struct
555{
556 struct channelList support_channels;
557 struct mod_chanmode default_modes;
558
559 unsigned long db_backup_frequency;
560 unsigned long channel_expire_frequency;
31f23f13 561 unsigned long ban_timeout_frequency;
d76ed9a9
AS
562
563 long info_delay;
564 unsigned int adjust_delay;
565 long channel_expire_delay;
566 unsigned int nodelete_level;
567
568 unsigned int adjust_threshold;
569 int join_flood_threshold;
570
571 unsigned int greeting_length;
572 unsigned int refresh_period;
a32da4c7 573 unsigned int giveownership_period;
d76ed9a9
AS
574
575 unsigned int max_owned;
576 unsigned int max_chan_users;
c8273589 577 unsigned int max_chan_bans; /* lamers */
d76ed9a9
AS
578 unsigned int max_userinfo_length;
579
580 struct string_list *set_shows;
581 struct string_list *eightball;
582 struct string_list *old_ban_names;
583
584 const char *ctcp_short_ban_duration;
585 const char *ctcp_long_ban_duration;
586
587 const char *irc_operator_epithet;
588 const char *network_helper_epithet;
589 const char *support_helper_epithet;
590} chanserv_conf;
591
592struct listData
593{
594 struct userNode *user;
595 struct userNode *bot;
596 struct chanNode *channel;
597 const char *search;
598 unsigned short lowest;
599 unsigned short highest;
600 struct userData **users;
601 struct helpfile_table table;
602};
603
604enum note_access_type
605{
606 NOTE_SET_CHANNEL_ACCESS,
607 NOTE_SET_CHANNEL_SETTER,
608 NOTE_SET_PRIVILEGED
609};
610
611enum note_visible_type
612{
613 NOTE_VIS_ALL,
614 NOTE_VIS_CHANNEL_USERS,
615 NOTE_VIS_PRIVILEGED
616};
617
618struct note_type
619{
620 enum note_access_type set_access_type;
621 union {
622 unsigned int min_opserv;
623 unsigned short min_ulevel;
624 } set_access;
625 enum note_visible_type visible_type;
626 unsigned int max_length;
627 unsigned int refs;
628 char name[1];
629};
630
631struct note
632{
633 struct note_type *type;
634 char setter[NICKSERV_HANDLE_LEN+1];
635 char note[1];
636};
637
638static unsigned int registered_channels;
639static unsigned int banCount;
640
641static const struct {
642 char *name;
643 char *title;
644 unsigned short level;
645 char ch;
a915f7d4 646} accessLevels[] = { /* MUST be orderd less to most! */
d76ed9a9 647 { "peon", "Peon", UL_PEON, '+' },
55342ce8 648 { "halfop", "HalfOp", UL_HALFOP, '%' },
d76ed9a9 649 { "op", "Op", UL_OP, '@' },
4048352e 650 { "manager", "Manager", UL_MANAGER, '%' },
d76ed9a9
AS
651 { "coowner", "Coowner", UL_COOWNER, '*' },
652 { "owner", "Owner", UL_OWNER, '!' },
653 { "helper", "BUG:", UL_HELPER, 'X' }
654};
655
35caf917 656/* If you change this, change the enum in chanserv.h also, or stack smashing will commence. */
d76ed9a9
AS
657static const struct {
658 char *format_name;
659 char *db_name;
660 unsigned short default_value;
661 unsigned int old_idx;
662 unsigned int old_flag;
663 unsigned short flag_value;
664} levelOptions[] = {
e3b2f789
AS
665 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
666 { "CSMSG_SET_ENFHALFOPS", "enfhalfops", 300, 1, 0, 0 },
667 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
668 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
669 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
670 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
671 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
672 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
d76ed9a9
AS
673 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
674};
675
676struct charOptionValues {
677 char value;
678 char *format_name;
4b6129c0
AS
679} automodeValues[] = {
680 { 'n', "CSMSG_AUTOMODE_NONE" },
681 { 'y', "CSMSG_AUTOMODE_NORMAL" },
682 { 'v', "CSMSG_AUTOMODE_VOICE" },
683 { 'h', "CSMSG_AUTOMODE_HOP" },
684 { 'o', "CSMSG_AUTOMODE_OP" },
685 { 'm', "CSMSG_AUTOMODE_MUTE" }
c8ca69a0 686}, protectValues[] = {
d76ed9a9
AS
687 { 'a', "CSMSG_PROTECT_ALL" },
688 { 'e', "CSMSG_PROTECT_EQUAL" },
689 { 'l', "CSMSG_PROTECT_LOWER" },
690 { 'n', "CSMSG_PROTECT_NONE" }
691}, toysValues[] = {
692 { 'd', "CSMSG_TOYS_DISABLED" },
693 { 'n', "CSMSG_TOYS_PRIVATE" },
694 { 'p', "CSMSG_TOYS_PUBLIC" }
695}, topicRefreshValues[] = {
696 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
697 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
698 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
699 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
700 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
701}, ctcpReactionValues[] = {
35caf917 702 { 'n', "CSMSG_CTCPREACTION_NONE" },
d76ed9a9
AS
703 { 'k', "CSMSG_CTCPREACTION_KICK" },
704 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
705 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
706 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
31f23f13
AS
707}, banTimeoutValues[] = {
708 { '0', "CSMSG_BANTIMEOUT_NONE" },
709 { '1', "CSMSG_BANTIMEOUT_10M" },
710 { '2', "CSMSG_BANTIMEOUT_2H" },
711 { '3', "CSMSG_BANTIMEOUT_4H" },
712 { '4', "CSMSG_BANTIMEOUT_1D" },
713 { '5', "CSMSG_BANTIMEOUT_1W" }
7637f48f 714},
715resyncValues[] = {
716 { 'n', "CSMSG_RESYNC_NEVER" },
717 { '1', "CSMSG_RESYNC_3_HOURS" },
718 { '2', "CSMSG_RESYNC_6_HOURS" },
719 { '3', "CSMSG_RESYNC_12_HOURS" },
720 { '4', "CSMSG_RESYNC_24_HOURS" }
d76ed9a9
AS
721};
722
723static const struct {
724 char *format_name;
725 char *db_name;
726 char default_value;
727 unsigned int old_idx;
728 unsigned char count;
729 struct charOptionValues *values;
730} charOptions[] = {
4b6129c0 731 { "CSMSG_SET_AUTOMODE", "automode", 'y', 99, ArrayLength(automodeValues), automodeValues },
c8ca69a0
AS
732 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
733 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
d76ed9a9 734 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
31f23f13 735 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 'n', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues },
7637f48f 736 { "CSMSG_SET_BANTIMEOUT", "bantimeout", '0', 11, ArrayLength(banTimeoutValues), banTimeoutValues },
737 { "CSMSG_SET_RESYNC", "resync", 'n', 12, ArrayLength(resyncValues), resyncValues },
d76ed9a9
AS
738};
739
740struct userData *helperList;
741struct chanData *channelList;
742static struct module *chanserv_module;
743static unsigned int userCount;
c8ca69a0
AS
744unsigned int chanserv_read_version = 0; /* db version control */
745
746#define CHANSERV_DB_VERSION 2
d76ed9a9
AS
747
748#define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
749#define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
750#define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
751
752unsigned short
753user_level_from_name(const char *name, unsigned short clamp_level)
754{
755 unsigned int level = 0, ii;
756 if(isdigit(name[0]))
757 level = strtoul(name, NULL, 10);
758 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
759 if(!irccasecmp(name, accessLevels[ii].name))
760 level = accessLevels[ii].level;
761 if(level > clamp_level)
762 return 0;
763 return level;
764}
765
e6892204
AS
766char *
767user_level_name_from_level(int level)
768{
769 unsigned int ii;
a915f7d4
AS
770 char* highest;
771
772 highest = "None";
773 if(level >= 1)
774 highest = "Peon";
e6892204 775 for(ii = 0; (ii < ArrayLength(accessLevels)); ii++)
a915f7d4
AS
776 if(level >= accessLevels[ii].level)
777 highest = accessLevels[ii].title;
778 return(highest);
e6892204
AS
779}
780
781
d76ed9a9
AS
782int
783parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
784{
785 char *sep;
786 *minl = strtoul(arg, &sep, 10);
787 if(*sep == '\0')
788 {
789 *maxl = *minl;
790 return 1;
791 }
792 else if(*sep == '-')
793 {
794 *maxl = strtoul(sep+1, &sep, 10);
795 return *sep == '\0';
796 }
797 else
798 return 0;
799}
800
801struct userData*
802_GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
803{
804 struct userData *uData, **head;
805
806 if(!channel || !handle)
807 return NULL;
808
809 if(override && HANDLE_FLAGGED(handle, HELPING)
810 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
811 {
812 for(uData = helperList;
813 uData && uData->handle != handle;
814 uData = uData->next);
815
816 if(!uData)
817 {
818 uData = calloc(1, sizeof(struct userData));
819 uData->handle = handle;
820
821 uData->access = UL_HELPER;
822 uData->seen = 0;
823
824 uData->info = NULL;
825
826 uData->prev = NULL;
827 uData->next = helperList;
828 if(helperList)
829 helperList->prev = uData;
830 helperList = uData;
831 }
832
833 head = &helperList;
834 }
835 else
836 {
837 for(uData = channel->users; uData; uData = uData->next)
838 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
839 break;
840
841 head = &(channel->users);
842 }
843
844 if(uData && (uData != *head))
845 {
846 /* Shuffle the user to the head of whatever list he was in. */
847 if(uData->next)
848 uData->next->prev = uData->prev;
849 if(uData->prev)
850 uData->prev->next = uData->next;
851
852 uData->prev = NULL;
853 uData->next = *head;
854
855 if(*head)
856 (**head).prev = uData;
857 *head = uData;
858 }
859
860 return uData;
861}
862
863/* Returns non-zero if user has at least the minimum access.
864 * exempt_owner is set when handling !set, so the owner can set things
865 * to/from >500.
866 */
867int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
868{
869 struct userData *uData;
870 struct chanData *cData = channel->channel_info;
871 unsigned short minimum = cData->lvlOpts[opt];
872 if(!minimum)
873 return 1;
874 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
875 if(!uData)
876 return 0;
877 if(minimum <= uData->access)
878 return 1;
879 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
880 return 1;
881 return 0;
882}
883
884/* Scan for other users authenticated to the same handle
885 still in the channel. If so, keep them listed as present.
886
887 user is optional, if not null, it skips checking that userNode
888 (for the handle_part function) */
889static void
890scan_user_presence(struct userData *uData, struct userNode *user)
891{
892 struct modeNode *mn;
893
894 if(IsSuspended(uData->channel)
895 || IsUserSuspended(uData)
896 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
897 {
898 uData->present = 0;
899 }
900 else
901 {
902 uData->present = 1;
903 uData->seen = now;
904 }
905}
906
907static void
908chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
909{
910 unsigned int eflags, argc;
911 char *argv[4];
912 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
913
914 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
915 if(!channel->channel_info
916 || IsSuspended(channel->channel_info)
917 || IsService(user)
918 || !ircncasecmp(text, "ACTION ", 7))
919 return;
35caf917
AS
920 /* We dont punish people we know -Rubin
921 * * Figure out the minimum level needed to CTCP the channel *
922 *
923 * if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
924 * return;
925 */
926 /* If they are a user of the channel, they are exempt */
927 if(_GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
928 return;
d76ed9a9
AS
929 /* We need to enforce against them; do so. */
930 eflags = 0;
931 argv[0] = text;
932 argv[1] = user->nick;
933 argc = 2;
934 if(GetUserMode(channel, user))
935 eflags |= ACTION_KICK;
936 switch(channel->channel_info->chOpts[chCTCPReaction]) {
35caf917
AS
937 default: case 'n': return;
938 case 'k':
939 eflags |= ACTION_KICK;
940 break;
d76ed9a9
AS
941 case 'b':
942 eflags |= ACTION_BAN;
943 break;
944 case 't':
c8273589 945 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9
AS
946 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
947 break;
948 case 'T':
c8273589 949 eflags |= ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER;
d76ed9a9
AS
950 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
951 break;
952 }
953 argv[argc++] = bad_ctcp_reason;
954 eject_user(chanserv, channel, argc, argv, NULL, eflags);
955}
956
957struct note_type *
958chanserv_create_note_type(const char *name)
959{
960 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
961 strcpy(ntype->name, name);
962 ntype->refs = 1;
963 dict_insert(note_types, ntype->name, ntype);
964 return ntype;
965}
966
967static void
968chanserv_deref_note_type(void *data)
969{
970 struct note_type *ntype = data;
971
972 if(--ntype->refs > 0)
973 return;
974 free(ntype);
975}
976
977static void
978chanserv_flush_note_type(struct note_type *ntype)
979{
980 struct chanData *cData;
981 for(cData = channelList; cData; cData = cData->next)
982 dict_remove(cData->notes, ntype->name);
983}
984
985static void
986chanserv_truncate_notes(struct note_type *ntype)
987{
988 struct chanData *cData;
989 struct note *note;
990 unsigned int size = sizeof(*note) + ntype->max_length;
991
992 for(cData = channelList; cData; cData = cData->next) {
993 note = dict_find(cData->notes, ntype->name, NULL);
994 if(!note)
995 continue;
996 if(strlen(note->note) <= ntype->max_length)
997 continue;
998 dict_remove2(cData->notes, ntype->name, 1);
999 note = realloc(note, size);
1000 note->note[ntype->max_length] = 0;
1001 dict_insert(cData->notes, ntype->name, note);
1002 }
1003}
1004
1005static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
1006
1007static struct note *
1008chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
1009{
1010 struct note *note;
1011 unsigned int len = strlen(text);
1012
1013 if(len > type->max_length) len = type->max_length;
1014 note = calloc(1, sizeof(*note) + len);
1015 note->type = type;
1016 strncpy(note->setter, setter, sizeof(note->setter)-1);
1017 memcpy(note->note, text, len);
1018 note->note[len] = 0;
1019 dict_insert(channel->notes, type->name, note);
1020 type->refs++;
1021 return note;
1022}
1023
1024static void
1025chanserv_free_note(void *data)
1026{
1027 struct note *note = data;
1028
1029 chanserv_deref_note_type(note->type);
1030 assert(note->type->refs > 0); /* must use delnote to remove the type */
1031 free(note);
1032}
1033
1034static MODCMD_FUNC(cmd_createnote) {
1035 struct note_type *ntype;
1036 unsigned int arg = 1, existed = 0, max_length;
1037
1038 if((ntype = dict_find(note_types, argv[1], NULL)))
1039 existed = 1;
1040 else
1041 ntype = chanserv_create_note_type(argv[arg]);
1042 if(!irccasecmp(argv[++arg], "privileged"))
1043 {
1044 arg++;
1045 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1046 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1047 }
1048 else if(!irccasecmp(argv[arg], "channel"))
1049 {
1050 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1051 if(!ulvl)
1052 {
1053 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1054 goto fail;
1055 }
1056 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1057 ntype->set_access.min_ulevel = ulvl;
1058 }
1059 else if(!irccasecmp(argv[arg], "setter"))
1060 {
1061 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1062 }
1063 else
1064 {
1065 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1066 goto fail;
1067 }
1068
1069 if(!irccasecmp(argv[++arg], "privileged"))
1070 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1071 else if(!irccasecmp(argv[arg], "channel_users"))
1072 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1073 else if(!irccasecmp(argv[arg], "all"))
1074 ntype->visible_type = NOTE_VIS_ALL;
1075 else {
1076 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1077 goto fail;
1078 }
1079
1080 if((arg+1) >= argc) {
1081 reply("MSG_MISSING_PARAMS", argv[0]);
1082 goto fail;
1083 }
1084 max_length = strtoul(argv[++arg], NULL, 0);
1085 if(max_length < 20 || max_length > 450)
1086 {
1087 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1088 goto fail;
1089 }
1090 if(existed && (max_length < ntype->max_length))
1091 {
1092 ntype->max_length = max_length;
1093 chanserv_truncate_notes(ntype);
1094 }
1095 ntype->max_length = max_length;
1096
1097 if(existed)
1098 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1099 else
1100 reply("CSMSG_NOTE_CREATED", ntype->name);
1101 return 1;
1102
1103fail:
1104 if(!existed)
1105 dict_remove(note_types, ntype->name);
1106 return 0;
1107}
1108
1109static MODCMD_FUNC(cmd_removenote) {
1110 struct note_type *ntype;
1111 int force;
1112
1113 ntype = dict_find(note_types, argv[1], NULL);
1114 force = (argc > 2) && !irccasecmp(argv[2], "force");
1115 if(!ntype)
1116 {
1117 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1118 return 0;
1119 }
1120 if(ntype->refs > 1)
1121 {
1122 if(!force)
1123 {
1124 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1125 return 0;
1126 }
1127 chanserv_flush_note_type(ntype);
1128 }
1129 dict_remove(note_types, argv[1]);
1130 reply("CSMSG_NOTE_DELETED", argv[1]);
1131 return 1;
1132}
1133
1134static int
1135mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1136{
1137 if(!orig)
1138 return 0;
1139 if(orig->modes_set & change->modes_clear)
1140 return 1;
1141 if(orig->modes_clear & change->modes_set)
1142 return 1;
1143 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1144 && strcmp(orig->new_key, change->new_key))
1145 return 1;
1146 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1147 && (orig->new_limit != change->new_limit))
1148 return 1;
1149 return 0;
1150}
1151
1152static char max_length_text[MAXLEN+1][16];
1153
1154static struct helpfile_expansion
1155chanserv_expand_variable(const char *variable)
1156{
1157 struct helpfile_expansion exp;
1158
1159 if(!irccasecmp(variable, "notes"))
1160 {
1161 dict_iterator_t it;
1162 exp.type = HF_TABLE;
1163 exp.value.table.length = 1;
1164 exp.value.table.width = 3;
1165 exp.value.table.flags = 0;
1166 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1167 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1168 exp.value.table.contents[0][0] = "Note Type";
1169 exp.value.table.contents[0][1] = "Visibility";
1170 exp.value.table.contents[0][2] = "Max Length";
1171 for(it=dict_first(note_types); it; it=iter_next(it))
1172 {
1173 struct note_type *ntype = iter_data(it);
1174 int row;
1175
1176 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1177 row = exp.value.table.length++;
1178 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1179 exp.value.table.contents[row][0] = ntype->name;
1180 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1181 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1182 "unknown";
1183 if(!max_length_text[ntype->max_length][0])
1184 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1185 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1186 }
1187 return exp;
1188 }
1189
1190 exp.type = HF_STRING;
1191 exp.value.str = NULL;
1192 return exp;
1193}
1194
1195static struct chanData*
1196register_channel(struct chanNode *cNode, char *registrar)
1197{
1198 struct chanData *channel;
1199 enum levelOption lvlOpt;
1200 enum charOption chOpt;
1201
1202 channel = calloc(1, sizeof(struct chanData));
1203
1204 channel->notes = dict_new();
1205 dict_set_free_data(channel->notes, chanserv_free_note);
1206
1207 channel->registrar = strdup(registrar);
1208 channel->registered = now;
1209 channel->visited = now;
1210 channel->limitAdjusted = now;
a32da4c7 1211 channel->ownerTransfer = now;
d76ed9a9
AS
1212 channel->flags = CHANNEL_DEFAULT_FLAGS;
1213 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1214 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1215 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1216 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1217
1218 channel->prev = NULL;
1219 channel->next = channelList;
1220
1221 if(channelList)
1222 channelList->prev = channel;
1223 channelList = channel;
1224 registered_channels++;
1225
1226 channel->channel = cNode;
1227 LockChannel(cNode);
1228 cNode->channel_info = channel;
1229
1230 return channel;
1231}
1232
1233static struct userData*
1234add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1235{
1236 struct userData *ud;
1237
1238 if(access > UL_OWNER)
1239 return NULL;
1240
1241 ud = calloc(1, sizeof(*ud));
1242 ud->channel = channel;
1243 ud->handle = handle;
1244 ud->seen = seen;
1245 ud->access = access;
1246 ud->info = info ? strdup(info) : NULL;
1247
1248 ud->prev = NULL;
1249 ud->next = channel->users;
1250 if(channel->users)
1251 channel->users->prev = ud;
1252 channel->users = ud;
1253
1254 channel->userCount++;
1255 userCount++;
1256
1257 ud->u_prev = NULL;
1258 ud->u_next = ud->handle->channels;
1259 if(ud->u_next)
1260 ud->u_next->u_prev = ud;
1261 ud->handle->channels = ud;
1262
c8ca69a0 1263 ud->flags = USER_FLAGS_DEFAULT;
d76ed9a9
AS
1264 return ud;
1265}
1266
1267static void unregister_channel(struct chanData *channel, const char *reason);
1268
1269void
1270del_channel_user(struct userData *user, int do_gc)
1271{
1272 struct chanData *channel = user->channel;
1273
1274 channel->userCount--;
1275 userCount--;
1276
1277 if(user->prev)
1278 user->prev->next = user->next;
1279 else
1280 channel->users = user->next;
1281 if(user->next)
1282 user->next->prev = user->prev;
1283
1284 if(user->u_prev)
1285 user->u_prev->u_next = user->u_next;
1286 else
1287 user->handle->channels = user->u_next;
1288 if(user->u_next)
1289 user->u_next->u_prev = user->u_prev;
1290
1291 free(user->info);
1292 free(user);
1293 if(do_gc && !channel->users && !IsProtected(channel))
1294 unregister_channel(channel, "lost all users.");
1295}
1296
ac3bdc8d
AS
1297static struct adduserPending*
1298add_adduser_pending(struct chanNode *channel, struct userNode *user, int level)
1299{
1300 struct adduserPending *ap;
1301 ap = calloc(1,sizeof(struct adduserPending));
1302 ap->channel = channel;
1303 ap->user = user;
1304 ap->level = level;
1305 ap->created = time(NULL);
1306
1307 /* ap->prev defaults to NULL already.. */
1308 ap->next = adduser_pendings;
1309 if(adduser_pendings)
1310 adduser_pendings->prev = ap;
1311 adduser_pendings = ap;
1312 adduser_pendings_count++;
1313 return(ap);
1314}
1315
1316static void
1317del_adduser_pending(struct adduserPending *ap)
1318{
1319 if(ap->prev)
1320 ap->prev->next = ap->next;
1321 else
1322 adduser_pendings = ap->next;
1323
1324 if(ap->next)
1325 ap->next->prev = ap->prev;
1326 free(ap);
1327}
1328
1329static void expire_adduser_pending();
1330
1331/* find_adduser_pending(channel, user) will find an arbitrary record
1332 * from user, channel, or user and channel.
1333 * if user or channel are NULL, they will match any records.
1334 */
1335static struct adduserPending*
1336find_adduser_pending(struct chanNode *channel, struct userNode *user)
1337{
1338 struct adduserPending *ap;
1339
1340 expire_adduser_pending(); /* why not here.. */
1341
1342 if(!channel && !user) /* 2 nulls matches all */
1343 return(adduser_pendings);
1344 for(ap = adduser_pendings;ap;ap = ap->next)
1345 {
1346 if((channel == ap->channel && (user == NULL || user == ap->user)) || (user==ap->user && channel==NULL))
1347 return ap;
1348 }
1349 return NULL;
1350}
1351
1352
1353/* Remove all pendings for a user or channel
1354 *
1355 * called in nickserv.c DelUser() and proto-* unregister_channel()
1356 */
1357void
1358wipe_adduser_pending(struct chanNode *channel, struct userNode *user)
1359{
1360 struct adduserPending *ap;
1361
1362 /* So this is a bit wastefull, i hate dealing with linked lists.
1363 * if its a problem we'll rewrite it right */
1364 while((ap = find_adduser_pending(channel, user))) {
1365 del_adduser_pending(ap);
1366 }
1367}
1368
1369/* Called from nickserv.c cmd_auth after someone auths */
1370void
1371process_adduser_pending(struct userNode *user)
1372{
1373 struct adduserPending *ap;
a03d6c77
AS
1374 if(!user->handle_info)
1375 return; /* not associated with an account */
ac3bdc8d
AS
1376 while((ap = find_adduser_pending(NULL, user)))
1377 {
1378 struct userData *actee;
5ca901c3
AS
1379 if(GetTrueChannelAccess(ap->channel->channel_info, ap->user->handle_info))
1380 {
1381 /* Already on the userlist. do nothing*/
1382 }
1383 else
1384 {
1385 actee = add_channel_user(ap->channel->channel_info, ap->user->handle_info, ap->level, 0, NULL);
1386 scan_user_presence(actee, NULL);
1387 }
ac3bdc8d
AS
1388 del_adduser_pending(ap);
1389 }
1390}
1391
1392static void
1393expire_adduser_pending()
1394{
1395 struct adduserPending *ap, *ap_next;
1396 ap = adduser_pendings;
1397 while(ap)
1398 {
1399 if((ap->created + ADDUSER_PENDING_EXPIRE) < time(NULL))
1400 { /* expire it */
1401 ap_next = ap->next; /* save next */
1402 del_adduser_pending(ap); /* free and relink */
1403 ap = ap_next; /* advance */
1404 }
1405 else
1406 ap = ap->next;
1407 }
1408}
1409
d76ed9a9
AS
1410static void expire_ban(void *data);
1411
1412static struct banData*
1413add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1414{
1415 struct banData *bd;
1416 unsigned int ii, l1, l2;
1417
1418 if(!mask)
1419 return NULL;
1420
1421 bd = malloc(sizeof(struct banData));
1422
1423 bd->channel = channel;
1424 bd->set = set;
1425 bd->triggered = triggered;
1426 bd->expires = expires;
1427
1428 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1429 {
1430 extern const char *hidden_host_suffix;
1431 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1432 char *new_mask;
1433
1434 l1 = strlen(mask);
1435 l2 = strlen(old_name);
1436 if(l2+2 > l1)
1437 continue;
1438 if(irccasecmp(mask + l1 - l2, old_name))
1439 continue;
1440 new_mask = alloca(MAXLEN);
a32da4c7 1441 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
d76ed9a9
AS
1442 mask = new_mask;
1443 }
1444 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1445 if(owner)
1446 safestrncpy(bd->owner, owner, sizeof(bd->owner));
ec1a68c8 1447 bd->reason = strdup(reason);
d76ed9a9
AS
1448
1449 if(expires)
1450 timeq_add(expires, expire_ban, bd);
1451
1452 bd->prev = NULL;
c8273589 1453 bd->next = channel->bans; /* lamers */
d76ed9a9
AS
1454 if(channel->bans)
1455 channel->bans->prev = bd;
1456 channel->bans = bd;
1457 channel->banCount++;
1458 banCount++;
1459
1460 return bd;
1461}
1462
1463static void
1464del_channel_ban(struct banData *ban)
1465{
1466 ban->channel->banCount--;
1467 banCount--;
1468
1469 if(ban->prev)
1470 ban->prev->next = ban->next;
1471 else
1472 ban->channel->bans = ban->next;
1473
1474 if(ban->next)
1475 ban->next->prev = ban->prev;
1476
1477 if(ban->expires)
1478 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1479
1480 if(ban->reason)
1481 free(ban->reason);
1482
1483 free(ban);
1484}
1485
1486static void
c8273589 1487expire_ban(void *data) /* lamer.. */
d76ed9a9
AS
1488{
1489 struct banData *bd = data;
1490 if(!IsSuspended(bd->channel))
1491 {
1492 struct banList bans;
1493 struct mod_chanmode change;
1494 unsigned int ii;
1495 bans = bd->channel->channel->banlist;
1496 mod_chanmode_init(&change);
1497 for(ii=0; ii<bans.used; ii++)
1498 {
1499 if(!strcmp(bans.list[ii]->ban, bd->mask))
1500 {
1501 change.argc = 1;
1502 change.args[0].mode = MODE_REMOVE|MODE_BAN;
a32da4c7 1503 change.args[0].u.hostmask = bd->mask;
d76ed9a9
AS
1504 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1505 break;
1506 }
1507 }
1508 }
1509 bd->expires = 0;
1510 del_channel_ban(bd);
1511}
1512
1513static void chanserv_expire_suspension(void *data);
1514
1515static void
1516unregister_channel(struct chanData *channel, const char *reason)
1517{
1518 struct mod_chanmode change;
1519 char msgbuf[MAXLEN];
1520
1521 /* After channel unregistration, the following must be cleaned
1522 up:
1523 - Channel information.
1524 - Channel users.
c8273589 1525 - Channel bans. (lamers)
d76ed9a9 1526 - Channel suspension data.
ac3bdc8d 1527 - adduser_pending data.
d76ed9a9
AS
1528 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1529 */
1530
1531 if(!channel)
1532 return;
1533
1534 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1535
1117fc5a 1536 if(off_channel > 0)
a32da4c7 1537 {
1538 mod_chanmode_init(&change);
1539 change.modes_clear |= MODE_REGISTERED;
1540 mod_chanmode_announce(chanserv, channel->channel, &change);
1541 }
d76ed9a9 1542
ac3bdc8d
AS
1543 wipe_adduser_pending(channel->channel, NULL);
1544
d76ed9a9
AS
1545 while(channel->users)
1546 del_channel_user(channel->users, 0);
1547
1548 while(channel->bans)
1549 del_channel_ban(channel->bans);
1550
1551 free(channel->topic);
1552 free(channel->registrar);
1553 free(channel->greeting);
1554 free(channel->user_greeting);
1555 free(channel->topic_mask);
1556
1557 if(channel->prev)
1558 channel->prev->next = channel->next;
1559 else
1560 channelList = channel->next;
1561
1562 if(channel->next)
1563 channel->next->prev = channel->prev;
1564
1565 if(channel->suspended)
1566 {
1567 struct chanNode *cNode = channel->channel;
1568 struct suspended *suspended, *next_suspended;
1569
1570 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1571 {
1572 next_suspended = suspended->previous;
1573 free(suspended->suspender);
1574 free(suspended->reason);
1575 if(suspended->expires)
1576 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1577 free(suspended);
1578 }
1579
1580 if(cNode)
1581 cNode->channel_info = NULL;
1582 }
1583 channel->channel->channel_info = NULL;
1584
1585 dict_delete(channel->notes);
1586 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1587 if(!IsSuspended(channel))
1588 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1589 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1590 UnlockChannel(channel->channel);
1591 free(channel);
1592 registered_channels--;
1593}
1594
1595static void
1596expire_channels(UNUSED_ARG(void *data))
1597{
1598 struct chanData *channel, *next;
1599 struct userData *user;
1600 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1601
1602 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1603 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1604
1605 for(channel = channelList; channel; channel = next)
1606 {
1607 next = channel->next;
1608
1609 /* See if the channel can be expired. */
1610 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1611 || IsProtected(channel))
1612 continue;
1613
1614 /* Make sure there are no high-ranking users still in the channel. */
1615 for(user=channel->users; user; user=user->next)
1616 if(user->present && (user->access >= UL_PRESENT))
1617 break;
1618 if(user)
1619 continue;
1620
1621 /* Unregister the channel */
1622 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1623 unregister_channel(channel, "registration expired.");
1624 }
1625
1626 if(chanserv_conf.channel_expire_frequency)
1627 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1628}
1629
1630static int
1631protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1632{
1633 char protect = channel->chOpts[chProtect];
1634 struct userData *cs_victim, *cs_aggressor;
1635
1636 /* Don't protect if no one is to be protected, someone is attacking
1637 himself, or if the aggressor is an IRC Operator. */
4cf6e271 1638 if(protect == 'n' || victim == aggressor /* Opers dont get special treatment :/ || IsOper(aggressor) */)
d76ed9a9
AS
1639 return 0;
1640
1641 /* Don't protect if the victim isn't authenticated (because they
1642 can't be a channel user), unless we are to protect non-users
1643 also. */
1644 cs_victim = GetChannelAccess(channel, victim->handle_info);
1645 if(protect != 'a' && !cs_victim)
1646 return 0;
1647
1648 /* Protect if the aggressor isn't a user because at this point,
1649 the aggressor can only be less than or equal to the victim. */
1650 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1651 if(!cs_aggressor)
1652 return 1;
1653
1654 /* If the aggressor was a user, then the victim can't be helped. */
1655 if(!cs_victim)
1656 return 0;
1657
1658 switch(protect)
1659 {
1660 case 'l':
1661 if(cs_victim->access > cs_aggressor->access)
1662 return 1;
1663 break;
1664 case 'a':
1665 case 'e':
1666 if(cs_victim->access >= cs_aggressor->access)
1667 return 1;
1668 break;
1669 }
1670
1671 return 0;
1672}
1673
1674static int
1675validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1676{
1677 struct chanData *cData = channel->channel_info;
1678 struct userData *cs_victim;
1679
1680 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1681 || (cs_victim->access < UL_OP /* cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9
AS
1682 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1683 {
1684 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1685 return 0;
1686 }
1687
1688 return 1;
1689}
1690
55342ce8 1691static int
1692validate_halfop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1693{
1694 struct chanData *cData = channel->channel_info;
1695 struct userData *cs_victim;
1696
1697 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
b75e24a3 1698 || (cs_victim->access < UL_HALFOP /* cData->lvlOpts[lvlGiveHalfOps] */))
55342ce8 1699 && !check_user_level(channel, user, lvlEnfHalfOps, 0, 0))
1700 {
1701 send_message(user, chanserv, "CSMSG_HOPBY_LOCKED");
1702 return 0;
1703 }
1704
1705 return 1;
1706}
1707
1708
d76ed9a9
AS
1709static int
1710validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1711{
1712 if(IsService(victim))
1713 {
1714 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1715 return 0;
1716 }
1717
1718 if(protect_user(victim, user, channel->channel_info))
1719 {
1720 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1721 return 0;
1722 }
1723
1724 return 1;
1725}
1726
55342ce8 1727static int
1728validate_dehop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1729{
1730 if(IsService(victim))
1731 {
1732 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1733 return 0;
1734 }
1735
1736 if(protect_user(victim, user, channel->channel_info))
1737 {
1738 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1739 return 0;
1740 }
1741
1742 return 1;
1743}
1744
d76ed9a9
AS
1745static struct do_not_register *
1746chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1747{
1748 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1749 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1750 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1751 strcpy(dnr->reason, reason);
1752 dnr->set = now;
1753 if(dnr->chan_name[0] == '*')
1754 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1755 else if(strpbrk(dnr->chan_name, "*?"))
1756 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1757 else
1758 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1759 return dnr;
1760}
1761
1762static struct dnrList
1117fc5a 1763chanserv_find_dnrs(const char *chan_name, const char *handle)
d76ed9a9
AS
1764{
1765 struct dnrList list;
1766 dict_iterator_t it;
1767 struct do_not_register *dnr;
1768
1769 dnrList_init(&list);
1117fc5a 1770 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
d76ed9a9
AS
1771 dnrList_append(&list, dnr);
1772 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1773 dnrList_append(&list, dnr);
1774 if(chan_name)
1775 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1776 if(match_ircglob(chan_name, iter_key(it)))
1777 dnrList_append(&list, iter_data(it));
1778 return list;
1779}
1780
1781static unsigned int
1117fc5a 1782chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
d76ed9a9
AS
1783{
1784 struct dnrList list;
1785 struct do_not_register *dnr;
1786 unsigned int ii;
1787 char buf[INTERVALLEN];
1788
1789 list = chanserv_find_dnrs(chan_name, handle);
1790 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1791 {
1792 dnr = list.list[ii];
1793 if(dnr->set)
1794 {
1795 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1796 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1797 }
1798 else
1799 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1800 }
1801 if(ii < list.used)
1802 reply("CSMSG_MORE_DNRS", list.used - ii);
1803 free(list.list);
1804 return ii;
1805}
1806
1807struct do_not_register *
1808chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1809{
1810 struct do_not_register *dnr;
1811 dict_iterator_t it;
1812
1813 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1814 return dnr;
1815 if(chan_name)
1816 {
1817 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1818 return dnr;
1819 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1820 if(match_ircglob(chan_name, iter_key(it)))
1821 return iter_data(it);
1822 }
1823 return NULL;
1824}
1825
1826static CHANSERV_FUNC(cmd_noregister)
1827{
1828 const char *target;
1829 struct do_not_register *dnr;
1830 char buf[INTERVALLEN];
1831 unsigned int matches;
1832
1833 if(argc < 2)
1834 {
1835 dict_iterator_t it;
1836
1837 reply("CSMSG_DNR_SEARCH_RESULTS");
cde20a3f
AS
1838 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
1839 reply("CSMSG_BAR");
d76ed9a9
AS
1840 matches = 0;
1841 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1842 {
1843 dnr = iter_data(it);
1844 if(dnr->set)
1845 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1846 else
1847 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1848 matches++;
1849 }
1850 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1851 {
1852 dnr = iter_data(it);
1853 if(dnr->set)
1854 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1855 else
1856 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1857 matches++;
1858 }
1859 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1860 {
1861 dnr = iter_data(it);
1862 if(dnr->set)
1863 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1864 else
1865 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1866 matches++;
1867 }
1868
1869 if(matches)
1870 reply("MSG_MATCH_COUNT", matches);
1871 else
1872 reply("MSG_NO_MATCHES");
1873 return 0;
1874 }
1875
1876 target = argv[1];
1877
1878 if(!IsChannelName(target) && (*target != '*'))
1879 {
1880 reply("CSMSG_NOT_DNR", target);
1881 return 0;
1882 }
1883
1884 if(argc > 2)
1885 {
1886 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1887 if((*target == '*') && !get_handle_info(target + 1))
1888 {
1889 reply("MSG_HANDLE_UNKNOWN", target + 1);
1890 return 0;
1891 }
1892 chanserv_add_dnr(target, user->handle_info->handle, reason);
1893 reply("CSMSG_NOREGISTER_CHANNEL", target);
1894 return 1;
1895 }
1896
1897 reply("CSMSG_DNR_SEARCH_RESULTS");
cde20a3f
AS
1898 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
1899 reply("CSMSG_BAR");
d76ed9a9 1900 if(*target == '*')
1117fc5a 1901 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
d76ed9a9
AS
1902 else
1903 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1904 if(!matches)
1905 reply("MSG_NO_MATCHES");
1906 return 0;
1907}
1908
1909static CHANSERV_FUNC(cmd_allowregister)
1910{
1911 const char *chan_name = argv[1];
1912
1913 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1914 {
1915 dict_remove(handle_dnrs, chan_name+1);
1916 reply("CSMSG_DNR_REMOVED", chan_name);
1917 }
1918 else if(dict_find(plain_dnrs, chan_name, NULL))
1919 {
1920 dict_remove(plain_dnrs, chan_name);
1921 reply("CSMSG_DNR_REMOVED", chan_name);
1922 }
1923 else if(dict_find(mask_dnrs, chan_name, NULL))
1924 {
1925 dict_remove(mask_dnrs, chan_name);
1926 reply("CSMSG_DNR_REMOVED", chan_name);
1927 }
1928 else
1929 {
1930 reply("CSMSG_NO_SUCH_DNR", chan_name);
1931 return 0;
1932 }
1933 return 1;
1934}
1935
1936unsigned int
1937chanserv_get_owned_count(struct handle_info *hi)
1938{
1939 struct userData *cList;
1940 unsigned int owned;
1941
1942 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1943 if(cList->access == UL_OWNER)
1944 owned++;
1945 return owned;
1946}
1947
1948static CHANSERV_FUNC(cmd_register)
1949{
d76ed9a9
AS
1950 struct handle_info *handle;
1951 struct chanData *cData;
1952 struct modeNode *mn;
1953 char reason[MAXLEN];
1954 char *chan_name;
1955 unsigned int new_channel, force=0;
1956 struct do_not_register *dnr;
f8f30e75
AS
1957 unsigned int n;
1958
d76ed9a9
AS
1959
1960 if(channel)
1961 {
1962 if(channel->channel_info)
1963 {
1964 reply("CSMSG_ALREADY_REGGED", channel->name);
1965 return 0;
1966 }
1967
1968 if(channel->bad_channel)
1969 {
1970 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1971 return 0;
1972 }
1973
f8f30e75 1974 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
d76ed9a9
AS
1975 {
1976 reply("CSMSG_MUST_BE_OPPED", channel->name);
1977 return 0;
1978 }
1979
1980 new_channel = 0;
1981 chan_name = channel->name;
1982 }
1983 else
1984 {
fc8798ec
AS
1985 if(argc < 2)
1986 {
1987 reply("MSG_MISSING_PARAMS", cmd->name);
567a5f26 1988 svccmd_send_help_brief(user, chanserv, cmd);
fc8798ec
AS
1989 return 0;
1990 }
1991 if(!IsChannelName(argv[1]))
d76ed9a9
AS
1992 {
1993 reply("MSG_NOT_CHANNEL_NAME");
1994 return 0;
1995 }
1996
1997 if(opserv_bad_channel(argv[1]))
1998 {
1999 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2000 return 0;
2001 }
2002
2003 new_channel = 1;
2004 chan_name = argv[1];
2005 }
2006
2007 if(argc >= (new_channel+2))
2008 {
2009 if(!IsHelping(user))
2010 {
2011 reply("CSMSG_PROXY_FORBIDDEN");
2012 return 0;
2013 }
2014
2015 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2016 return 0;
2017 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2018 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75
AS
2019
2020 /* Check if they are over the limit.. */
2021 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2022 {
2023 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2024 return 0;
2025 }
2026
d76ed9a9
AS
2027 }
2028 else
2029 {
f8f30e75 2030 handle = user->handle_info;
d76ed9a9 2031 dnr = chanserv_is_dnr(chan_name, handle);
f8f30e75
AS
2032 /* Check if they are over the limit.. */
2033 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2034 {
2035 reply("CSMSG_YOU_OWN_TOO_MANY", chanserv_conf.max_owned);
2036 return 0;
2037 }
2038 /* Check if another service is in the channel */
2039 if(channel)
2040 for(n = 0; n < channel->members.used; n++)
2041 {
2042 mn = channel->members.list[n];
2043 if((mn && mn->user && (mn->user->modes & FLAGS_SERVICE)) || IsLocal(mn->user))
2044 {
2045 reply("CSMSG_ANOTHER_SERVICE");
2046 return 0;
2047 }
2048 }
d76ed9a9
AS
2049 }
2050 if(dnr && !force)
2051 {
2052 if(!IsHelping(user))
2053 reply("CSMSG_DNR_CHANNEL", chan_name);
2054 else
1117fc5a 2055 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
d76ed9a9
AS
2056 return 0;
2057 }
2058
f8f30e75 2059 /* now handled above for message specilization *
d76ed9a9
AS
2060 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2061 {
2062 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2063 return 0;
2064 }
f8f30e75 2065 */
d76ed9a9
AS
2066
2067 if(new_channel)
2aef5f4b 2068 channel = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9
AS
2069
2070 cData = register_channel(channel, user->handle_info->handle);
2071 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2072 cData->modes = chanserv_conf.default_modes;
1117fc5a 2073 if(off_channel > 0)
a32da4c7 2074 cData->modes.modes_set |= MODE_REGISTERED;
ec1a68c8 2075 if (IsOffChannel(cData))
2076 {
2077 mod_chanmode_announce(chanserv, channel, &cData->modes);
2078 }
2079 else
2080 {
2081 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2082 change->args[change->argc].mode = MODE_CHANOP;
2083 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2084 change->argc++;
2085 mod_chanmode_announce(chanserv, channel, change);
2086 mod_chanmode_free(change);
2087 }
d76ed9a9
AS
2088
2089 /* Initialize the channel's max user record. */
2090 cData->max = channel->members.used;
2091
2092 if(handle != user->handle_info)
2093 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2094 else
2095 reply("CSMSG_REG_SUCCESS", channel->name);
2096
2097 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2098 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2099 return 1;
2100}
2101
2102static const char *
2103make_confirmation_string(struct userData *uData)
2104{
2105 static char strbuf[16];
2106 char *src;
2107 unsigned int accum;
2108
2109 accum = 0;
2110 for(src = uData->handle->handle; *src; )
2111 accum = accum * 31 + toupper(*src++);
2112 if(uData->channel)
2113 for(src = uData->channel->channel->name; *src; )
2114 accum = accum * 31 + toupper(*src++);
2115 sprintf(strbuf, "%08x", accum);
2116 return strbuf;
2117}
2118
2119static CHANSERV_FUNC(cmd_unregister)
2120{
2121 char *name;
2122 char reason[MAXLEN];
2123 struct chanData *cData;
2124 struct userData *uData;
2125
2126 cData = channel->channel_info;
2127 if(!cData)
2128 {
2129 reply("CSMSG_NOT_REGISTERED", channel->name);
2130 return 0;
2131 }
2132
2133 uData = GetChannelUser(cData, user->handle_info);
2134 if(!uData || (uData->access < UL_OWNER))
2135 {
2136 reply("CSMSG_NO_ACCESS");
2137 return 0;
2138 }
2139
2140 if(IsProtected(cData))
2141 {
2142 reply("CSMSG_UNREG_NODELETE", channel->name);
2143 return 0;
2144 }
2145
2146 if(!IsHelping(user))
2147 {
2148 const char *confirm_string;
2149 if(IsSuspended(cData))
2150 {
2151 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2152 return 0;
2153 }
2154 confirm_string = make_confirmation_string(uData);
2155 if((argc < 2) || strcmp(argv[1], confirm_string))
2156 {
fa0fac3e 2157 reply("CSMSG_CONFIRM_UNREG", channel->name, confirm_string);
d76ed9a9
AS
2158 return 0;
2159 }
2160 }
2161
2162 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2163 name = strdup(channel->name);
2164 unregister_channel(cData, reason);
2165 reply("CSMSG_UNREG_SUCCESS", name);
2166 free(name);
2167 return 1;
2168}
2169
2170static CHANSERV_FUNC(cmd_move)
2171{
1117fc5a 2172 struct mod_chanmode change;
d76ed9a9
AS
2173 struct chanNode *target;
2174 struct modeNode *mn;
2175 struct userData *uData;
2176 char reason[MAXLEN];
2177 struct do_not_register *dnr;
2178
2179 REQUIRE_PARAMS(2);
2180
2181 if(IsProtected(channel->channel_info))
2182 {
2183 reply("CSMSG_MOVE_NODELETE", channel->name);
2184 return 0;
2185 }
2186
2187 if(!IsChannelName(argv[1]))
2188 {
2189 reply("MSG_NOT_CHANNEL_NAME");
2190 return 0;
2191 }
2192
2193 if(opserv_bad_channel(argv[1]))
2194 {
2195 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2196 return 0;
2197 }
2198
2199 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2200 {
2201 for(uData = channel->channel_info->users; uData; uData = uData->next)
2202 {
2203 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2204 {
2205 if(!IsHelping(user))
2206 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2207 else
1117fc5a 2208 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
d76ed9a9
AS
2209 return 0;
2210 }
2211 }
2212 }
2213
1117fc5a 2214 mod_chanmode_init(&change);
d76ed9a9
AS
2215 if(!(target = GetChannel(argv[1])))
2216 {
2aef5f4b 2217 target = AddChannel(argv[1], now, NULL, NULL, NULL);
d76ed9a9
AS
2218 if(!IsSuspended(channel->channel_info))
2219 AddChannelUser(chanserv, target);
2220 }
2221 else if(target->channel_info)
2222 {
2223 reply("CSMSG_ALREADY_REGGED", target->name);
2224 return 0;
2225 }
2226 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2227 && !IsHelping(user))
2228 {
2229 reply("CSMSG_MUST_BE_OPPED", target->name);
2230 return 0;
2231 }
2232 else if(!IsSuspended(channel->channel_info))
2233 {
d76ed9a9
AS
2234 change.argc = 1;
2235 change.args[0].mode = MODE_CHANOP;
a32da4c7 2236 change.args[0].u.member = AddChannelUser(chanserv, target);
d76ed9a9
AS
2237 mod_chanmode_announce(chanserv, target, &change);
2238 }
2239
1117fc5a 2240 if(off_channel > 0)
2241 {
2242 /* Clear MODE_REGISTERED from old channel, add it to new. */
2243 change.argc = 0;
2244 change.modes_clear = MODE_REGISTERED;
2245 mod_chanmode_announce(chanserv, channel, &change);
2246 change.modes_clear = 0;
2247 change.modes_set = MODE_REGISTERED;
2248 mod_chanmode_announce(chanserv, target, &change);
2249 }
2250
d76ed9a9
AS
2251 /* Move the channel_info to the target channel; it
2252 shouldn't be necessary to clear timeq callbacks
2253 for the old channel. */
2254 target->channel_info = channel->channel_info;
2255 target->channel_info->channel = target;
2256 channel->channel_info = NULL;
2257
2258 reply("CSMSG_MOVE_SUCCESS", target->name);
2259
2260 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2261 if(!IsSuspended(target->channel_info))
2262 {
2263 char reason2[MAXLEN];
2264 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2265 DelChannelUser(chanserv, channel, reason2, 0);
2266 }
2267 UnlockChannel(channel);
2268 LockChannel(target);
2269 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2270 return 1;
2271}
2272
2273static void
2274merge_users(struct chanData *source, struct chanData *target)
2275{
2276 struct userData *suData, *tuData, *next;
2277 dict_iterator_t it;
2278 dict_t merge;
2279
2280 merge = dict_new();
2281
2282 /* Insert the source's users into the scratch area. */
2283 for(suData = source->users; suData; suData = suData->next)
2284 dict_insert(merge, suData->handle->handle, suData);
2285
2286 /* Iterate through the target's users, looking for
2287 users common to both channels. The lower access is
2288 removed from either the scratch area or target user
2289 list. */
2290 for(tuData = target->users; tuData; tuData = next)
2291 {
2292 struct userData *choice;
2293
2294 next = tuData->next;
2295
2296 /* If a source user exists with the same handle as a target
2297 channel's user, resolve the conflict by removing one. */
2298 suData = dict_find(merge, tuData->handle->handle, NULL);
2299 if(!suData)
2300 continue;
2301
2302 /* Pick the data we want to keep. */
2303 /* If the access is the same, use the later seen time. */
2304 if(suData->access == tuData->access)
2305 choice = (suData->seen > tuData->seen) ? suData : tuData;
2306 else /* Otherwise, keep the higher access level. */
2307 choice = (suData->access > tuData->access) ? suData : tuData;
2308
2309 /* Remove the user that wasn't picked. */
2310 if(choice == tuData)
2311 {
2312 dict_remove(merge, suData->handle->handle);
2313 del_channel_user(suData, 0);
2314 }
2315 else
2316 del_channel_user(tuData, 0);
2317 }
2318
2319 /* Move the remaining users to the target channel. */
2320 for(it = dict_first(merge); it; it = iter_next(it))
2321 {
2322 suData = iter_data(it);
2323
2324 /* Insert the user into the target channel's linked list. */
2325 suData->prev = NULL;
2326 suData->next = target->users;
2327 suData->channel = target;
2328
2329 if(target->users)
2330 target->users->prev = suData;
2331 target->users = suData;
2332
2333 /* Update the user counts for the target channel; the
2334 source counts are left alone. */
2335 target->userCount++;
2336 }
2337
2338 /* Possible to assert (source->users == NULL) here. */
2339 source->users = NULL;
2340 dict_delete(merge);
2341}
2342
2343static void
2344merge_bans(struct chanData *source, struct chanData *target)
2345{
2346 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2347
2348 /* Hold on to the original head of the target ban list
2349 to avoid comparing source bans with source bans. */
2350 tFront = target->bans;
2351
2352 /* Perform a totally expensive O(n*m) merge, ick. */
2353 for(sbData = source->bans; sbData; sbData = sNext)
2354 {
2355 /* Flag to track whether the ban's been moved
2356 to the destination yet. */
2357 int moved = 0;
2358
2359 /* Possible to assert (sbData->prev == NULL) here. */
2360 sNext = sbData->next;
2361
2362 for(tbData = tFront; tbData; tbData = tNext)
2363 {
2364 tNext = tbData->next;
2365
2366 /* Perform two comparisons between each source
2367 and target ban, conflicts are resolved by
2368 keeping the broader ban and copying the later
2369 expiration and triggered time. */
2370 if(match_ircglobs(tbData->mask, sbData->mask))
2371 {
2372 /* There is a broader ban in the target channel that
2373 overrides one in the source channel; remove the
2374 source ban and break. */
2375 if(sbData->expires > tbData->expires)
2376 tbData->expires = sbData->expires;
2377 if(sbData->triggered > tbData->triggered)
2378 tbData->triggered = sbData->triggered;
2379 del_channel_ban(sbData);
2380 break;
2381 }
2382 else if(match_ircglobs(sbData->mask, tbData->mask))
2383 {
2384 /* There is a broader ban in the source channel that
2385 overrides one in the target channel; remove the
2386 target ban, fall through and move the source over. */
2387 if(tbData->expires > sbData->expires)
2388 sbData->expires = tbData->expires;
2389 if(tbData->triggered > sbData->triggered)
2390 sbData->triggered = tbData->triggered;
2391 if(tbData == tFront)
2392 tFront = tNext;
2393 del_channel_ban(tbData);
2394 }
2395
2396 /* Source bans can override multiple target bans, so
2397 we allow a source to run through this loop multiple
2398 times, but we can only move it once. */
2399 if(moved)
2400 continue;
2401 moved = 1;
2402
2403 /* Remove the source ban from the source ban list. */
2404 if(sbData->next)
2405 sbData->next->prev = sbData->prev;
2406
2407 /* Modify the source ban's associated channel. */
2408 sbData->channel = target;
2409
2410 /* Insert the ban into the target channel's linked list. */
2411 sbData->prev = NULL;
2412 sbData->next = target->bans;
2413
2414 if(target->bans)
2415 target->bans->prev = sbData;
2416 target->bans = sbData;
2417
2418 /* Update the user counts for the target channel. */
2419 target->banCount++;
2420 }
2421 }
2422
2423 /* Possible to assert (source->bans == NULL) here. */
2424 source->bans = NULL;
2425}
2426
2427static void
2428merge_data(struct chanData *source, struct chanData *target)
2429{
697f4c9a 2430 /* Use more recent visited and owner-transfer time; use older
2431 * registered time. Bitwise or may_opchan. Use higher max.
2432 * Do not touch last_refresh, ban count or user counts.
2433 */
d76ed9a9
AS
2434 if(source->visited > target->visited)
2435 target->visited = source->visited;
697f4c9a 2436 if(source->registered < target->registered)
2437 target->registered = source->registered;
2438 if(source->ownerTransfer > target->ownerTransfer)
2439 target->ownerTransfer = source->ownerTransfer;
2440 if(source->may_opchan)
2441 target->may_opchan = 1;
2442 if(source->max > target->max)
2443 target->max = source->max;
d76ed9a9
AS
2444}
2445
2446static void
2447merge_channel(struct chanData *source, struct chanData *target)
2448{
2449 merge_users(source, target);
2450 merge_bans(source, target);
2451 merge_data(source, target);
2452}
2453
2454static CHANSERV_FUNC(cmd_merge)
2455{
2456 struct userData *target_user;
2457 struct chanNode *target;
2458 char reason[MAXLEN];
2459
2460 REQUIRE_PARAMS(2);
2461
2462 /* Make sure the target channel exists and is registered to the user
2463 performing the command. */
2464 if(!(target = GetChannel(argv[1])))
2465 {
2466 reply("MSG_INVALID_CHANNEL");
2467 return 0;
2468 }
2469
2470 if(!target->channel_info)
2471 {
2472 reply("CSMSG_NOT_REGISTERED", target->name);
2473 return 0;
2474 }
2475
2476 if(IsProtected(channel->channel_info))
2477 {
2478 reply("CSMSG_MERGE_NODELETE");
2479 return 0;
2480 }
2481
2482 if(IsSuspended(target->channel_info))
2483 {
2484 reply("CSMSG_MERGE_SUSPENDED");
2485 return 0;
2486 }
2487
2488 if(channel == target)
2489 {
2490 reply("CSMSG_MERGE_SELF");
2491 return 0;
2492 }
2493
2494 target_user = GetChannelUser(target->channel_info, user->handle_info);
2495 if(!target_user || (target_user->access < UL_OWNER))
2496 {
2497 reply("CSMSG_MERGE_NOT_OWNER");
2498 return 0;
2499 }
2500
2501 /* Merge the channel structures and associated data. */
2502 merge_channel(channel->channel_info, target->channel_info);
2503 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2504 unregister_channel(channel->channel_info, reason);
2505 reply("CSMSG_MERGE_SUCCESS", target->name);
2506 return 1;
2507}
2508
2509static CHANSERV_FUNC(cmd_opchan)
2510{
2511 struct mod_chanmode change;
2512 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2513 {
2514 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2515 return 0;
2516 }
2517 channel->channel_info->may_opchan = 0;
2518 mod_chanmode_init(&change);
2519 change.argc = 1;
2520 change.args[0].mode = MODE_CHANOP;
a32da4c7 2521 change.args[0].u.member = GetUserMode(channel, chanserv);
d76ed9a9
AS
2522 mod_chanmode_announce(chanserv, channel, &change);
2523 reply("CSMSG_OPCHAN_DONE", channel->name);
2524 return 1;
2525}
2526
2527static CHANSERV_FUNC(cmd_adduser)
2528{
2529 struct userData *actee;
2530 struct userData *actor;
2531 struct handle_info *handle;
2532 unsigned short access;
2533
2534 REQUIRE_PARAMS(3);
2535
2536 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2537 {
2538 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2539 return 0;
2540 }
2541
2542 access = user_level_from_name(argv[2], UL_OWNER);
2543 if(!access)
2544 {
2545 reply("CSMSG_INVALID_ACCESS", argv[2]);
2546 return 0;
2547 }
2548
2549 actor = GetChannelUser(channel->channel_info, user->handle_info);
2550 if(actor->access <= access)
2551 {
2552 reply("CSMSG_NO_BUMP_ACCESS");
2553 return 0;
2554 }
2555
2556 if(!(handle = modcmd_get_handle_info(user, argv[1])))
ac3bdc8d
AS
2557 {
2558 // 'kevin must first authenticate with AuthServ.' is sent to user
2559 struct userNode *unode;
2560 unode = GetUserH(argv[1]); /* find user struct by nick */
2561 if(unode)
2562 {
2563 if(find_adduser_pending(channel, unode)) {
2564 reply("CSMSG_ADDUSER_PENDING_ALREADY", channel->name);
2565 }
2566 else {
2567 if(IsInChannel(channel, unode)) {
2568 reply("CSMSG_ADDUSER_PENDING");
2569 add_adduser_pending(channel, unode, access);
2570 send_message_type(1,unode, chanserv, "CSMSG_ADDUSER_PENDING_TARGET", user->nick, channel->name);
2571 }
2572 /* this results in user must auth AND not in chan errors. too confusing..
2573 else {
2574 reply("CSMSG_ADDUSER_PENDING_NOTINCHAN", channel->name);
2575 }
2576 */
2577 }
2578 }
d76ed9a9 2579 return 0;
ac3bdc8d 2580 }
d76ed9a9
AS
2581
2582 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2583 {
a915f7d4 2584 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, user_level_name_from_level(actee->access));
d76ed9a9
AS
2585 return 0;
2586 }
2587
2588 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2589 scan_user_presence(actee, NULL);
de9510bc 2590 reply("CSMSG_ADDED_USER", handle->handle, channel->name, user_level_name_from_level(access), access);
d76ed9a9
AS
2591 return 1;
2592}
2593
2594static CHANSERV_FUNC(cmd_clvl)
2595{
2596 struct handle_info *handle;
2597 struct userData *victim;
2598 struct userData *actor;
2599 unsigned short new_access;
2600 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2601
2602 REQUIRE_PARAMS(3);
2603
2604 actor = GetChannelUser(channel->channel_info, user->handle_info);
2605
2606 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2607 return 0;
2608
2609 if(handle == user->handle_info && !privileged)
2610 {
2611 reply("CSMSG_NO_SELF_CLVL");
2612 return 0;
2613 }
2614
2615 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2616 {
2617 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2618 return 0;
2619 }
2620
2621 if(actor->access <= victim->access && !privileged)
2622 {
2623 reply("MSG_USER_OUTRANKED", handle->handle);
2624 return 0;
2625 }
2626
2627 new_access = user_level_from_name(argv[2], UL_OWNER);
2628
2629 if(!new_access)
2630 {
2631 reply("CSMSG_INVALID_ACCESS", argv[2]);
2632 return 0;
2633 }
2634
2635 if(new_access >= actor->access && !privileged)
2636 {
2637 reply("CSMSG_NO_BUMP_ACCESS");
2638 return 0;
2639 }
2640
2641 victim->access = new_access;
de9510bc 2642 reply("CSMSG_CHANGED_ACCESS", handle->handle, user_level_name_from_level(new_access), new_access, channel->name);
d76ed9a9
AS
2643 return 1;
2644}
2645
2646static CHANSERV_FUNC(cmd_deluser)
2647{
2648 struct handle_info *handle;
2649 struct userData *victim;
2650 struct userData *actor;
2651 unsigned short access;
2652 char *chan_name;
2653
2654 REQUIRE_PARAMS(2);
2655
2656 actor = GetChannelUser(channel->channel_info, user->handle_info);
2657
2658 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2659 return 0;
2660
2661 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2662 {
2663 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2664 return 0;
2665 }
2666
2667 if(argc > 2)
2668 {
2669 access = user_level_from_name(argv[1], UL_OWNER);
2670 if(!access)
2671 {
2672 reply("CSMSG_INVALID_ACCESS", argv[1]);
2673 return 0;
2674 }
2675 if(access != victim->access)
2676 {
a915f7d4 2677 reply("CSMSG_INCORRECT_ACCESS", handle->handle, user_level_name_from_level(victim->access), argv[1]);
d76ed9a9
AS
2678 return 0;
2679 }
2680 }
2681 else
2682 {
2683 access = victim->access;
2684 }
2685
2686 if((actor->access <= victim->access) && !IsHelping(user))
2687 {
2688 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2689 return 0;
2690 }
2691
2692 chan_name = strdup(channel->name);
2693 del_channel_user(victim, 1);
2694 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2695 free(chan_name);
2696 return 1;
2697}
2698
2699static int
2700cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2701{
2702 struct userData *actor, *uData, *next;
2703
2704 actor = GetChannelUser(channel->channel_info, user->handle_info);
2705
2706 if(min_access > max_access)
2707 {
2708 reply("CSMSG_BAD_RANGE", min_access, max_access);
2709 return 0;
2710 }
2711
2712 if((actor->access <= max_access) && !IsHelping(user))
2713 {
2714 reply("CSMSG_NO_ACCESS");
2715 return 0;
2716 }
2717
2718 for(uData = channel->channel_info->users; uData; uData = next)
2719 {
2720 next = uData->next;
2721
2722 if((uData->access >= min_access)
2723 && (uData->access <= max_access)
2724 && match_ircglob(uData->handle->handle, mask))
2725 del_channel_user(uData, 1);
2726 }
2727
2728 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2729 return 1;
2730}
2731
2732static CHANSERV_FUNC(cmd_mdelowner)
2733{
2734 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2735}
2736
2737static CHANSERV_FUNC(cmd_mdelcoowner)
2738{
2739 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2740}
2741
4048352e 2742static CHANSERV_FUNC(cmd_mdelmanager)
d76ed9a9 2743{
4048352e 2744 return cmd_mdel_user(user, channel, UL_MANAGER, UL_MANAGER, argv[1], cmd);
d76ed9a9
AS
2745}
2746
2747static CHANSERV_FUNC(cmd_mdelop)
2748{
2749 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2750}
2751
2752static CHANSERV_FUNC(cmd_mdelpeon)
2753{
2754 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2755}
2756
55342ce8 2757static CHANSERV_FUNC(cmd_mdelhalfop)
2758{
2759 return cmd_mdel_user(user, channel, UL_HALFOP, UL_HALFOP, argv[1], cmd);
2760}
2761
2762
c8273589 2763/* trim_lamers.. */
d76ed9a9
AS
2764static int
2765cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2766{
2767 struct banData *bData, *next;
2768 char interval[INTERVALLEN];
2769 unsigned int count;
2770 time_t limit;
2771
2772 count = 0;
2773 limit = now - duration;
2774 for(bData = channel->channel_info->bans; bData; bData = next)
2775 {
2776 next = bData->next;
2777
2778 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2779 continue;
2780
2781 del_channel_ban(bData);
2782 count++;
2783 }
2784
2785 intervalString(interval, duration, user->handle_info);
c8273589 2786 send_message(user, chanserv, "CSMSG_TRIMMED_LAMERS", count, channel->name, interval);
d76ed9a9
AS
2787 return 1;
2788}
2789
2790static int
2f61d1d7 2791cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
d76ed9a9
AS
2792{
2793 struct userData *actor, *uData, *next;
2794 char interval[INTERVALLEN];
2795 unsigned int count;
2796 time_t limit;
2797
2798 actor = GetChannelUser(channel->channel_info, user->handle_info);
2799 if(min_access > max_access)
2800 {
2801 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2802 return 0;
2803 }
2804
2805 if((actor->access <= max_access) && !IsHelping(user))
2806 {
2807 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2808 return 0;
2809 }
2810
2811 count = 0;
2812 limit = now - duration;
2813 for(uData = channel->channel_info->users; uData; uData = next)
2814 {
2815 next = uData->next;
2816
2f61d1d7 2817 if((uData->seen > limit)
2818 || uData->present
2819 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
d76ed9a9
AS
2820 continue;
2821
2822 if(((uData->access >= min_access) && (uData->access <= max_access))
2823 || (!max_access && (uData->access < actor->access)))
2824 {
2825 del_channel_user(uData, 1);
2826 count++;
2827 }
2828 }
2829
2830 if(!max_access)
2831 {
2832 min_access = 1;
a32da4c7 2833 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
d76ed9a9
AS
2834 }
2835 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2836 return 1;
2837}
2838
2839static CHANSERV_FUNC(cmd_trim)
2840{
2841 unsigned long duration;
2842 unsigned short min_level, max_level;
2f61d1d7 2843 int vacation;
d76ed9a9
AS
2844
2845 REQUIRE_PARAMS(3);
2846
2f61d1d7 2847 vacation = argc > 3 && !strcmp(argv[3], "vacation");
d76ed9a9
AS
2848 duration = ParseInterval(argv[2]);
2849 if(duration < 60)
2850 {
2851 reply("CSMSG_CANNOT_TRIM");
2852 return 0;
2853 }
2854
c8273589 2855 if(!irccasecmp(argv[1], "lamers"))
d76ed9a9 2856 {
c8273589 2857 cmd_trim_bans(user, channel, duration); /* trim_lamers.. */
d76ed9a9
AS
2858 return 1;
2859 }
2860 else if(!irccasecmp(argv[1], "users"))
2861 {
2f61d1d7 2862 cmd_trim_users(user, channel, 0, 0, duration, vacation);
d76ed9a9
AS
2863 return 1;
2864 }
2865 else if(parse_level_range(&min_level, &max_level, argv[1]))
2866 {
2f61d1d7 2867 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
d76ed9a9
AS
2868 return 1;
2869 }
2870 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2871 {
2f61d1d7 2872 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
d76ed9a9
AS
2873 return 1;
2874 }
2875 else
2876 {
2877 reply("CSMSG_INVALID_TRIM", argv[1]);
2878 return 0;
2879 }
2880}
2881
2882/* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2883 to the user. cmd_all takes advantage of this. */
2884static CHANSERV_FUNC(cmd_up)
2885{
2886 struct mod_chanmode change;
2887 struct userData *uData;
2888 const char *errmsg;
2889
2890 mod_chanmode_init(&change);
2891 change.argc = 1;
a32da4c7 2892 change.args[0].u.member = GetUserMode(channel, user);
2893 if(!change.args[0].u.member)
d76ed9a9
AS
2894 {
2895 if(argc)
2896 reply("MSG_CHANNEL_ABSENT", channel->name);
2897 return 0;
2898 }
2899
2900 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2901 if(!uData)
2902 {
2903 if(argc)
2904 reply("CSMSG_GODMODE_UP", argv[0]);
2905 return 0;
2906 }
4b6129c0 2907 else if(uData->access >= UL_OP)
d76ed9a9
AS
2908 {
2909 change.args[0].mode = MODE_CHANOP;
2910 errmsg = "CSMSG_ALREADY_OPPED";
2911 }
4b6129c0 2912 else if(uData->access >= UL_HALFOP )
55342ce8 2913 {
2914 change.args[0].mode = MODE_HALFOP;
2915 errmsg = "CSMSG_ALREADY_HALFOPPED";
2916 }
4b6129c0 2917 else if(uData->access >= UL_PEON && (channel->channel_info->chOpts[chAutomode] != 'm' ))
d76ed9a9
AS
2918 {
2919 change.args[0].mode = MODE_VOICE;
2920 errmsg = "CSMSG_ALREADY_VOICED";
2921 }
2922 else
2923 {
2924 if(argc)
2925 reply("CSMSG_NO_ACCESS");
2926 return 0;
2927 }
a32da4c7 2928 change.args[0].mode &= ~change.args[0].u.member->modes;
d76ed9a9
AS
2929 if(!change.args[0].mode)
2930 {
2931 if(argc)
2932 reply(errmsg, channel->name);
2933 return 0;
2934 }
2935 modcmd_chanmode_announce(&change);
2936 return 1;
2937}
2938
2939static CHANSERV_FUNC(cmd_down)
2940{
2941 struct mod_chanmode change;
2942
2943 mod_chanmode_init(&change);
2944 change.argc = 1;
a32da4c7 2945 change.args[0].u.member = GetUserMode(channel, user);
2946 if(!change.args[0].u.member)
d76ed9a9
AS
2947 {
2948 if(argc)
2949 reply("MSG_CHANNEL_ABSENT", channel->name);
2950 return 0;
2951 }
2952
a32da4c7 2953 if(!change.args[0].u.member->modes)
d76ed9a9
AS
2954 {
2955 if(argc)
2956 reply("CSMSG_ALREADY_DOWN", channel->name);
2957 return 0;
2958 }
2959
a32da4c7 2960 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
d76ed9a9
AS
2961 modcmd_chanmode_announce(&change);
2962 return 1;
2963}
2964
2965static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2966{
2967 struct userData *cList;
2968
2969 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2970 {
2971 if(IsSuspended(cList->channel)
2972 || IsUserSuspended(cList)
2973 || !GetUserMode(cList->channel->channel, user))
2974 continue;
2975
2976 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2977 }
2978
2979 return 1;
2980}
2981
2982static CHANSERV_FUNC(cmd_upall)
2983{
2984 return cmd_all(CSFUNC_ARGS, cmd_up);
2985}
2986
2987static CHANSERV_FUNC(cmd_downall)
2988{
2989 return cmd_all(CSFUNC_ARGS, cmd_down);
2990}
2991
2992typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2993typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2994
2995static int
2996modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
2997{
2998 unsigned int ii, valid;
2999 struct userNode *victim;
3000 struct mod_chanmode *change;
3001
3002 change = mod_chanmode_alloc(argc - 1);
3003
3004 for(ii=valid=0; ++ii < argc; )
3005 {
3006 if(!(victim = GetUserH(argv[ii])))
3007 continue;
3008 change->args[valid].mode = mode;
a32da4c7 3009 change->args[valid].u.member = GetUserMode(channel, victim);
3010 if(!change->args[valid].u.member)
d76ed9a9
AS
3011 continue;
3012 if(validate && !validate(user, channel, victim))
3013 continue;
3014 valid++;
3015 }
3016
3017 change->argc = valid;
3018 if(valid < (argc-1))
3019 reply("CSMSG_PROCESS_FAILED");
3020 if(valid)
3021 {
3022 modcmd_chanmode_announce(change);
3023 reply(action, channel->name);
3024 }
3025 mod_chanmode_free(change);
3026 return 1;
3027}
3028
3029static CHANSERV_FUNC(cmd_op)
3030{
3031 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3032}
3033
55342ce8 3034static CHANSERV_FUNC(cmd_hop)
3035{
3036 return modify_users(CSFUNC_ARGS, validate_halfop, MODE_HALFOP, "CSMSG_HALFOPPED_USERS");
3037}
3038
d76ed9a9
AS
3039static CHANSERV_FUNC(cmd_deop)
3040{
3041 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3042}
3043
55342ce8 3044static CHANSERV_FUNC(cmd_dehop)
3045{
3046 return modify_users(CSFUNC_ARGS, validate_dehop, MODE_REMOVE|MODE_HALFOP, "CSMSG_DEHALFOPPED_USERS");
3047}
3048
d76ed9a9
AS
3049static CHANSERV_FUNC(cmd_voice)
3050{
3051 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3052}
3053
3054static CHANSERV_FUNC(cmd_devoice)
3055{
3056 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3057}
3058
3059static int
697f4c9a 3060bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
d76ed9a9
AS
3061{
3062 unsigned int ii;
3063
3064 if(victimCount)
3065 *victimCount = 0;
3066 for(ii=0; ii<channel->members.used; ii++)
3067 {
3068 struct modeNode *mn = channel->members.list[ii];
3069
3070 if(IsService(mn->user))
3071 continue;
3072
2f61d1d7 3073 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9
AS
3074 continue;
3075
3076 if(protect_user(mn->user, user, channel->channel_info))
3077 return 1;
3078
3079 if(victims)
3080 victims[(*victimCount)++] = mn;
3081 }
3082 return 0;
3083}
3084
3085static int
3086eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3087{
3088 struct userNode *victim;
3089 struct modeNode **victims;
3090 unsigned int offset, n, victimCount, duration = 0;
3091 char *reason = "Bye.", *ban, *name;
3092 char interval[INTERVALLEN];
3093
c8273589 3094 offset = (action & ACTION_ADD_TIMED_LAMER) ? 3 : 2;
d76ed9a9
AS
3095 REQUIRE_PARAMS(offset);
3096 if(argc > offset)
3097 {
3098 reason = unsplit_string(argv + offset, argc - offset, NULL);
3099 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3100 {
3101 /* Truncate the reason to a length of TOPICLEN, as
3102 the ircd does; however, leave room for an ellipsis
3103 and the kicker's nick. */
3104 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3105 }
3106 }
3107
3108 if((victim = GetUserH(argv[1])))
3109 {
3110 victims = alloca(sizeof(victims[0]));
3111 victims[0] = GetUserMode(channel, victim);
3112 /* XXX: The comparison with ACTION_KICK is just because all
3113 * other actions can work on users outside the channel, and we
3114 * want to allow those (e.g. unbans) in that case. If we add
3115 * some other ejection action for in-channel users, change
3116 * this too. */
3117 victimCount = victims[0] ? 1 : 0;
3118
3119 if(IsService(victim))
3120 {
35caf917
AS
3121 if(cmd)
3122 reply("MSG_SERVICE_IMMUNE", victim->nick);
d76ed9a9
AS
3123 return 0;
3124 }
3125
3126 if((action == ACTION_KICK) && !victimCount)
3127 {
35caf917
AS
3128 if(cmd)
3129 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
d76ed9a9
AS
3130 return 0;
3131 }
3132
3133 if(protect_user(victim, user, channel->channel_info))
3134 {
35caf917
AS
3135 // This translates to send_message(user, cmd->parent->bot, ...)
3136 // if user is x3 (ctcp action) cmd is null and segfault.
3137 if(cmd)
3138 reply("CSMSG_USER_PROTECTED", victim->nick);
d76ed9a9
AS
3139 return 0;
3140 }
3141
3142 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3143 name = victim->nick;
3144 }
3145 else
3146 {
3147 if(!is_ircmask(argv[1]))
3148 {
35caf917
AS
3149 if(cmd)
3150 reply("MSG_NICK_UNKNOWN", argv[1]);
d76ed9a9
AS
3151 return 0;
3152 }
3153
3154 victims = alloca(sizeof(victims[0]) * channel->members.used);
3155
3156 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3157 {
35caf917
AS
3158 if(cmd)
3159 reply("CSMSG_MASK_PROTECTED", argv[1]);
d76ed9a9
AS
3160 return 0;
3161 }
d1a65675
AS
3162/* If i want to ban *.nl and theres 5 of them, what is it to the bot?!?
3163// if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3164 And, ^^^^^^^^^ BAH!
3165 We use x2 style over-mask detection instead because it doesnt stop channel owners from doing
3166 reasonable bans, but does stop *@*, *@*a* *@*b* etc type masks. Yes, you can defeat it with
3167 some creativity, but its not x3's job to be the ban censor anyway. */
3168 if(is_overmask(argv[1]))
d76ed9a9 3169 {
35caf917
AS
3170 if(cmd)
3171 reply("CSMSG_LAME_MASK", argv[1]);
d76ed9a9
AS
3172 return 0;
3173 }
3174
3175 if((action == ACTION_KICK) && (victimCount == 0))
3176 {
35caf917
AS
3177 if(cmd)
3178 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
d76ed9a9
AS
3179 return 0;
3180 }
3181
3182 name = ban = strdup(argv[1]);
3183 }
3184
3185 /* Truncate the ban in place if necessary; we must ensure
3186 that 'ban' is a valid ban mask before sanitizing it. */
3187 sanitize_ircmask(ban);
3188
c8273589 3189 if(action & ACTION_ADD_LAMER)
d76ed9a9
AS
3190 {
3191 struct banData *bData, *next;
3192
c8273589 3193 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans) /* ..lamers.. */
d76ed9a9 3194 {
35caf917
AS
3195 if(cmd)
3196 reply("CSMSG_MAXIMUM_LAMERS", chanserv_conf.max_chan_bans); /* ..lamers.. */
d76ed9a9
AS
3197 free(ban);
3198 return 0;
3199 }
3200
c8273589 3201 if(action & ACTION_ADD_TIMED_LAMER)
d76ed9a9
AS
3202 {
3203 duration = ParseInterval(argv[2]);
3204
3205 if(duration < 15)
3206 {
35caf917
AS
3207 if(cmd)
3208 reply("CSMSG_DURATION_TOO_LOW");
d76ed9a9
AS
3209 free(ban);
3210 return 0;
3211 }
3212 else if(duration > (86400 * 365 * 2))
3213 {
35caf917
AS
3214 if(cmd)
3215 reply("CSMSG_DURATION_TOO_HIGH");
d76ed9a9
AS
3216 free(ban);
3217 return 0;
3218 }
3219 }
3220
c8273589 3221 /* lamers... */
d76ed9a9
AS
3222 for(bData = channel->channel_info->bans; bData; bData = next)
3223 {
3224 if(match_ircglobs(bData->mask, ban))
3225 {
3226 int exact = !irccasecmp(bData->mask, ban);
3227
3228 /* The ban is redundant; there is already a ban
3229 with the same effect in place. */
3230 if(exact)
3231 {
3232 if(bData->reason)
3233 free(bData->reason);
3234 bData->reason = strdup(reason);
3235 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3236 if(cmd)
3237 reply("CSMSG_REASON_CHANGE", ban);
3238 if(!bData->expires)
3239 goto post_add_ban;
3240 }
3241 if(exact && bData->expires)
3242 {
3243 int reset = 0;
3244
3245 /* If the ban matches an existing one exactly,
3246 extend the expiration time if the provided
3247 duration is longer. */
3248 if(duration && ((time_t)(now + duration) > bData->expires))
3249 {
3250 bData->expires = now + duration;
3251 reset = 1;
3252 }
3253 else if(!duration)
3254 {
3255 bData->expires = 0;
3256 reset = 1;
3257 }
3258
3259 if(reset)
3260 {
3261 /* Delete the expiration timeq entry and
3262 requeue if necessary. */
3263 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3264
3265 if(bData->expires)
3266 timeq_add(bData->expires, expire_ban, bData);
3267
3268 if(!cmd)
3269 {
35caf917 3270 /* automated kickban, dont reply */
d76ed9a9
AS
3271 }
3272 else if(duration)
c8273589 3273 reply("CSMSG_LAMER_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
d76ed9a9 3274 else
c8273589 3275 reply("CSMSG_LAMER_ADDED", name, channel->name);
d76ed9a9
AS
3276
3277 goto post_add_ban;
3278 }
3279 }
3280 if(cmd)
c8273589 3281 reply("CSMSG_REDUNDANT_LAMER", name, channel->name);
d76ed9a9
AS
3282
3283 free(ban);
3284 return 0;
3285 }
3286
3287 next = bData->next;
3288 if(match_ircglobs(ban, bData->mask))
3289 {
3290 /* The ban we are adding makes previously existing
3291 bans redundant; silently remove them. */
3292 del_channel_ban(bData);
3293 }
3294 }
3295
3296 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3297 free(ban);
3298 name = ban = strdup(bData->mask);
3299 }
3300 else if(ban)
3301 {
19fe6b99 3302 /* WHAT DOES THIS DO?? -Rubin */
d76ed9a9
AS
3303 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3304 {
3305 extern const char *hidden_host_suffix;
3306 const char *old_name = chanserv_conf.old_ban_names->list[n];
3307 char *new_mask;
3308 unsigned int l1, l2;
3309
3310 l1 = strlen(ban);
3311 l2 = strlen(old_name);
3312 if(l2+2 > l1)
3313 continue;
3314 if(irccasecmp(ban + l1 - l2, old_name))
3315 continue;
3316 new_mask = malloc(MAXLEN);
a32da4c7 3317 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
d76ed9a9
AS
3318 free(ban);
3319 name = ban = new_mask;
3320 }
3321 }
3322
3323 post_add_ban:
3324 if(action & ACTION_BAN)
3325 {
3326 unsigned int exists;
3327 struct mod_chanmode *change;
3328
3329 if(channel->banlist.used >= MAXBANS)
3330 {
3331 if(cmd)
3332 reply("CSMSG_BANLIST_FULL", channel->name);
3333 free(ban);
3334 return 0;
3335 }
3336
3337 exists = ChannelBanExists(channel, ban);
3338 change = mod_chanmode_alloc(victimCount + 1);
3339 for(n = 0; n < victimCount; ++n)
3340 {
55342ce8 3341 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_HALFOP|MODE_VOICE;
a32da4c7 3342 change->args[n].u.member = victims[n];
d76ed9a9
AS
3343 }
3344 if(!exists)
3345 {
3346 change->args[n].mode = MODE_BAN;
a32da4c7 3347 change->args[n++].u.hostmask = ban;
d76ed9a9
AS
3348 }
3349 change->argc = n;
3350 if(cmd)
3351 modcmd_chanmode_announce(change);
3352 else
3353 mod_chanmode_announce(chanserv, channel, change);
3354 mod_chanmode_free(change);
3355
3356 if(exists && (action == ACTION_BAN))
3357 {
3358 if(cmd)
3359 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3360 free(ban);
3361 return 0;
3362 }
3363 }
3364
3365 if(action & ACTION_KICK)
3366 {
3367 char kick_reason[MAXLEN];
3368 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3369
3370 for(n = 0; n < victimCount; n++)
3371 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3372 }
3373
3374 if(!cmd)
3375 {
3376 /* No response, since it was automated. */
3377 }
c8273589 3378 else if(action & ACTION_ADD_LAMER)
d76ed9a9
AS
3379 {
3380 if(duration)
c8273589 3381 reply("CSMSG_TIMED_LAMER_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
d76ed9a9 3382 else
c8273589 3383 reply("CSMSG_LAMER_ADDED", name, channel->name);
d76ed9a9
AS
3384 }
3385 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3386 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3387 else if(action & ACTION_BAN)
3388 reply("CSMSG_BAN_DONE", name, channel->name);
3389 else if(action & ACTION_KICK && victimCount)
3390 reply("CSMSG_KICK_DONE", name, channel->name);
3391
3392 free(ban);
3393 return 1;
3394}
3395
3396static CHANSERV_FUNC(cmd_kickban)
3397{
3398 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3399}
3400
3401static CHANSERV_FUNC(cmd_kick)
3402{
3403 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3404}
3405
3406static CHANSERV_FUNC(cmd_ban)
3407{
3408 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3409}
3410
c8273589 3411static CHANSERV_FUNC(cmd_addlamer)
d76ed9a9 3412{
c8273589 3413 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER);
d76ed9a9
AS
3414}
3415
c8273589 3416static CHANSERV_FUNC(cmd_addtimedlamer)
d76ed9a9 3417{
c8273589 3418 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_LAMER | ACTION_ADD_TIMED_LAMER);
d76ed9a9
AS
3419}
3420
3421static struct mod_chanmode *
3422find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3423{
3424 struct mod_chanmode *change;
3425 unsigned char *match;
3426 unsigned int ii, count;
3427
3428 match = alloca(bans->used);
3429 if(actee)
3430 {
3431 for(ii = count = 0; ii < bans->used; ++ii)
3432 {
2f61d1d7 3433 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3434 MATCH_USENICK | MATCH_VISIBLE);
d76ed9a9
AS
3435 if(match[ii])
3436 count++;
3437 }
3438 }
3439 else
3440 {
3441 for(ii = count = 0; ii < bans->used; ++ii)
3442 {
3443 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3444 if(match[ii])
3445 count++;
3446 }
3447 }
3448 if(!count)
3449 return NULL;
3450 change = mod_chanmode_alloc(count);
3451 for(ii = count = 0; ii < bans->used; ++ii)
3452 {
3453 if(!match[ii])
3454 continue;
3455 change->args[count].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 3456 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
d76ed9a9 3457 }
ec1a68c8 3458 assert(count == change->argc);
d76ed9a9
AS
3459 return change;
3460}
3461
7153d2c7
AS
3462void expire_bans(UNUSED_ARG(void* data)) /* Real bans, not lamers */
3463{
3464 unsigned int jj, ii, count;
3465 struct banNode *bn;
3466 struct chanData *channel;
3467 time_t bantimeout;
3468 struct mod_chanmode *change;
3469
3470 log_module(CS_LOG, LOG_DEBUG, "Checking for expired bans");
3471 /* Walk through every channel */
3472 for(channel = channelList; channel; channel = channel->next) {
3473 switch(channel->chOpts[chBanTimeout])
3474 {
3475 default: case '0': continue; /* Dont remove bans in this chan */
3476 case '1': bantimeout = now - (10 * 60); break; /* 10 minutes */
3477 case '2': bantimeout = now - (2 * 60 * 60); break; /* 2 hours */
3478 case '3': bantimeout = now - (4 * 60 * 60); break; /* 4 hours */
3479 case '4': bantimeout = now - (24 * 60 * 60); break; /* 24 hours */
3480 case '5': bantimeout = now - (7 * 24 * 60 * 60); break; /* 1 week */
3481 }
3482 count = 0;
3483 /* First find out how many bans were going to unset */
3484 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3485 if(channel->channel->banlist.list[jj]->set < bantimeout)
3486 count++;
3487 }
3488 if(count > 0) {
3489 /* At least one ban, so setup a removal */
3490 change = mod_chanmode_alloc(count);
3491 ii = 0;
3492 /* Walk over every ban in this channel.. */
3493 for (jj=0; jj < channel->channel->banlist.used; ++jj) {
3494 bn = channel->channel->banlist.list[jj];
3495 if (bn->set < bantimeout) {
3496 log_module(CS_LOG, LOG_DEBUG, "Removing ban %s from %s", bn->ban, channel->channel->name);
3497
3498 /* Add this ban to the mode change */
3499 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3500 change->args[ii].u.hostmask = strdup(bn->ban);
3501 ii++;
3502 /* Pull this ban out of the list */
3503 banList_remove(&(channel->channel->banlist), bn);
3504 jj--;
3505 free(bn);
3506 }
3507 }
3508 /* Send the modes to IRC */
3509 mod_chanmode_announce(chanserv, channel->channel, change);
3510
3511 /* free memory from strdup above */
3512 for(ii = 0; ii < count; ++ii)
3513 free((char*)change->args[ii].u.hostmask);
3514
3515 mod_chanmode_free(change);
3516 }
3517 }
3518 /* Set this function to run again */
3519 if(chanserv_conf.ban_timeout_frequency)
3520 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
3521}
3522
3523
d76ed9a9
AS
3524static int
3525unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3526{
3527 struct userNode *actee;
3528 char *mask = NULL;
3529 int acted = 0;
3530
3531 REQUIRE_PARAMS(2);
3532
3533 /* may want to allow a comma delimited list of users... */
3534 if(!(actee = GetUserH(argv[1])))
3535 {
3536 if(!is_ircmask(argv[1]))
3537 {
3538 reply("MSG_NICK_UNKNOWN", argv[1]);
3539 return 0;
3540 }
3541
3542 mask = strdup(argv[1]);
3543 }
3544
3545 /* We don't sanitize the mask here because ircu
3546 doesn't do it. */
3547 if(action & ACTION_UNBAN)
3548 {
3549 struct mod_chanmode *change;
3550 change = find_matching_bans(&channel->banlist, actee, mask);
3551 if(change)
3552 {
ec1a68c8 3553 unsigned int ii;
3554
aa222db2 3555 modcmd_chanmode_announce(change);
ec1a68c8 3556 for(ii = 0; ii < change->argc; ++ii)
3557 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
3558 mod_chanmode_free(change);
3559 acted = 1;
3560 }
3561 }
3562
c8273589 3563 if(action & ACTION_DEL_LAMER)
d76ed9a9
AS
3564 {
3565 struct banData *ban, *next;
3566
c8273589 3567 ban = channel->channel_info->bans; /* lamers */
d76ed9a9
AS
3568 while(ban)
3569 {
3570 if(actee)
2f61d1d7 3571 for( ; ban && !user_matches_glob(actee, ban->mask, MATCH_USENICK | MATCH_VISIBLE);
d76ed9a9
AS
3572 ban = ban->next);
3573 else
3574 for( ; ban && !match_ircglobs(mask, ban->mask);
3575 ban = ban->next);
3576 if(!ban)
3577 break;
3578 next = ban->next;
3579 del_channel_ban(ban);
3580 ban = next;
3581 acted = 1;
3582 }
3583 }
3584
3585 if(!acted)
3586 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3587 else
3588 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3589 if(mask)
3590 free(mask);
3591 return 1;
3592}
3593
3594static CHANSERV_FUNC(cmd_unban)
3595{
3596 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3597}
3598
c8273589 3599static CHANSERV_FUNC(cmd_dellamer)
d76ed9a9
AS
3600{
3601 /* it doesn't necessarily have to remove the channel ban - may want
3602 to make that an option. */
c8273589 3603 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_LAMER);
d76ed9a9
AS
3604}
3605
3606static CHANSERV_FUNC(cmd_unbanme)
3607{
3608 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3609 long flags = ACTION_UNBAN;
3610
3611 /* remove permanent bans if the user has the proper access. */
4048352e 3612 if(uData->access >= UL_MANAGER)
c8273589 3613 flags |= ACTION_DEL_LAMER;
d76ed9a9
AS
3614
3615 argv[1] = user->nick;
3616 return unban_user(user, channel, 2, argv, cmd, flags);
3617}
3618
3619static CHANSERV_FUNC(cmd_unbanall)
3620{
3621 struct mod_chanmode *change;
3622 unsigned int ii;
3623
3624 if(!channel->banlist.used)
3625 {
3626 reply("CSMSG_NO_BANS", channel->name);
3627 return 0;
3628 }
3629
3630 change = mod_chanmode_alloc(channel->banlist.used);
3631 for(ii=0; ii<channel->banlist.used; ii++)
3632 {
3633 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 3634 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
d76ed9a9
AS
3635 }
3636 modcmd_chanmode_announce(change);
ec1a68c8 3637 for(ii = 0; ii < change->argc; ++ii)
3638 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
3639 mod_chanmode_free(change);
3640 reply("CSMSG_BANS_REMOVED", channel->name);
3641 return 1;
3642}
3643
3644static CHANSERV_FUNC(cmd_open)
3645{
3646 struct mod_chanmode *change;
ec1a68c8 3647 unsigned int ii;
d76ed9a9
AS
3648
3649 change = find_matching_bans(&channel->banlist, user, NULL);
3650 if(!change)
3651 change = mod_chanmode_alloc(0);
3652 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3653 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3654 && channel->channel_info->modes.modes_set)
3655 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3656 modcmd_chanmode_announce(change);
3657 reply("CSMSG_CHANNEL_OPENED", channel->name);
ec1a68c8 3658 for(ii = 0; ii < change->argc; ++ii)
3659 free((char*)change->args[ii].u.hostmask);
d76ed9a9
AS
3660 mod_chanmode_free(change);
3661 return 1;
3662}
3663
3664static CHANSERV_FUNC(cmd_myaccess)
3665{
3666 static struct string_buffer sbuf;
3667 struct handle_info *target_handle;
3668 struct userData *uData;
3669
3670 if(argc < 2)
3671 target_handle = user->handle_info;
3672 else if(!IsHelping(user))
3673 {
3674 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3675 return 0;
3676 }
3677 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3678 return 0;
3679
3680 if(!target_handle->channels)
3681 {
3682 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3683 return 1;
3684 }
3685
3686 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3687 for(uData = target_handle->channels; uData; uData = uData->u_next)
3688 {
3689 struct chanData *cData = uData->channel;
3690
3691 if(uData->access > UL_OWNER)
3692 continue;
3693 if(IsProtected(cData)
3694 && (target_handle != user->handle_info)
3695 && !GetTrueChannelAccess(cData, user->handle_info))
3696 continue;
3697 sbuf.used = 0;
3698 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
c8ca69a0 3699 if(uData->flags == USER_AUTO_OP)
d76ed9a9
AS
3700 string_buffer_append(&sbuf, ',');
3701 if(IsUserSuspended(uData))
3702 string_buffer_append(&sbuf, 's');
3703 if(IsUserAutoOp(uData))
3704 {
4b6129c0 3705 if(uData->access >= UL_OP )
d76ed9a9 3706 string_buffer_append(&sbuf, 'o');
4b6129c0 3707 else if(uData->access >= UL_HALFOP )
55342ce8 3708 string_buffer_append(&sbuf, 'h');
4b6129c0 3709 else if(uData->access >= UL_PEON )
d76ed9a9
AS
3710 string_buffer_append(&sbuf, 'v');
3711 }
3712 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3713 string_buffer_append(&sbuf, 'i');
3714 if(uData->info)
3715 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3716 else
3717 string_buffer_append_string(&sbuf, ")]");
3718 string_buffer_append(&sbuf, '\0');
a32da4c7 3719 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
d76ed9a9
AS
3720 }
3721
3722 return 1;
3723}
3724
3725static CHANSERV_FUNC(cmd_access)
3726{
3727 struct userNode *target;
3728 struct handle_info *target_handle;
3729 struct userData *uData;
3730 int helping;
3731 char prefix[MAXLEN];
3732
3733 if(argc < 2)
3734 {
3735 target = user;
3736 target_handle = target->handle_info;
3737 }
3738 else if((target = GetUserH(argv[1])))
3739 {
3740 target_handle = target->handle_info;
3741 }
3742 else if(argv[1][0] == '*')
3743 {
3744 if(!(target_handle = get_handle_info(argv[1]+1)))
3745 {
3746 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3747 return 0;
3748 }
3749 }
3750 else
3751 {
3752 reply("MSG_NICK_UNKNOWN", argv[1]);
3753 return 0;
3754 }
3755
3756 assert(target || target_handle);
3757
3758 if(target == chanserv)
3759 {
3760 reply("CSMSG_IS_CHANSERV");
3761 return 1;
3762 }
3763
3764 if(!target_handle)
3765 {
3766 if(IsOper(target))
3767 {
3768 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3769 return 0;
3770 }
3771 if(target != user)
3772 {
3773 reply("MSG_USER_AUTHENTICATE", target->nick);
3774 return 0;
3775 }
3776 reply("MSG_AUTHENTICATE");
3777 return 0;
3778 }
3779
3780 if(target)
3781 {
3782 const char *epithet = NULL, *type = NULL;
3783 if(IsOper(target))
3784 {
3785 epithet = chanserv_conf.irc_operator_epithet;
3786 type = "IRCOp";
3787 }
3788 else if(IsNetworkHelper(target))
3789 {
3790 epithet = chanserv_conf.network_helper_epithet;
3791 type = "network helper";
3792 }
3793 else if(IsSupportHelper(target))
3794 {
3795 epithet = chanserv_conf.support_helper_epithet;
3796 type = "support helper";
3797 }
3798 if(epithet)
3799 {
3800 if(target_handle->epithet)
3801 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3802 else if(epithet)
3803 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3804 }
3805 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3806 }
3807 else
3808 {
3809 sprintf(prefix, "%s", target_handle->handle);
3810 }
3811
3812 if(!channel->channel_info)
3813 {
3814 reply("CSMSG_NOT_REGISTERED", channel->name);
3815 return 1;
3816 }
3817
3818 helping = HANDLE_FLAGGED(target_handle, HELPING)
3819 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3820 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3821 {
de9510bc 3822 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, user_level_name_from_level(uData->access), uData->access, channel->name);
d76ed9a9
AS
3823 /* To prevent possible information leaks, only show infolines
3824 * if the requestor is in the channel or it's their own
3825 * handle. */
3826 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3827 {
3828 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3829 }
3830 /* Likewise, only say it's suspended if the user has active
3831 * access in that channel or it's their own entry. */
3832 if(IsUserSuspended(uData)
3833 && (GetChannelUser(channel->channel_info, user->handle_info)
3834 || (user->handle_info == uData->handle)))
3835 {
3836 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3837 }
3838 }
3839 else
3840 {
3841 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3842 }
3843
3844 return 1;
3845}
3846
23475fc6 3847/* This is never used...
d76ed9a9
AS
3848static void
3849zoot_list(struct listData *list)
3850{
3851 struct userData *uData;
3852 unsigned int start, curr, highest, lowest;
3853 struct helpfile_table tmp_table;
3854 const char **temp, *msg;
3855
3856 if(list->table.length == 1)
3857 {
3858 if(list->search)
a915f7d4 3859 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
d76ed9a9 3860 else
a915f7d4 3861 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
d76ed9a9
AS
3862 msg = user_find_message(list->user, "MSG_NONE");
3863 send_message_type(4, list->user, list->bot, " %s", msg);
3864 }
3865 tmp_table.width = list->table.width;
3866 tmp_table.flags = list->table.flags;
3867 list->table.contents[0][0] = " ";
3868 highest = list->highest;
3869 if(list->lowest != 0)
3870 lowest = list->lowest;
3871 else if(highest < 100)
3872 lowest = 1;
3873 else
3874 lowest = highest - 100;
3875 for(start = curr = 1; curr < list->table.length; )
3876 {
3877 uData = list->users[curr-1];
3878 list->table.contents[curr++][0] = " ";
3879 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3880 {
3881 if(list->search)
a915f7d4 3882 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest), list->search);
d76ed9a9 3883 else
a915f7d4 3884 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, user_level_name_from_level(lowest), user_level_name_from_level(highest));
d76ed9a9
AS
3885 temp = list->table.contents[--start];
3886 list->table.contents[start] = list->table.contents[0];
3887 tmp_table.contents = list->table.contents + start;
3888 tmp_table.length = curr - start;
3889 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3890 list->table.contents[start] = temp;
3891 start = curr;
3892 highest = lowest - 1;
3893 lowest = (highest < 100) ? 0 : (highest - 99);
3894 }
3895 }
3896}
23475fc6 3897*/
d76ed9a9
AS
3898
3899static void
338a82b5 3900normal_list(struct listData *list)
d76ed9a9
AS
3901{
3902 const char *msg;
3903 if(list->search)
338a82b5 3904 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
d76ed9a9 3905 else
338a82b5 3906 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_NORMAL", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
d76ed9a9
AS
3907 if(list->table.length == 1)
3908 {
3909 msg = user_find_message(list->user, "MSG_NONE");
3910 send_message_type(4, list->user, list->bot, " %s", msg);
3911 }
8e11460f
AS
3912 else
3913 table_send(list->bot, list->user->nick, 0, NULL, list->table);
d76ed9a9
AS
3914}
3915
338a82b5
AS
3916/* if these need changed, uncomment and customize
3917static void
3918clean_list(struct listData *list)
3919{
3920 const char *msg;
3921 if(list->search)
3922 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3923 else
3924 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLEAN", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3925 if(list->table.length == 1)
3926 {
3927 msg = user_find_message(list->user, "MSG_NONE");
3928 send_message_type(4, list->user, list->bot, " %s", msg);
3929 }
3930 else
3931 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3932}
3933
3934static void
3935advanced_list(struct listData *list)
3936{
3937 const char *msg;
3938 if(list->search)
3939 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest), list->search);
3940 else
3941 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_ADVANCED", list->channel->name, user_level_name_from_level(list->lowest), user_level_name_from_level(list->highest));
3942 if(list->table.length == 1)
3943 {
3944 msg = user_find_message(list->user, "MSG_NONE");
3945 send_message_type(4, list->user, list->bot, " %s", msg);
3946 }
3947 else
3948 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3949}
3950
d9896a83
AS
3951static void
3952classic_list(struct listData *list)
3953{
3954 const char *msg;
3955 if(list->search)
3956 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest, list->search);
3957 else
3958 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER_CLASSIC", list->channel->name, list->lowest, list->highest);
3959 if(list->table.length == 1)
3960 {
3961 msg = user_find_message(list->user, "MSG_NONE");
3962 send_message_type(4, list->user, list->bot, " %s", msg);
3963 }
3964 else
3965 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3966}
338a82b5
AS
3967*/
3968
d76ed9a9
AS
3969static int
3970userData_access_comp(const void *arg_a, const void *arg_b)
3971{
3972 const struct userData *a = *(struct userData**)arg_a;
3973 const struct userData *b = *(struct userData**)arg_b;
3974 int res;
3975 if(a->access != b->access)
3976 res = b->access - a->access;
3977 else
3978 res = irccasecmp(a->handle->handle, b->handle->handle);
3979 return res;
3980}
3981
3982static int
3983cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3984{
3985 void (*send_list)(struct listData *);
3986 struct userData *uData;
3987 struct listData lData;
3988 unsigned int matches;
3989 const char **ary;
338a82b5
AS
3990 int i = 0;
3991 int seen_index;
d76ed9a9
AS
3992
3993 lData.user = user;
3994 lData.bot = cmd->parent->bot;
3995 lData.channel = channel;
3996 lData.lowest = lowest;
3997 lData.highest = highest;
3998 lData.search = (argc > 1) ? argv[1] : NULL;
338a82b5 3999 send_list = normal_list;
e6892204 4000 /* What does the following line do exactly?? */
8e11460f 4001 /*(void)zoot_list; ** since it doesn't show user levels */
d76ed9a9 4002
338a82b5 4003 /*
d76ed9a9
AS
4004 if(user->handle_info)
4005 {
338a82b5
AS
4006 switch(user->handle_info->userlist_style)
4007 {
4008 case HI_STYLE_CLEAN:
4009 send_list = clean_list;
4010 break;
4011 case HI_STYLE_ADVANCED:
4012 send_list = advanced_list;
4013 break;
d9896a83
AS
4014 case HI_STYLE_CLASSIC:
4015 send_list = classic_list;
4016 break;
338a82b5
AS
4017 case HI_STYLE_NORMAL:
4018 default:
4019 send_list = normal_list;
4020 break;
4021 }
d76ed9a9 4022 }
e6892204 4023 */
338a82b5 4024 send_list = normal_list;
d76ed9a9
AS
4025
4026 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4027 matches = 0;
4028 for(uData = channel->channel_info->users; uData; uData = uData->next)
4029 {
4030 if((uData->access < lowest)
4031 || (uData->access > highest)
4032 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4033 continue;
4034 lData.users[matches++] = uData;
4035 }
4036 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4037
4038 lData.table.length = matches+1;
d76ed9a9
AS
4039 lData.table.flags = TABLE_NO_FREE;
4040 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
338a82b5 4041
3498a411 4042 if(user->handle_info && user->handle_info->userlist_style == HI_STYLE_ADVANCED)
338a82b5
AS
4043 lData.table.width = 5; /* with level = 5 */
4044 else
4045 lData.table.width = 4; /* without = 4 */
d76ed9a9
AS
4046 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4047 lData.table.contents[0] = ary;
d9896a83
AS
4048 if(user->handle_info) {
4049 switch(user->handle_info->userlist_style) {
4050 case HI_STYLE_CLASSIC:
4051 ary[i++] = "Level";
4052 break;
4053 case HI_STYLE_ADVANCED:
4054 ary[i++] = "Access";
4055 ary[i++] = "Level";
4056 break;
4057 case HI_STYLE_CLEAN:
4058 ary[i++] = "Access";
4059 break;
4060 case HI_STYLE_NORMAL:
4061 default:
4062 ary[i++] = "Access";
4063 break;
4064 }
4065 }
4066 else {
4067 ary[i++] = "Access";
4068 }
338a82b5
AS
4069 ary[i++] = "Account";
4070 ary[i] = "Last Seen";
4071 seen_index = i++;
4072 ary[i++] = "Status";
d76ed9a9
AS
4073 for(matches = 1; matches < lData.table.length; ++matches)
4074 {
4075 struct userData *uData = lData.users[matches-1];
4076 char seen[INTERVALLEN];
4077
338a82b5 4078 i = 0;
d76ed9a9
AS
4079 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4080 lData.table.contents[matches] = ary;
d9896a83
AS
4081 if(user->handle_info) {
4082 switch(user->handle_info->userlist_style) {
4083 case HI_STYLE_CLASSIC:
4084 ary[i++] = strtab(uData->access);
4085 break;
4086 case HI_STYLE_ADVANCED:
4087 ary[i++] = user_level_name_from_level(uData->access);
4088 ary[i++] = strtab(uData->access);
4089 break;
4090 case HI_STYLE_CLEAN:
4091 ary[i++] = user_level_name_from_level(uData->access);
4092 break;
4093 case HI_STYLE_NORMAL:
4094 default:
4095 ary[i++] = user_level_name_from_level(uData->access);
4096 break;
4097 }
4098 }
4099 else {
4100 ary[i++] = user_level_name_from_level(uData->access);
4101 }
338a82b5 4102 ary[i++] = uData->handle->handle;
d76ed9a9 4103 if(uData->present)
338a82b5 4104 ary[i] = "Here";
d76ed9a9 4105 else if(!uData->seen)
338a82b5 4106 ary[i] = "Never";
d76ed9a9 4107 else
338a82b5
AS
4108 ary[i] = intervalString(seen, now - uData->seen, user->handle_info);
4109 ary[i] = strdup(ary[i]);
4110 i++;
d76ed9a9 4111 if(IsUserSuspended(uData))
338a82b5 4112 ary[i++] = "Suspended";
d76ed9a9 4113 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
338a82b5 4114 ary[i++] = "Vacation";
d76ed9a9 4115 else
338a82b5 4116 ary[i++] = "Normal";
d76ed9a9
AS
4117 }
4118 send_list(&lData);
4119 for(matches = 1; matches < lData.table.length; ++matches)
4120 {
338a82b5
AS
4121 /* Free strdup above */
4122 free((char*)lData.table.contents[matches][seen_index]);
d76ed9a9
AS
4123 free(lData.table.contents[matches]);
4124 }
4125 free(lData.table.contents[0]);
4126 free(lData.table.contents);
4127 return 1;
4128}
4129
1d957482
AS
4130/* Remove this now that debugging is over? or improve it for
4131 * users? Would it be better tied into USERS somehow? -Rubin */
ac3bdc8d
AS
4132static CHANSERV_FUNC(cmd_pending)
4133{
4134 struct adduserPending *ap;
1d957482 4135 reply("CSMSG_ADDUSER_PENDING_HEADER");
cde20a3f
AS
4136 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4137 reply("CSMSG_BAR");
ac3bdc8d
AS
4138 for(ap = adduser_pendings;ap;ap = ap->next)
4139 reply("CSMSG_ADDUSER_PENDING_LIST", ap->channel->name, ap->user->nick);
1d957482 4140 reply("CSMSG_ADDUSER_PENDING_FOOTER");
ac3bdc8d
AS
4141 return 1;
4142}
4143
d76ed9a9
AS
4144static CHANSERV_FUNC(cmd_users)
4145{
4146 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4147}
4148
4149static CHANSERV_FUNC(cmd_wlist)
4150{
4151 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4152}
4153
4154static CHANSERV_FUNC(cmd_clist)
4155{
4156 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4157}
4158
4159static CHANSERV_FUNC(cmd_mlist)
4160{
4048352e 4161 return cmd_list_users(CSFUNC_ARGS, UL_MANAGER, UL_COOWNER-1);
d76ed9a9
AS
4162}
4163
4164static CHANSERV_FUNC(cmd_olist)
4165{
4048352e 4166 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MANAGER-1);
d76ed9a9
AS
4167}
4168
55342ce8 4169static CHANSERV_FUNC(cmd_hlist)
4170{
4171 return cmd_list_users(CSFUNC_ARGS, UL_HALFOP, UL_OP-1);
4172}
4173
d76ed9a9
AS
4174static CHANSERV_FUNC(cmd_plist)
4175{
55342ce8 4176 return cmd_list_users(CSFUNC_ARGS, 1, UL_HALFOP-1);
d76ed9a9
AS
4177}
4178
c8273589 4179static CHANSERV_FUNC(cmd_lamers)
d76ed9a9
AS
4180{
4181 struct helpfile_table tbl;
4182 unsigned int matches = 0, timed = 0, ii;
4183 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4184 const char *msg_never, *triggered, *expires;
c8273589 4185 struct banData *ban, **bans; /* lamers */
d76ed9a9
AS
4186
4187 if(argc > 1)
4188 search = argv[1];
4189 else
4190 search = NULL;
4191
c8273589
AS
4192 reply("CSMSG_LAMERS_HEADER", channel->name);
4193 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *)); /* lamers */
d76ed9a9 4194
c8273589 4195 /* lamers */
d76ed9a9
AS
4196 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4197 {
4198 if(search && !match_ircglobs(search, ban->mask))
4199 continue;
4200 bans[matches++] = ban;
4201 if(ban->expires)
4202 timed = 1;
4203 }
4204
4205 tbl.length = matches + 1;
4206 tbl.width = 4 + timed;
4207 tbl.flags = 0;
4208 tbl.flags = TABLE_NO_FREE;
4209 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4210 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4211 tbl.contents[0][0] = "Mask";
4212 tbl.contents[0][1] = "Set By";
4213 tbl.contents[0][2] = "Triggered";
4214 if(timed)
4215 {
4216 tbl.contents[0][3] = "Expires";
4217 tbl.contents[0][4] = "Reason";
4218 }
4219 else
4220 tbl.contents[0][3] = "Reason";
4221 if(!matches)
4222 {
4223 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4224 reply("MSG_NONE");
4225 free(tbl.contents[0]);
4226 free(tbl.contents);
4227 return 0;
4228 }
4229
4230 msg_never = user_find_message(user, "MSG_NEVER");
4231 for(ii = 0; ii < matches; )
4232 {
4233 ban = bans[ii];
4234
4235 if(!timed)
4236 expires = "";
4237 else if(ban->expires)
4238 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4239 else
4240 expires = msg_never;
4241
4242 if(ban->triggered)
4243 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4244 else
4245 triggered = msg_never;
4246
4247 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4248 tbl.contents[ii][0] = ban->mask;
4249 tbl.contents[ii][1] = ban->owner;
4250 tbl.contents[ii][2] = strdup(triggered);
4251 if(timed)
4252 {
4253 tbl.contents[ii][3] = strdup(expires);
4254 tbl.contents[ii][4] = ban->reason;
4255 }
4256 else
4257 tbl.contents[ii][3] = ban->reason;
4258 }
4259 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
de9510bc 4260 /* reply("MSG_MATCH_COUNT", matches); */
d76ed9a9
AS
4261 for(ii = 1; ii < tbl.length; ++ii)
4262 {
4263 free((char*)tbl.contents[ii][2]);
4264 if(timed)
4265 free((char*)tbl.contents[ii][3]);
4266 free(tbl.contents[ii]);
4267 }
4268 free(tbl.contents[0]);
4269 free(tbl.contents);
4270 return 1;
4271}
4272
b75e24a3
AS
4273/* bad_topic
4274 *
4275 * return + if the user does NOT have the right to set the topic, and
4276 * the topic is changed.
4277 */
d76ed9a9
AS
4278static int
4279bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4280{
4281 struct chanData *cData = channel->channel_info;
4282 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4283 return 0;
d76ed9a9
AS
4284 else if(cData->topic)
4285 return irccasecmp(new_topic, cData->topic);
4286 else
4287 return 0;
4288}
4289
b75e24a3
AS
4290/* conform_topic
4291 *
4292 * Makes a givin topic fit into a givin topic mask and returns
4293 * the results.
4294 *
4295 * topic_mask - the mask to conform to
4296 * topic - the topic to make conform
4297 * new_topic - the pre-allocated char* to put the new topic into
4298 *
4299 * modifies: new_topic
4300 */
4301void
4302conform_topic(char* topic_mask, char* topic, char *new_topic)
4303{
4304 //char *topic_mask = cData->topic_mask;
4305 char tchar;
4306 int pos=0, starpos=-1, dpos=0, len;
4307
4308 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4309 {
4310 switch(tchar)
4311 {
4312 case '*':
4313 if(starpos != -1)
4314 {
4315 strcpy(new_topic, "");
4316 return;
4317 }
4318 len = strlen(topic);
4319 if((dpos + len) > TOPICLEN)
4320 len = TOPICLEN + 1 - dpos;
4321 memcpy(new_topic+dpos, topic, len);
4322 dpos += len;
4323 starpos = pos;
4324 break;
4325 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4326 default: new_topic[dpos++] = tchar; break;
4327 }
4328 }
4329 if((dpos > TOPICLEN) || tchar)
4330 {
4331 strcpy(new_topic, "");
4332 return;
4333 }
4334 new_topic[dpos] = 0;
4335 return;
4336}
4337
d76ed9a9
AS
4338static CHANSERV_FUNC(cmd_topic)
4339{
4340 struct chanData *cData;
4341 char *topic;
7fda2b52 4342 int p10 = 0;
4343
4344#ifdef WITH_PROTOCOL_P10
4345 p10 = 1;
4346#endif
d76ed9a9
AS
4347
4348 cData = channel->channel_info;
4349 if(argc < 2)
4350 {
b75e24a3
AS
4351 if(cData->topic)
4352 {
7fda2b52 4353 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, cData->topic, 1);
b75e24a3 4354 reply("CSMSG_TOPIC_SET", cData->topic);
d76ed9a9 4355 return 1;
b75e24a3 4356 }
d76ed9a9
AS
4357
4358 reply("CSMSG_NO_TOPIC", channel->name);
4359 return 0;
4360 }
4361
4362 topic = unsplit_string(argv + 1, argc - 1, NULL);
4363 /* If they say "!topic *", use an empty topic. */
4364 if((topic[0] == '*') && (topic[1] == 0))
4365 topic[0] = 0;
b75e24a3 4366
d76ed9a9
AS
4367 if(bad_topic(channel, user, topic))
4368 {
b75e24a3
AS
4369 reply("CSMSG_TOPIC_LOCKED", channel->name);
4370 return 0;
4371 }
4372 else
4373 {
4374 /* If there is a topicmask set, and the new topic doesnt match, make it */
4375 if(cData->topic_mask && !match_ircglob(topic, cData->topic_mask))
d76ed9a9 4376 {
b75e24a3
AS
4377 char *topic_mask = cData->topic_mask;
4378 char new_topic[TOPICLEN+1];
d76ed9a9 4379
b75e24a3
AS
4380 /* make a new topic fitting mask */
4381 conform_topic(topic_mask, topic, new_topic);
4382 if(!*new_topic)
d76ed9a9 4383 {
b75e24a3 4384 /* Topic couldnt fit into mask, was too long */
d76ed9a9
AS
4385 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4386 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4387 return 0;
4388 }
7fda2b52 4389 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, new_topic, 1);
d76ed9a9 4390 }
b75e24a3 4391 else /* No mask set, just set the topic */
7fda2b52 4392 SetChannelTopic(channel, chanserv, p10 ? user : chanserv, topic, 1);
d76ed9a9 4393 }
d76ed9a9
AS
4394
4395 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4396 {
4397 /* Grab the topic and save it as the default topic. */
4398 free(cData->topic);
4399 cData->topic = strdup(channel->topic);
4400 }
4401
4402 return 1;
4403}
4404
4405static CHANSERV_FUNC(cmd_mode)
4406{
2f61d1d7 4407 struct userData *uData;
d76ed9a9 4408 struct mod_chanmode *change;
2f61d1d7 4409 short base_oplevel;
d76ed9a9
AS
4410
4411 if(argc < 2)
4412 {
4413 change = &channel->channel_info->modes;
4414 if(change->modes_set || change->modes_clear) {
4415 modcmd_chanmode_announce(change);
4416 reply("CSMSG_DEFAULTED_MODES", channel->name);
4417 } else
4418 reply("CSMSG_NO_MODES", channel->name);
4419 return 1;
4420 }
4421
2f61d1d7 4422 uData = GetChannelUser(channel->channel_info, user->handle_info);
4423 if (!uData)
4424 base_oplevel = MAXOPLEVEL;
4425 else if (uData->access >= UL_OWNER)
4426 base_oplevel = 1;
4427 else
4428 base_oplevel = 1 + UL_OWNER - uData->access;
4429 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4430
d76ed9a9
AS
4431 if(!change)
4432 {
4433 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4434 return 0;
4435 }
4436
4437 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4438 && mode_lock_violated(&channel->channel_info->modes, change))
4439 {
4440 char modes[MAXLEN];
4441 mod_chanmode_format(&channel->channel_info->modes, modes);
4442 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4443 return 0;
4444 }
4445
4446 modcmd_chanmode_announce(change);
4447 mod_chanmode_free(change);
4448 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4449 return 1;
4450}
4451
4452static CHANSERV_FUNC(cmd_invite)
4453{
4454 struct userData *uData;
4455 struct userNode *invite;
4456
4457 uData = GetChannelUser(channel->channel_info, user->handle_info);
4458
4459 if(argc > 1)
4460 {
4461 if(!(invite = GetUserH(argv[1])))
4462 {
4463 reply("MSG_NICK_UNKNOWN", argv[1]);
4464 return 0;
4465 }
4466 }
4467 else
4468 invite = user;
4469
4470 if(GetUserMode(channel, invite))
4471 {
4472 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4473 return 0;
4474 }
4475
4476 if(user != invite)
4477 {
4478 if(argc > 2)
4479 {
4480 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4481 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4482 }
4483 else
4484 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4485 }
4486 irc_invite(chanserv, invite, channel);
4487 if(argc > 1)
4488 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4489
4490 return 1;
4491}
4492
4493static CHANSERV_FUNC(cmd_inviteme)
4494{
4495 if(GetUserMode(channel, user))
4496 {
4497 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4498 return 0;
4499 }
4500 if(channel->channel_info
4501 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4502 {
4503 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4504 return 0;
4505 }
4506 irc_invite(cmd->parent->bot, user, channel);
4507 return 1;
4508}
4509
4510static void
4511show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4512{
4513 unsigned int combo;
4514 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4515
4516 /* We display things based on two dimensions:
4517 * - Issue time: present or absent
4518 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4519 * (in order of precedence, so something both expired and revoked
4520 * only counts as revoked)
4521 */
4522 combo = (suspended->issued ? 4 : 0)
4523 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4524 switch(combo) {
4525 case 0: /* no issue time, indefinite expiration */
4526 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4527 break;
4528 case 1: /* no issue time, expires in future */
4529 intervalString(buf1, suspended->expires-now, user->handle_info);
4530 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4531 break;
4532 case 2: /* no issue time, expired */
4533 intervalString(buf1, now-suspended->expires, user->handle_info);
4534 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4535 break;
4536 case 3: /* no issue time, revoked */
4537 intervalString(buf1, now-suspended->revoked, user->handle_info);
4538 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4539 break;
4540 case 4: /* issue time set, indefinite expiration */
4541 intervalString(buf1, now-suspended->issued, user->handle_info);
4542 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4543 break;
4544 case 5: /* issue time set, expires in future */
4545 intervalString(buf1, now-suspended->issued, user->handle_info);
4546 intervalString(buf2, suspended->expires-now, user->handle_info);
4547 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4548 break;
4549 case 6: /* issue time set, expired */
4550 intervalString(buf1, now-suspended->issued, user->handle_info);
4551 intervalString(buf2, now-suspended->expires, user->handle_info);
4552 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4553 break;
4554 case 7: /* issue time set, revoked */
4555 intervalString(buf1, now-suspended->issued, user->handle_info);
4556 intervalString(buf2, now-suspended->revoked, user->handle_info);
4557 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4558 break;
4559 default:
4560 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4561 return;
4562 }
4563}
4564
82f37c08 4565static void
4566show_giveownership_info(struct svccmd *cmd, struct userNode *user, struct giveownership *giveownership)
4567{
4568 char buf[MAXLEN];
4569 const char *fmt = "%a %b %d %H:%M %Y";
4570 strftime(buf, sizeof(buf), fmt, localtime(&giveownership->issued));
4571
4572 if(giveownership->staff_issuer)
4573 {
4574 if(giveownership->reason)
4575 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF_REASON", giveownership->old_owner,
4576 giveownership->target, giveownership->target_access,
4577 giveownership->staff_issuer, buf, giveownership->reason);
4578 else
4579 reply("CSMSG_CHANNEL_OWNERSHIP_STAFF", giveownership->old_owner,
4580 giveownership->target, giveownership->target_access,
4581 giveownership->staff_issuer, buf);
4582 }
4583 else
4584 {
4585 reply("CSMSG_CHANNEL_OWNERSHIP_NORMAL", giveownership->old_owner, giveownership->target, giveownership->target_access, buf);
4586 }
4587}
4588
4589
d76ed9a9
AS
4590static CHANSERV_FUNC(cmd_info)
4591{
4592 char modes[MAXLEN], buffer[INTERVALLEN];
4593 struct userData *uData, *owner;
4594 struct chanData *cData;
4595 struct do_not_register *dnr;
4596 struct note *note;
4597 dict_iterator_t it;
4598 int privileged;
4599
4600 cData = channel->channel_info;
4601 reply("CSMSG_CHANNEL_INFO", channel->name);
cde20a3f
AS
4602 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4603 reply("CSMSG_BAR");
d76ed9a9
AS
4604
4605 uData = GetChannelUser(cData, user->handle_info);
b75e24a3 4606 if(uData && (uData->access >= UL_OP /*cData->lvlOpts[lvlGiveOps]*/))
d76ed9a9
AS
4607 {
4608 mod_chanmode_format(&cData->modes, modes);
4609 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4610 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4611 }
4612
4613 for(it = dict_first(cData->notes); it; it = iter_next(it))
4614 {
4615 int padding;
4616
4617 note = iter_data(it);
4618 if(!note_type_visible_to_user(cData, note->type, user))
4619 continue;
4620
4621 padding = PADLEN - 1 - strlen(iter_key(it));
4622 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4623 }
4624
4625 reply("CSMSG_CHANNEL_MAX", cData->max);
4626 for(owner = cData->users; owner; owner = owner->next)
4627 if(owner->access == UL_OWNER)
4628 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4629 reply("CSMSG_CHANNEL_USERS", cData->userCount);
c8273589 4630 reply("CSMSG_CHANNEL_LAMERS", cData->banCount);
d76ed9a9 4631 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
d76ed9a9
AS
4632
4633 privileged = IsStaff(user);
697f4c9a 4634 if(privileged)
4635 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
d76ed9a9
AS
4636 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4637 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4638
4639 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4640 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4641
4642 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4643 {
4644 struct suspended *suspended;
4645 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4646 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4647 show_suspension_info(cmd, user, suspended);
4648 }
4649 else if(IsSuspended(cData))
4650 {
4651 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4652 show_suspension_info(cmd, user, cData->suspended);
4653 }
82f37c08 4654 if(cData->giveownership && ((uData && (uData->access >= UL_COOWNER)) || IsStaff(user)))
4655 {
4656 struct giveownership *giveownership;
4657 reply("CSMSG_CHANNEL_OWNERSHIP_HISTORY", channel->name);
4658 for(giveownership = cData->giveownership; giveownership; giveownership = giveownership->previous)
4659 show_giveownership_info(cmd, user, giveownership);
4660 }
cde20a3f
AS
4661 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4662 reply("CSMSG_CHANNEL_END");
4663 else
4664 reply("CSMSG_CHANNEL_END_CLEAN");
d76ed9a9
AS
4665 return 1;
4666}
4667
4668static CHANSERV_FUNC(cmd_netinfo)
4669{
4670 extern time_t boot_time;
4671 extern unsigned long burst_length;
4672 char interval[INTERVALLEN];
4673
4674 reply("CSMSG_NETWORK_INFO");
4675 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4676 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4677 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4678 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
c8273589 4679 reply("CSMSG_NETWORK_LAMERS", banCount);
d76ed9a9
AS
4680 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4681 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4682 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4683 return 1;
4684}
4685
4686static void
4687send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4688{
4689 struct helpfile_table table;
4690 unsigned int nn;
4691 struct userNode *user;
4692 char *nick;
4693
4694 table.length = 0;
4695 table.width = 1;
4696 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4697 table.contents = alloca(list->used*sizeof(*table.contents));
4698 for(nn=0; nn<list->used; nn++)
4699 {
4700 user = list->list[nn];
4701 if(user->modes & skip_flags)
4702 continue;
4703 if(IsBot(user))
4704 continue;
4705 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4706 if(IsAway(user))
4707 {
4708 nick = alloca(strlen(user->nick)+3);
4709 sprintf(nick, "(%s)", user->nick);
4710 }
4711 else
4712 nick = user->nick;
4713 table.contents[table.length][0] = nick;
4714 table.length++;
4715 }
4716 table_send(chanserv, to->nick, 0, NULL, table);
4717}
4718
4719static CHANSERV_FUNC(cmd_ircops)
4720{
4721 reply("CSMSG_STAFF_OPERS");
4722 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4723 return 1;
4724}
4725
4726static CHANSERV_FUNC(cmd_helpers)
4727{
4728 reply("CSMSG_STAFF_HELPERS");
4729 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4730 return 1;
4731}
4732
4733static CHANSERV_FUNC(cmd_staff)
4734{
4735 reply("CSMSG_NETWORK_STAFF");
4736 cmd_ircops(CSFUNC_ARGS);
4737 cmd_helpers(CSFUNC_ARGS);
4738 return 1;
4739}
4740
4741static CHANSERV_FUNC(cmd_peek)
4742{
4743 struct modeNode *mn;
4744 char modes[MODELEN];
4745 unsigned int n;
4746 struct helpfile_table table;
4747
4748 irc_make_chanmode(channel, modes);
4749
4750 reply("CSMSG_PEEK_INFO", channel->name);
cde20a3f
AS
4751 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
4752 reply("CSMSG_BAR");
d76ed9a9
AS
4753 reply("CSMSG_PEEK_TOPIC", channel->topic);
4754 reply("CSMSG_PEEK_MODES", modes);
4755 reply("CSMSG_PEEK_USERS", channel->members.used);
4756
4757 table.length = 0;
4758 table.width = 1;
4759 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4760 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4761 for(n = 0; n < channel->members.used; n++)
4762 {
4763 mn = channel->members.list[n];
4764 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4765 continue;
4766 table.contents[table.length] = alloca(sizeof(**table.contents));
4767 table.contents[table.length][0] = mn->user->nick;
4768 table.length++;
4769 }
4770 if(table.length)
4771 {
4772 reply("CSMSG_PEEK_OPS");
4773 table_send(chanserv, user->nick, 0, NULL, table);
4774 }
4775 else
4776 reply("CSMSG_PEEK_NO_OPS");
de9510bc 4777 reply("CSMSG_PEEK_END");
d76ed9a9
AS
4778 return 1;
4779}
4780
4781static MODCMD_FUNC(cmd_wipeinfo)
4782{
4783 struct handle_info *victim;
4784 struct userData *ud, *actor;
4785
4786 REQUIRE_PARAMS(2);
4787 actor = GetChannelUser(channel->channel_info, user->handle_info);
4788 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4789 return 0;
4790 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4791 {
4792 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4793 return 0;
4794 }
4795 if((ud->access >= actor->access) && (ud != actor))
4796 {
4797 reply("MSG_USER_OUTRANKED", victim->handle);
4798 return 0;
4799 }
4800 if(ud->info)
4801 free(ud->info);
4802 ud->info = NULL;
4803 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4804 return 1;
4805}
4806
7637f48f 4807static void
4808resync_channel(struct chanNode *channel)
d76ed9a9
AS
4809{
4810 struct mod_chanmode *changes;
4811 struct chanData *cData = channel->channel_info;
4812 unsigned int ii, used;
4813
e3b2f789
AS
4814 /* 6 = worst case -ovh+ovh on everyone */
4815 changes = mod_chanmode_alloc(channel->members.used * 6);
d76ed9a9
AS
4816 for(ii = used = 0; ii < channel->members.used; ++ii)
4817 {
4818 struct modeNode *mn = channel->members.list[ii];
4819 struct userData *uData;
4820
4821 if(IsService(mn->user))
4822 continue;
4823
e3b2f789 4824
d76ed9a9 4825 uData = GetChannelAccess(cData, mn->user->handle_info);
e3b2f789
AS
4826
4827 /* If the channel is in no-mode mode, de-mode EVERYONE */
4828 if(cData->chOpts[chAutomode] == 'n')
55342ce8 4829 {
e3b2f789
AS
4830 if(mn->modes)
4831 {
4832 changes->args[used].mode = MODE_REMOVE | mn->modes;
4833 changes->args[used++].u.member = mn;
4834 }
55342ce8 4835 }
e3b2f789 4836 else /* Give various userlevels their modes.. */
d76ed9a9 4837 {
e3b2f789 4838 if(uData && uData->access >= UL_OP )
d76ed9a9 4839 {
e3b2f789
AS
4840 if(!(mn->modes & MODE_CHANOP))
4841 {
4842 changes->args[used].mode = MODE_CHANOP;
4843 changes->args[used++].u.member = mn;
4844 }
d76ed9a9 4845 }
e3b2f789 4846 else if(uData && uData->access >= UL_HALFOP)
55342ce8 4847 {
e3b2f789
AS
4848 if(mn->modes & MODE_CHANOP)
4849 {
4850 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4851 changes->args[used++].u.member = mn;
4852 }
4853 if(!(mn->modes & MODE_HALFOP))
4854 {
4855 changes->args[used].mode = MODE_HALFOP;
4856 changes->args[used++].u.member = mn;
4857 }
55342ce8 4858 }
e3b2f789 4859 else if(uData && uData->access >= UL_PEON )
d76ed9a9 4860 {
e3b2f789
AS
4861 if(mn->modes & MODE_CHANOP)
4862 {
4863 changes->args[used].mode = MODE_REMOVE | MODE_CHANOP;
4864 changes->args[used++].u.member = mn;
4865 }
4866 if(mn->modes & MODE_HALFOP)
4867 {
4868 changes->args[used].mode = MODE_REMOVE | MODE_HALFOP;
4869 changes->args[used++].u.member = mn;
4870 }
4871 /* Don't voice peons if were in mode m */
4872 if( cData->chOpts[chAutomode] == 'm')
4873 {
4874 if(mn->modes & MODE_VOICE)
4875 {
4876 changes->args[used].mode = MODE_REMOVE | MODE_VOICE;
4877 changes->args[used++].u.member = mn;
4878 }
4879 }
4880 /* otherwise, make user they do have voice */
4881 else if(!(mn->modes & MODE_VOICE))
4882 {
4883 changes->args[used].mode = MODE_VOICE;
4884 changes->args[used++].u.member = mn;
4885 }
d76ed9a9 4886 }
e3b2f789 4887 else /* They arnt on the userlist.. */
d76ed9a9 4888 {
e3b2f789
AS
4889 /* If we voice everyone, but they dont.. */
4890 if(cData->chOpts[chAutomode] == 'v')
4891 {
4892 /* Remove anything except v */
4893 if(mn->modes & ~MODE_VOICE)
4894 {
4895 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4896 changes->args[used++].u.member = mn;
4897 }
4898 /* Add v */
4899 if(!(mn->modes & MODE_VOICE))
4900 {
4901 changes->args[used].mode = MODE_VOICE;
4902 changes->args[used++].u.member = mn;
4903 }
4904 }
4905 /* If we hop everyone, but they dont.. */
4906 else if(cData->chOpts[chAutomode] == 'h')
4907 {
4908 /* Remove anything except h */
4909 if(mn->modes & ~MODE_HALFOP)
4910 {
4911 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_HALFOP);
4912 changes->args[used++].u.member = mn;
4913 }
4914 /* Add h */
4915 if(!(mn->modes & MODE_HALFOP))
4916 {
4917 changes->args[used].mode = MODE_HALFOP;
4918 changes->args[used++].u.member = mn;
4919 }
4920 }
4921 /* If we op everyone, but they dont.. */
4922 else if(cData->chOpts[chAutomode] == 'o')
4923 {
4924 /* Remove anything except h */
4925 if(mn->modes & ~MODE_CHANOP)
4926 {
4927 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_CHANOP);
4928 changes->args[used++].u.member = mn;
4929 }
4930 /* Add h */
4931 if(!(mn->modes & MODE_CHANOP))
4932 {
4933 changes->args[used].mode = MODE_CHANOP;
4934 changes->args[used++].u.member = mn;
4935 }
4936 }
4937 /* they have no excuse for having modes, de-everything them */
4938 else
4939 {
4940 if(mn->modes)
4941 {
4942 changes->args[used].mode = MODE_REMOVE | mn->modes;
4943 changes->args[used++].u.member = mn;
4944 }
4945 }
d76ed9a9
AS
4946 }
4947 }
4948 }
4949 changes->argc = used;
7637f48f 4950 mod_chanmode_announce(chanserv, channel, changes);
d76ed9a9 4951 mod_chanmode_free(changes);
7637f48f 4952}
4953
4954static CHANSERV_FUNC(cmd_resync)
4955{
4956 resync_channel(channel);
d76ed9a9
AS
4957 reply("CSMSG_RESYNCED_USERS", channel->name);
4958 return 1;
4959}
4960
4961static CHANSERV_FUNC(cmd_seen)
4962{
4963 struct userData *uData;
4964 struct handle_info *handle;
4965 char seen[INTERVALLEN];
4966
4967 REQUIRE_PARAMS(2);
4968
4969 if(!irccasecmp(argv[1], chanserv->nick))
4970 {
4971 reply("CSMSG_IS_CHANSERV");
4972 return 1;
4973 }
4974
4975 if(!(handle = get_handle_info(argv[1])))
4976 {
4977 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4978 return 0;
4979 }
4980
4981 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4982 {
4983 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4984 return 0;
4985 }
4986
4987 if(uData->present)
4988 reply("CSMSG_USER_PRESENT", handle->handle);
4989 else if(uData->seen)
4990 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4991 else
4992 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4993
4994 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4995 reply("CSMSG_USER_VACATION", handle->handle);
4996
4997 return 1;
4998}
4999
5000static MODCMD_FUNC(cmd_names)
5001{
5002 struct userNode *targ;
5003 struct userData *targData;
5004 unsigned int ii, pos;
5005 char buf[400];
5006
5007 for(ii=pos=0; ii<channel->members.used; ++ii)
5008 {
5009 targ = channel->members.list[ii]->user;
5010 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
5011 if(!targData)
5012 continue;
5013 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
5014 {
5015 buf[pos] = 0;
5016 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5017 pos = 0;
5018 }
5019 buf[pos++] = ' ';
5020 if(IsUserSuspended(targData))
5021 buf[pos++] = 's';
5022 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
5023 }
5024 buf[pos] = 0;
5025 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
5026 reply("CSMSG_END_NAMES", channel->name);
5027 return 1;
5028}
5029
5030static int
5031note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
5032{
5033 switch(ntype->visible_type)
5034 {
5035 case NOTE_VIS_ALL: return 1;
5036 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
5037 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
5038 }
5039}
5040
5041static int
5042note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
5043{
5044 struct userData *uData;
5045
5046 switch(ntype->set_access_type)
5047 {
338a82b5
AS
5048 case NOTE_SET_CHANNEL_ACCESS:
5049 if(!user->handle_info)
5050 return 0;
5051 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
5052 return 0;
5053 return uData->access >= ntype->set_access.min_ulevel;
5054 case NOTE_SET_CHANNEL_SETTER:
5055 return check_user_level(channel, user, lvlSetters, 1, 0);
5056 case NOTE_SET_PRIVILEGED: default:
5057 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
d76ed9a9
AS
5058 }
5059}
5060
5061static CHANSERV_FUNC(cmd_note)
5062{
5063 struct chanData *cData;
5064 struct note *note;
5065 struct note_type *ntype;
5066
5067 cData = channel->channel_info;
5068 if(!cData)
5069 {
5070 reply("CSMSG_NOT_REGISTERED", channel->name);
5071 return 0;
5072 }
5073
5074 /* If no arguments, show all visible notes for the channel. */
5075 if(argc < 2)
5076 {
5077 dict_iterator_t it;
5078 unsigned int count;
5079
5080 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5081 {
5082 note = iter_data(it);
5083 if(!note_type_visible_to_user(cData, note->type, user))
5084 continue;
5085 if(!count++)
5086 reply("CSMSG_NOTELIST_HEADER", channel->name);
5087 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5088 }
5089 if(count)
5090 reply("CSMSG_NOTELIST_END", channel->name);
5091 else
5092 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5093 }
5094 /* If one argument, show the named note. */
5095 else if(argc == 2)
5096 {
5097 if((note = dict_find(cData->notes, argv[1], NULL))
5098 && note_type_visible_to_user(cData, note->type, user))
5099 {
5100 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5101 }
5102 else if((ntype = dict_find(note_types, argv[1], NULL))
5103 && note_type_visible_to_user(NULL, ntype, user))
5104 {
5105 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5106 return 0;
5107 }
5108 else
5109 {
5110 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5111 return 0;
5112 }
5113 }
5114 /* Assume they're trying to set a note. */
5115 else
5116 {
5117 char *note_text;
5118 ntype = dict_find(note_types, argv[1], NULL);
5119 if(!ntype)
5120 {
5121 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5122 return 0;
5123 }
5124 else if(note_type_settable_by_user(channel, ntype, user))
5125 {
5126 note_text = unsplit_string(argv+2, argc-2, NULL);
5127 if((note = dict_find(cData->notes, argv[1], NULL)))
5128 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5129 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5130 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5131
5132 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5133 {
5134 /* The note is viewable to staff only, so return 0
5135 to keep the invocation from getting logged (or
5136 regular users can see it in !events). */
5137 return 0;
5138 }
5139 }
5140 else
5141 {
5142 reply("CSMSG_NO_ACCESS");
5143 return 0;
5144 }
5145 }
5146 return 1;
5147}
5148
5149static CHANSERV_FUNC(cmd_delnote)
5150{
5151 struct note *note;
5152
5153 REQUIRE_PARAMS(2);
5154 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5155 || !note_type_settable_by_user(channel, note->type, user))
5156 {
5157 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5158 return 0;
5159 }
5160 dict_remove(channel->channel_info->notes, note->type->name);
5161 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5162 return 1;
5163}
5164
23475fc6 5165static CHANSERV_FUNC(cmd_last)
5166{
5167 int numoflines;
5168
5169 REQUIRE_PARAMS(1);
5170
5171 numoflines = (argc > 1) ? atoi(argv[1]) : 10;
5172
5173 if(numoflines < 1 || numoflines > 200)
5174 {
5175 reply("CSMSG_LAST_INVALID");
5176 return 0;
5177 }
5178 ShowLog(user, channel, "*", "*", "*", "*", numoflines);
5179 return 1;
5180}
5181
d76ed9a9
AS
5182static CHANSERV_FUNC(cmd_events)
5183{
5184 struct logSearch discrim;
5185 struct logReport report;
5186 unsigned int matches, limit;
5187
5188 limit = (argc > 1) ? atoi(argv[1]) : 10;
5189 if(limit < 1 || limit > 200)
5190 limit = 10;
5191
5192 memset(&discrim, 0, sizeof(discrim));
5193 discrim.masks.bot = chanserv;
5194 discrim.masks.channel_name = channel->name;
5195 if(argc > 2)
5196 discrim.masks.command = argv[2];
5197 discrim.limit = limit;
5198 discrim.max_time = INT_MAX;
5199 discrim.severities = 1 << LOG_COMMAND;
5200 report.reporter = chanserv;
5201 report.user = user;
de9510bc 5202 reply("CSMSG_EVENT_SEARCH_RESULTS", channel->name);
cde20a3f
AS
5203 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5204 reply("CSMSG_BAR");
d76ed9a9
AS
5205 matches = log_entry_search(&discrim, log_report_entry, &report);
5206 if(matches)
5207 reply("MSG_MATCH_COUNT", matches);
5208 else
5209 reply("MSG_NO_MATCHES");
5210 return 1;
5211}
5212
5213static CHANSERV_FUNC(cmd_say)
5214{
5215 char *msg;
5216 if(channel)
5217 {
5218 REQUIRE_PARAMS(2);
5219 msg = unsplit_string(argv + 1, argc - 1, NULL);
5220 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5221 }
5222 else if(GetUserH(argv[1]))
5223 {
5224 REQUIRE_PARAMS(3);
5225 msg = unsplit_string(argv + 2, argc - 2, NULL);
5226 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5227 }
5228 else
5229 {
5230 reply("MSG_NOT_TARGET_NAME");
5231 return 0;
5232 }
5233 return 1;
5234}
5235
5236static CHANSERV_FUNC(cmd_emote)
5237{
5238 char *msg;
5239 assert(argc >= 2);
5240 if(channel)
5241 {
5242 /* CTCP is so annoying. */
5243 msg = unsplit_string(argv + 1, argc - 1, NULL);
5244 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5245 }
5246 else if(GetUserH(argv[1]))
5247 {
5248 msg = unsplit_string(argv + 2, argc - 2, NULL);
5249 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5250 }
5251 else
5252 {
5253 reply("MSG_NOT_TARGET_NAME");
5254 return 0;
5255 }
5256 return 1;
5257}
5258
5259struct channelList *
5260chanserv_support_channels(void)
5261{
5262 return &chanserv_conf.support_channels;
5263}
5264
5265static CHANSERV_FUNC(cmd_expire)
5266{
5267 int channel_count = registered_channels;
5268 expire_channels(NULL);
5269 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5270 return 1;
5271}
5272
5273static void
5274chanserv_expire_suspension(void *data)
5275{
5276 struct suspended *suspended = data;
5277 struct chanNode *channel;
d76ed9a9
AS
5278
5279 if(!suspended->expires || (now < suspended->expires))
5280 suspended->revoked = now;
5281 channel = suspended->cData->channel;
5282 suspended->cData->channel = channel;
5283 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
0d16e639 5284 if(!IsOffChannel(suspended->cData))
5285 {
5286 struct mod_chanmode change;
5287 mod_chanmode_init(&change);
5288 change.argc = 1;
5289 change.args[0].mode = MODE_CHANOP;
5290 change.args[0].u.member = AddChannelUser(chanserv, channel);
5291 mod_chanmode_announce(chanserv, channel, &change);
5292 }
d76ed9a9
AS
5293}
5294
5295static CHANSERV_FUNC(cmd_csuspend)
5296{
5297 struct suspended *suspended;
5298 char reason[MAXLEN];
5299 time_t expiry, duration;
5300 struct userData *uData;
5301
5302 REQUIRE_PARAMS(3);
5303
5304 if(IsProtected(channel->channel_info))
5305 {
5306 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5307 return 0;
5308 }
5309
5310 if(argv[1][0] == '!')
5311 argv[1]++;
5312 else if(IsSuspended(channel->channel_info))
5313 {
5314 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5315 show_suspension_info(cmd, user, channel->channel_info->suspended);
5316 return 0;
5317 }
5318
5319 if(!strcmp(argv[1], "0"))
5320 expiry = 0;
5321 else if((duration = ParseInterval(argv[1])))
5322 expiry = now + duration;
5323 else
5324 {
5325 reply("MSG_INVALID_DURATION", argv[1]);
5326 return 0;
5327 }
5328
5329 unsplit_string(argv + 2, argc - 2, reason);
5330
5331 suspended = calloc(1, sizeof(*suspended));
5332 suspended->revoked = 0;
5333 suspended->issued = now;
5334 suspended->suspender = strdup(user->handle_info->handle);
5335 suspended->expires = expiry;
5336 suspended->reason = strdup(reason);
5337 suspended->cData = channel->channel_info;
5338 suspended->previous = suspended->cData->suspended;
5339 suspended->cData->suspended = suspended;
5340
5341 if(suspended->expires)
5342 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5343
5344 if(IsSuspended(channel->channel_info))
5345 {
5346 suspended->previous->revoked = now;
5347 if(suspended->previous->expires)
5348 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5349 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5350 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5351 }
5352 else
5353 {
5354 /* Mark all users in channel as absent. */
5355 for(uData = channel->channel_info->users; uData; uData = uData->next)
5356 {
5357 if(uData->present)
5358 {
5359 uData->seen = now;
5360 uData->present = 0;
5361 }
5362 }
5363
5364 /* Mark the channel as suspended, then part. */
5365 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5366 DelChannelUser(chanserv, channel, suspended->reason, 0);
5367 reply("CSMSG_SUSPENDED", channel->name);
5368 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5369 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5370 }
5371 return 1;
5372}
5373
5374static CHANSERV_FUNC(cmd_cunsuspend)
5375{
5376 struct suspended *suspended;
5377 char message[MAXLEN];
5378
5379 if(!IsSuspended(channel->channel_info))
5380 {
5381 reply("CSMSG_NOT_SUSPENDED", channel->name);
5382 return 0;
5383 }
5384
5385 suspended = channel->channel_info->suspended;
5386
5387 /* Expire the suspension and join ChanServ to the channel. */
5388 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5389 chanserv_expire_suspension(suspended);
5390 reply("CSMSG_UNSUSPENDED", channel->name);
5391 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5392 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5393 return 1;
5394}
5395
5396typedef struct chanservSearch
5397{
5398 char *name;
5399 char *registrar;
5400
5401 time_t unvisited;
5402 time_t registered;
5403
5404 unsigned long flags;
5405 unsigned int limit;
5406} *search_t;
5407
5408typedef void (*channel_search_func)(struct chanData *channel, void *data);
5409
5410static search_t
5411chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5412{
5413 search_t search;
5414 unsigned int i;
5415
5416 search = malloc(sizeof(struct chanservSearch));
5417 memset(search, 0, sizeof(*search));
5418 search->limit = 25;
5419
5420 for(i = 0; i < argc; i++)
5421 {
5422 /* Assume all criteria require arguments. */
5423 if(i == (argc - 1))
5424 {
5425 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5426 goto fail;
5427 }
5428
5429 if(!irccasecmp(argv[i], "name"))
5430 search->name = argv[++i];
5431 else if(!irccasecmp(argv[i], "registrar"))
5432 search->registrar = argv[++i];
5433 else if(!irccasecmp(argv[i], "unvisited"))
5434 search->unvisited = ParseInterval(argv[++i]);
5435 else if(!irccasecmp(argv[i], "registered"))
5436 search->registered = ParseInterval(argv[++i]);
5437 else if(!irccasecmp(argv[i], "flags"))
5438 {
5439 i++;
5440 if(!irccasecmp(argv[i], "nodelete"))
5441 search->flags |= CHANNEL_NODELETE;
5442 else if(!irccasecmp(argv[i], "suspended"))
5443 search->flags |= CHANNEL_SUSPENDED;
5444 else
5445 {
5446 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5447 goto fail;
5448 }
5449 }
5450 else if(!irccasecmp(argv[i], "limit"))
5451 search->limit = strtoul(argv[++i], NULL, 10);
5452 else
5453 {
5454 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5455 goto fail;
5456 }
5457 }
5458
5459 if(search->name && !strcmp(search->name, "*"))
5460 search->name = 0;
5461 if(search->registrar && !strcmp(search->registrar, "*"))
5462 search->registrar = 0;
5463
5464 return search;
5465 fail:
5466 free(search);
5467 return NULL;
5468}
5469
5470static int
5471chanserv_channel_match(struct chanData *channel, search_t search)
5472{
5473 const char *name = channel->channel->name;
5474 if((search->name && !match_ircglob(name, search->name)) ||
5475 (search->registrar && !channel->registrar) ||
5476 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5477 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5478 (search->registered && (now - channel->registered) > search->registered) ||
5479 (search->flags && ((search->flags & channel->flags) != search->flags)))
5480 return 0;
5481
5482 return 1;
5483}
5484
5485static unsigned int
5486chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5487{
5488 struct chanData *channel;
5489 unsigned int matches = 0;
5490
5491 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5492 {
5493 if(!chanserv_channel_match(channel, search))
5494 continue;
5495 matches++;
5496 smf(channel, data);
5497 }
5498
5499 return matches;
5500}
5501
5502static void
5503search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5504{
5505}
5506
5507static void
5508search_print(struct chanData *channel, void *data)
5509{
5510 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5511}
5512
5513static CHANSERV_FUNC(cmd_search)
5514{
5515 search_t search;
5516 unsigned int matches;
5517 channel_search_func action;
5518
5519 REQUIRE_PARAMS(3);
5520
5521 if(!irccasecmp(argv[1], "count"))
5522 action = search_count;
5523 else if(!irccasecmp(argv[1], "print"))
5524 action = search_print;
5525 else
5526 {
5527 reply("CSMSG_ACTION_INVALID", argv[1]);
5528 return 0;
5529 }
5530
5531 search = chanserv_search_create(user, argc - 2, argv + 2);
5532 if(!search)
5533 return 0;
5534
5535 if(action == search_count)
5536 search->limit = INT_MAX;
5537
5538 if(action == search_print)
de9510bc 5539 {
cde20a3f
AS
5540 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5541 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
5542 reply("CSMSG_BAR");
de9510bc 5543 }
d76ed9a9
AS
5544
5545 matches = chanserv_channel_search(search, action, user);
5546
5547 if(matches)
5548 reply("MSG_MATCH_COUNT", matches);
5549 else
5550 reply("MSG_NO_MATCHES");
5551
5552 free(search);
5553 return 1;
5554}
5555
5556static CHANSERV_FUNC(cmd_unvisited)
5557{
5558 struct chanData *cData;
5559 time_t interval = chanserv_conf.channel_expire_delay;
5560 char buffer[INTERVALLEN];
5561 unsigned int limit = 25, matches = 0;
5562
5563 if(argc > 1)
5564 {
5565 interval = ParseInterval(argv[1]);
5566 if(argc > 2)
5567 limit = atoi(argv[2]);
5568 }
5569
5570 intervalString(buffer, interval, user->handle_info);
5571 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5572
5573 for(cData = channelList; cData && matches < limit; cData = cData->next)
5574 {
5575 if((now - cData->visited) < interval)
5576 continue;
5577
5578 intervalString(buffer, now - cData->visited, user->handle_info);
5579 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5580 matches++;
5581 }
5582
5583 return 1;
5584}
5585
5586static MODCMD_FUNC(chan_opt_defaulttopic)
5587{
5588 if(argc > 1)
5589 {
5590 char *topic;
5591
5592 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5593 {
5594 reply("CSMSG_TOPIC_LOCKED", channel->name);
5595 return 0;
5596 }
5597
5598 topic = unsplit_string(argv+1, argc-1, NULL);
5599
5600 free(channel->channel_info->topic);
5601 if(topic[0] == '*' && topic[1] == 0)
5602 {
5603 topic = channel->channel_info->topic = NULL;
5604 }
5605 else
5606 {
5607 topic = channel->channel_info->topic = strdup(topic);
5608 if(channel->channel_info->topic_mask
5609 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5610 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5611 }
7fda2b52 5612 SetChannelTopic(channel, chanserv, chanserv, topic ? topic : "", 1);
d76ed9a9
AS
5613 }
5614
5615 if(channel->channel_info->topic)
5616 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5617 else
5618 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5619 return 1;
5620}
5621
5622static MODCMD_FUNC(chan_opt_topicmask)
5623{
5624 if(argc > 1)
5625 {
5626 struct chanData *cData = channel->channel_info;
5627 char *mask;
5628
5629 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5630 {
5631 reply("CSMSG_TOPIC_LOCKED", channel->name);
5632 return 0;
5633 }
5634
5635 mask = unsplit_string(argv+1, argc-1, NULL);
5636
5637 if(cData->topic_mask)
5638 free(cData->topic_mask);
5639 if(mask[0] == '*' && mask[1] == 0)
5640 {
5641 cData->topic_mask = 0;
5642 }
5643 else
5644 {
5645 cData->topic_mask = strdup(mask);
5646 if(!cData->topic)
5647 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5648 else if(!match_ircglob(cData->topic, cData->topic_mask))
5649 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5650 }
5651 }
5652
5653 if(channel->channel_info->topic_mask)
5654 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5655 else
5656 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5657 return 1;
5658}
5659
5660int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5661{
5662 if(argc > 1)
5663 {
5664 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5665 char *previous;
5666
5667 previous = *data;
5668 if(greeting[0] == '*' && greeting[1] == 0)
5669 *data = NULL;
5670 else
5671 {
5672 unsigned int length = strlen(greeting);
5673 if(length > chanserv_conf.greeting_length)
5674 {
5675 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5676 return 0;
5677 }
5678 *data = strdup(greeting);
5679 }
5680 if(previous)
5681 free(previous);
5682 }
5683
5684 if(*data)
5685 reply(name, *data);
5686 else
5687 reply(name, user_find_message(user, "MSG_NONE"));
5688 return 1;
5689}
5690
5691static MODCMD_FUNC(chan_opt_greeting)
5692{
5693 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5694}
5695
5696static MODCMD_FUNC(chan_opt_usergreeting)
5697{
5698 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5699}
5700
5701static MODCMD_FUNC(chan_opt_modes)
5702{
5703 struct mod_chanmode *new_modes;
5704 char modes[MODELEN];
5705
5706 if(argc > 1)
5707 {
5708 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5709 {
5710 reply("CSMSG_NO_ACCESS");
5711 return 0;
5712 }
5713 if(argv[1][0] == '*' && argv[1][1] == 0)
5714 {
5715 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5716 }
2f61d1d7 5717 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1,MCP_KEY_FREE|MCP_REGISTERED, 0)))
d76ed9a9
AS
5718 {
5719 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5720 return 0;
5721 }
5722 else if(new_modes->argc > 1)
5723 {
5724 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5725 mod_chanmode_free(new_modes);
5726 return 0;
5727 }
5728 else
5729 {
5730 channel->channel_info->modes = *new_modes;
5731 modcmd_chanmode_announce(new_modes);
5732 mod_chanmode_free(new_modes);
5733 }
5734 }
5735
5736 mod_chanmode_format(&channel->channel_info->modes, modes);
5737 if(modes[0])
5738 reply("CSMSG_SET_MODES", modes);
5739 else
5740 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5741 return 1;
5742}
5743
5744#define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5745static int
5746channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5747{
5748 struct chanData *cData = channel->channel_info;
5749 int value;
5750
5751 if(argc > 1)
5752 {
5753 /* Set flag according to value. */
5754 if(enabled_string(argv[1]))
5755 {
5756 cData->flags |= mask;
5757 value = 1;
5758 }
5759 else if(disabled_string(argv[1]))
5760 {
5761 cData->flags &= ~mask;
5762 value = 0;
5763 }
5764 else
5765 {
5766 reply("MSG_INVALID_BINARY", argv[1]);
5767 return 0;
5768 }
5769 }
5770 else
5771 {
5772 /* Find current option value. */
5773 value = (cData->flags & mask) ? 1 : 0;
5774 }
5775
5776 if(value)
5777 reply(name, user_find_message(user, "MSG_ON"));
5778 else
5779 reply(name, user_find_message(user, "MSG_OFF"));
5780 return 1;
5781}
5782
5783static MODCMD_FUNC(chan_opt_nodelete)
5784{
5785 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5786 {
5787 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5788 return 0;
5789 }
5790
5791 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5792}
5793
5794static MODCMD_FUNC(chan_opt_dynlimit)
5795{
5796 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5797}
5798
5799static MODCMD_FUNC(chan_opt_offchannel)
5800{
5801 struct chanData *cData = channel->channel_info;
5802 int value;
5803
5804 if(argc > 1)
5805 {
5806 /* Set flag according to value. */
5807 if(enabled_string(argv[1]))
5808 {
5809 if(!IsOffChannel(cData))
5810 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5811 cData->flags |= CHANNEL_OFFCHANNEL;
5812 value = 1;
5813 }
5814 else if(disabled_string(argv[1]))
5815 {
5816 if(IsOffChannel(cData))
5817 {
5818 struct mod_chanmode change;
5819 mod_chanmode_init(&change);
5820 change.argc = 1;
5821 change.args[0].mode = MODE_CHANOP;
a32da4c7 5822 change.args[0].u.member = AddChannelUser(chanserv, channel);
d76ed9a9
AS
5823 mod_chanmode_announce(chanserv, channel, &change);
5824 }
5825 cData->flags &= ~CHANNEL_OFFCHANNEL;
5826 value = 0;
5827 }
5828 else
5829 {
5830 reply("MSG_INVALID_BINARY", argv[1]);
5831 return 0;
5832 }
5833 }
5834 else
5835 {
5836 /* Find current option value. */
5837 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5838 }
5839
5840 if(value)
5841 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5842 else
5843 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5844 return 1;
5845}
5846
5847static MODCMD_FUNC(chan_opt_defaults)
5848{
5849 struct userData *uData;
5850 struct chanData *cData;
5851 const char *confirm;
5852 enum levelOption lvlOpt;
5853 enum charOption chOpt;
5854
5855 cData = channel->channel_info;
5856 uData = GetChannelUser(cData, user->handle_info);
5857 if(!uData || (uData->access < UL_OWNER))
5858 {
5859 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5860 return 0;
5861 }
5862 confirm = make_confirmation_string(uData);
5863 if((argc < 2) || strcmp(argv[1], confirm))
5864 {
5865 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5866 return 0;
5867 }
5868 cData->flags = CHANNEL_DEFAULT_FLAGS;
5869 cData->modes = chanserv_conf.default_modes;
5870 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5871 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5872 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5873 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5874 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5875 return 1;
5876}
5877
5878static int
5879channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5880{
5881 struct chanData *cData = channel->channel_info;
5882 struct userData *uData;
5883 unsigned short value;
5884
5885 if(argc > 1)
5886 {
5887 if(!check_user_level(channel, user, option, 1, 1))
5888 {
5889 reply("CSMSG_CANNOT_SET");
5890 return 0;
5891 }
5892 value = user_level_from_name(argv[1], UL_OWNER+1);
5893 if(!value && strcmp(argv[1], "0"))
5894 {
5895 reply("CSMSG_INVALID_ACCESS", argv[1]);
5896 return 0;
5897 }
5898 uData = GetChannelUser(cData, user->handle_info);
5899 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5900 {
5901 reply("CSMSG_BAD_SETLEVEL");
5902 return 0;
5903 }
5904 switch(option)
5905 {
4b6129c0
AS
5906 case lvlSetters:
5907 /* This test only applies to owners, since non-owners
5908 * trying to set an option to above their level get caught
5909 * by the CSMSG_BAD_SETLEVEL test above.
5910 */
5911 if(value > uData->access)
5912 {
5913 reply("CSMSG_BAD_SETTERS");
5914 return 0;
5915 }
5916 break;
5917 default:
5918 break;
d76ed9a9
AS
5919 }
5920 cData->lvlOpts[option] = value;
5921 }
5922 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5923 return argc > 1;
5924}
5925
5926static MODCMD_FUNC(chan_opt_enfops)
5927{
5928 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5929}
5930
55342ce8 5931static MODCMD_FUNC(chan_opt_enfhalfops)
5932{
5933 return channel_level_option(lvlEnfHalfOps, CSFUNC_ARGS);
5934}
d76ed9a9
AS
5935static MODCMD_FUNC(chan_opt_enfmodes)
5936{
5937 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5938}
5939
5940static MODCMD_FUNC(chan_opt_enftopic)
5941{
5942 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5943}
5944
5945static MODCMD_FUNC(chan_opt_pubcmd)
5946{
5947 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5948}
5949
5950static MODCMD_FUNC(chan_opt_setters)
5951{
5952 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5953}
5954
d76ed9a9
AS
5955static MODCMD_FUNC(chan_opt_userinfo)
5956{
5957 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5958}
5959
d76ed9a9
AS
5960static MODCMD_FUNC(chan_opt_topicsnarf)
5961{
5962 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5963}
5964
5965static MODCMD_FUNC(chan_opt_inviteme)
5966{
5967 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5968}
5969
31f23f13
AS
5970/* TODO: Make look like this when no args are
5971 * given:
5972 * -X3- -------------------------------
5973 * -X3- BanTimeout: Bans are removed:
5974 * -X3- ----- * indicates current -----
5975 * -X3- 0: [*] Never.
5976 * -X3- 1: [ ] After 10 minutes.
5977 * -X3- 2: [ ] After 2 hours.
5978 * -X3- 3: [ ] After 4 hours.
5979 * -X3- 4: [ ] After 24 hours.
5980 * -X3- 5: [ ] After one week.
5981 * -X3- ------------- End -------------
5982 */
d76ed9a9
AS
5983static int
5984channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5985{
5986 struct chanData *cData = channel->channel_info;
5987 int count = charOptions[option].count, index;
5988
5989 if(argc > 1)
5990 {
5991 index = atoi(argv[1]);
5992
5993 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5994 {
5995 reply("CSMSG_INVALID_NUMERIC", index);
5996 /* Show possible values. */
5997 for(index = 0; index < count; index++)
5998 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5999 return 0;
6000 }
6001
6002 cData->chOpts[option] = charOptions[option].values[index].value;
6003 }
6004 else
6005 {
6006 /* Find current option value. */
6007 find_value:
6008 for(index = 0;
6009 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
6010 index++);
6011 if(index == count)
6012 {
6013 /* Somehow, the option value is corrupt; reset it to the default. */
6014 cData->chOpts[option] = charOptions[option].default_value;
6015 goto find_value;
6016 }
6017 }
6018
6019 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
6020 return 1;
6021}
6022
4b6129c0 6023static MODCMD_FUNC(chan_opt_automode)
c8ca69a0 6024{
4b6129c0 6025 return channel_multiple_option(chAutomode, CSFUNC_ARGS);
c8ca69a0
AS
6026}
6027
d76ed9a9
AS
6028static MODCMD_FUNC(chan_opt_protect)
6029{
6030 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6031}
6032
6033static MODCMD_FUNC(chan_opt_toys)
6034{
6035 return channel_multiple_option(chToys, CSFUNC_ARGS);
6036}
6037
6038static MODCMD_FUNC(chan_opt_ctcpreaction)
6039{
6040 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6041}
6042
31f23f13
AS
6043static MODCMD_FUNC(chan_opt_bantimeout)
6044{
6045 return channel_multiple_option(chBanTimeout, CSFUNC_ARGS);
6046}
6047
d76ed9a9
AS
6048static MODCMD_FUNC(chan_opt_topicrefresh)
6049{
6050 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6051}
6052
7637f48f 6053static MODCMD_FUNC(chan_opt_resync)
6054{
6055 return channel_multiple_option(chResync, CSFUNC_ARGS);
6056}
6057
d76ed9a9
AS
6058static struct svccmd_list set_shows_list;
6059
6060static void
6061handle_svccmd_unbind(struct svccmd *target) {
6062 unsigned int ii;
6063 for(ii=0; ii<set_shows_list.used; ++ii)
6064 if(target == set_shows_list.list[ii])
6065 set_shows_list.used = 0;
6066}
6067
6068static CHANSERV_FUNC(cmd_set)
6069{
6070 struct svccmd *subcmd;
6071 char buf[MAXLEN];
6072 unsigned int ii;
6073
6074 /* Check if we need to (re-)initialize set_shows_list. */
6075 if(!set_shows_list.used)
6076 {
6077 if(!set_shows_list.size)
6078 {
6079 set_shows_list.size = chanserv_conf.set_shows->used;
6080 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6081 }
6082 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6083 {
6084 const char *name = chanserv_conf.set_shows->list[ii];
6085 sprintf(buf, "%s %s", argv[0], name);
6086 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6087 if(!subcmd)
6088 {
6089 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6090 continue;
6091 }
6092 svccmd_list_append(&set_shows_list, subcmd);
6093 }
6094 }
6095
6096 if(argc < 2)
6097 {
cde20a3f
AS
6098 reply("CSMSG_CHANNEL_OPTIONS", channel->name);
6099 if(user->handle_info && user->handle_info->userlist_style != HI_STYLE_CLEAN)
6100 reply("CSMSG_BAR");
d76ed9a9
AS
6101 for(ii = 0; ii < set_shows_list.used; ii++)
6102 {
6103 subcmd = set_shows_list.list[ii];
6104 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6105 }
de9510bc 6106 reply("CSMSG_CHANNEL_OPTIONS_END");
d76ed9a9
AS
6107 return 1;
6108 }
6109
6110 sprintf(buf, "%s %s", argv[0], argv[1]);
6111 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6112 if(!subcmd)
6113 {
6114 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6115 return 0;
6116 }
6117 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6118 {
6119 reply("CSMSG_NO_ACCESS");
6120 return 0;
6121 }
6122
6123 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6124}
6125
6126static int
6127user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6128{
6129 struct userData *uData;
6130
6131 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6132 if(!uData)
6133 {
6134 reply("CSMSG_NOT_USER", channel->name);
6135 return 0;
6136 }
6137
6138 if(argc < 2)
6139 {
6140 /* Just show current option value. */
6141 }
6142 else if(enabled_string(argv[1]))
6143 {
6144 uData->flags |= mask;
6145 }
6146 else if(disabled_string(argv[1]))
6147 {
6148 uData->flags &= ~mask;
6149 }
6150 else
6151 {
6152 reply("MSG_INVALID_BINARY", argv[1]);
6153 return 0;
6154 }
6155
6156 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6157 return 1;
6158}
6159
c8ca69a0 6160static MODCMD_FUNC(user_opt_autoop)
d76ed9a9
AS
6161{
6162 struct userData *uData;
6163
6164 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6165 if(!uData)
6166 {
6167 reply("CSMSG_NOT_USER", channel->name);
6168 return 0;
6169 }
b75e24a3 6170 if(uData->access < UL_OP /*channel->channel_info->lvlOpts[lvlGiveOps]*/)
c8ca69a0 6171 return user_binary_option("CSMSG_USET_AUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
d76ed9a9 6172 else
c8ca69a0 6173 return user_binary_option("CSMSG_USET_AUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
b75e24a3 6174 /* TODO: add halfops error message? or is the op one generic enough? */
d76ed9a9
AS
6175}
6176
6177static MODCMD_FUNC(user_opt_autoinvite)
6178{
6179 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6180}
6181
6182static MODCMD_FUNC(user_opt_info)
6183{
6184 struct userData *uData;
6185 char *infoline;
6186
6187 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6188
6189 if(!uData)
6190 {
6191 /* If they got past the command restrictions (which require access)
6192 * but fail this test, we have some fool with security override on.
6193 */
6194 reply("CSMSG_NOT_USER", channel->name);
6195 return 0;
6196 }
6197
6198 if(argc > 1)
6199 {
6200 size_t bp;
6201 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6202 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6203 {
6204 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6205 return 0;
6206 }
6207 bp = strcspn(infoline, "\001");
6208 if(infoline[bp])
6209 {
6210 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6211 return 0;
6212 }
6213 if(uData->info)
6214 free(uData->info);
6215 if(infoline[0] == '*' && infoline[1] == 0)
6216 uData->info = NULL;
6217 else
6218 uData->info = strdup(infoline);
6219 }
6220 if(uData->info)
6221 reply("CSMSG_USET_INFO", uData->info);
6222 else
6223 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6224 return 1;
6225}
6226
6227struct svccmd_list uset_shows_list;
6228
6229static CHANSERV_FUNC(cmd_uset)
6230{
6231 struct svccmd *subcmd;
6232 char buf[MAXLEN];
6233 unsigned int ii;
6234
6235 /* Check if we need to (re-)initialize uset_shows_list. */
6236 if(!uset_shows_list.used)
6237 {
6238 char *options[] =
6239 {
c8ca69a0 6240 "AutoOp", "AutoInvite", "Info"
d76ed9a9
AS
6241 };
6242
6243 if(!uset_shows_list.size)
6244 {
6245 uset_shows_list.size = ArrayLength(options);
6246 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6247 }
6248 for(ii = 0; ii < ArrayLength(options); ii++)
6249 {
6250 const char *name = options[ii];
6251 sprintf(buf, "%s %s", argv[0], name);
6252 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6253 if(!subcmd)
6254 {
6255 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6256 continue;
6257 }
6258 svccmd_list_append(&uset_shows_list, subcmd);
6259 }
6260 }
6261
6262 if(argc < 2)
6263 {
6264 /* Do this so options are presented in a consistent order. */
6265 reply("CSMSG_USER_OPTIONS");
6266 for(ii = 0; ii < uset_shows_list.used; ii++)
6267 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6268 return 1;
6269 }
6270
6271 sprintf(buf, "%s %s", argv[0], argv[1]);
6272 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6273 if(!subcmd)
6274 {
6275 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6276 return 0;
6277 }
6278
6279 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6280}
6281
6282static CHANSERV_FUNC(cmd_giveownership)
6283{
6284 struct handle_info *new_owner_hi;
6285 struct userData *new_owner, *curr_user;
6286 struct chanData *cData = channel->channel_info;
6287 struct do_not_register *dnr;
82f37c08 6288 struct giveownership *giveownership;
6289 unsigned int force, override;
6290 unsigned short co_access, new_owner_old_access;
6291 char reason[MAXLEN], transfer_reason[MAXLEN];
d76ed9a9
AS
6292
6293 REQUIRE_PARAMS(2);
6294 curr_user = GetChannelAccess(cData, user->handle_info);
6295 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
82f37c08 6296
6297 struct userData *uData = _GetChannelUser(channel->channel_info, user->handle_info, 1, 0);
6298 override = ((cmd->effective_flags & MODCMD_REQUIRE_CHANUSER)
6299 && (uData->access > 500)
6300 && (!(uData = _GetChannelUser(channel->channel_info, user->handle_info, 0, 0))
6301 || uData->access < 500));
6302
6303
d76ed9a9
AS
6304 if(!curr_user || (curr_user->access != UL_OWNER))
6305 {
6306 struct userData *owner = NULL;
6307 for(curr_user = channel->channel_info->users;
6308 curr_user;
6309 curr_user = curr_user->next)
6310 {
6311 if(curr_user->access != UL_OWNER)
6312 continue;
6313 if(owner)
6314 {
6315 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6316 return 0;
6317 }
6318 owner = curr_user;
6319 }
6320 curr_user = owner;
6321 }
a32da4c7 6322 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
6323 {
6324 char delay[INTERVALLEN];
6325 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6326 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6327 return 0;
6328 }
d76ed9a9
AS
6329 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6330 return 0;
6331 if(new_owner_hi == user->handle_info)
6332 {
6333 reply("CSMSG_NO_TRANSFER_SELF");
6334 return 0;
6335 }
6336 new_owner = GetChannelAccess(cData, new_owner_hi);
6337 if(!new_owner)
6338 {
0d16e639 6339 if(force)
6340 {
6341 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
6342 }
6343 else
6344 {
6345 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6346 return 0;
6347 }
d76ed9a9
AS
6348 }
6349 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6350 {
6351 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6352 return 0;
6353 }
6354 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6355 if(!IsHelping(user))
6356 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6357 else
1117fc5a 6358 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
d76ed9a9
AS
6359 return 0;
6360 }
82f37c08 6361
6362 new_owner_old_access = new_owner->access;
d76ed9a9
AS
6363 if(new_owner->access >= UL_COOWNER)
6364 co_access = new_owner->access;
6365 else
6366 co_access = UL_COOWNER;
6367 new_owner->access = UL_OWNER;
6368 if(curr_user)
6369 curr_user->access = co_access;
a32da4c7 6370 cData->ownerTransfer = now;
82f37c08 6371
6372 giveownership = calloc(1, sizeof(*giveownership));
6373 giveownership->issued = now;
6374 giveownership->old_owner = curr_user->handle->handle;
6375 giveownership->target = new_owner_hi->handle;
6376 giveownership->target_access = new_owner_old_access;
6377 if(override)
6378 {
6379 if(argc > (2 + force))
6380 {
6381 unsplit_string(argv + 2 + force, argc - 2 - force, transfer_reason);
6382 giveownership->reason = strdup(transfer_reason);
6383 }
6384 giveownership->staff_issuer = strdup(user->handle_info->handle);
6385 }
6386
6387 giveownership->previous = channel->channel_info->giveownership;
6388 channel->channel_info->giveownership = giveownership;
6389
d76ed9a9
AS
6390 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6391 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6392 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6393 return 1;
6394}
6395
b10abdb2 6396static void
6397chanserv_expire_user_suspension(void *data)
6398{
6399 struct userData *target = data;
6400
6401 target->expires = 0;
6402 target->flags &= ~USER_SUSPENDED;
6403}
6404
d76ed9a9
AS
6405static CHANSERV_FUNC(cmd_suspend)
6406{
6407 struct handle_info *hi;
6408 struct userData *self, *target;
b10abdb2 6409 time_t expiry;
d76ed9a9 6410
b10abdb2 6411 REQUIRE_PARAMS(3);
d76ed9a9
AS
6412 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6413 self = GetChannelUser(channel->channel_info, user->handle_info);
6414 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6415 {
6416 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6417 return 0;
6418 }
6419 if(target->access >= self->access)
6420 {
6421 reply("MSG_USER_OUTRANKED", hi->handle);
6422 return 0;
6423 }
6424 if(target->flags & USER_SUSPENDED)
6425 {
6426 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6427 return 0;
6428 }
6429 if(target->present)
6430 {
6431 target->present = 0;
6432 target->seen = now;
6433 }
b10abdb2 6434 if(!strcmp(argv[2], "0"))
6435 expiry = 0;
6436 else
6437 {
6438 unsigned int duration;
6439 if(!(duration = ParseInterval(argv[2])))
6440 {
6441 reply("MSG_INVALID_DURATION", argv[2]);
6442 return 0;
6443 }
6444 expiry = now + duration;
6445 }
6446
6447 target->expires = expiry;
6448
6449 if(target->expires)
6450 timeq_add(target->expires, chanserv_expire_user_suspension, target);
6451
d76ed9a9
AS
6452 target->flags |= USER_SUSPENDED;
6453 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6454 return 1;
6455}
6456
6457static CHANSERV_FUNC(cmd_unsuspend)
6458{
6459 struct handle_info *hi;
6460 struct userData *self, *target;
6461
6462 REQUIRE_PARAMS(2);
6463 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6464 self = GetChannelUser(channel->channel_info, user->handle_info);
6465 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6466 {
6467 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6468 return 0;
6469 }
6470 if(target->access >= self->access)
6471 {
6472 reply("MSG_USER_OUTRANKED", hi->handle);
6473 return 0;
6474 }
6475 if(!(target->flags & USER_SUSPENDED))
6476 {
6477 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6478 return 0;
6479 }
6480 target->flags &= ~USER_SUSPENDED;
a32da4c7 6481 scan_user_presence(target, NULL);
b10abdb2 6482 timeq_del(target->expires, chanserv_expire_user_suspension, target, 0);
d76ed9a9
AS
6483 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6484 return 1;
6485}
6486
6487static MODCMD_FUNC(cmd_deleteme)
6488{
6489 struct handle_info *hi;
6490 struct userData *target;
6491 const char *confirm_string;
6492 unsigned short access;
6493 char *channel_name;
6494
6495 hi = user->handle_info;
6496 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6497 {
6498 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6499 return 0;
6500 }
6501 if(target->access == UL_OWNER)
6502 {
6503 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6504 return 0;
6505 }
6506 confirm_string = make_confirmation_string(target);
6507 if((argc < 2) || strcmp(argv[1], confirm_string))
6508 {
6509 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6510 return 0;
6511 }
6512 access = target->access;
6513 channel_name = strdup(channel->name);
6514 del_channel_user(target, 1);
6515 reply("CSMSG_DELETED_YOU", access, channel_name);
6516 free(channel_name);
6517 return 1;
6518}
6519
6520static void
6521chanserv_refresh_topics(UNUSED_ARG(void *data))
6522{
6523 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6524 struct chanData *cData;
6525 char opt;
6526
6527 for(cData = channelList; cData; cData = cData->next)
6528 {
6529 if(IsSuspended(cData))
6530 continue;
6531 opt = cData->chOpts[chTopicRefresh];
6532 if(opt == 'n')
6533 continue;
6534 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6535 continue;
6536 if(cData->topic)
7fda2b52 6537 SetChannelTopic(cData->channel, chanserv, chanserv, cData->topic, 1);
d76ed9a9
AS
6538 cData->last_refresh = refresh_num;
6539 }
6540 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6541}
6542
7637f48f 6543static void
6544chanserv_auto_resync(UNUSED_ARG(void *data))
6545{
6546 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6547 struct chanData *cData;
6548 char opt;
6549
6550 for(cData = channelList; cData; cData = cData->next)
6551 {
6552 if(IsSuspended(cData)) continue;
6553 opt = cData->chOpts[chResync];
6554 if(opt == 'n') continue;
6555 if((refresh_num - cData->last_resync) < (unsigned int)(1 << (opt - '1'))) continue;
6556 resync_channel(cData->channel);
6557 cData->last_resync = refresh_num;
6558 }
6559 timeq_add(now + chanserv_conf.refresh_period, chanserv_auto_resync, NULL);
6560}
6561
d76ed9a9
AS
6562static CHANSERV_FUNC(cmd_unf)
6563{
6564 if(channel)
6565 {
6566 char response[MAXLEN];
6567 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6568 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6569 irc_privmsg(cmd->parent->bot, channel->name, response);
6570 }
6571 else
6572 reply("CSMSG_UNF_RESPONSE");
6573 return 1;
6574}
6575
6576static CHANSERV_FUNC(cmd_ping)
6577{
6578 if(channel)
6579 {
6580 char response[MAXLEN];
6581 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6582 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6583 irc_privmsg(cmd->parent->bot, channel->name, response);
6584 }
6585 else
6586 reply("CSMSG_PING_RESPONSE");
6587 return 1;
6588}
6589
6590static CHANSERV_FUNC(cmd_wut)
6591{
6592 if(channel)
6593 {
6594 char response[MAXLEN];
6595 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6596 sprintf(response, "\ 2%s\ 2: %s", user->nick, fmt);
6597 irc_privmsg(cmd->parent->bot, channel->name, response);
6598 }
6599 else
6600 reply("CSMSG_WUT_RESPONSE");
6601 return 1;
6602}
6603
240a3274 6604#ifdef lame8ball
d76ed9a9
AS
6605static CHANSERV_FUNC(cmd_8ball)
6606{
6607 unsigned int i, j, accum;
6608 const char *resp;
6609
6610 REQUIRE_PARAMS(2);
6611 accum = 0;
6612 for(i=1; i<argc; i++)
6613 for(j=0; argv[i][j]; j++)
6614 accum = (accum << 5) - accum + toupper(argv[i][j]);
6615 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6616 if(channel)
6617 {
6618 char response[MAXLEN];
6619 sprintf(response, "\ 2%s\ 2: %s", user->nick, resp);
6620 irc_privmsg(cmd->parent->bot, channel->name, response);
6621 }
6622 else
6623 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6624 return 1;
6625}
6626
240a3274
AS
6627#else /* Use cool 8ball instead */
6628
6629void eightball(char *outcome, int method, unsigned int seed)
6630{
6631 int answer = 0;
6632
6633#define NUMOFCOLORS 18
6634 char ballcolors[50][50] = {"blue", "red", "green", "yellow",
6635 "white", "black", "grey", "brown",
6636 "yellow", "pink", "purple", "orange", "teal", "burgandy",
6637 "fuchsia","turquoise","magenta", "cyan"};
6638#define NUMOFLOCATIONS 50
6639 char balllocations[50][55] = {
6640 "Locke's house", "Oregon", "California", "Indiana", "Canada",
6641 "Russia", "Japan", "Florida", "the Bahamas", "Hiroshima",
6642 "the Caribbean", "the Everglades", "your head", "your pants", "your school",
6643 "the Statue of Liberty", "Mt. Fugi", "your mother's house", "IRC", "OSU",
6644 "Locke's cat", "the closet", "the washroom", "the lake", "Spain",
6645 "the bathtub", "the toilet", "the sewer", "a horse", "Jupiter",
6646 "Uranus", "Pluto", "a dark place", "your undies", "your shirt",
6647 "your bra", "your hair", "your bed", "the couch", "the wall",
6648 "Reed", "here --> [X]", "your brain", "Italy", "the Netherlands",
6649 "Mars", "my hardware", "the bar", "Neverland Ranch", "Germany" };
6650#define NUMOFPREPS 15
6651 char ballpreps[50][50] = {
6652 "Near", "Somewhere near", "In", "In", "In",
6653 "In", "Hiding in", "Under", "Next to", "Over",
6654 "Crying in", "Right beside", "Nowhere near", "North of", "Trying to find"};
6655#define NUMOFNUMS 34
6656 char ballnums[50][50] = {
6657 "A hundred", "A thousand", "A few", "42",
6658 "About 1", "About 2", "About 3", "About 4", "About 5", "About 6", "About 7", "About 8", "About 9", "About 10",
6659 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6660 "1", "2", "3", "4", "5", "6", "7", "8", "9", "Ten",
6661 };
6662#define NUMOFMULTS 8
6663 char ballmults[50][50] = { " million", " or so", " thousand", "", " or less", " or more", "", ""};
6664
6665 /* Method:
6666 * 0: normal (Not used in x3)
6667 * 1: color
6668 * 2: where is
6669 * 3: how many
6670 */
6671
6672 srand(seed);
6673 if (method == 1) /* A Color */
6674 {
6675 char tmp[MAXLEN];
6676
6677 answer = (rand() % 12); /* Make sure this is the # of entries */
6678 switch(answer)
6679 {
6680 case 0: strcpy(tmp, "Very bright %s, I'd say.");
6681 break;
6682 case 1: strcpy(tmp, "Sort of a light %s color.");
6683 break;
6684 case 2: strcpy(tmp, "Dark and dreary %s.");
6685 break;
6686 case 3: strcpy(tmp, "Quite a pale shade of %s.");
6687 break;
6688 case 4: strcpy(tmp, "A gross kind of mucky %s.");
6689 break;
6690 case 5: strcpy(tmp, "Brilliant whiteish %s.");
338a82b5
AS
6691 break;
6692 case 6: case 7: case 8: case 9: strcpy(tmp, "%s.");
6693 break;
6694 case 10: strcpy(tmp, "Solid %s.");
6695 break;
6696 case 11: strcpy(tmp, "Transparent %s.");
6697 break;
240a3274
AS
6698 default: strcpy(outcome, "An invalid random number was generated.");
6699 return;
6700 }
6701 sprintf(outcome, tmp, ballcolors[rand() % NUMOFCOLORS]);
6702 return;
6703 }
6704 else if (method == 2) /* Location */
6705 {
6706 sprintf(outcome, "%s %s.", ballpreps[rand() % NUMOFPREPS], balllocations[rand() % NUMOFLOCATIONS]);
6707 }
6708 else if (method == 3) /* Number of ___ */
6709 {
6710 sprintf(outcome, "%s%s.", ballnums[rand() % NUMOFNUMS], ballmults[rand() % NUMOFMULTS]);
6711 }
6712 else
6713 {
6714 //Debug(DBGWARNING, "Error in 8ball.");
6715 }
6716 return;
6717}
6718
6719static CHANSERV_FUNC(cmd_8ball)
6720{
6721 char *word1, *word2, *word3;
6722 static char eb[MAXLEN];
6723 unsigned int accum, i, j;
6724
132f7859 6725 REQUIRE_PARAMS(2);
240a3274
AS
6726 accum = 0;
6727 for(i=1; i<argc; i++)
6728 for(j=0; argv[i][j]; j++)
6729 accum = (accum << 5) - accum + toupper(argv[i][j]);
6730
6731 accum += time(NULL)/3600;
6732 word1 = argv[1];
6733 word2 = argc>2?argv[2]:"";
6734 word3 = argc>3?argv[3]:"";
6735
6736/*** COLOR *****/
6737 if((word2) && strcasecmp(word1, "what") == 0 && strcasecmp(word2, "color") == 0)
6738 eightball(eb, 1, accum);
6739 else if((word3) && strcasecmp(word1, "what's") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6740 eightball(eb, 1, accum);
6741 else if((word3) && strcasecmp(word1, "whats") == 0 && strcasecmp(word2, "the") == 0 && strcasecmp(word3, "color") == 0)
6742 eightball(eb, 1, accum);
6743/*** LOCATION *****/
6744 else if(
6745 (
6746 word2 &&
6747 (
6748 (strcasecmp(word1, "where") == 0) &&
6749 (strcasecmp(word2, "is") == 0)
6750 )
6751 ) ||
6752 (
6753 strcasecmp(word1, "where's") == 0
6754 )
6755 )
6756 eightball(eb, 2, accum);
6757/*** NUMBER *****/
6758 else if((word2) && strcasecmp(word1, "how") == 0 && strcasecmp(word2, "many") == 0)
6759 eightball(eb, 3, accum);
6760/*** GENERIC *****/
6761 else
6762 {
6763 /* Generic 8ball question.. so pull from x3.conf srvx style */
6764 const char *resp;
6765
6766 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6767 if(channel)
6768 {
6769 char response[MAXLEN];
6770 sprintf(response, "\002%s\002: %s", user->nick, resp);
6771 irc_privmsg(cmd->parent->bot, channel->name, response);
6772 }
6773 else
6774 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6775 return 1;
6776 }
6777
6778 if(channel)
6779 {
6780 char response[MAXLEN];
6781 sprintf(response, "\002%s\002: %s", user->nick, eb);
6782 irc_privmsg(cmd->parent->bot, channel->name, response);
6783 }
6784 else
6785 send_message_type(4, user, cmd->parent->bot, "%s", eb);
6786 return 1;
6787}
6788#endif
6789
d76ed9a9
AS
6790static CHANSERV_FUNC(cmd_d)
6791{
6792 unsigned long sides, count, modifier, ii, total;
6793 char response[MAXLEN], *sep;
6794 const char *fmt;
6795
6796 REQUIRE_PARAMS(2);
6797 if((count = strtoul(argv[1], &sep, 10)) < 1)
6798 goto no_dice;
6799 if(sep[0] == 0)
6800 {
6801 if(count == 1)
6802 goto no_dice;
6803 sides = count;
6804 count = 1;
6805 modifier = 0;
6806 }
6807 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6808 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6809 {
6810 if(sep[0] == 0)
6811 modifier = 0;
6812 else if((sep[0] == '-') && isdigit(sep[1]))
6813 modifier = strtoul(sep, NULL, 10);
6814 else if((sep[0] == '+') && isdigit(sep[1]))
6815 modifier = strtoul(sep+1, NULL, 10);
6816 else
6817 goto no_dice;
6818 }
6819 else
6820 {
6821 no_dice:
6822 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6823 return 0;
6824 }
6825 if(count > 10)
6826 {
6827 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6828 return 0;
6829 }
6830 for(total = ii = 0; ii < count; ++ii)
6831 total += (rand() % sides) + 1;
6832 total += modifier;
6833
6834 if((count > 1) || modifier)
6835 {
6836 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6837 sprintf(response, fmt, total, count, sides, modifier);
6838 }
6839 else
6840 {
6841 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6842 sprintf(response, fmt, total, sides);
6843 }
6844 if(channel)
6845 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6846 else
6847 send_message_type(4, user, cmd->parent->bot, "%s", response);
6848 return 1;
6849}
6850
6851static CHANSERV_FUNC(cmd_huggle)
6852{
6853 /* CTCP must be via PRIVMSG, never notice */
6854 if(channel)
6855 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6856 else
6857 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6858 return 1;
6859}
6860
e5a8f7cd 6861static CHANSERV_FUNC(cmd_calc)
6862{
6863 char response[MAXLEN];
6864
6865 REQUIRE_PARAMS(2);
6866 do_math(response, unsplit_string(argv + 1, argc - 1, NULL));
6867
6868 if(channel)
d8cf9c21 6869 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
e5a8f7cd 6870 else
6871 send_message_type(4, user, cmd->parent->bot, "%s", response);
6872 return 1;
6873}
6874
d76ed9a9
AS
6875static void
6876chanserv_adjust_limit(void *data)
6877{
6878 struct mod_chanmode change;
6879 struct chanData *cData = data;
6880 struct chanNode *channel = cData->channel;
6881 unsigned int limit;
6882
6883 if(IsSuspended(cData))
6884 return;
6885
6886 cData->limitAdjusted = now;
6887 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6888 if(cData->modes.modes_set & MODE_LIMIT)
6889 {
6890 if(limit > cData->modes.new_limit)
6891 limit = cData->modes.new_limit;
6892 else if(limit == cData->modes.new_limit)
6893 return;
6894 }
6895
6896 mod_chanmode_init(&change);
6897 change.modes_set = MODE_LIMIT;
6898 change.new_limit = limit;
6899 mod_chanmode_announce(chanserv, channel, &change);
6900}
6901
6902static void
6903handle_new_channel(struct chanNode *channel)
6904{
6905 struct chanData *cData;
6906
6907 if(!(cData = channel->channel_info))
6908 return;
6909
6910 if(cData->modes.modes_set || cData->modes.modes_clear)
6911 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6912
6913 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7fda2b52 6914 SetChannelTopic(channel, chanserv, chanserv, channel->channel_info->topic, 1);
d76ed9a9
AS
6915}
6916
6917/* Welcome to my worst nightmare. Warning: Read (or modify)
6918 the code below at your own risk. */
6919static int
6920handle_join(struct modeNode *mNode)
6921{
6922 struct mod_chanmode change;
6923 struct userNode *user = mNode->user;
6924 struct chanNode *channel = mNode->channel;
6925 struct chanData *cData;
6926 struct userData *uData = NULL;
6927 struct banData *bData;
6928 struct handle_info *handle;
6929 unsigned int modes = 0, info = 0;
6930 char *greeting;
6931
6932 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6933 return 0;
6934
6935 cData = channel->channel_info;
6936 if(channel->members.used > cData->max)
6937 cData->max = channel->members.used;
6938
12673a59 6939#ifdef notdef
2f61d1d7 6940 /* Check for bans. If they're joining through a ban, one of two
6941 * cases applies:
6942 * 1: Join during a netburst, by riding the break. Kick them
6943 * unless they have ops or voice in the channel.
6944 * 2: They're allowed to join through the ban (an invite in
6945 * ircu2.10, or a +e on Hybrid, or something).
6946 * If they're not joining through a ban, and the banlist is not
6947 * full, see if they're on the banlist for the channel. If so,
6948 * kickban them.
6949 */
6950 if(user->uplink->burst && !mNode->modes)
6951 {
6952 unsigned int ii;
6953 for(ii = 0; ii < channel->banlist.used; ii++)
6954 {
6955 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6956 {
6957 /* Riding a netburst. Naughty. */
6958 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6959 return 1;
6960 }
6961 }
6962 }
12673a59 6963#endif
2f61d1d7 6964
d76ed9a9
AS
6965 mod_chanmode_init(&change);
6966 change.argc = 1;
6967 if(channel->banlist.used < MAXBANS)
6968 {
6969 /* Not joining through a ban. */
6970 for(bData = cData->bans;
2f61d1d7 6971 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6972 bData = bData->next);
d76ed9a9
AS
6973
6974 if(bData)
6975 {
6976 char kick_reason[MAXLEN];
6977 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6978
6979 bData->triggered = now;
6980 if(bData != cData->bans)
6981 {
6982 /* Shuffle the ban to the head of the list. */
6983 if(bData->next)
6984 bData->next->prev = bData->prev;
6985 if(bData->prev)
6986 bData->prev->next = bData->next;
6987
6988 bData->prev = NULL;
6989 bData->next = cData->bans;
6990
6991 if(cData->bans)
6992 cData->bans->prev = bData;
6993 cData->bans = bData;
6994 }
6995
6996 change.args[0].mode = MODE_BAN;
a32da4c7 6997 change.args[0].u.hostmask = bData->mask;
d76ed9a9
AS
6998 mod_chanmode_announce(chanserv, channel, &change);
6999 KickChannelUser(user, channel, chanserv, kick_reason);
7000 return 1;
7001 }
7002 }
7003
7004 /* ChanServ will not modify the limits in join-flooded channels.
7005 It will also skip DynLimit processing when the user (or srvx)
7006 is bursting in, because there are likely more incoming. */
7007 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7008 && !user->uplink->burst
7009 && !channel->join_flooded
7010 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7011 {
7012 /* The user count has begun "bumping" into the channel limit,
7013 so set a timer to raise the limit a bit. Any previous
7014 timers are removed so three incoming users within the delay
7015 results in one limit change, not three. */
7016
7017 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7018 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7019 }
7020
4b6129c0
AS
7021 /* Give automodes exept during join-floods */
7022 if(!channel->join_flooded)
d76ed9a9 7023 {
4b6129c0
AS
7024 if(cData->chOpts[chAutomode] == 'v')
7025 modes |= MODE_VOICE;
7026 else if(cData->chOpts[chAutomode] == 'h')
7027 modes |= MODE_HALFOP;
7028 else if(cData->chOpts[chAutomode] == 'o')
7029 modes |= MODE_CHANOP;
d76ed9a9 7030 }
c8ca69a0 7031
d76ed9a9
AS
7032 greeting = cData->greeting;
7033 if(user->handle_info)
7034 {
7035 handle = user->handle_info;
7036
7037 if(IsHelper(user) && !IsHelping(user))
7038 {
7039 unsigned int ii;
7040 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7041 {
7042 if(channel == chanserv_conf.support_channels.list[ii])
7043 {
7044 HANDLE_SET_FLAG(user->handle_info, HELPING);
7045 break;
7046 }
7047 }
7048 }
7049
7050 uData = GetTrueChannelAccess(cData, handle);
7051 if(uData && !IsUserSuspended(uData))
7052 {
4b6129c0
AS
7053 /* non users getting automodes are handled above. */
7054 if(IsUserAutoOp(uData) && cData->chOpts[chAutomode] != 'n')
d76ed9a9 7055 {
4b6129c0 7056 if(uData->access >= UL_OP )
d76ed9a9 7057 modes |= MODE_CHANOP;
4b6129c0 7058 else if(uData->access >= UL_HALFOP )
55342ce8 7059 modes |= MODE_HALFOP;
4b6129c0 7060 else if(uData->access >= UL_PEON && cData->chOpts[chAutomode] != 'm')
d76ed9a9
AS
7061 modes |= MODE_VOICE;
7062 }
7063 if(uData->access >= UL_PRESENT)
7064 cData->visited = now;
7065 if(cData->user_greeting)
7066 greeting = cData->user_greeting;
7067 if(uData->info
7068 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7069 && ((now - uData->seen) >= chanserv_conf.info_delay)
7070 && !uData->present)
7071 info = 1;
7072 uData->seen = now;
7073 uData->present = 1;
7074 }
7075 }
2f61d1d7 7076
7077 /* If user joining normally (not during burst), apply op or voice,
7078 * and send greeting/userinfo as appropriate.
7079 */
d76ed9a9
AS
7080 if(!user->uplink->burst)
7081 {
7082 if(modes)
7083 {
4b6129c0 7084 /* -- I'd rather have ops get voice too, if automode is v. -Rubin
55342ce8 7085 if(modes & MODE_CHANOP) {
7086 modes &= ~MODE_HALFOP;
d76ed9a9 7087 modes &= ~MODE_VOICE;
55342ce8 7088 }
4b6129c0 7089 */
d76ed9a9 7090 change.args[0].mode = modes;
a32da4c7 7091 change.args[0].u.member = mNode;
d76ed9a9
AS
7092 mod_chanmode_announce(chanserv, channel, &change);
7093 }
2f61d1d7 7094 if(greeting)
d76ed9a9
AS
7095 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7096 if(uData && info)
7097 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7098 }
7099 return 0;
7100}
7101
7102static void
7103handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7104{
7105 struct mod_chanmode change;
7106 struct userData *channel;
5177fd21 7107 unsigned int ii, jj, i;
d76ed9a9
AS
7108
7109 if(!user->handle_info)
7110 return;
7111
7112 mod_chanmode_init(&change);
7113 change.argc = 1;
7114 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7115 {
7116 struct chanNode *cn;
7117 struct modeNode *mn;
7118 if(IsUserSuspended(channel)
7119 || IsSuspended(channel->channel)
7120 || !(cn = channel->channel->channel))
7121 continue;
7122
7123 mn = GetUserMode(cn, user);
7124 if(!mn)
7125 {
7126 if(!IsUserSuspended(channel)
7127 && IsUserAutoInvite(channel)
7128 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7129 && !self->burst
7130 && !user->uplink->burst)
7131 irc_invite(chanserv, user, cn);
7132 continue;
7133 }
7134
7135 if(channel->access >= UL_PRESENT)
7136 channel->channel->visited = now;
7137
7138 if(IsUserAutoOp(channel))
7139 {
4b6129c0 7140 if(channel->access >= UL_OP )
d76ed9a9 7141 change.args[0].mode = MODE_CHANOP;
4b6129c0 7142 else if(channel->access >= UL_HALFOP )
55342ce8 7143 change.args[0].mode = MODE_HALFOP;
4b6129c0 7144 else if(channel->access >= UL_PEON )
d76ed9a9
AS
7145 change.args[0].mode = MODE_VOICE;
7146 else
7147 change.args[0].mode = 0;
a32da4c7 7148 change.args[0].u.member = mn;
d76ed9a9
AS
7149 if(change.args[0].mode)
7150 mod_chanmode_announce(chanserv, cn, &change);
7151 }
7152
7153 channel->seen = now;
7154 channel->present = 1;
7155 }
7156
7157 for(ii = 0; ii < user->channels.used; ++ii)
7158 {
7159 struct chanNode *channel = user->channels.list[ii]->channel;
7160 struct banData *ban;
7161
55342ce8 7162 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP|MODE_VOICE))
1117fc5a 7163 || !channel->channel_info
7164 || IsSuspended(channel->channel_info))
d76ed9a9
AS
7165 continue;
7166 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 7167 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9
AS
7168 break;
7169 if(jj < channel->banlist.used)
7170 continue;
7171 for(ban = channel->channel_info->bans; ban; ban = ban->next)
7172 {
7173 char kick_reason[MAXLEN];
2f61d1d7 7174 if(!user_matches_glob(user, ban->mask,MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9
AS
7175 continue;
7176 change.args[0].mode = MODE_BAN;
a32da4c7 7177 change.args[0].u.hostmask = ban->mask;
d76ed9a9
AS
7178 mod_chanmode_announce(chanserv, channel, &change);
7179 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7180 KickChannelUser(user, channel, chanserv, kick_reason);
7181 ban->triggered = now;
7182 break;
7183 }
7184 }
7185
7186 if(IsSupportHelper(user))
7187 {
7188 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7189 {
7190 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7191 {
7192 HANDLE_SET_FLAG(user->handle_info, HELPING);
7193 break;
7194 }
7195 }
7196 }
5177fd21 7197
7198 if (user->handle_info->ignores->used) {
7199 for (i=0; i < user->handle_info->ignores->used; i++) {
7200 irc_silence(user, user->handle_info->ignores->list[i], 1);
7201 }
7202 }
56958740 7203
7204 if (user->handle_info->epithet)
7205 irc_swhois(chanserv, user, user->handle_info->epithet);
d76ed9a9
AS
7206}
7207
7208static void
7209handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7210{
7211 struct chanData *cData;
7212 struct userData *uData;
7213
7214 cData = mn->channel->channel_info;
7215 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7216 return;
7217
7218 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7219 {
7220 /* Allow for a bit of padding so that the limit doesn't
7221 track the user count exactly, which could get annoying. */
7222 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7223 {
7224 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7225 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7226 }
7227 }
7228
7229 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7230 {
7231 scan_user_presence(uData, mn->user);
7232 uData->seen = now;
7233 }
7234
7235 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7236 {
7237 unsigned int ii, jj;
7238 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7239 {
7240 for(jj = 0; jj < mn->user->channels.used; ++jj)
7241 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
7242 break;
7243 if(jj < mn->user->channels.used)
7244 break;
7245 }
7246 if(ii == chanserv_conf.support_channels.used)
7247 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7248 }
7249}
7250
7251static void
7252handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7253{
7254 struct userData *uData;
7255
7256 if(!channel->channel_info || !kicker || IsService(kicker)
7257 || (kicker == victim) || IsSuspended(channel->channel_info)
7258 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7259 return;
7260
7261 if(protect_user(victim, kicker, channel->channel_info))
7262 {
31f23f13 7263 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_KICK");
d76ed9a9
AS
7264 KickChannelUser(kicker, channel, chanserv, reason);
7265 }
7266
7267 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7268 uData->seen = now;
7269}
7270
7271static int
7272handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7273{
7274 struct chanData *cData;
7275
7276 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7277 return 0;
7278
7279 cData = channel->channel_info;
7280 if(bad_topic(channel, user, channel->topic))
b75e24a3 7281 { /* User doesnt have privs to set topics. Undo it */
d76ed9a9 7282 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7fda2b52 7283 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
d76ed9a9
AS
7284 return 1;
7285 }
b75e24a3
AS
7286 /* If there is a topic mask set, and the new topic doesnt match,
7287 * set the topic to mask + new_topic */
7288 if(cData->topic_mask && !match_ircglob(channel->topic, cData->topic_mask))
7289 {
7290 char new_topic[TOPICLEN+1];
7291 conform_topic(cData->topic_mask, channel->topic, new_topic);
7292 if(*new_topic)
7293 {
7fda2b52 7294 SetChannelTopic(channel, chanserv, chanserv, new_topic, 1);
b75e24a3
AS
7295 /* and fall through to topicsnarf code below.. */
7296 }
7297 else /* Topic couldnt fit into mask, was too long */
7298 {
7fda2b52 7299 SetChannelTopic(channel, chanserv, chanserv, old_topic, 1);
b75e24a3
AS
7300 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT1", channel->name, cData->topic_mask);
7301 send_message(user, chanserv, "CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
7302 return 1;
7303 }
7304 }
d76ed9a9
AS
7305 /* With topicsnarf, grab the topic and save it as the default topic. */
7306 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7307 {
7308 free(cData->topic);
7309 cData->topic = strdup(channel->topic);
7310 }
7311 return 0;
7312}
7313
7314static void
7315handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7316{
7317 struct mod_chanmode *bounce = NULL;
7318 unsigned int bnc, ii;
7319 char deopped = 0;
7320
7321 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7322 return;
7323
7324 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7325 && mode_lock_violated(&channel->channel_info->modes, change))
7326 {
7327 char correct[MAXLEN];
7328 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7329 mod_chanmode_format(&channel->channel_info->modes, correct);
7330 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7331 }
7332 for(ii = bnc = 0; ii < change->argc; ++ii)
7333 {
7334 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7335 {
a32da4c7 7336 const struct userNode *victim = change->args[ii].u.member->user;
d76ed9a9
AS
7337 if(!protect_user(victim, user, channel->channel_info))
7338 continue;
7339 if(!bounce)
7340 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7341 if(!deopped)
7342 {
7343 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7344 bounce->args[bnc].u.member = GetUserMode(channel, user);
7345 if(bounce->args[bnc].u.member)
d76ed9a9
AS
7346 bnc++;
7347 deopped = 1;
7348 }
7349 bounce->args[bnc].mode = MODE_CHANOP;
a32da4c7 7350 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
7351 bnc++;
7352 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7353 }
7354 else if(change->args[ii].mode & MODE_CHANOP)
7355 {
a32da4c7 7356 const struct userNode *victim = change->args[ii].u.member->user;
d76ed9a9
AS
7357 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7358 continue;
7359 if(!bounce)
7360 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7361 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
a32da4c7 7362 bounce->args[bnc].u.member = change->args[ii].u.member;
d76ed9a9
AS
7363 bnc++;
7364 }
7365 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7366 {
a32da4c7 7367 const char *ban = change->args[ii].u.hostmask;
d76ed9a9
AS
7368 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7369 continue;
7370 if(!bounce)
7371 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7372 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
ec1a68c8 7373 bounce->args[bnc].u.hostmask = strdup(ban);
d76ed9a9
AS
7374 bnc++;
7375 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7376 }
7377 }
7378 if(bounce)
7379 {
7380 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7381 mod_chanmode_announce(chanserv, channel, bounce);
ec1a68c8 7382 for(ii = 0; ii < change->argc; ++ii)
7383 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7384 free((char*)bounce->args[ii].u.hostmask);
d76ed9a9
AS
7385 mod_chanmode_free(bounce);
7386 }
7387}
7388
7389static void
7390handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7391{
7392 struct chanNode *channel;
7393 struct banData *bData;
7394 struct mod_chanmode change;
7395 unsigned int ii, jj;
7396 char kick_reason[MAXLEN];
7397
7398 mod_chanmode_init(&change);
7399 change.argc = 1;
7400 change.args[0].mode = MODE_BAN;
7401 for(ii = 0; ii < user->channels.used; ++ii)
7402 {
7403 channel = user->channels.list[ii]->channel;
7404 /* Need not check for bans if they're opped or voiced. */
4b6129c0
AS
7405 /* TODO: does this make sense in automode v, h, and o? *
7406 * lets still enforce on voice people anyway, and see how that goes -Rubin */
7407 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_HALFOP /*|MODE_VOICE */))
d76ed9a9
AS
7408 continue;
7409 /* Need not check for bans unless channel registration is active. */
7410 if(!channel->channel_info || IsSuspended(channel->channel_info))
7411 continue;
7412 /* Look for a matching ban already on the channel. */
7413 for(jj = 0; jj < channel->banlist.used; ++jj)
2f61d1d7 7414 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
d76ed9a9
AS
7415 break;
7416 /* Need not act if we found one. */
7417 if(jj < channel->banlist.used)
7418 continue;
7419 /* Look for a matching ban in this channel. */
7420 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7421 {
2f61d1d7 7422 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
d76ed9a9 7423 continue;
a32da4c7 7424 change.args[0].u.hostmask = bData->mask;
d76ed9a9
AS
7425 mod_chanmode_announce(chanserv, channel, &change);
7426 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7427 KickChannelUser(user, channel, chanserv, kick_reason);
7428 bData->triggered = now;
7429 break; /* we don't need to check any more bans in the channel */
7430 }
7431 }
7432}
7433
7434static void handle_rename(struct handle_info *handle, const char *old_handle)
7435{
7436 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7437
7438 if(dnr)
7439 {
7440 dict_remove2(handle_dnrs, old_handle, 1);
7441 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7442 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7443 }
7444}
7445
7446static void
7447handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7448{
7449 struct userNode *h_user;
7450
7451 if(handle->channels)
7452 {
7453 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7454 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7455
7456 while(handle->channels)
7457 del_channel_user(handle->channels, 1);
7458 }
7459}
7460
7461static void
7462handle_server_link(UNUSED_ARG(struct server *server))
7463{
7464 struct chanData *cData;
7465
7466 for(cData = channelList; cData; cData = cData->next)
7467 {
7468 if(!IsSuspended(cData))
7469 cData->may_opchan = 1;
7470 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7471 && !cData->channel->join_flooded
7472 && ((cData->channel->limit - cData->channel->members.used)
7473 < chanserv_conf.adjust_threshold))
7474 {
7475 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7476 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7477 }
7478 }
7479}
7480
7481static void
7482chanserv_conf_read(void)
7483{
7484 dict_t conf_node;
7485 const char *str;
7486 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7487 struct mod_chanmode *change;
7488 struct string_list *strlist;
7489 struct chanNode *chan;
7490 unsigned int ii;
7491
7492 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7493 {
7494 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7495 return;
7496 }
7497 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7498 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7499 chanserv_conf.support_channels.used = 0;
7500 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7501 {
7502 for(ii = 0; ii < strlist->used; ++ii)
7503 {
7504 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7505 if(!str2)
7506 str2 = "+nt";
2aef5f4b 7507 chan = AddChannel(strlist->list[ii], now, str2, NULL, NULL);
d76ed9a9
AS
7508 LockChannel(chan);
7509 channelList_append(&chanserv_conf.support_channels, chan);
7510 }
7511 }
7512 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7513 {
7514 const char *str2;
7515 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7516 if(!str2)
7517 str2 = "+nt";
2aef5f4b 7518 chan = AddChannel(str, now, str2, NULL, NULL);
d76ed9a9
AS
7519 LockChannel(chan);
7520 channelList_append(&chanserv_conf.support_channels, chan);
7521 }
7522 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7523 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7524 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7525 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7526 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
1117fc5a 7527 chanserv_conf.greeting_length = str ? atoi(str) : 200;
d76ed9a9
AS
7528 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7529 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7530 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7531 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7532 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7533 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
31f23f13
AS
7534 str = database_get_data(conf_node, KEY_BAN_TIMEOUT_FREQ, RECDB_QSTRING);
7535 chanserv_conf.ban_timeout_frequency = str ? ParseInterval(str) : 600;
d76ed9a9
AS
7536 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7537 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7538 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7539 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7540 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7541 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7542 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7543 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7544 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7545 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7546 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7547 if(chanserv && str)
7548 NickChange(chanserv, str, 0);
7549 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7550 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
a32da4c7 7551 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7552 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
d76ed9a9
AS
7553 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7554 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7555 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7556 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7557 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7558 chanserv_conf.max_owned = str ? atoi(str) : 5;
7559 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7560 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7561 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7562 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7563 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7564 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7637f48f 7565 str = database_get_data(conf_node, KEY_GOD_TIMEOUT, RECDB_QSTRING);
7566 god_timeout = str ? ParseInterval(str) : 60*15;
d76ed9a9
AS
7567 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7568 if(!str)
7569 str = "+nt";
7570 safestrncpy(mode_line, str, sizeof(mode_line));
7571 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
2f61d1d7 7572 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7573 && (change->argc < 2))
d76ed9a9
AS
7574 {
7575 chanserv_conf.default_modes = *change;
7576 mod_chanmode_free(change);
7577 }
7578 free_string_list(chanserv_conf.set_shows);
7579 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7580 if(strlist)
7581 strlist = string_list_copy(strlist);
7582 else
7583 {
7584 static const char *list[] = {
7585 /* free form text */
7586 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7587 /* options based on user level */
4b6129c0
AS
7588 "PubCmd", "InviteMe", "UserInfo","EnfOps",
7589 "EnfHalfOps", "EnfModes", "EnfTopic", "TopicSnarf", "Setters",
d76ed9a9 7590 /* multiple choice options */
7637f48f 7591 "AutoMode", "CtcpReaction", "Protect", "Toys", "TopicRefresh", "Resync",
d76ed9a9 7592 /* binary options */
31f23f13 7593 "DynLimit", "NoDelete", "BanTimeout",
d76ed9a9
AS
7594 /* delimiter */
7595 NULL
7596 };
7597 unsigned int ii;
7598 strlist = alloc_string_list(ArrayLength(list)-1);
7599 for(ii=0; list[ii]; ii++)
7600 string_list_append(strlist, strdup(list[ii]));
7601 }
7602 chanserv_conf.set_shows = strlist;
7603 /* We don't look things up now, in case the list refers to options
7604 * defined by modules initialized after this point. Just mark the
7605 * function list as invalid, so it will be initialized.
7606 */
7607 set_shows_list.used = 0;
7608 free_string_list(chanserv_conf.eightball);
7609 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7610 if(strlist)
7611 {
7612 strlist = string_list_copy(strlist);
7613 }
7614 else
7615 {
7616 strlist = alloc_string_list(4);
7617 string_list_append(strlist, strdup("Yes."));
7618 string_list_append(strlist, strdup("No."));
7619 string_list_append(strlist, strdup("Maybe so."));
7620 }
7621 chanserv_conf.eightball = strlist;
7622 free_string_list(chanserv_conf.old_ban_names);
7623 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7624 if(strlist)
7625 strlist = string_list_copy(strlist);
7626 else
7627 strlist = alloc_string_list(2);
7628 chanserv_conf.old_ban_names = strlist;
d76ed9a9 7629 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
1117fc5a 7630 off_channel = str ? atoi(str) : 0;
d76ed9a9
AS
7631}
7632
7633static void
7634chanserv_note_type_read(const char *key, struct record_data *rd)
7635{
7636 dict_t obj;
7637 struct note_type *ntype;
7638 const char *str;
7639
7640 if(!(obj = GET_RECORD_OBJECT(rd)))
7641 {
7642 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7643 return;
7644 }
7645 if(!(ntype = chanserv_create_note_type(key)))
7646 {
7647 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7648 return;
7649 }
7650
7651 /* Figure out set access */
7652 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7653 {
7654 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7655 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7656 }
7657 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7658 {
7659 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7660 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7661 }
7662 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7663 {
7664 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7665 }
7666 else
7667 {
7668 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7669 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7670 ntype->set_access.min_opserv = 0;
7671 }
7672
7673 /* Figure out visibility */
7674 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7675 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7676 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7677 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7678 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7679 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7680 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7681 ntype->visible_type = NOTE_VIS_ALL;
7682 else
7683 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7684
7685 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7686 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7687}
7688
7689static void
7690user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7691{
7692 struct handle_info *handle;
7693 struct userData *uData;
b10abdb2 7694 char *seen, *inf, *flags, *expires;
d76ed9a9
AS
7695 time_t last_seen;
7696 unsigned short access;
7697
7698 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7699 {
7700 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7701 return;
7702 }
7703
7704 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7705 if(access > UL_OWNER)
7706 {
7707 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7708 return;
7709 }
7710
7711 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7712 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7713 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7714 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
b10abdb2 7715 expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
d76ed9a9
AS
7716 handle = get_handle_info(key);
7717 if(!handle)
7718 {
7719 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7720 return;
7721 }
7722
7723 uData = add_channel_user(chan, handle, access, last_seen, inf);
7724 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
b10abdb2 7725 uData->expires = expires ? strtoul(expires, NULL, 0) : 0;
7726
7727 if((uData->flags & USER_SUSPENDED) && uData->expires)
7728 {
7729 if(uData->expires > now)
7730 timeq_add(uData->expires, chanserv_expire_user_suspension, uData);
7731 else
7732 uData->flags &= ~USER_SUSPENDED;
7733 }
c8ca69a0
AS
7734
7735 /* Upgrade: set autoop to the inverse of noautoop */
7736 if(chanserv_read_version < 2)
7737 {
7738 /* if noautoop is true, set autoop false, and vice versa */
7739 if(uData->flags & USER_NOAUTO_OP)
7740 uData->flags = uData->flags & ~USER_AUTO_OP;
7741 else
7742 uData->flags = uData->flags | USER_AUTO_OP;
7743 log_module(CS_LOG, LOG_INFO, "UPGRADE: to db version 2 from %u. Changing flag to %d for %s in %s.", chanserv_read_version, uData->flags, key, chan->channel->name);
7744 }
7745
d76ed9a9
AS
7746}
7747
7748static void
7749ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7750{
7751 struct banData *bData;
7752 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7753 time_t set_time, triggered_time, expires_time;
7754
7755 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7756 {
7757 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7758 return;
7759 }
7760
7761 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7762 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7763 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7764 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7765 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7766 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
0d16e639 7767 if (!reason || !owner)
7768 return;
d76ed9a9
AS
7769
7770 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7771 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7772 if(s_expires)
7773 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7774 else if(s_duration)
7775 expires_time = set_time + atoi(s_duration);
7776 else
7777 expires_time = 0;
7778
ec1a68c8 7779 if(!reason || (expires_time && (expires_time < now)))
d76ed9a9
AS
7780 return;
7781
7782 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7783}
7784
7785static struct suspended *
7786chanserv_read_suspended(dict_t obj)
7787{
7788 struct suspended *suspended = calloc(1, sizeof(*suspended));
7789 char *str;
7790 dict_t previous;
7791
7792 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7793 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7794 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7795 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7796 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7797 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7798 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7799 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7800 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7801 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7802 return suspended;
7803}
7804
82f37c08 7805static struct giveownership *
7806chanserv_read_giveownership(dict_t obj)
7807{
7808 struct giveownership *giveownership = calloc(1, sizeof(*giveownership));
7809 char *str;
7810 dict_t previous;
7811
7812 str = database_get_data(obj, KEY_STAFF_ISSUER, RECDB_QSTRING);
7813 giveownership->staff_issuer = str ? strdup(str) : NULL;
7814
7815 giveownership->old_owner = strdup(database_get_data(obj, KEY_OLD_OWNER, RECDB_QSTRING));
7816
7817 giveownership->target = strdup(database_get_data(obj, KEY_TARGET, RECDB_QSTRING));
7818 giveownership->target_access = atoi(database_get_data(obj, KEY_TARGET_ACCESS, RECDB_QSTRING));
7819
7820 str = database_get_data(obj, KEY_REASON, RECDB_QSTRING);
7821 giveownership->reason = str ? strdup(str) : NULL;
7822 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7823 giveownership->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7824
7825 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7826 giveownership->previous = previous ? chanserv_read_giveownership(previous) : NULL;
7827 return giveownership;
7828}
7829
d76ed9a9
AS
7830static int
7831chanserv_channel_read(const char *key, struct record_data *hir)
7832{
7833 struct suspended *suspended;
82f37c08 7834 struct giveownership *giveownership;
d76ed9a9
AS
7835 struct mod_chanmode *modes;
7836 struct chanNode *cNode;
7837 struct chanData *cData;
7838 struct dict *channel, *obj;
7839 char *str, *argv[10];
7840 dict_iterator_t it;
7841 unsigned int argc;
7842
7843 channel = hir->d.object;
7844
7845 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7846 if(!str)
7847 str = "<unknown>";
2aef5f4b 7848 cNode = AddChannel(key, now, NULL, NULL, NULL);
d76ed9a9
AS
7849 if(!cNode)
7850 {
7851 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7852 return 0;
7853 }
7854 cData = register_channel(cNode, str);
7855 if(!cData)
7856 {
7857 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7858 return 0;
7859 }
7860
7861 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7862 {
7863 enum levelOption lvlOpt;
7864 enum charOption chOpt;
7865
7866 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7867 cData->flags = atoi(str);
7868
7869 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7870 {
7871 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7872 if(str)
7873 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7874 else if(levelOptions[lvlOpt].old_flag)
7875 {
7876 if(cData->flags & levelOptions[lvlOpt].old_flag)
7877 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7878 else
7879 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7880 }
7881 }
7882
7883 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7884 {
7885 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7886 continue;
7887 cData->chOpts[chOpt] = str[0];
7888 }
7889 }
7890 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7891 {
7892 enum levelOption lvlOpt;
7893 enum charOption chOpt;
7894 unsigned int count;
7895
7896 cData->flags = base64toint(str, 5);
7897 count = strlen(str += 5);
7898 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7899 {
7900 unsigned short lvl;
7901 if(levelOptions[lvlOpt].old_flag)
7902 {
7903 if(cData->flags & levelOptions[lvlOpt].old_flag)
7904 lvl = levelOptions[lvlOpt].flag_value;
7905 else
7906 lvl = levelOptions[lvlOpt].default_value;
7907 }
7908 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7909 {
7910 case 'c': lvl = UL_COOWNER; break;
4048352e 7911 case 'm': lvl = UL_MANAGER; break;
d76ed9a9
AS
7912 case 'n': lvl = UL_OWNER+1; break;
7913 case 'o': lvl = UL_OP; break;
7914 case 'p': lvl = UL_PEON; break;
55342ce8 7915 case 'h': lvl = UL_HALFOP; break;
d76ed9a9
AS
7916 case 'w': lvl = UL_OWNER; break;
7917 default: lvl = 0; break;
7918 }
7919 cData->lvlOpts[lvlOpt] = lvl;
7920 }
7921 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7922 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7923 }
7924
7925 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7926 {
7927 suspended = chanserv_read_suspended(obj);
7928 cData->suspended = suspended;
7929 suspended->cData = cData;
7930 /* We could use suspended->expires and suspended->revoked to
7931 * set the CHANNEL_SUSPENDED flag, but we don't. */
7932 }
a32da4c7 7933 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
d76ed9a9
AS
7934 {
7935 suspended = calloc(1, sizeof(*suspended));
7936 suspended->issued = 0;
7937 suspended->revoked = 0;
a32da4c7 7938 suspended->suspender = strdup(str);
d76ed9a9
AS
7939 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7940 suspended->expires = str ? atoi(str) : 0;
d76ed9a9
AS
7941 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7942 suspended->reason = strdup(str ? str : "No reason");
7943 suspended->previous = NULL;
7944 cData->suspended = suspended;
7945 suspended->cData = cData;
7946 }
7947 else
a32da4c7 7948 {
7949 cData->flags &= ~CHANNEL_SUSPENDED;
d76ed9a9 7950 suspended = NULL; /* to squelch a warning */
a32da4c7 7951 }
d76ed9a9
AS
7952
7953 if(IsSuspended(cData)) {
7954 if(suspended->expires > now)
7955 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7956 else if(suspended->expires)
7957 cData->flags &= ~CHANNEL_SUSPENDED;
7958 }
7959
82f37c08 7960 if((obj = database_get_data(hir->d.object, KEY_GIVEOWNERSHIP, RECDB_OBJECT)))
7961 {
7962 giveownership = chanserv_read_giveownership(obj);
7963 cData->giveownership = giveownership;
7964 }
7965
d76ed9a9
AS
7966 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7967 struct mod_chanmode change;
7968 mod_chanmode_init(&change);
7969 change.argc = 1;
7970 change.args[0].mode = MODE_CHANOP;
a32da4c7 7971 change.args[0].u.member = AddChannelUser(chanserv, cNode);
d76ed9a9
AS
7972 mod_chanmode_announce(chanserv, cNode, &change);
7973 }
7974
7975 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7976 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7977 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7978 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
a32da4c7 7979 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7980 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
d76ed9a9
AS
7981 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7982 cData->max = str ? atoi(str) : 0;
7983 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7984 cData->greeting = str ? strdup(str) : NULL;
7985 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7986 cData->user_greeting = str ? strdup(str) : NULL;
7987 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7988 cData->topic_mask = str ? strdup(str) : NULL;
7989 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7990 cData->topic = str ? strdup(str) : NULL;
7991
7992 if(!IsSuspended(cData)
7993 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7994 && (argc = split_line(str, 0, ArrayLength(argv), argv))
2f61d1d7 7995 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
d76ed9a9 7996 cData->modes = *modes;
1117fc5a 7997 if(off_channel > 0)
a32da4c7 7998 cData->modes.modes_set |= MODE_REGISTERED;
d76ed9a9
AS
7999 if(cData->modes.argc > 1)
8000 cData->modes.argc = 1;
8001 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8002 mod_chanmode_free(modes);
8003 }
8004
8005 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8006 for(it = dict_first(obj); it; it = iter_next(it))
8007 user_read_helper(iter_key(it), iter_data(it), cData);
8008
8009 if(!cData->users && !IsProtected(cData))
8010 {
8011 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8012 unregister_channel(cData, "has empty user list.");
8013 return 0;
8014 }
8015
8016 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8017 for(it = dict_first(obj); it; it = iter_next(it))
8018 ban_read_helper(iter_key(it), iter_data(it), cData);
8019
8020 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8021 for(it = dict_first(obj); it; it = iter_next(it))
8022 {
8023 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8024 struct record_data *rd = iter_data(it);
8025 const char *note, *setter;
8026
8027 if(rd->type != RECDB_OBJECT)
8028 {
8029 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8030 }
8031 else if(!ntype)
8032 {
8033 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8034 }
8035 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8036 {
8037 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8038 }
8039 else
8040 {
8041 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8042 if(!setter) setter = "<unknown>";
8043 chanserv_add_channel_note(cData, ntype, setter, note);
8044 }
8045 }
8046
8047 return 0;
8048}
8049
8050static void
8051chanserv_dnr_read(const char *key, struct record_data *hir)
8052{
8053 const char *setter, *reason, *str;
8054 struct do_not_register *dnr;
8055
8056 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8057 if(!setter)
8058 {
8059 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8060 return;
8061 }
8062 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8063 if(!reason)
8064 {
8065 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8066 return;
8067 }
8068 dnr = chanserv_add_dnr(key, setter, reason);
8069 if(!dnr)
8070 return;
8071 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8072 if(str)
8073 dnr->set = atoi(str);
8074 else
8075 dnr->set = 0;
8076}
8077
c8ca69a0
AS
8078static void
8079chanserv_version_read(struct dict *section)
8080{
8081 /* global var.. */
8082 char *str;
8083 str = database_get_data(section, KEY_VERSION_NUMBER, RECDB_QSTRING);
8084 if(str)
8085 chanserv_read_version = atoi(str);
8086 log_module(CS_LOG, LOG_DEBUG, "Chanserv db version is %d.", chanserv_read_version);
8087}
8088
d76ed9a9
AS
8089static int
8090chanserv_saxdb_read(struct dict *database)
8091{
8092 struct dict *section;
8093 dict_iterator_t it;
8094
c8ca69a0
AS
8095 if((section = database_get_data(database, KEY_VERSION_CONTROL, RECDB_OBJECT)))
8096 chanserv_version_read(section);
8097
d76ed9a9
AS
8098 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8099 for(it = dict_first(section); it; it = iter_next(it))
8100 chanserv_note_type_read(iter_key(it), iter_data(it));
8101
8102 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8103 for(it = dict_first(section); it; it = iter_next(it))
8104 chanserv_channel_read(iter_key(it), iter_data(it));
8105
8106 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8107 for(it = dict_first(section); it; it = iter_next(it))
8108 chanserv_dnr_read(iter_key(it), iter_data(it));
8109
8110 return 0;
8111}
8112
8113static int
8114chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8115{
8116 int high_present = 0;
8117 saxdb_start_record(ctx, KEY_USERS, 1);
8118 for(; uData; uData = uData->next)
8119 {
8120 if((uData->access >= UL_PRESENT) && uData->present)
8121 high_present = 1;
8122 saxdb_start_record(ctx, uData->handle->handle, 0);
8123 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8124 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8125 if(uData->flags)
8126 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
b10abdb2 8127 if(uData->expires)
8128 saxdb_write_int(ctx, KEY_EXPIRES, uData->expires);
d76ed9a9
AS
8129 if(uData->info)
8130 saxdb_write_string(ctx, KEY_INFO, uData->info);
8131 saxdb_end_record(ctx);
8132 }
8133 saxdb_end_record(ctx);
8134 return high_present;
8135}
8136
8137static void
8138chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8139{
8140 if(!bData)
8141 return;
8142 saxdb_start_record(ctx, KEY_BANS, 1);
8143 for(; bData; bData = bData->next)
8144 {
8145 saxdb_start_record(ctx, bData->mask, 0);
8146 saxdb_write_int(ctx, KEY_SET, bData->set);
8147 if(bData->triggered)
8148 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8149 if(bData->expires)
8150 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8151 if(bData->owner[0])
8152 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8153 if(bData->reason)
8154 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8155 saxdb_end_record(ctx);
8156 }
8157 saxdb_end_record(ctx);
8158}
8159
8160static void
8161chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8162{
8163 saxdb_start_record(ctx, name, 0);
8164 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8165 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8166 if(susp->issued)
8167 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8168 if(susp->expires)
8169 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8170 if(susp->revoked)
8171 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8172 if(susp->previous)
8173 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8174 saxdb_end_record(ctx);
8175}
8176
82f37c08 8177static void
8178chanserv_write_giveownership(struct saxdb_context *ctx, const char *name, struct giveownership *giveownership)
8179{
8180 saxdb_start_record(ctx, name, 0);
8181 if(giveownership->staff_issuer)
8182 saxdb_write_string(ctx, KEY_STAFF_ISSUER, giveownership->staff_issuer);
8183 if(giveownership->old_owner)
8184 saxdb_write_string(ctx, KEY_OLD_OWNER, giveownership->old_owner);
8185 if(giveownership->target)
8186 saxdb_write_string(ctx, KEY_TARGET, giveownership->target);
8187 if(giveownership->target_access)
8188 saxdb_write_int(ctx, KEY_TARGET_ACCESS, giveownership->target_access);
8189 if(giveownership->reason)
8190 saxdb_write_string(ctx, KEY_REASON, giveownership->reason);
8191 if(giveownership->issued)
8192 saxdb_write_int(ctx, KEY_ISSUED, giveownership->issued);
8193 if(giveownership->previous)
8194 chanserv_write_giveownership(ctx, KEY_PREVIOUS, giveownership->previous);
8195 saxdb_end_record(ctx);
8196}
8197
d76ed9a9
AS
8198static void
8199chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8200{
8201 char buf[MAXLEN];
8202 int high_present;
8203 enum levelOption lvlOpt;
8204 enum charOption chOpt;
8205
8206 saxdb_start_record(ctx, channel->channel->name, 1);
8207
8208 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8209 saxdb_write_int(ctx, KEY_MAX, channel->max);
8210 if(channel->topic)
8211 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8212 if(channel->registrar)
8213 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8214 if(channel->greeting)
8215 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8216 if(channel->user_greeting)
8217 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8218 if(channel->topic_mask)
8219 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8220 if(channel->suspended)
8221 chanserv_write_suspended(ctx, "suspended", channel->suspended);
82f37c08 8222 if(channel->giveownership)
8223 chanserv_write_giveownership(ctx, "giveownership", channel->giveownership);
d76ed9a9
AS
8224
8225 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8226 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8227 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8228 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8229 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8230 {
8231 buf[0] = channel->chOpts[chOpt];
8232 buf[1] = '\0';
8233 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8234 }
8235 saxdb_end_record(ctx);
8236
8237 if(channel->modes.modes_set || channel->modes.modes_clear)
8238 {
8239 mod_chanmode_format(&channel->modes, buf);
8240 saxdb_write_string(ctx, KEY_MODES, buf);
8241 }
8242
8243 high_present = chanserv_write_users(ctx, channel->users);
8244 chanserv_write_bans(ctx, channel->bans);
8245
8246 if(dict_size(channel->notes))
8247 {
8248 dict_iterator_t it;
8249
8250 saxdb_start_record(ctx, KEY_NOTES, 1);
8251 for(it = dict_first(channel->notes); it; it = iter_next(it))
8252 {
8253 struct note *note = iter_data(it);
8254 saxdb_start_record(ctx, iter_key(it), 0);
8255 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8256 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8257 saxdb_end_record(ctx);
8258 }
8259 saxdb_end_record(ctx);
8260 }
8261
a32da4c7 8262 if(channel->ownerTransfer)
8263 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
d76ed9a9
AS
8264 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8265 saxdb_end_record(ctx);
8266}
8267
8268static void
8269chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8270{
8271 const char *str;
8272
8273 saxdb_start_record(ctx, ntype->name, 0);
8274 switch(ntype->set_access_type)
8275 {
338a82b5
AS
8276 case NOTE_SET_CHANNEL_ACCESS:
8277 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8278 break;
8279 case NOTE_SET_CHANNEL_SETTER:
8280 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8281 break;
8282 case NOTE_SET_PRIVILEGED: default:
8283 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8284 break;
d76ed9a9
AS
8285 }
8286 switch(ntype->visible_type)
8287 {
338a82b5
AS
8288 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8289 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8290 case NOTE_VIS_PRIVILEGED:
8291 default: str = KEY_NOTE_VIS_PRIVILEGED; break;
d76ed9a9
AS
8292 }
8293 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8294 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8295 saxdb_end_record(ctx);
8296}
8297
8298static void
8299write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8300{
8301 struct do_not_register *dnr;
8302 dict_iterator_t it;
8303
8304 for(it = dict_first(dnrs); it; it = iter_next(it))
8305 {
8306 dnr = iter_data(it);
8307 saxdb_start_record(ctx, dnr->chan_name, 0);
8308 if(dnr->set)
8309 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8310 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8311 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8312 saxdb_end_record(ctx);
8313 }
8314}
8315
8316static int
8317chanserv_saxdb_write(struct saxdb_context *ctx)
8318{
8319 dict_iterator_t it;
8320 struct chanData *channel;
8321
c8ca69a0
AS
8322 /* Version Control*/
8323 saxdb_start_record(ctx, KEY_VERSION_CONTROL, 1);
8324 saxdb_write_int(ctx, KEY_VERSION_NUMBER, CHANSERV_DB_VERSION);
8325 saxdb_end_record(ctx);
8326
d76ed9a9
AS
8327 /* Notes */
8328 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8329 for(it = dict_first(note_types); it; it = iter_next(it))
8330 chanserv_write_note_type(ctx, iter_data(it));
8331 saxdb_end_record(ctx);
8332
8333 /* DNRs */
8334 saxdb_start_record(ctx, KEY_DNR, 1);
8335 write_dnrs_helper(ctx, handle_dnrs);
8336 write_dnrs_helper(ctx, plain_dnrs);
8337 write_dnrs_helper(ctx, mask_dnrs);
8338 saxdb_end_record(ctx);
8339
8340 /* Channels */
8341 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8342 for(channel = channelList; channel; channel = channel->next)
8343 chanserv_write_channel(ctx, channel);
8344 saxdb_end_record(ctx);
8345
8346 return 0;
8347}
8348
8349static void
8350chanserv_db_cleanup(void) {
8351 unsigned int ii;
8352 unreg_part_func(handle_part);
8353 while(channelList)
8354 unregister_channel(channelList, "terminating.");
8355 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8356 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8357 free(chanserv_conf.support_channels.list);
8358 dict_delete(handle_dnrs);
8359 dict_delete(plain_dnrs);
8360 dict_delete(mask_dnrs);
8361 dict_delete(note_types);
8362 free_string_list(chanserv_conf.eightball);
8363 free_string_list(chanserv_conf.old_ban_names);
8364 free_string_list(chanserv_conf.set_shows);
8365 free(set_shows_list.list);
8366 free(uset_shows_list.list);
8367 while(helperList)
8368 {
8369 struct userData *helper = helperList;
8370 helperList = helperList->next;
8371 free(helper);
8372 }
8373}
8374
8375#define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
8376#define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8377#define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8378
8379void
8380init_chanserv(const char *nick)
8381{
7637f48f 8382 struct chanNode *chan;
8383 unsigned int i;
d76ed9a9
AS
8384 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8385 conf_register_reload(chanserv_conf_read);
8386
8387 reg_server_link_func(handle_server_link);
8388
8389 reg_new_channel_func(handle_new_channel);
8390 reg_join_func(handle_join);
8391 reg_part_func(handle_part);
8392 reg_kick_func(handle_kick);
8393 reg_topic_func(handle_topic);
8394 reg_mode_change_func(handle_mode);
8395 reg_nick_change_func(handle_nick_change);
8396
8397 reg_auth_func(handle_auth);
8398 reg_handle_rename_func(handle_rename);
8399 reg_unreg_func(handle_unreg);
8400
8401 handle_dnrs = dict_new();
8402 dict_set_free_data(handle_dnrs, free);
8403 plain_dnrs = dict_new();
8404 dict_set_free_data(plain_dnrs, free);
8405 mask_dnrs = dict_new();
8406 dict_set_free_data(mask_dnrs, free);
8407
8408 reg_svccmd_unbind_func(handle_svccmd_unbind);
8409 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8410 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8411 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8412 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8413 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8414 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8415 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8416 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8417 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8418
ac3bdc8d
AS
8419 DEFINE_COMMAND(pending, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8420
d76ed9a9
AS
8421 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8422 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8423
4048352e
AS
8424 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8425 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8426 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8427 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
8428 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8429
8430 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8431 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
4048352e
AS
8432 DEFINE_COMMAND(mdelmanager, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8433 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8434 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
55342ce8 8435 DEFINE_COMMAND(mdelhalfop, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8436
4048352e 8437 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8438 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
4048352e 8439 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9
AS
8440 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8441
8442 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8443 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8444 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8445 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
55342ce8 8446 DEFINE_COMMAND(hop, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
d76ed9a9
AS
8447 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8448 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
55342ce8 8449 DEFINE_COMMAND(dehop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
d76ed9a9
AS
8450 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8451 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8452
c8273589
AS
8453 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8454 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8455 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "hop", NULL);
8456 DEFINE_COMMAND(unban, 2, 0, "template", "hop", NULL);
8457 DEFINE_COMMAND(unbanall, 1, 0, "template", "hop", NULL);
8458 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "hop", NULL);
d76ed9a9 8459 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
c8273589 8460 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "hop", "flags", "+never_csuspend", NULL);
d76ed9a9
AS
8461 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8462 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
4048352e 8463 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "manager", NULL);
d76ed9a9 8464 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
4048352e
AS
8465 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
8466 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "manager", NULL);
d76ed9a9 8467
c8273589 8468 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
23475fc6 8469 DEFINE_COMMAND(last, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "manager", NULL);
c8273589
AS
8470 DEFINE_COMMAND(addlamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8471 DEFINE_COMMAND(addtimedlamer, 3, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
8472
8473 /* if you change dellamer access, see also places
8474 * like unbanme which have manager hardcoded. */
8475 DEFINE_COMMAND(dellamer, 2, MODCMD_REQUIRE_REGCHAN, "access", "manager", NULL);
d76ed9a9
AS
8476 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8477
c8273589
AS
8478 DEFINE_COMMAND(lamers, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8479
d76ed9a9
AS
8480 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8481
8482 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8483 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8484 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8485 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8486 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8487 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8488 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
55342ce8 8489 DEFINE_COMMAND(hlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
d76ed9a9
AS
8490 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8491 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8492 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8493 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8494
8495 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8496 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8497
8498 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8499 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8500 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8501 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8502
8503 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8504 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8505 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8506 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8507 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8508
8509 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8510 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8511 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8512 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8513 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8514 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
b8daef0f 8515 DEFINE_COMMAND(calc, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
d76ed9a9
AS
8516
8517 /* Channel options */
8518 DEFINE_CHANNEL_OPTION(defaulttopic);
8519 DEFINE_CHANNEL_OPTION(topicmask);
8520 DEFINE_CHANNEL_OPTION(greeting);
8521 DEFINE_CHANNEL_OPTION(usergreeting);
8522 DEFINE_CHANNEL_OPTION(modes);
8523 DEFINE_CHANNEL_OPTION(enfops);
55342ce8 8524 DEFINE_CHANNEL_OPTION(enfhalfops);
4b6129c0 8525 DEFINE_CHANNEL_OPTION(automode);
d76ed9a9
AS
8526 DEFINE_CHANNEL_OPTION(protect);
8527 DEFINE_CHANNEL_OPTION(enfmodes);
8528 DEFINE_CHANNEL_OPTION(enftopic);
8529 DEFINE_CHANNEL_OPTION(pubcmd);
d76ed9a9
AS
8530 DEFINE_CHANNEL_OPTION(userinfo);
8531 DEFINE_CHANNEL_OPTION(dynlimit);
8532 DEFINE_CHANNEL_OPTION(topicsnarf);
8533 DEFINE_CHANNEL_OPTION(nodelete);
8534 DEFINE_CHANNEL_OPTION(toys);
8535 DEFINE_CHANNEL_OPTION(setters);
8536 DEFINE_CHANNEL_OPTION(topicrefresh);
7637f48f 8537 DEFINE_CHANNEL_OPTION(resync);
d76ed9a9 8538 DEFINE_CHANNEL_OPTION(ctcpreaction);
31f23f13 8539 DEFINE_CHANNEL_OPTION(bantimeout);
d76ed9a9 8540 DEFINE_CHANNEL_OPTION(inviteme);
1117fc5a 8541 if(off_channel > 1)
d76ed9a9
AS
8542 DEFINE_CHANNEL_OPTION(offchannel);
8543 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8544
8545 /* Alias set topic to set defaulttopic for compatibility. */
8546 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8547
8548 /* User options */
d76ed9a9
AS
8549 DEFINE_USER_OPTION(autoinvite);
8550 DEFINE_USER_OPTION(info);
c8ca69a0 8551 DEFINE_USER_OPTION(autoop);
d76ed9a9
AS
8552
8553 /* Alias uset autovoice to uset autoop. */
c8ca69a0 8554 modcmd_register(chanserv_module, "uset autovoice", user_opt_autoop, 1, 0, NULL);
d76ed9a9
AS
8555
8556 note_types = dict_new();
8557 dict_set_free_data(note_types, chanserv_deref_note_type);
8558 if(nick)
8559 {
a32da4c7 8560 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8561 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
d76ed9a9
AS
8562 service_register(chanserv)->trigger = '!';
8563 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8564 }
c8ca69a0 8565
d76ed9a9
AS
8566 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8567
8568 if(chanserv_conf.channel_expire_frequency)
8569 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8570
31f23f13
AS
8571 if(chanserv_conf.ban_timeout_frequency)
8572 timeq_add(now + chanserv_conf.ban_timeout_frequency, expire_bans, NULL);
8573
d76ed9a9
AS
8574 if(chanserv_conf.refresh_period)
8575 {
8576 time_t next_refresh;
8577 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8578 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7637f48f 8579 timeq_add(next_refresh, chanserv_auto_resync, NULL);
8580 }
8581
8582 if (autojoin_channels && chanserv) {
8583 for (i = 0; i < autojoin_channels->used; i++) {
8584 chan = AddChannel(autojoin_channels->list[i], now, "+nt", NULL, NULL);
8585 AddChannelUser(chanserv, chan)->modes |= MODE_CHANOP;
8586 }
d76ed9a9 8587 }
1117fc5a 8588
d76ed9a9
AS
8589 reg_exit_func(chanserv_db_cleanup);
8590 message_register_table(msgtab);
8591}