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