1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() and devnull management */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
61 #define KEY_REVOKE_MODE_A "revoke_mode_a"
62 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
63 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
64 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
66 /* ChanServ database */
67 #define KEY_CHANNELS "channels"
68 #define KEY_NOTE_TYPES "note_types"
70 /* Note type parameters */
71 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
72 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
73 #define KEY_NOTE_SETTER_ACCESS "setter_access"
74 #define KEY_NOTE_VISIBILITY "visibility"
75 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
76 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
77 #define KEY_NOTE_VIS_ALL "all"
78 #define KEY_NOTE_MAX_LENGTH "max_length"
79 #define KEY_NOTE_SETTER "setter"
80 #define KEY_NOTE_NOTE "note"
82 /* Do-not-register channels */
84 #define KEY_DNR_SET "set"
85 #define KEY_DNR_SETTER "setter"
86 #define KEY_DNR_REASON "reason"
89 #define KEY_REGISTERED "registered"
90 #define KEY_REGISTRAR "registrar"
91 #define KEY_SUSPENDED "suspended"
92 #define KEY_PREVIOUS "previous"
93 #define KEY_SUSPENDER "suspender"
94 #define KEY_ISSUED "issued"
95 #define KEY_REVOKED "revoked"
96 #define KEY_SUSPEND_EXPIRES "suspend_expires"
97 #define KEY_SUSPEND_REASON "suspend_reason"
98 #define KEY_VISITED "visited"
99 #define KEY_TOPIC "topic"
100 #define KEY_GREETING "greeting"
101 #define KEY_USER_GREETING "user_greeting"
102 #define KEY_MODES "modes"
103 #define KEY_FLAGS "flags"
104 #define KEY_OPTIONS "options"
105 #define KEY_USERS "users"
106 #define KEY_BANS "bans"
107 #define KEY_MAX "max"
108 #define KEY_MAX_TIME "max_time"
109 #define KEY_NOTES "notes"
110 #define KEY_TOPIC_MASK "topic_mask"
111 #define KEY_OWNER_TRANSFER "owner_transfer"
112 #define KEY_EXPIRE "expire"
115 #define KEY_LEVEL "level"
116 #define KEY_INFO "info"
117 #define KEY_SEEN "seen"
120 #define KEY_VOTE "vote"
121 #define KEY_VOTE_START "votestart"
122 #define KEY_VOTE_OPTIONS "voptions"
123 #define KEY_VOTE_OPTION_NAME "voptionname"
124 #define KEY_VOTE_VOTED "vvoted"
125 #define KEY_VOTE_VOTEDFOR "vvotefor"
126 #define KEY_VOTE_OPTION_ID "voptionid"
127 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
130 #define KEY_OWNER "owner"
131 #define KEY_REASON "reason"
132 #define KEY_SET "set"
133 #define KEY_DURATION "duration"
134 #define KEY_EXPIRES "expires"
135 #define KEY_TRIGGERED "triggered"
137 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
138 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
139 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
141 /* Administrative messages */
142 static const struct message_entry msgtab[] = {
143 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
145 /* Channel registration */
146 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
147 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
148 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
149 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
150 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
151 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
153 /* Do-not-register channels */
154 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
155 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
156 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
158 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
159 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
160 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
161 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
162 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
163 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
164 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
165 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
166 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
167 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
169 /* Channel unregistration */
170 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
171 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
172 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
173 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
176 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
177 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
179 /* Channel merging */
180 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
181 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
182 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
183 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
184 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
186 /* Handle unregistration */
187 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
190 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
191 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
192 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
193 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
194 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
195 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
196 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
197 { "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." },
198 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
199 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
200 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
201 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
202 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
203 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
205 /* Removing yourself from a channel. */
206 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
207 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
208 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
210 /* User management */
211 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
212 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
213 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be lower than maximum (%d)." },
214 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
215 { "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." },
216 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
217 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
218 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
220 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
221 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
222 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
223 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
224 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
225 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
226 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
229 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
230 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
231 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
232 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
233 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
234 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
235 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
236 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
237 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
238 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
239 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
240 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
241 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
242 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
243 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
244 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
246 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
248 /* Channel management */
249 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
250 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
251 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
253 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
254 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
255 { "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" },
256 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
257 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
258 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
259 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
261 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
262 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
263 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
264 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
265 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
266 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
267 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
268 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
269 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
270 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
271 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
272 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
273 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
274 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
275 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
276 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
277 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
278 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
279 { "CSMSG_SET_MODES", "$bModes $b %s" },
280 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
281 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
282 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
283 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
284 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
285 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
286 { "CSMSG_SET_VOTE", "$bVote $b %d" },
287 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
288 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
289 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
290 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
291 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
292 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
293 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
294 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
295 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
296 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
297 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
298 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
299 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
300 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
301 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
302 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
303 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
304 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
305 { "CSMSG_USET_INFO", "$bInfo $b %s" },
307 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
308 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
309 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
310 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
311 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
312 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
313 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
314 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
315 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
316 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
317 { "CSMSG_PROTECT_NONE", "No users will be protected." },
318 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
319 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
320 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
321 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
322 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
323 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
324 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
325 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
326 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
327 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
329 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
331 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
332 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
333 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
334 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
335 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
336 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
337 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
338 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
340 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
341 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
342 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
344 /* Channel userlist */
345 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
346 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
347 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
348 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
349 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
351 /* Channel note list */
352 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
353 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
354 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
355 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
356 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
357 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
358 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
359 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
360 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
361 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
362 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
363 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
364 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
365 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
366 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
368 /* Channel [un]suspension */
369 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
370 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
371 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
372 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
373 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
374 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
375 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
377 /* Access information */
378 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
379 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
380 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
381 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
382 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
383 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
384 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
385 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
386 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
387 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
388 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
389 { "CSMSG_UC_H_TITLE", "network helper" },
390 { "CSMSG_LC_H_TITLE", "support helper" },
391 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
392 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
393 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
396 /* Seen information */
397 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
398 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
399 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
400 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
402 /* Names information */
403 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
404 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
406 /* Channel information */
407 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
408 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
409 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
410 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
411 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
412 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
413 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
414 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
415 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
416 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
417 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
418 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
419 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
424 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
425 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
426 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
427 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
428 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
430 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
431 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
432 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
433 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
434 { "CSMSG_PEEK_OPS", "$bOps:$b" },
435 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
437 /* Network information */
438 { "CSMSG_NETWORK_INFO", "Network Information:" },
439 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
440 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
441 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
442 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
443 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
444 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
445 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
446 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
449 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
450 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
451 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
453 /* Channel searches */
454 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
455 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
456 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
457 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
459 /* Channel configuration */
460 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
461 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
462 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
463 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
466 { "CSMSG_USER_OPTIONS", "User Options:" },
467 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
470 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
471 { "CSMSG_PING_RESPONSE", "Pong!" },
472 { "CSMSG_WUT_RESPONSE", "wut" },
473 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
474 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
475 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
476 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
477 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
478 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
479 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
482 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
483 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
484 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
485 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
486 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
487 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
488 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
489 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
490 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
491 { "CSMSG_VOTE_QUESTION", "Question: %s" },
492 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
493 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
494 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
495 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
496 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
497 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
498 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
499 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
500 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
501 { "CSMSG_VOTE_VOTED", "You have already voted." },
502 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
503 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
504 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
505 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
508 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
512 /* eject_user and unban_user flags */
513 #define ACTION_KICK 0x0001
514 #define ACTION_BAN 0x0002
515 #define ACTION_ADD_BAN 0x0004
516 #define ACTION_ADD_TIMED_BAN 0x0008
517 #define ACTION_UNBAN 0x0010
518 #define ACTION_DEL_BAN 0x0020
520 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
521 #define MODELEN 40 + KEYLEN
525 #define CSFUNC_ARGS user, channel, argc, argv, cmd
527 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
528 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
529 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
530 reply("MSG_MISSING_PARAMS", argv[0]); \
534 DECLARE_LIST(dnrList, struct do_not_register *);
535 DEFINE_LIST(dnrList, struct do_not_register *)
537 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
539 struct userNode *chanserv;
542 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
543 static struct log_type *CS_LOG;
547 struct channelList support_channels;
548 struct mod_chanmode default_modes;
550 unsigned long db_backup_frequency;
551 unsigned long channel_expire_frequency;
552 unsigned long dnr_expire_frequency;
554 unsigned long invited_timeout;
556 unsigned long info_delay;
557 unsigned long adjust_delay;
558 unsigned long channel_expire_delay;
559 unsigned int nodelete_level;
561 unsigned int adjust_threshold;
562 int join_flood_threshold;
564 unsigned int greeting_length;
565 unsigned int refresh_period;
566 unsigned int giveownership_period;
568 unsigned int max_owned;
569 unsigned int max_chan_users;
570 unsigned int max_chan_bans;
571 unsigned int max_userinfo_length;
573 unsigned int revoke_mode_a;
575 struct string_list *set_shows;
576 struct string_list *eightball;
577 struct string_list *old_ban_names;
579 const char *ctcp_short_ban_duration;
580 const char *ctcp_long_ban_duration;
582 const char *irc_operator_epithet;
583 const char *network_helper_epithet;
584 const char *support_helper_epithet;
586 const char *new_channel_authed;
587 const char *new_channel_unauthed;
588 const char *new_channel_msg;
593 struct userNode *user;
594 struct userNode *bot;
595 struct chanNode *channel;
597 unsigned short lowest;
598 unsigned short highest;
599 struct userData **users;
600 struct helpfile_table table;
605 struct userNode *user;
606 struct chanNode *chan;
609 enum note_access_type
611 NOTE_SET_CHANNEL_ACCESS,
612 NOTE_SET_CHANNEL_SETTER,
616 enum note_visible_type
619 NOTE_VIS_CHANNEL_USERS,
625 enum note_access_type set_access_type;
627 unsigned int min_opserv;
628 unsigned short min_ulevel;
630 enum note_visible_type visible_type;
631 unsigned int max_length;
638 struct note_type *type;
639 char setter[NICKSERV_HANDLE_LEN+1];
643 static unsigned int registered_channels;
644 static unsigned int banCount;
646 static const struct {
649 unsigned short level;
652 { "peon", "Peon", UL_PEON, '+' },
653 { "op", "Op", UL_OP, '@' },
654 { "master", "Master", UL_MASTER, '%' },
655 { "coowner", "Coowner", UL_COOWNER, '*' },
656 { "owner", "Owner", UL_OWNER, '!' },
657 { "helper", "BUG:", UL_HELPER, 'X' }
660 static const struct {
663 unsigned short default_value;
664 unsigned int old_idx;
665 unsigned int old_flag;
666 unsigned short flag_value;
668 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
669 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
670 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
671 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
672 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
673 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
674 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
675 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
676 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
677 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
678 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
679 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
682 struct charOptionValues {
685 } protectValues[] = {
686 { 'a', "CSMSG_PROTECT_ALL" },
687 { 'e', "CSMSG_PROTECT_EQUAL" },
688 { 'l', "CSMSG_PROTECT_LOWER" },
689 { 'n', "CSMSG_PROTECT_NONE" }
691 { 'd', "CSMSG_TOYS_DISABLED" },
692 { 'n', "CSMSG_TOYS_PRIVATE" },
693 { 'p', "CSMSG_TOYS_PUBLIC" }
694 }, topicRefreshValues[] = {
695 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
696 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
697 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
698 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
699 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
700 }, ctcpReactionValues[] = {
701 { 'k', "CSMSG_CTCPREACTION_KICK" },
702 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
703 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
704 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
707 static const struct {
711 unsigned int old_idx;
713 struct charOptionValues *values;
715 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
716 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
717 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
718 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
721 struct userData *helperList;
722 struct chanData *channelList;
723 static struct module *chanserv_module;
724 static unsigned int userCount;
726 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
727 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
728 static void unregister_channel(struct chanData *channel, const char *reason);
731 user_level_from_name(const char *name, unsigned short clamp_level)
733 unsigned int level = 0, ii;
735 level = strtoul(name, NULL, 10);
736 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
737 if(!irccasecmp(name, accessLevels[ii].name))
738 level = accessLevels[ii].level;
739 if(level > clamp_level)
745 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
748 *minl = strtoul(arg, &sep, 10);
756 *maxl = strtoul(sep+1, &sep, 10);
764 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
766 struct userData *uData, **head;
768 if(!channel || !handle)
771 if(override && HANDLE_FLAGGED(handle, HELPING)
772 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
774 for(uData = helperList;
775 uData && uData->handle != handle;
776 uData = uData->next);
780 uData = calloc(1, sizeof(struct userData));
781 uData->handle = handle;
783 uData->access = UL_HELPER;
789 uData->next = helperList;
791 helperList->prev = uData;
799 for(uData = channel->users; uData; uData = uData->next)
800 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
803 head = &(channel->users);
806 if(uData && (uData != *head))
808 /* Shuffle the user to the head of whatever list he was in. */
810 uData->next->prev = uData->prev;
812 uData->prev->next = uData->next;
818 (**head).prev = uData;
825 /* Returns non-zero if user has at least the minimum access.
826 * exempt_owner is set when handling !set, so the owner can set things
829 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
831 struct userData *uData;
832 struct chanData *cData = channel->channel_info;
833 unsigned short minimum = cData->lvlOpts[opt];
836 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
839 if(minimum <= uData->access)
841 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
846 /* Scan for other users authenticated to the same handle
847 still in the channel. If so, keep them listed as present.
849 user is optional, if not null, it skips checking that userNode
850 (for the handle_part function) */
852 scan_user_presence(struct userData *uData, struct userNode *user)
856 if(IsSuspended(uData->channel)
857 || IsUserSuspended(uData)
858 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
870 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
872 unsigned int eflags, argc;
874 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
876 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
877 if(!channel->channel_info
878 || IsSuspended(channel->channel_info)
880 || !ircncasecmp(text, "ACTION ", 7))
882 /* Figure out the minimum level needed to CTCP the channel */
883 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
885 /* We need to enforce against them; do so. */
887 argv[0] = (char*)text;
888 argv[1] = user->nick;
890 if(GetUserMode(channel, user))
891 eflags |= ACTION_KICK;
892 switch(channel->channel_info->chOpts[chCTCPReaction]) {
893 default: case 'k': /* just do the kick */ break;
895 eflags |= ACTION_BAN;
898 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
899 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
902 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
903 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
906 argv[argc++] = bad_ctcp_reason;
907 eject_user(chanserv, channel, argc, argv, NULL, eflags);
911 chanserv_create_note_type(const char *name)
913 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
914 strcpy(ntype->name, name);
916 dict_insert(note_types, ntype->name, ntype);
921 free_vote_options(void *data)
923 struct vote_option *vOpt = data;
925 free(vOpt->option_str);
930 chanserv_deref_note_type(void *data)
932 struct note_type *ntype = data;
934 if(--ntype->refs > 0)
940 chanserv_flush_note_type(struct note_type *ntype)
942 struct chanData *cData;
943 for(cData = channelList; cData; cData = cData->next)
944 dict_remove(cData->notes, ntype->name);
948 chanserv_truncate_notes(struct note_type *ntype)
950 struct chanData *cData;
952 unsigned int size = sizeof(*note) + ntype->max_length;
954 for(cData = channelList; cData; cData = cData->next) {
955 note = dict_find(cData->notes, ntype->name, NULL);
958 if(strlen(note->note) <= ntype->max_length)
960 dict_remove2(cData->notes, ntype->name, 1);
961 note = realloc(note, size);
962 note->note[ntype->max_length] = 0;
963 dict_insert(cData->notes, ntype->name, note);
967 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
970 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
973 unsigned int len = strlen(text);
975 if(len > type->max_length) len = type->max_length;
976 note = calloc(1, sizeof(*note) + len);
978 strncpy(note->setter, setter, sizeof(note->setter)-1);
979 memcpy(note->note, text, len);
981 dict_insert(channel->notes, type->name, note);
987 chanserv_free_note(void *data)
989 struct note *note = data;
991 chanserv_deref_note_type(note->type);
992 assert(note->type->refs > 0); /* must use delnote to remove the type */
996 static MODCMD_FUNC(cmd_createnote) {
997 struct note_type *ntype;
998 unsigned int arg = 1, existed = 0, max_length;
1000 if((ntype = dict_find(note_types, argv[1], NULL)))
1003 ntype = chanserv_create_note_type(argv[arg]);
1004 if(!irccasecmp(argv[++arg], "privileged"))
1007 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1008 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1010 else if(!irccasecmp(argv[arg], "channel"))
1012 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1015 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1018 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1019 ntype->set_access.min_ulevel = ulvl;
1021 else if(!irccasecmp(argv[arg], "setter"))
1023 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1027 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1031 if(!irccasecmp(argv[++arg], "privileged"))
1032 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1033 else if(!irccasecmp(argv[arg], "channel_users"))
1034 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1035 else if(!irccasecmp(argv[arg], "all"))
1036 ntype->visible_type = NOTE_VIS_ALL;
1038 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1042 if((arg+1) >= argc) {
1043 reply("MSG_MISSING_PARAMS", argv[0]);
1046 max_length = strtoul(argv[++arg], NULL, 0);
1047 if(max_length < 20 || max_length > 450)
1049 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1052 if(existed && (max_length < ntype->max_length))
1054 ntype->max_length = max_length;
1055 chanserv_truncate_notes(ntype);
1057 ntype->max_length = max_length;
1060 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1062 reply("CSMSG_NOTE_CREATED", ntype->name);
1067 dict_remove(note_types, ntype->name);
1071 static MODCMD_FUNC(cmd_removenote) {
1072 struct note_type *ntype;
1075 ntype = dict_find(note_types, argv[1], NULL);
1076 force = (argc > 2) && !irccasecmp(argv[2], "force");
1079 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1086 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1089 chanserv_flush_note_type(ntype);
1091 dict_remove(note_types, argv[1]);
1092 reply("CSMSG_NOTE_DELETED", argv[1]);
1097 chanserv_expire_channel(void *data)
1099 struct chanData *channel = data;
1100 char reason[MAXLEN];
1101 sprintf(reason, "channel expired.");
1102 channel->expiry = 0;
1103 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1104 unregister_channel(channel, reason);
1107 static MODCMD_FUNC(chan_opt_expire)
1109 struct chanData *cData = channel->channel_info;
1110 unsigned long value = cData->expiry;
1114 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1116 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1119 unsigned long expiry,duration;
1121 /* The two directions can have different ACLs. */
1122 if(!strcmp(argv[1], "0"))
1124 else if((duration = ParseInterval(argv[1])))
1125 expiry = now + duration;
1128 reply("MSG_INVALID_DURATION", argv[1]);
1132 if (expiry != value)
1136 timeq_del(value, chanserv_expire_channel, cData, 0);
1139 cData->expiry = value;
1142 timeq_add(expiry, chanserv_expire_channel, cData);
1147 if(cData->expiry > now) {
1148 char expirestr[INTERVALLEN];
1149 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1151 reply("CSMSG_SET_EXPIRE_OFF");
1156 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1160 if(orig->modes_set & change->modes_clear)
1162 if(orig->modes_clear & change->modes_set)
1164 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1165 && strcmp(orig->new_key, change->new_key))
1167 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1168 && (orig->new_limit != change->new_limit))
1173 static char max_length_text[MAXLEN+1][16];
1175 static struct helpfile_expansion
1176 chanserv_expand_variable(const char *variable)
1178 struct helpfile_expansion exp;
1180 if(!irccasecmp(variable, "notes"))
1183 exp.type = HF_TABLE;
1184 exp.value.table.length = 1;
1185 exp.value.table.width = 3;
1186 exp.value.table.flags = 0;
1187 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1188 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1189 exp.value.table.contents[0][0] = "Note Type";
1190 exp.value.table.contents[0][1] = "Visibility";
1191 exp.value.table.contents[0][2] = "Max Length";
1192 for(it=dict_first(note_types); it; it=iter_next(it))
1194 struct note_type *ntype = iter_data(it);
1197 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1198 row = exp.value.table.length++;
1199 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1200 exp.value.table.contents[row][0] = ntype->name;
1201 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1202 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1204 if(!max_length_text[ntype->max_length][0])
1205 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1206 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1211 exp.type = HF_STRING;
1212 exp.value.str = NULL;
1216 static struct chanData*
1217 register_channel(struct chanNode *cNode, char *registrar)
1219 struct chanData *channel;
1220 enum levelOption lvlOpt;
1221 enum charOption chOpt;
1223 channel = calloc(1, sizeof(struct chanData));
1225 channel->notes = dict_new();
1226 dict_set_free_data(channel->notes, chanserv_free_note);
1228 channel->registrar = strdup(registrar);
1229 channel->registered = now;
1230 channel->visited = now;
1231 channel->limitAdjusted = now;
1232 channel->ownerTransfer = now;
1233 channel->flags = CHANNEL_DEFAULT_FLAGS;
1234 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1235 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1236 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1237 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1239 channel->prev = NULL;
1240 channel->next = channelList;
1243 channelList->prev = channel;
1244 channelList = channel;
1245 registered_channels++;
1247 channel->channel = cNode;
1249 cNode->channel_info = channel;
1251 channel->vote = NULL;
1256 static struct userData*
1257 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1259 struct userData *ud;
1261 if(access_level > UL_OWNER)
1264 ud = calloc(1, sizeof(*ud));
1265 ud->channel = channel;
1266 ud->handle = handle;
1268 ud->access = access_level;
1269 ud->info = info ? strdup(info) : NULL;
1272 ud->next = channel->users;
1274 channel->users->prev = ud;
1275 channel->users = ud;
1277 channel->userCount++;
1281 ud->u_next = ud->handle->channels;
1283 ud->u_next->u_prev = ud;
1284 ud->handle->channels = ud;
1290 del_channel_user(struct userData *user, int do_gc)
1292 struct chanData *channel = user->channel;
1294 channel->userCount--;
1298 user->prev->next = user->next;
1300 channel->users = user->next;
1302 user->next->prev = user->prev;
1305 user->u_prev->u_next = user->u_next;
1307 user->handle->channels = user->u_next;
1309 user->u_next->u_prev = user->u_prev;
1313 if(do_gc && !channel->users && !IsProtected(channel)) {
1314 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1315 unregister_channel(channel, "lost all users.");
1319 static void expire_ban(void *data);
1322 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1325 unsigned int ii, l1, l2;
1330 bd = malloc(sizeof(struct banData));
1332 bd->channel = channel;
1334 bd->triggered = triggered;
1335 bd->expires = expires;
1337 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1339 extern const char *hidden_host_suffix;
1340 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1344 l2 = strlen(old_name);
1347 if(irccasecmp(mask + l1 - l2, old_name))
1349 new_mask = alloca(MAXLEN);
1350 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1353 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1355 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1356 bd->reason = strdup(reason);
1359 timeq_add(expires, expire_ban, bd);
1362 bd->next = channel->bans;
1364 channel->bans->prev = bd;
1366 channel->banCount++;
1373 del_channel_ban(struct banData *ban)
1375 ban->channel->banCount--;
1379 ban->prev->next = ban->next;
1381 ban->channel->bans = ban->next;
1384 ban->next->prev = ban->prev;
1387 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1396 expire_ban(void *data)
1398 struct banData *bd = data;
1399 if(!IsSuspended(bd->channel))
1401 struct banList bans;
1402 struct mod_chanmode change;
1404 bans = bd->channel->channel->banlist;
1405 mod_chanmode_init(&change);
1406 for(ii=0; ii<bans.used; ii++)
1408 if(!strcmp(bans.list[ii]->ban, bd->mask))
1411 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1412 change.args[0].u.hostmask = bd->mask;
1413 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1419 del_channel_ban(bd);
1422 static void chanserv_expire_suspension(void *data);
1425 unregister_channel(struct chanData *channel, const char *reason)
1427 struct mod_chanmode change;
1428 char msgbuf[MAXLEN];
1430 /* After channel unregistration, the following must be cleaned
1432 - Channel information.
1435 - Channel suspension data.
1436 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1442 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1444 if(off_channel > 0 || chanserv_conf.revoke_mode_a) {
1445 mod_chanmode_init(&change);
1447 change.modes_clear |= MODE_REGISTERED;
1448 if(chanserv_conf.revoke_mode_a)
1449 change.modes_clear |= MODE_ACCESS;
1450 mod_chanmode_announce(chanserv, channel->channel, &change);
1453 while(channel->users)
1454 del_channel_user(channel->users, 0);
1456 while(channel->bans)
1457 del_channel_ban(channel->bans);
1459 free(channel->topic);
1460 free(channel->registrar);
1461 free(channel->greeting);
1462 free(channel->user_greeting);
1463 free(channel->topic_mask);
1466 channel->prev->next = channel->next;
1468 channelList = channel->next;
1471 channel->next->prev = channel->prev;
1473 if(channel->suspended)
1475 struct chanNode *cNode = channel->channel;
1476 struct suspended *suspended, *next_suspended;
1478 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1480 next_suspended = suspended->previous;
1481 free(suspended->suspender);
1482 free(suspended->reason);
1483 if(suspended->expires)
1484 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1489 cNode->channel_info = NULL;
1492 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1493 channel->channel->channel_info = NULL;
1495 dict_delete(channel->notes);
1496 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1497 if(!IsSuspended(channel))
1498 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1499 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1500 UnlockChannel(channel->channel);
1502 registered_channels--;
1506 expire_channels(void *data)
1508 struct chanData *channel, *next;
1509 struct userData *user;
1510 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1512 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1513 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1515 for(channel = channelList; channel; channel = next)
1517 next = channel->next;
1519 /* See if the channel can be expired. */
1520 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1521 || IsProtected(channel))
1524 /* Make sure there are no high-ranking users still in the channel. */
1525 for(user=channel->users; user; user=user->next)
1526 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1531 /* Unregister the channel */
1532 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1533 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1534 unregister_channel(channel, "registration expired.");
1537 if(chanserv_conf.channel_expire_frequency && !data)
1538 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1542 expire_dnrs(UNUSED_ARG(void *data))
1544 dict_iterator_t it, next;
1545 struct do_not_register *dnr;
1547 for(it = dict_first(handle_dnrs); it; it = next)
1549 dnr = iter_data(it);
1550 next = iter_next(it);
1551 if(dnr->expires && dnr->expires <= now)
1552 dict_remove(handle_dnrs, dnr->chan_name + 1);
1554 for(it = dict_first(plain_dnrs); it; it = next)
1556 dnr = iter_data(it);
1557 next = iter_next(it);
1558 if(dnr->expires && dnr->expires <= now)
1559 dict_remove(plain_dnrs, dnr->chan_name + 1);
1561 for(it = dict_first(mask_dnrs); it; it = next)
1563 dnr = iter_data(it);
1564 next = iter_next(it);
1565 if(dnr->expires && dnr->expires <= now)
1566 dict_remove(mask_dnrs, dnr->chan_name + 1);
1569 if(chanserv_conf.dnr_expire_frequency)
1570 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1574 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1576 char protect = channel->chOpts[chProtect];
1577 struct userData *cs_victim, *cs_aggressor;
1579 /* Don't protect if no one is to be protected, someone is attacking
1580 himself, or if the aggressor is an IRC Operator. */
1581 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1584 /* Don't protect if the victim isn't authenticated (because they
1585 can't be a channel user), unless we are to protect non-users
1587 cs_victim = GetChannelAccess(channel, victim->handle_info);
1588 if(protect != 'a' && !cs_victim)
1591 /* Protect if the aggressor isn't a user because at this point,
1592 the aggressor can only be less than or equal to the victim. */
1593 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1597 /* If the aggressor was a user, then the victim can't be helped. */
1604 if(cs_victim->access > cs_aggressor->access)
1609 if(cs_victim->access >= cs_aggressor->access)
1618 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1620 struct chanData *cData = channel->channel_info;
1621 struct userData *cs_victim;
1623 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1624 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1625 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1627 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1635 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1637 if(IsService(victim))
1639 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1643 if(protect_user(victim, user, channel->channel_info))
1645 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1652 static struct do_not_register *
1653 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1655 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1656 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1657 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1658 strcpy(dnr->reason, reason);
1660 dnr->expires = expires;
1661 if(dnr->chan_name[0] == '*')
1662 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1663 else if(strpbrk(dnr->chan_name, "*?"))
1664 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1666 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1670 static struct dnrList
1671 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1673 struct dnrList list;
1674 dict_iterator_t it, next;
1675 struct do_not_register *dnr;
1677 dnrList_init(&list);
1679 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1681 if(dnr->expires && dnr->expires <= now)
1682 dict_remove(handle_dnrs, handle);
1683 else if(list.used < max)
1684 dnrList_append(&list, dnr);
1687 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1689 if(dnr->expires && dnr->expires <= now)
1690 dict_remove(plain_dnrs, chan_name);
1691 else if(list.used < max)
1692 dnrList_append(&list, dnr);
1697 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1699 next = iter_next(it);
1700 if(!match_ircglob(chan_name, iter_key(it)))
1702 dnr = iter_data(it);
1703 if(dnr->expires && dnr->expires <= now)
1704 dict_remove(mask_dnrs, iter_key(it));
1706 dnrList_append(&list, dnr);
1713 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1715 struct userNode *user;
1716 char buf1[INTERVALLEN];
1717 char buf2[INTERVALLEN];
1724 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1729 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1730 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1734 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1737 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1742 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1744 struct dnrList list;
1747 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1748 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1749 dnr_print_func(list.list[ii], user);
1751 reply("CSMSG_MORE_DNRS", list.used - ii);
1756 struct do_not_register *
1757 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1759 struct dnrList list;
1760 struct do_not_register *dnr;
1762 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1763 dnr = list.used ? list.list[0] : NULL;
1768 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1770 struct do_not_register *dnr;
1771 dict_iterator_t it, next;
1772 unsigned int matches = 0;
1774 for(it = dict_first(dict); it; it = next)
1776 dnr = iter_data(it);
1777 next = iter_next(it);
1778 if(dnr->expires && dnr->expires <= now)
1780 dict_remove(dict, iter_key(it));
1783 dnr_print_func(dnr, user);
1790 static CHANSERV_FUNC(cmd_noregister)
1794 unsigned long expiry, duration;
1795 unsigned int matches;
1799 reply("CSMSG_DNR_SEARCH_RESULTS");
1800 matches = send_dnrs(user, handle_dnrs);
1801 matches += send_dnrs(user, plain_dnrs);
1802 matches += send_dnrs(user, mask_dnrs);
1804 reply("MSG_MATCH_COUNT", matches);
1806 reply("MSG_NO_MATCHES");
1812 if(!IsChannelName(target) && (*target != '*'))
1814 reply("CSMSG_NOT_DNR", target);
1822 reply("MSG_INVALID_DURATION", argv[2]);
1826 if(!strcmp(argv[2], "0"))
1828 else if((duration = ParseInterval(argv[2])))
1829 expiry = now + duration;
1832 reply("MSG_INVALID_DURATION", argv[2]);
1836 reason = unsplit_string(argv + 3, argc - 3, NULL);
1837 if((*target == '*') && !get_handle_info(target + 1))
1839 reply("MSG_HANDLE_UNKNOWN", target + 1);
1842 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1843 reply("CSMSG_NOREGISTER_CHANNEL", target);
1847 reply("CSMSG_DNR_SEARCH_RESULTS");
1849 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1851 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1853 reply("MSG_NO_MATCHES");
1857 static CHANSERV_FUNC(cmd_allowregister)
1859 const char *chan_name = argv[1];
1861 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1862 || dict_remove(plain_dnrs, chan_name)
1863 || dict_remove(mask_dnrs, chan_name))
1865 reply("CSMSG_DNR_REMOVED", chan_name);
1868 reply("CSMSG_NO_SUCH_DNR", chan_name);
1873 struct userNode *source;
1877 unsigned long min_set, max_set;
1878 unsigned long min_expires, max_expires;
1883 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1885 return !((dnr->set < search->min_set)
1886 || (dnr->set > search->max_set)
1887 || (dnr->expires < search->min_expires)
1888 || (search->max_expires
1889 && ((dnr->expires == 0)
1890 || (dnr->expires > search->max_expires)))
1891 || (search->chan_mask
1892 && !match_ircglob(dnr->chan_name, search->chan_mask))
1893 || (search->setter_mask
1894 && !match_ircglob(dnr->setter, search->setter_mask))
1895 || (search->reason_mask
1896 && !match_ircglob(dnr->reason, search->reason_mask)));
1899 static struct dnr_search *
1900 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1902 struct dnr_search *discrim;
1905 discrim = calloc(1, sizeof(*discrim));
1906 discrim->source = user;
1907 discrim->chan_mask = NULL;
1908 discrim->setter_mask = NULL;
1909 discrim->reason_mask = NULL;
1910 discrim->max_set = INT_MAX;
1911 discrim->limit = 50;
1913 for(ii=0; ii<argc; ++ii)
1917 reply("MSG_MISSING_PARAMS", argv[ii]);
1920 else if(0 == irccasecmp(argv[ii], "channel"))
1922 discrim->chan_mask = argv[++ii];
1924 else if(0 == irccasecmp(argv[ii], "setter"))
1926 discrim->setter_mask = argv[++ii];
1928 else if(0 == irccasecmp(argv[ii], "reason"))
1930 discrim->reason_mask = argv[++ii];
1932 else if(0 == irccasecmp(argv[ii], "limit"))
1934 discrim->limit = strtoul(argv[++ii], NULL, 0);
1936 else if(0 == irccasecmp(argv[ii], "set"))
1938 const char *cmp = argv[++ii];
1941 discrim->min_set = now - ParseInterval(cmp + 2);
1943 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1944 } else if(cmp[0] == '=') {
1945 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1946 } else if(cmp[0] == '>') {
1948 discrim->max_set = now - ParseInterval(cmp + 2);
1950 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1952 discrim->max_set = now - (ParseInterval(cmp) - 1);
1955 else if(0 == irccasecmp(argv[ii], "expires"))
1957 const char *cmp = argv[++ii];
1960 discrim->max_expires = now + ParseInterval(cmp + 2);
1962 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1963 } else if(cmp[0] == '=') {
1964 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1965 } else if(cmp[0] == '>') {
1967 discrim->min_expires = now + ParseInterval(cmp + 2);
1969 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1971 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1976 reply("MSG_INVALID_CRITERIA", argv[ii]);
1987 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1990 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1992 struct do_not_register *dnr;
1993 dict_iterator_t next;
1998 /* Initialize local variables. */
2001 if(discrim->chan_mask)
2003 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
2004 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2008 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2010 /* Check against account-based DNRs. */
2011 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2012 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2015 else if(target_fixed)
2017 /* Check against channel-based DNRs. */
2018 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2019 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2024 /* Exhaustively search account DNRs. */
2025 for(it = dict_first(handle_dnrs); it; it = next)
2027 next = iter_next(it);
2028 dnr = iter_data(it);
2029 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2033 /* Do the same for channel DNRs. */
2034 for(it = dict_first(plain_dnrs); it; it = next)
2036 next = iter_next(it);
2037 dnr = iter_data(it);
2038 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2042 /* Do the same for wildcarded channel DNRs. */
2043 for(it = dict_first(mask_dnrs); it; it = next)
2045 next = iter_next(it);
2046 dnr = iter_data(it);
2047 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2055 dnr_remove_func(struct do_not_register *match, void *extra)
2057 struct userNode *user;
2060 chan_name = alloca(strlen(match->chan_name) + 1);
2061 strcpy(chan_name, match->chan_name);
2063 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2064 || dict_remove(plain_dnrs, chan_name)
2065 || dict_remove(mask_dnrs, chan_name))
2067 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2073 dnr_count_func(struct do_not_register *match, void *extra)
2075 return 0; (void)match; (void)extra;
2078 static MODCMD_FUNC(cmd_dnrsearch)
2080 struct dnr_search *discrim;
2081 dnr_search_func action;
2082 struct svccmd *subcmd;
2083 unsigned int matches;
2086 sprintf(buf, "dnrsearch %s", argv[1]);
2087 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2090 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2093 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2095 if(!irccasecmp(argv[1], "print"))
2096 action = dnr_print_func;
2097 else if(!irccasecmp(argv[1], "remove"))
2098 action = dnr_remove_func;
2099 else if(!irccasecmp(argv[1], "count"))
2100 action = dnr_count_func;
2103 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2107 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2111 if(action == dnr_print_func)
2112 reply("CSMSG_DNR_SEARCH_RESULTS");
2113 matches = dnr_search(discrim, action, user);
2115 reply("MSG_MATCH_COUNT", matches);
2117 reply("MSG_NO_MATCHES");
2123 chanserv_get_owned_count(struct handle_info *hi)
2125 struct userData *cList;
2128 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2129 if(cList->access == UL_OWNER)
2134 static CHANSERV_FUNC(cmd_register)
2136 struct handle_info *handle;
2137 struct chanData *cData;
2138 struct modeNode *mn;
2139 char reason[MAXLEN];
2141 unsigned int new_channel, force=0;
2142 struct do_not_register *dnr;
2146 if(channel->channel_info)
2148 reply("CSMSG_ALREADY_REGGED", channel->name);
2152 if(channel->bad_channel)
2154 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2159 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2161 reply("CSMSG_MUST_BE_OPPED", channel->name);
2166 chan_name = channel->name;
2170 if((argc < 2) || !IsChannelName(argv[1]))
2172 reply("MSG_NOT_CHANNEL_NAME");
2176 if(opserv_bad_channel(argv[1]))
2178 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2183 chan_name = argv[1];
2186 if(argc >= (new_channel+2))
2188 if(!IsHelping(user))
2190 reply("CSMSG_PROXY_FORBIDDEN");
2194 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2196 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2197 dnr = chanserv_is_dnr(chan_name, handle);
2201 handle = user->handle_info;
2202 dnr = chanserv_is_dnr(chan_name, handle);
2206 if(!IsHelping(user))
2207 reply("CSMSG_DNR_CHANNEL", chan_name);
2209 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2213 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2215 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2220 channel = AddChannel(argv[1], now, NULL, NULL);
2222 cData = register_channel(channel, user->handle_info->handle);
2223 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2224 cData->modes = chanserv_conf.default_modes;
2226 cData->modes.modes_set |= MODE_REGISTERED;
2227 if (IsOffChannel(cData))
2229 mod_chanmode_announce(chanserv, channel, &cData->modes);
2233 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2234 change->args[change->argc].mode = MODE_CHANOP;
2235 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2237 mod_chanmode_announce(chanserv, channel, change);
2238 mod_chanmode_free(change);
2241 /* Initialize the channel's max user record. */
2242 cData->max = channel->members.used;
2243 cData->max_time = 0;
2245 if(handle != user->handle_info)
2246 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2248 reply("CSMSG_REG_SUCCESS", channel->name);
2250 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2251 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2256 make_confirmation_string(struct userData *uData)
2258 static char strbuf[16];
2263 for(src = uData->handle->handle; *src; )
2264 accum = accum * 31 + toupper(*src++);
2266 for(src = uData->channel->channel->name; *src; )
2267 accum = accum * 31 + toupper(*src++);
2268 sprintf(strbuf, "%08x", accum);
2272 static CHANSERV_FUNC(cmd_unregister)
2275 char reason[MAXLEN];
2276 struct chanData *cData;
2277 struct userData *uData;
2279 cData = channel->channel_info;
2282 reply("CSMSG_NOT_REGISTERED", channel->name);
2286 uData = GetChannelUser(cData, user->handle_info);
2287 if(!uData || (uData->access < UL_OWNER))
2289 reply("CSMSG_NO_ACCESS");
2293 if(IsProtected(cData) && !IsOper(user))
2295 reply("CSMSG_UNREG_NODELETE", channel->name);
2299 if(!IsHelping(user))
2301 const char *confirm_string;
2302 if(IsSuspended(cData))
2304 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2307 confirm_string = make_confirmation_string(uData);
2308 if((argc < 2) || strcmp(argv[1], confirm_string))
2310 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2315 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2316 name = strdup(channel->name);
2317 unregister_channel(cData, reason);
2318 spamserv_cs_unregister(user, channel, manually, "unregistered");
2319 reply("CSMSG_UNREG_SUCCESS", name);
2325 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2327 extern struct userNode *spamserv;
2328 struct mod_chanmode *change;
2330 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2332 change = mod_chanmode_alloc(2);
2334 change->args[0].mode = MODE_CHANOP;
2335 change->args[0].u.member = AddChannelUser(chanserv, channel);
2336 change->args[1].mode = MODE_CHANOP;
2337 change->args[1].u.member = AddChannelUser(spamserv, channel);
2341 change = mod_chanmode_alloc(1);
2343 change->args[0].mode = MODE_CHANOP;
2344 change->args[0].u.member = AddChannelUser(chanserv, channel);
2347 mod_chanmode_announce(chanserv, channel, change);
2348 mod_chanmode_free(change);
2351 static CHANSERV_FUNC(cmd_move)
2353 struct mod_chanmode change;
2354 struct chanNode *target;
2355 struct modeNode *mn;
2356 struct userData *uData;
2357 char reason[MAXLEN];
2358 struct do_not_register *dnr;
2359 int chanserv_join = 0, spamserv_join;
2363 if(IsProtected(channel->channel_info))
2365 reply("CSMSG_MOVE_NODELETE", channel->name);
2369 if(!IsChannelName(argv[1]))
2371 reply("MSG_NOT_CHANNEL_NAME");
2375 if(opserv_bad_channel(argv[1]))
2377 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2381 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2383 for(uData = channel->channel_info->users; uData; uData = uData->next)
2385 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2387 if(!IsHelping(user))
2388 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2390 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2396 mod_chanmode_init(&change);
2397 if(!(target = GetChannel(argv[1])))
2399 target = AddChannel(argv[1], now, NULL, NULL);
2400 if(!IsSuspended(channel->channel_info))
2403 else if(target->channel_info)
2405 reply("CSMSG_ALREADY_REGGED", target->name);
2408 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2409 && !IsHelping(user))
2411 reply("CSMSG_MUST_BE_OPPED", target->name);
2414 else if(!IsSuspended(channel->channel_info))
2419 /* Clear MODE_REGISTERED from old channel, add it to new. */
2421 change.modes_clear = MODE_REGISTERED;
2422 mod_chanmode_announce(chanserv, channel, &change);
2423 change.modes_clear = 0;
2424 change.modes_set = MODE_REGISTERED;
2425 mod_chanmode_announce(chanserv, target, &change);
2428 /* Move the channel_info to the target channel; it
2429 shouldn't be necessary to clear timeq callbacks
2430 for the old channel. */
2431 target->channel_info = channel->channel_info;
2432 target->channel_info->channel = target;
2433 channel->channel_info = NULL;
2435 /* Check whether users are present in the new channel. */
2436 for(uData = target->channel_info->users; uData; uData = uData->next)
2437 scan_user_presence(uData, NULL);
2439 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2442 ss_cs_join_channel(target, spamserv_join);
2444 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2445 if(!IsSuspended(target->channel_info))
2447 char reason2[MAXLEN];
2448 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2449 DelChannelUser(chanserv, channel, reason2, 0);
2451 UnlockChannel(channel);
2452 LockChannel(target);
2453 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2454 reply("CSMSG_MOVE_SUCCESS", target->name);
2459 merge_users(struct chanData *source, struct chanData *target)
2461 struct userData *suData, *tuData, *next;
2467 /* Insert the source's users into the scratch area. */
2468 for(suData = source->users; suData; suData = suData->next)
2469 dict_insert(merge, suData->handle->handle, suData);
2471 /* Iterate through the target's users, looking for
2472 users common to both channels. The lower access is
2473 removed from either the scratch area or target user
2475 for(tuData = target->users; tuData; tuData = next)
2477 struct userData *choice;
2479 next = tuData->next;
2481 /* If a source user exists with the same handle as a target
2482 channel's user, resolve the conflict by removing one. */
2483 suData = dict_find(merge, tuData->handle->handle, NULL);
2487 /* Pick the data we want to keep. */
2488 /* If the access is the same, use the later seen time. */
2489 if(suData->access == tuData->access)
2490 choice = (suData->seen > tuData->seen) ? suData : tuData;
2491 else /* Otherwise, keep the higher access level. */
2492 choice = (suData->access > tuData->access) ? suData : tuData;
2493 /* Use the later seen time. */
2494 if(suData->seen < tuData->seen)
2495 suData->seen = tuData->seen;
2497 tuData->seen = suData->seen;
2499 /* Remove the user that wasn't picked. */
2500 if(choice == tuData)
2502 dict_remove(merge, suData->handle->handle);
2503 del_channel_user(suData, 0);
2506 del_channel_user(tuData, 0);
2509 /* Move the remaining users to the target channel. */
2510 for(it = dict_first(merge); it; it = iter_next(it))
2512 suData = iter_data(it);
2514 /* Insert the user into the target channel's linked list. */
2515 suData->prev = NULL;
2516 suData->next = target->users;
2517 suData->channel = target;
2520 target->users->prev = suData;
2521 target->users = suData;
2523 /* Update the user counts for the target channel; the
2524 source counts are left alone. */
2525 target->userCount++;
2527 /* Check whether the user is in the target channel. */
2528 scan_user_presence(suData, NULL);
2531 /* Possible to assert (source->users == NULL) here. */
2532 source->users = NULL;
2537 merge_bans(struct chanData *source, struct chanData *target)
2539 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2541 /* Hold on to the original head of the target ban list
2542 to avoid comparing source bans with source bans. */
2543 tFront = target->bans;
2545 /* Perform a totally expensive O(n*m) merge, ick. */
2546 for(sbData = source->bans; sbData; sbData = sNext)
2548 /* Flag to track whether the ban's been moved
2549 to the destination yet. */
2552 /* Possible to assert (sbData->prev == NULL) here. */
2553 sNext = sbData->next;
2555 for(tbData = tFront; tbData; tbData = tNext)
2557 tNext = tbData->next;
2559 /* Perform two comparisons between each source
2560 and target ban, conflicts are resolved by
2561 keeping the broader ban and copying the later
2562 expiration and triggered time. */
2563 if(match_ircglobs(tbData->mask, sbData->mask))
2565 /* There is a broader ban in the target channel that
2566 overrides one in the source channel; remove the
2567 source ban and break. */
2568 if(sbData->expires > tbData->expires)
2569 tbData->expires = sbData->expires;
2570 if(sbData->triggered > tbData->triggered)
2571 tbData->triggered = sbData->triggered;
2572 del_channel_ban(sbData);
2575 else if(match_ircglobs(sbData->mask, tbData->mask))
2577 /* There is a broader ban in the source channel that
2578 overrides one in the target channel; remove the
2579 target ban, fall through and move the source over. */
2580 if(tbData->expires > sbData->expires)
2581 sbData->expires = tbData->expires;
2582 if(tbData->triggered > sbData->triggered)
2583 sbData->triggered = tbData->triggered;
2584 if(tbData == tFront)
2586 del_channel_ban(tbData);
2589 /* Source bans can override multiple target bans, so
2590 we allow a source to run through this loop multiple
2591 times, but we can only move it once. */
2596 /* Remove the source ban from the source ban list. */
2598 sbData->next->prev = sbData->prev;
2600 /* Modify the source ban's associated channel. */
2601 sbData->channel = target;
2603 /* Insert the ban into the target channel's linked list. */
2604 sbData->prev = NULL;
2605 sbData->next = target->bans;
2608 target->bans->prev = sbData;
2609 target->bans = sbData;
2611 /* Update the user counts for the target channel. */
2616 /* Possible to assert (source->bans == NULL) here. */
2617 source->bans = NULL;
2621 merge_data(struct chanData *source, struct chanData *target)
2623 /* Use more recent visited and owner-transfer time; use older
2624 * registered time. Bitwise or may_opchan. Use higher max.
2625 * Do not touch last_refresh, ban count or user counts.
2627 if(source->visited > target->visited)
2628 target->visited = source->visited;
2629 if(source->registered < target->registered)
2630 target->registered = source->registered;
2631 if(source->ownerTransfer > target->ownerTransfer)
2632 target->ownerTransfer = source->ownerTransfer;
2633 if(source->may_opchan)
2634 target->may_opchan = 1;
2635 if(source->max > target->max) {
2636 target->max = source->max;
2637 target->max_time = source->max_time;
2642 merge_channel(struct chanData *source, struct chanData *target)
2644 merge_users(source, target);
2645 merge_bans(source, target);
2646 merge_data(source, target);
2649 static CHANSERV_FUNC(cmd_merge)
2651 struct userData *target_user;
2652 struct chanNode *target;
2653 char reason[MAXLEN];
2657 /* Make sure the target channel exists and is registered to the user
2658 performing the command. */
2659 if(!(target = GetChannel(argv[1])))
2661 reply("MSG_INVALID_CHANNEL");
2665 if(!target->channel_info)
2667 reply("CSMSG_NOT_REGISTERED", target->name);
2671 if(IsProtected(channel->channel_info))
2673 reply("CSMSG_MERGE_NODELETE");
2677 if(IsSuspended(target->channel_info))
2679 reply("CSMSG_MERGE_SUSPENDED");
2683 if(channel == target)
2685 reply("CSMSG_MERGE_SELF");
2689 target_user = GetChannelUser(target->channel_info, user->handle_info);
2690 if(!target_user || (target_user->access < UL_OWNER))
2692 reply("CSMSG_MERGE_NOT_OWNER");
2696 /* Merge the channel structures and associated data. */
2697 merge_channel(channel->channel_info, target->channel_info);
2698 spamserv_cs_move_merge(user, channel, target, 0);
2699 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2700 unregister_channel(channel->channel_info, reason);
2701 reply("CSMSG_MERGE_SUCCESS", target->name);
2705 static CHANSERV_FUNC(cmd_opchan)
2707 struct mod_chanmode change;
2708 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2710 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2713 channel->channel_info->may_opchan = 0;
2714 mod_chanmode_init(&change);
2716 change.args[0].mode = MODE_CHANOP;
2717 change.args[0].u.member = GetUserMode(channel, chanserv);
2718 if(!change.args[0].u.member)
2720 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2723 mod_chanmode_announce(chanserv, channel, &change);
2724 reply("CSMSG_OPCHAN_DONE", channel->name);
2728 static CHANSERV_FUNC(cmd_adduser)
2730 struct userData *actee;
2731 struct userData *actor, *real_actor;
2732 struct handle_info *handle;
2733 unsigned short access_level, override = 0;
2737 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2739 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2743 access_level = user_level_from_name(argv[2], UL_OWNER);
2746 reply("CSMSG_INVALID_ACCESS", argv[2]);
2750 actor = GetChannelUser(channel->channel_info, user->handle_info);
2751 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2753 if(actor->access <= access_level)
2755 reply("CSMSG_NO_BUMP_ACCESS");
2759 /* Trying to add someone with equal/more access? */
2760 if (!real_actor || real_actor->access <= access_level)
2761 override = CMD_LOG_OVERRIDE;
2763 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2766 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2768 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2772 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2773 scan_user_presence(actee, NULL);
2774 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2775 return 1 | override;
2778 static CHANSERV_FUNC(cmd_clvl)
2780 struct handle_info *handle;
2781 struct userData *victim;
2782 struct userData *actor, *real_actor;
2783 unsigned short new_access, override = 0;
2784 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2788 actor = GetChannelUser(channel->channel_info, user->handle_info);
2789 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2791 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2794 if(handle == user->handle_info && !privileged)
2796 reply("CSMSG_NO_SELF_CLVL");
2800 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2802 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2806 if(actor->access <= victim->access && !privileged)
2808 reply("MSG_USER_OUTRANKED", handle->handle);
2812 new_access = user_level_from_name(argv[2], UL_OWNER);
2816 reply("CSMSG_INVALID_ACCESS", argv[2]);
2820 if(new_access >= actor->access && !privileged)
2822 reply("CSMSG_NO_BUMP_ACCESS");
2826 /* Trying to clvl a equal/higher user? */
2827 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2828 override = CMD_LOG_OVERRIDE;
2829 /* Trying to clvl someone to equal/higher access? */
2830 if(!real_actor || new_access >= real_actor->access)
2831 override = CMD_LOG_OVERRIDE;
2832 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2833 * If they lower their own access it's not a big problem.
2836 victim->access = new_access;
2837 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2838 return 1 | override;
2841 static CHANSERV_FUNC(cmd_deluser)
2843 struct handle_info *handle;
2844 struct userData *victim;
2845 struct userData *actor, *real_actor;
2846 unsigned short access_level, override = 0;
2851 actor = GetChannelUser(channel->channel_info, user->handle_info);
2852 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2854 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2857 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2859 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2865 access_level = user_level_from_name(argv[1], UL_OWNER);
2868 reply("CSMSG_INVALID_ACCESS", argv[1]);
2871 if(access_level != victim->access)
2873 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2879 access_level = victim->access;
2882 if((actor->access <= victim->access) && !IsHelping(user))
2884 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2888 /* If people delete themselves it is an override, but they
2889 * could've used deleteme so we don't log it as an override
2891 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2892 override = CMD_LOG_OVERRIDE;
2894 chan_name = strdup(channel->name);
2895 del_channel_user(victim, 1);
2896 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2898 return 1 | override;
2902 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2904 struct userData *actor, *real_actor, *uData, *next;
2905 unsigned int override = 0;
2907 actor = GetChannelUser(channel->channel_info, user->handle_info);
2908 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2910 if(min_access > max_access)
2912 reply("CSMSG_BAD_RANGE", min_access, max_access);
2916 if(actor->access <= max_access)
2918 reply("CSMSG_NO_ACCESS");
2922 if(!real_actor || real_actor->access <= max_access)
2923 override = CMD_LOG_OVERRIDE;
2925 for(uData = channel->channel_info->users; uData; uData = next)
2929 if((uData->access >= min_access)
2930 && (uData->access <= max_access)
2931 && match_ircglob(uData->handle->handle, mask))
2932 del_channel_user(uData, 1);
2935 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2936 return 1 | override;
2939 static CHANSERV_FUNC(cmd_mdelowner)
2941 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2944 static CHANSERV_FUNC(cmd_mdelcoowner)
2946 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2949 static CHANSERV_FUNC(cmd_mdelmaster)
2951 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelop)
2956 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2959 static CHANSERV_FUNC(cmd_mdelpeon)
2961 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2965 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2967 struct banData *bData, *next;
2968 char interval[INTERVALLEN];
2970 unsigned long limit;
2973 limit = now - duration;
2974 for(bData = channel->channel_info->bans; bData; bData = next)
2978 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2981 del_channel_ban(bData);
2985 intervalString(interval, duration, user->handle_info);
2986 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2991 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2993 struct userData *actor, *uData, *next;
2994 char interval[INTERVALLEN];
2996 unsigned long limit;
2998 actor = GetChannelUser(channel->channel_info, user->handle_info);
2999 if(min_access > max_access)
3001 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3005 if(!actor || actor->access <= max_access)
3007 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3012 limit = now - duration;
3013 for(uData = channel->channel_info->users; uData; uData = next)
3017 if((uData->seen > limit)
3019 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3022 if(((uData->access >= min_access) && (uData->access <= max_access))
3023 || (!max_access && (uData->access < actor->access)))
3025 del_channel_user(uData, 1);
3033 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3035 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3039 static CHANSERV_FUNC(cmd_trim)
3041 unsigned long duration;
3042 unsigned short min_level, max_level;
3047 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3048 duration = ParseInterval(argv[2]);
3051 reply("CSMSG_CANNOT_TRIM");
3055 if(!irccasecmp(argv[1], "bans"))
3057 cmd_trim_bans(user, channel, duration);
3060 else if(!irccasecmp(argv[1], "users"))
3062 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3065 else if(parse_level_range(&min_level, &max_level, argv[1]))
3067 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3070 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3072 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3077 reply("CSMSG_INVALID_TRIM", argv[1]);
3082 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3083 to the user. cmd_all takes advantage of this. */
3084 static CHANSERV_FUNC(cmd_up)
3086 struct mod_chanmode change;
3087 struct userData *uData;
3090 mod_chanmode_init(&change);
3092 change.args[0].u.member = GetUserMode(channel, user);
3093 if(!change.args[0].u.member)
3096 reply("MSG_CHANNEL_ABSENT", channel->name);
3100 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3104 reply("CSMSG_GODMODE_UP", argv[0]);
3107 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3109 change.args[0].mode = MODE_CHANOP;
3110 errmsg = "CSMSG_ALREADY_OPPED";
3112 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3114 change.args[0].mode = MODE_VOICE;
3115 errmsg = "CSMSG_ALREADY_VOICED";
3120 reply("CSMSG_NO_ACCESS");
3123 change.args[0].mode &= ~change.args[0].u.member->modes;
3124 if(!change.args[0].mode)
3127 reply(errmsg, channel->name);
3130 modcmd_chanmode_announce(&change);
3134 static CHANSERV_FUNC(cmd_down)
3136 struct mod_chanmode change;
3138 mod_chanmode_init(&change);
3140 change.args[0].u.member = GetUserMode(channel, user);
3141 if(!change.args[0].u.member)
3144 reply("MSG_CHANNEL_ABSENT", channel->name);
3148 if(!change.args[0].u.member->modes)
3151 reply("CSMSG_ALREADY_DOWN", channel->name);
3155 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3156 modcmd_chanmode_announce(&change);
3160 static 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)
3162 struct userData *cList;
3164 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3166 if(IsSuspended(cList->channel)
3167 || IsUserSuspended(cList)
3168 || !GetUserMode(cList->channel->channel, user))
3171 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3177 static CHANSERV_FUNC(cmd_upall)
3179 return cmd_all(CSFUNC_ARGS, cmd_up);
3182 static CHANSERV_FUNC(cmd_downall)
3184 return cmd_all(CSFUNC_ARGS, cmd_down);
3187 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3188 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3191 modify_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)
3193 unsigned int ii, valid;
3194 struct userNode *victim;
3195 struct mod_chanmode *change;
3197 change = mod_chanmode_alloc(argc - 1);
3199 for(ii=valid=0; ++ii < argc; )
3201 if(!(victim = GetUserH(argv[ii])))
3203 change->args[valid].mode = mode;
3204 change->args[valid].u.member = GetUserMode(channel, victim);
3205 if(!change->args[valid].u.member)
3207 if(validate && !validate(user, channel, victim))
3212 change->argc = valid;
3213 if(valid < (argc-1))
3214 reply("CSMSG_PROCESS_FAILED");
3217 modcmd_chanmode_announce(change);
3218 reply(action, channel->name);
3220 mod_chanmode_free(change);
3224 static CHANSERV_FUNC(cmd_op)
3226 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3229 static CHANSERV_FUNC(cmd_deop)
3231 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3234 static CHANSERV_FUNC(cmd_voice)
3236 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3239 static CHANSERV_FUNC(cmd_devoice)
3241 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3244 static CHANSERV_FUNC(cmd_opme)
3246 struct mod_chanmode change;
3249 mod_chanmode_init(&change);
3251 change.args[0].u.member = GetUserMode(channel, user);
3252 if(!change.args[0].u.member)
3255 reply("MSG_CHANNEL_ABSENT", channel->name);
3259 struct devnull_class *devnull;
3260 if(user->handle_info->devnull && (devnull = devnull_get(user->handle_info->devnull)) && (devnull->modes & DEVNULL_MODE_OPME))
3262 change.args[0].mode = MODE_CHANOP;
3263 errmsg = "CSMSG_ALREADY_OPPED";
3268 reply("CSMSG_NO_ACCESS");
3271 change.args[0].mode &= ~change.args[0].u.member->modes;
3272 if(!change.args[0].mode)
3275 reply(errmsg, channel->name);
3278 modcmd_chanmode_announce(&change);
3283 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3289 for(ii=0; ii<channel->members.used; ii++)
3291 struct modeNode *mn = channel->members.list[ii];
3293 if(IsService(mn->user))
3296 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3299 if(protect_user(mn->user, user, channel->channel_info))
3303 victims[(*victimCount)++] = mn;
3309 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3311 struct userNode *victim;
3312 struct modeNode **victims;
3313 unsigned int offset, n, victimCount, duration = 0;
3314 char *reason = "Bye.", *ban, *name;
3315 char interval[INTERVALLEN];
3317 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3318 REQUIRE_PARAMS(offset);
3319 if(argc > offset && IsNetServ(user))
3321 if(*argv[offset] == '$') {
3322 struct userNode *hib;
3323 const char *accountnameb = argv[offset] + 1;
3324 if(!(hib = GetUserH(accountnameb)))
3326 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3335 reason = unsplit_string(argv + offset, argc - offset, NULL);
3336 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3338 /* Truncate the reason to a length of TOPICLEN, as
3339 the ircd does; however, leave room for an ellipsis
3340 and the kicker's nick. */
3341 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3345 if((victim = GetUserH(argv[1])))
3347 victims = alloca(sizeof(victims[0]));
3348 victims[0] = GetUserMode(channel, victim);
3349 /* XXX: The comparison with ACTION_KICK is just because all
3350 * other actions can work on users outside the channel, and we
3351 * want to allow those (e.g. unbans) in that case. If we add
3352 * some other ejection action for in-channel users, change
3354 victimCount = victims[0] ? 1 : 0;
3356 if(IsService(victim))
3358 reply("MSG_SERVICE_IMMUNE", victim->nick);
3362 if((action == ACTION_KICK) && !victimCount)
3364 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3368 if(protect_user(victim, user, channel->channel_info))
3370 reply("CSMSG_USER_PROTECTED", victim->nick);
3374 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3375 name = victim->nick;
3377 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3379 struct handle_info *hi;
3380 extern const char *titlehost_suffix;
3381 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3382 const char *accountname = argv[1] + 1;
3384 if(!(hi = get_handle_info(accountname)))
3386 reply("MSG_HANDLE_UNKNOWN", accountname);
3390 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3391 victims = alloca(sizeof(victims[0]) * channel->members.used);
3393 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3395 reply("CSMSG_MASK_PROTECTED", banmask);
3399 if((action == ACTION_KICK) && (victimCount == 0))
3401 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3405 name = ban = strdup(banmask);
3409 if(!is_ircmask(argv[1]))
3411 reply("MSG_NICK_UNKNOWN", argv[1]);
3415 victims = alloca(sizeof(victims[0]) * channel->members.used);
3417 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3419 reply("CSMSG_MASK_PROTECTED", argv[1]);
3423 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3425 reply("CSMSG_LAME_MASK", argv[1]);
3429 if((action == ACTION_KICK) && (victimCount == 0))
3431 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3435 name = ban = strdup(argv[1]);
3438 /* Truncate the ban in place if necessary; we must ensure
3439 that 'ban' is a valid ban mask before sanitizing it. */
3440 sanitize_ircmask(ban);
3442 if(action & ACTION_ADD_BAN)
3444 struct banData *bData, *next;
3446 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3448 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3453 if(action & ACTION_ADD_TIMED_BAN)
3455 duration = ParseInterval(argv[2]);
3459 reply("CSMSG_DURATION_TOO_LOW");
3463 else if(duration > (86400 * 365 * 2))
3465 reply("CSMSG_DURATION_TOO_HIGH");
3471 for(bData = channel->channel_info->bans; bData; bData = next)
3473 if(match_ircglobs(bData->mask, ban))
3475 int exact = !irccasecmp(bData->mask, ban);
3477 /* The ban is redundant; there is already a ban
3478 with the same effect in place. */
3482 free(bData->reason);
3483 bData->reason = strdup(reason);
3484 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3486 reply("CSMSG_REASON_CHANGE", ban);
3490 if(exact && bData->expires)
3494 /* If the ban matches an existing one exactly,
3495 extend the expiration time if the provided
3496 duration is longer. */
3497 if(duration && (now + duration > bData->expires))
3499 bData->expires = now + duration;
3510 /* Delete the expiration timeq entry and
3511 requeue if necessary. */
3512 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3515 timeq_add(bData->expires, expire_ban, bData);
3519 /* automated kickban */
3522 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3524 reply("CSMSG_BAN_ADDED", name, channel->name);
3530 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3537 if(match_ircglobs(ban, bData->mask))
3539 /* The ban we are adding makes previously existing
3540 bans redundant; silently remove them. */
3541 del_channel_ban(bData);
3545 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);
3547 name = ban = strdup(bData->mask);
3551 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3553 extern const char *hidden_host_suffix;
3554 const char *old_name = chanserv_conf.old_ban_names->list[n];
3556 unsigned int l1, l2;
3559 l2 = strlen(old_name);
3562 if(irccasecmp(ban + l1 - l2, old_name))
3564 new_mask = malloc(MAXLEN);
3565 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3567 name = ban = new_mask;
3572 if(action & ACTION_BAN)
3574 unsigned int exists;
3575 struct mod_chanmode *change;
3577 if(channel->banlist.used >= MAXBANS)
3580 reply("CSMSG_BANLIST_FULL", channel->name);
3585 exists = ChannelBanExists(channel, ban);
3586 change = mod_chanmode_alloc(victimCount + 1);
3587 for(n = 0; n < victimCount; ++n)
3589 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3590 change->args[n].u.member = victims[n];
3594 change->args[n].mode = MODE_BAN;
3595 change->args[n++].u.hostmask = ban;
3599 modcmd_chanmode_announce(change);
3601 mod_chanmode_announce(chanserv, channel, change);
3602 mod_chanmode_free(change);
3604 if(exists && (action == ACTION_BAN))
3607 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3613 if(action & ACTION_KICK)
3615 char kick_reason[MAXLEN];
3616 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3618 for(n = 0; n < victimCount; n++)
3619 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3624 /* No response, since it was automated. */
3626 else if(action & ACTION_ADD_BAN)
3629 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3631 reply("CSMSG_BAN_ADDED", name, channel->name);
3633 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3634 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3635 else if(action & ACTION_BAN)
3636 reply("CSMSG_BAN_DONE", name, channel->name);
3637 else if(action & ACTION_KICK && victimCount)
3638 reply("CSMSG_KICK_DONE", name, channel->name);
3644 static CHANSERV_FUNC(cmd_kickban)
3646 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3649 static CHANSERV_FUNC(cmd_kick)
3651 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3654 static CHANSERV_FUNC(cmd_ban)
3656 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3659 static CHANSERV_FUNC(cmd_addban)
3661 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3664 static CHANSERV_FUNC(cmd_addtimedban)
3666 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3669 struct mod_chanmode *
3670 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3672 struct mod_chanmode *change;
3673 unsigned char *match;
3674 unsigned int ii, count;
3676 match = alloca(bans->used);
3679 for(ii = count = 0; ii < bans->used; ++ii)
3681 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3682 MATCH_USENICK | MATCH_VISIBLE);
3689 for(ii = count = 0; ii < bans->used; ++ii)
3691 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3698 change = mod_chanmode_alloc(count);
3699 for(ii = count = 0; ii < bans->used; ++ii)
3703 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3704 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3706 assert(count == change->argc);
3711 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3713 struct userNode *actee;
3719 /* may want to allow a comma delimited list of users... */
3720 if(!(actee = GetUserH(argv[1])))
3722 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3724 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3725 const char *accountname = argv[1] + 1;
3727 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3728 mask = strdup(banmask);
3730 else if(!is_ircmask(argv[1]))
3732 reply("MSG_NICK_UNKNOWN", argv[1]);
3737 mask = strdup(argv[1]);
3741 /* We don't sanitize the mask here because ircu
3743 if(action & ACTION_UNBAN)
3745 struct mod_chanmode *change;
3746 change = find_matching_bans(&channel->banlist, actee, mask);
3751 modcmd_chanmode_announce(change);
3752 for(ii = 0; ii < change->argc; ++ii)
3753 free((char*)change->args[ii].u.hostmask);
3754 mod_chanmode_free(change);
3759 if(action & ACTION_DEL_BAN)
3761 struct banData *ban, *next;
3763 ban = channel->channel_info->bans;
3767 for( ; ban && !user_matches_glob(actee, ban->mask,
3768 MATCH_USENICK | MATCH_VISIBLE);
3771 for( ; ban && !match_ircglobs(mask, ban->mask);
3776 del_channel_ban(ban);
3783 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3785 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3791 static CHANSERV_FUNC(cmd_unban)
3793 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3796 static CHANSERV_FUNC(cmd_delban)
3798 /* it doesn't necessarily have to remove the channel ban - may want
3799 to make that an option. */
3800 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3803 static CHANSERV_FUNC(cmd_unbanme)
3805 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3806 long flags = ACTION_UNBAN;
3808 /* remove permanent bans if the user has the proper access. */
3809 if(uData->access >= UL_MASTER)
3810 flags |= ACTION_DEL_BAN;
3812 argv[1] = user->nick;
3813 return unban_user(user, channel, 2, argv, cmd, flags);
3816 static CHANSERV_FUNC(cmd_unbanall)
3818 struct mod_chanmode *change;
3821 if(!channel->banlist.used)
3823 reply("CSMSG_NO_BANS", channel->name);
3827 change = mod_chanmode_alloc(channel->banlist.used);
3828 for(ii=0; ii<channel->banlist.used; ii++)
3830 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3831 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3833 modcmd_chanmode_announce(change);
3834 for(ii = 0; ii < change->argc; ++ii)
3835 free((char*)change->args[ii].u.hostmask);
3836 mod_chanmode_free(change);
3837 reply("CSMSG_BANS_REMOVED", channel->name);
3841 static CHANSERV_FUNC(cmd_open)
3843 struct mod_chanmode *change;
3846 change = find_matching_bans(&channel->banlist, user, NULL);
3848 change = mod_chanmode_alloc(0);
3849 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3850 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3851 && channel->channel_info->modes.modes_set)
3852 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3853 modcmd_chanmode_announce(change);
3854 reply("CSMSG_CHANNEL_OPENED", channel->name);
3855 for(ii = 0; ii < change->argc; ++ii)
3856 free((char*)change->args[ii].u.hostmask);
3857 mod_chanmode_free(change);
3861 static CHANSERV_FUNC(cmd_myaccess)
3863 static struct string_buffer sbuf;
3864 struct handle_info *target_handle;
3865 struct userData *uData;
3870 target_handle = user->handle_info;
3871 else if(!IsStaff(user))
3873 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3876 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3879 if(!oper_outranks(user, target_handle))
3882 if(!target_handle->channels)
3884 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3888 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3889 for(uData = target_handle->channels; uData; uData = uData->u_next)
3891 struct chanData *cData = uData->channel;
3893 unsigned int base_len;
3895 if(uData->access > UL_OWNER)
3897 if(uData->access == UL_OWNER)
3900 if(IsProtected(cData)
3901 && (target_handle != user->handle_info)
3902 && !GetTrueChannelAccess(cData, user->handle_info)
3903 && !IsNetworkHelper(user))
3906 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3907 base_len = sbuf.used;
3908 if(IsUserSuspended(uData))
3909 string_buffer_append(&sbuf, 's');
3910 if(IsUserAutoOp(uData))
3912 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3913 string_buffer_append(&sbuf, 'o');
3914 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3915 string_buffer_append(&sbuf, 'v');
3917 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3918 string_buffer_append(&sbuf, 'i');
3919 if(sbuf.used==base_len)
3922 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3924 string_buffer_append_string(&sbuf, ")]");
3925 string_buffer_append(&sbuf, '\0');
3926 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3930 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3932 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3938 static CHANSERV_FUNC(cmd_access)
3940 struct userNode *target;
3941 struct handle_info *target_handle;
3942 struct userData *uData;
3944 char prefix[MAXLEN];
3949 target_handle = target->handle_info;
3951 else if((target = GetUserH(argv[1])))
3953 target_handle = target->handle_info;
3955 else if(argv[1][0] == '*')
3957 if(!(target_handle = get_handle_info(argv[1]+1)))
3959 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3965 reply("MSG_NICK_UNKNOWN", argv[1]);
3969 assert(target || target_handle);
3971 if(target == chanserv)
3973 reply("CSMSG_IS_CHANSERV");
3981 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3986 reply("MSG_USER_AUTHENTICATE", target->nick);
3989 reply("MSG_AUTHENTICATE");
3995 const char *epithet = NULL, *type = NULL;
3998 epithet = chanserv_conf.irc_operator_epithet;
3999 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
4001 else if(IsNetworkHelper(target))
4003 epithet = chanserv_conf.network_helper_epithet;
4004 type = user_find_message(user, "CSMSG_UC_H_TITLE");
4006 else if(IsSupportHelper(target))
4008 epithet = chanserv_conf.support_helper_epithet;
4009 type = user_find_message(user, "CSMSG_LC_H_TITLE");
4013 if(target_handle->epithet)
4014 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
4016 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
4018 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
4022 sprintf(prefix, "%s", target_handle->handle);
4025 if(!channel->channel_info)
4027 reply("CSMSG_NOT_REGISTERED", channel->name);
4031 helping = HANDLE_FLAGGED(target_handle, HELPING)
4032 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
4033 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
4035 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
4036 /* To prevent possible information leaks, only show infolines
4037 * if the requestor is in the channel or it's their own
4039 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
4041 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
4043 /* Likewise, only say it's suspended if the user has active
4044 * access in that channel or it's their own entry. */
4045 if(IsUserSuspended(uData)
4046 && (GetChannelUser(channel->channel_info, user->handle_info)
4047 || (user->handle_info == uData->handle)))
4049 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4054 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4061 def_list(struct listData *list)
4065 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4067 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4068 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4069 if(list->table.length == 1)
4071 msg = user_find_message(list->user, "MSG_NONE");
4072 send_message_type(4, list->user, list->bot, " %s", msg);
4077 userData_access_comp(const void *arg_a, const void *arg_b)
4079 const struct userData *a = *(struct userData**)arg_a;
4080 const struct userData *b = *(struct userData**)arg_b;
4082 if(a->access != b->access)
4083 res = b->access - a->access;
4085 res = irccasecmp(a->handle->handle, b->handle->handle);
4090 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4092 void (*send_list)(struct listData *);
4093 struct userData *uData;
4094 struct listData lData;
4095 unsigned int matches;
4099 lData.bot = cmd->parent->bot;
4100 lData.channel = channel;
4101 lData.lowest = lowest;
4102 lData.highest = highest;
4103 lData.search = (argc > 1) ? argv[1] : NULL;
4104 send_list = def_list;
4106 if(user->handle_info)
4108 switch(user->handle_info->userlist_style)
4110 case HI_STYLE_DEF: send_list = def_list; break;
4111 case HI_STYLE_ZOOT: send_list = def_list; break;
4115 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4117 for(uData = channel->channel_info->users; uData; uData = uData->next)
4119 if((uData->access < lowest)
4120 || (uData->access > highest)
4121 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4123 lData.users[matches++] = uData;
4125 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4127 lData.table.length = matches+1;
4128 lData.table.width = 4;
4129 lData.table.flags = TABLE_NO_FREE;
4130 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4131 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4132 lData.table.contents[0] = ary;
4135 ary[2] = "Last Seen";
4137 for(matches = 1; matches < lData.table.length; ++matches)
4139 char seen[INTERVALLEN];
4141 uData = lData.users[matches-1];
4142 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4143 lData.table.contents[matches] = ary;
4144 ary[0] = strtab(uData->access);
4145 ary[1] = uData->handle->handle;
4148 else if(!uData->seen)
4151 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4152 ary[2] = strdup(ary[2]);
4153 if(IsUserSuspended(uData))
4154 ary[3] = "Suspended";
4155 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4156 ary[3] = "Vacation";
4157 else if(HANDLE_FLAGGED(uData->handle, BOT))
4163 for(matches = 1; matches < lData.table.length; ++matches)
4165 free((char*)lData.table.contents[matches][2]);
4166 free(lData.table.contents[matches]);
4168 free(lData.table.contents[0]);
4169 free(lData.table.contents);
4170 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4174 static CHANSERV_FUNC(cmd_users)
4176 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4179 static CHANSERV_FUNC(cmd_wlist)
4181 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4184 static CHANSERV_FUNC(cmd_clist)
4186 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4189 static CHANSERV_FUNC(cmd_mlist)
4191 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4194 static CHANSERV_FUNC(cmd_olist)
4196 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4199 static CHANSERV_FUNC(cmd_plist)
4201 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4204 static CHANSERV_FUNC(cmd_bans)
4206 struct userNode *search_u = NULL;
4207 struct helpfile_table tbl;
4208 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4209 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4210 const char *msg_never, *triggered, *expires;
4211 struct banData *ban, **bans;
4215 else if(strchr(search = argv[1], '!'))
4218 search_wilds = search[strcspn(search, "?*")];
4220 else if(!(search_u = GetUserH(search)))
4221 reply("MSG_NICK_UNKNOWN", search);
4223 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4225 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4229 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4234 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4237 bans[matches++] = ban;
4242 tbl.length = matches + 1;
4243 tbl.width = 4 + timed;
4245 tbl.flags = TABLE_NO_FREE;
4246 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4247 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4248 tbl.contents[0][0] = "Mask";
4249 tbl.contents[0][1] = "Set By";
4250 tbl.contents[0][2] = "Triggered";
4253 tbl.contents[0][3] = "Expires";
4254 tbl.contents[0][4] = "Reason";
4257 tbl.contents[0][3] = "Reason";
4260 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4262 free(tbl.contents[0]);
4267 msg_never = user_find_message(user, "MSG_NEVER");
4268 for(ii = 0; ii < matches; )
4274 else if(ban->expires)
4275 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4277 expires = msg_never;
4280 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4282 triggered = msg_never;
4284 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4285 tbl.contents[ii][0] = ban->mask;
4286 tbl.contents[ii][1] = ban->owner;
4287 tbl.contents[ii][2] = strdup(triggered);
4290 tbl.contents[ii][3] = strdup(expires);
4291 tbl.contents[ii][4] = ban->reason;
4294 tbl.contents[ii][3] = ban->reason;
4296 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4297 reply("MSG_MATCH_COUNT", matches);
4298 for(ii = 1; ii < tbl.length; ++ii)
4300 free((char*)tbl.contents[ii][2]);
4302 free((char*)tbl.contents[ii][3]);
4303 free(tbl.contents[ii]);
4305 free(tbl.contents[0]);
4311 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4313 struct chanData *cData = channel->channel_info;
4314 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4316 if(cData->topic_mask)
4317 return !match_ircglob(new_topic, cData->topic_mask);
4318 else if(cData->topic)
4319 return irccasecmp(new_topic, cData->topic);
4324 static CHANSERV_FUNC(cmd_topic)
4326 struct chanData *cData;
4329 cData = channel->channel_info;
4334 SetChannelTopic(channel, chanserv, cData->topic, 1);
4335 reply("CSMSG_TOPIC_SET", cData->topic);
4339 reply("CSMSG_NO_TOPIC", channel->name);
4343 topic = unsplit_string(argv + 1, argc - 1, NULL);
4344 /* If they say "!topic *", use an empty topic. */
4345 if((topic[0] == '*') && (topic[1] == 0))
4347 if(bad_topic(channel, user, topic))
4349 char *topic_mask = cData->topic_mask;
4352 char new_topic[TOPICLEN+1], tchar;
4353 int pos=0, starpos=-1, dpos=0, len;
4355 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4362 len = strlen(topic);
4363 if((dpos + len) > TOPICLEN)
4364 len = TOPICLEN + 1 - dpos;
4365 memcpy(new_topic+dpos, topic, len);
4369 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4370 default: new_topic[dpos++] = tchar; break;
4373 if((dpos > TOPICLEN) || tchar)
4376 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4377 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4380 new_topic[dpos] = 0;
4381 SetChannelTopic(channel, chanserv, new_topic, 1);
4383 reply("CSMSG_TOPIC_LOCKED", channel->name);
4388 SetChannelTopic(channel, chanserv, topic, 1);
4390 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4392 /* Grab the topic and save it as the default topic. */
4394 cData->topic = strdup(channel->topic);
4400 static CHANSERV_FUNC(cmd_mode)
4402 struct userData *uData;
4403 struct mod_chanmode *change;
4409 change = &channel->channel_info->modes;
4410 if(change->modes_set || change->modes_clear) {
4411 modcmd_chanmode_announce(change);
4412 reply("CSMSG_DEFAULTED_MODES", channel->name);
4414 reply("CSMSG_NO_MODES", channel->name);
4418 uData = GetChannelUser(channel->channel_info, user->handle_info);
4420 base_oplevel = MAXOPLEVEL;
4421 else if (uData->access >= UL_OWNER)
4424 base_oplevel = 1 + UL_OWNER - uData->access;
4425 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4428 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4432 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4433 && mode_lock_violated(&channel->channel_info->modes, change))
4436 mod_chanmode_format(&channel->channel_info->modes, modes);
4437 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4441 modcmd_chanmode_announce(change);
4442 mod_chanmode_format(change, fmt);
4443 mod_chanmode_free(change);
4444 reply("CSMSG_MODES_SET", fmt);
4449 chanserv_del_invite_mark(void *data)
4451 struct ChanUser *chanuser = data;
4452 struct chanNode *channel = chanuser->chan;
4454 if(!channel) return;
4455 for(i = 0; i < channel->invited.used; i++)
4457 if(channel->invited.list[i] == chanuser->user) {
4458 userList_remove(&channel->invited, chanuser->user);
4464 static CHANSERV_FUNC(cmd_invite)
4466 struct userNode *invite;
4467 struct ChanUser *chanuser;
4472 if(!(invite = GetUserH(argv[1])))
4474 reply("MSG_NICK_UNKNOWN", argv[1]);
4481 if(GetUserMode(channel, invite))
4483 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4487 for(i = 0; i < channel->invited.used; i++)
4489 if(channel->invited.list[i] == invite) {
4490 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4499 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4500 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4503 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4505 irc_invite(chanserv, invite, channel);
4507 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4509 userList_append(&channel->invited, invite);
4510 chanuser = calloc(1, sizeof(*chanuser));
4511 chanuser->user=invite;
4512 chanuser->chan=channel;
4513 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4518 static CHANSERV_FUNC(cmd_inviteme)
4520 if(GetUserMode(channel, user))
4522 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4525 if(channel->channel_info
4526 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4528 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4531 irc_invite(cmd->parent->bot, user, channel);
4535 static CHANSERV_FUNC(cmd_invitemeall)
4537 struct handle_info *target = user->handle_info;
4538 struct userData *uData;
4540 if(!target->channels)
4542 reply("CSMSG_SQUAT_ACCESS", target->handle);
4546 for(uData = target->channels; uData; uData = uData->u_next)
4548 struct chanData *cData = uData->channel;
4549 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4551 irc_invite(cmd->parent->bot, user, cData->channel);
4558 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4561 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4563 /* We display things based on two dimensions:
4564 * - Issue time: present or absent
4565 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4566 * (in order of precedence, so something both expired and revoked
4567 * only counts as revoked)
4569 combo = (suspended->issued ? 4 : 0)
4570 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4572 case 0: /* no issue time, indefinite expiration */
4573 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4575 case 1: /* no issue time, expires in future */
4576 intervalString(buf1, suspended->expires-now, user->handle_info);
4577 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4579 case 2: /* no issue time, expired */
4580 intervalString(buf1, now-suspended->expires, user->handle_info);
4581 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4583 case 3: /* no issue time, revoked */
4584 intervalString(buf1, now-suspended->revoked, user->handle_info);
4585 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4587 case 4: /* issue time set, indefinite expiration */
4588 intervalString(buf1, now-suspended->issued, user->handle_info);
4589 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4591 case 5: /* issue time set, expires in future */
4592 intervalString(buf1, now-suspended->issued, user->handle_info);
4593 intervalString(buf2, suspended->expires-now, user->handle_info);
4594 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4596 case 6: /* issue time set, expired */
4597 intervalString(buf1, now-suspended->issued, user->handle_info);
4598 intervalString(buf2, now-suspended->expires, user->handle_info);
4599 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4601 case 7: /* issue time set, revoked */
4602 intervalString(buf1, now-suspended->issued, user->handle_info);
4603 intervalString(buf2, now-suspended->revoked, user->handle_info);
4604 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4607 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4612 static CHANSERV_FUNC(cmd_info)
4614 char modes[MAXLEN], buffer[INTERVALLEN];
4615 struct userData *uData, *owner;
4616 struct chanData *cData;
4617 struct do_not_register *dnr;
4622 cData = channel->channel_info;
4623 reply("CSMSG_CHANNEL_INFO", channel->name);
4625 uData = GetChannelUser(cData, user->handle_info);
4626 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4628 mod_chanmode_format(&cData->modes, modes);
4629 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4630 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4633 for(it = dict_first(cData->notes); it; it = iter_next(it))
4637 note = iter_data(it);
4638 if(!note_type_visible_to_user(cData, note->type, user))
4641 padding = PADLEN - 1 - strlen(iter_key(it));
4642 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4645 if(cData->max_time) {
4646 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4648 reply("CSMSG_CHANNEL_MAX", cData->max);
4650 for(owner = cData->users; owner; owner = owner->next)
4651 if(owner->access == UL_OWNER)
4652 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4653 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4654 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4655 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4657 privileged = IsStaff(user);
4659 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4660 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4661 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4663 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4664 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4666 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4668 struct suspended *suspended;
4669 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4670 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4671 show_suspension_info(cmd, user, suspended);
4673 else if(IsSuspended(cData))
4675 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4676 show_suspension_info(cmd, user, cData->suspended);
4681 static CHANSERV_FUNC(cmd_netinfo)
4683 extern unsigned long boot_time;
4684 extern unsigned long burst_length;
4685 char interval[INTERVALLEN];
4687 reply("CSMSG_NETWORK_INFO");
4688 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4689 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4690 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4691 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4692 reply("CSMSG_NETWORK_BANS", banCount);
4693 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4694 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4695 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4700 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4702 struct helpfile_table table;
4704 struct userNode *user;
4709 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4710 table.contents = alloca(list->used*sizeof(*table.contents));
4711 for(nn=0; nn<list->used; nn++)
4713 user = list->list[nn];
4714 if(user->modes & skip_flags)
4720 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4723 nick = alloca(strlen(user->nick)+3);
4724 sprintf(nick, "(%s)", user->nick);
4728 table.contents[table.length][0] = nick;
4731 table_send(chanserv, to->nick, 0, NULL, table);
4734 static CHANSERV_FUNC(cmd_ircops)
4736 reply("CSMSG_STAFF_OPERS");
4737 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4741 static CHANSERV_FUNC(cmd_helpers)
4743 reply("CSMSG_STAFF_HELPERS");
4744 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4748 static CHANSERV_FUNC(cmd_staff)
4750 reply("CSMSG_NETWORK_STAFF");
4751 cmd_ircops(CSFUNC_ARGS);
4752 cmd_helpers(CSFUNC_ARGS);
4756 static CHANSERV_FUNC(cmd_peek)
4758 struct modeNode *mn;
4759 char modes[MODELEN];
4761 struct helpfile_table table;
4762 int opcount = 0, voicecount = 0, srvcount = 0;
4764 irc_make_chanmode(channel, modes);
4766 reply("CSMSG_PEEK_INFO", channel->name);
4767 reply("CSMSG_PEEK_TOPIC", channel->topic);
4768 reply("CSMSG_PEEK_MODES", modes);
4772 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4773 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4774 for(n = 0; n < channel->members.used; n++)
4776 mn = channel->members.list[n];
4777 if(IsLocal(mn->user))
4779 else if(mn->modes & MODE_CHANOP)
4781 else if(mn->modes & MODE_VOICE)
4784 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4786 table.contents[table.length] = alloca(sizeof(**table.contents));
4787 table.contents[table.length][0] = mn->user->nick;
4791 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4792 (channel->members.used - opcount - voicecount - srvcount));
4796 reply("CSMSG_PEEK_OPS");
4797 table_send(chanserv, user->nick, 0, NULL, table);
4800 reply("CSMSG_PEEK_NO_OPS");
4804 static MODCMD_FUNC(cmd_wipeinfo)
4806 struct handle_info *victim;
4807 struct userData *ud, *actor, *real_actor;
4808 unsigned int override = 0;
4811 actor = GetChannelUser(channel->channel_info, user->handle_info);
4812 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4813 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4815 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4817 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4820 if((ud->access >= actor->access) && (ud != actor))
4822 reply("MSG_USER_OUTRANKED", victim->handle);
4825 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4826 override = CMD_LOG_OVERRIDE;
4830 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4831 return 1 | override;
4834 static CHANSERV_FUNC(cmd_resync)
4836 struct mod_chanmode *changes;
4837 struct chanData *cData = channel->channel_info;
4838 unsigned int ii, used;
4840 changes = mod_chanmode_alloc(channel->members.used * 2);
4841 for(ii = used = 0; ii < channel->members.used; ++ii)
4843 struct modeNode *mn = channel->members.list[ii];
4844 struct userData *uData;
4846 if(IsService(mn->user))
4849 uData = GetChannelAccess(cData, mn->user->handle_info);
4850 if(!cData->lvlOpts[lvlGiveOps]
4851 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4853 if(!(mn->modes & MODE_CHANOP))
4855 if(!uData || IsUserAutoOp(uData))
4857 changes->args[used].mode = MODE_CHANOP;
4858 changes->args[used++].u.member = mn;
4859 if(!(mn->modes & MODE_VOICE))
4861 changes->args[used].mode = MODE_VOICE;
4862 changes->args[used++].u.member = mn;
4867 else if(!cData->lvlOpts[lvlGiveVoice]
4868 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4870 if(mn->modes & MODE_CHANOP)
4872 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4873 changes->args[used++].u.member = mn;
4875 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4877 changes->args[used].mode = MODE_VOICE;
4878 changes->args[used++].u.member = mn;
4885 changes->args[used].mode = MODE_REMOVE | mn->modes;
4886 changes->args[used++].u.member = mn;
4890 changes->argc = used;
4891 modcmd_chanmode_announce(changes);
4892 mod_chanmode_free(changes);
4893 reply("CSMSG_RESYNCED_USERS", channel->name);
4897 static CHANSERV_FUNC(cmd_seen)
4899 struct userData *uData;
4900 struct handle_info *handle;
4901 char seen[INTERVALLEN];
4905 if(!irccasecmp(argv[1], chanserv->nick))
4907 reply("CSMSG_IS_CHANSERV");
4911 if(!(handle = get_handle_info(argv[1])))
4913 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4917 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4919 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4924 reply("CSMSG_USER_PRESENT", handle->handle);
4925 else if(uData->seen)
4926 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4928 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4930 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4931 reply("CSMSG_USER_VACATION", handle->handle);
4936 static MODCMD_FUNC(cmd_names)
4938 struct userNode *targ;
4939 struct userData *targData;
4940 unsigned int ii, pos;
4943 for(ii=pos=0; ii<channel->members.used; ++ii)
4945 targ = channel->members.list[ii]->user;
4946 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4949 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4952 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4956 if(IsUserSuspended(targData))
4958 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4961 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4962 reply("CSMSG_END_NAMES", channel->name);
4967 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4969 switch(ntype->visible_type)
4971 case NOTE_VIS_ALL: return 1;
4972 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4973 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4978 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4980 struct userData *uData;
4982 switch(ntype->set_access_type)
4984 case NOTE_SET_CHANNEL_ACCESS:
4985 if(!user->handle_info)
4987 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4989 return uData->access >= ntype->set_access.min_ulevel;
4990 case NOTE_SET_CHANNEL_SETTER:
4991 return check_user_level(channel, user, lvlSetters, 1, 0);
4992 case NOTE_SET_PRIVILEGED: default:
4993 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4997 static CHANSERV_FUNC(cmd_note)
4999 struct chanData *cData;
5001 struct note_type *ntype;
5003 cData = channel->channel_info;
5006 reply("CSMSG_NOT_REGISTERED", channel->name);
5010 /* If no arguments, show all visible notes for the channel. */
5016 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5018 note = iter_data(it);
5019 if(!note_type_visible_to_user(cData, note->type, user))
5022 reply("CSMSG_NOTELIST_HEADER", channel->name);
5023 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5026 reply("CSMSG_NOTELIST_END", channel->name);
5028 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5030 /* If one argument, show the named note. */
5033 if((note = dict_find(cData->notes, argv[1], NULL))
5034 && note_type_visible_to_user(cData, note->type, user))
5036 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5038 else if((ntype = dict_find(note_types, argv[1], NULL))
5039 && note_type_visible_to_user(NULL, ntype, user))
5041 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5046 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5050 /* Assume they're trying to set a note. */
5054 ntype = dict_find(note_types, argv[1], NULL);
5057 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5060 else if(note_type_settable_by_user(channel, ntype, user))
5062 note_text = unsplit_string(argv+2, argc-2, NULL);
5063 if((note = dict_find(cData->notes, argv[1], NULL)))
5064 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5065 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5066 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5068 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5070 /* The note is viewable to staff only, so return 0
5071 to keep the invocation from getting logged (or
5072 regular users can see it in !events). */
5078 reply("CSMSG_NO_ACCESS");
5085 static CHANSERV_FUNC(cmd_delnote)
5090 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5091 || !note_type_settable_by_user(channel, note->type, user))
5093 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5096 dict_remove(channel->channel_info->notes, note->type->name);
5097 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5101 static CHANSERV_FUNC(cmd_events)
5103 struct logSearch discrim;
5104 struct logReport report;
5105 unsigned int matches, limit;
5107 limit = (argc > 1) ? atoi(argv[1]) : 10;
5108 if(limit < 1 || limit > 200)
5111 memset(&discrim, 0, sizeof(discrim));
5112 discrim.masks.bot = chanserv;
5113 discrim.masks.channel_name = channel->name;
5115 discrim.masks.command = argv[2];
5116 discrim.limit = limit;
5117 discrim.max_time = INT_MAX;
5118 discrim.severities = 1 << LOG_COMMAND;
5119 report.reporter = chanserv;
5121 reply("CSMSG_EVENT_SEARCH_RESULTS");
5122 matches = log_entry_search(&discrim, log_report_entry, &report);
5124 reply("MSG_MATCH_COUNT", matches);
5126 reply("MSG_NO_MATCHES");
5130 static CHANSERV_FUNC(cmd_say)
5136 msg = unsplit_string(argv + 1, argc - 1, NULL);
5137 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5139 else if(*argv[1] == '*' && argv[1][1] != '\0')
5141 struct handle_info *hi;
5142 struct userNode *authed;
5145 msg = unsplit_string(argv + 2, argc - 2, NULL);
5147 if (!(hi = get_handle_info(argv[1] + 1)))
5149 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5153 for (authed = hi->users; authed; authed = authed->next_authed)
5154 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5156 else if(GetUserH(argv[1]))
5159 msg = unsplit_string(argv + 2, argc - 2, NULL);
5160 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5164 reply("MSG_NOT_TARGET_NAME");
5170 static CHANSERV_FUNC(cmd_emote)
5176 /* CTCP is so annoying. */
5177 msg = unsplit_string(argv + 1, argc - 1, NULL);
5178 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5180 else if(*argv[1] == '*' && argv[1][1] != '\0')
5182 struct handle_info *hi;
5183 struct userNode *authed;
5186 msg = unsplit_string(argv + 2, argc - 2, NULL);
5188 if (!(hi = get_handle_info(argv[1] + 1)))
5190 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5194 for (authed = hi->users; authed; authed = authed->next_authed)
5195 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5197 else if(GetUserH(argv[1]))
5199 msg = unsplit_string(argv + 2, argc - 2, NULL);
5200 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5204 reply("MSG_NOT_TARGET_NAME");
5210 struct channelList *
5211 chanserv_support_channels(void)
5213 return &chanserv_conf.support_channels;
5216 static CHANSERV_FUNC(cmd_expire)
5218 int channel_count = registered_channels;
5219 expire_channels(chanserv);
5220 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5225 chanserv_expire_suspension(void *data)
5227 struct suspended *suspended = data;
5228 struct chanNode *channel;
5231 /* Update the channel registration data structure. */
5232 if(!suspended->expires || (now < suspended->expires))
5233 suspended->revoked = now;
5234 channel = suspended->cData->channel;
5235 suspended->cData->channel = channel;
5236 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5238 /* If appropriate, re-join ChanServ to the channel. */
5239 if(!IsOffChannel(suspended->cData))
5241 spamserv_cs_suspend(channel, 0, 0, NULL);
5242 ss_cs_join_channel(channel, 1);
5245 /* Mark everyone currently in the channel as present. */
5246 for(ii = 0; ii < channel->members.used; ++ii)
5248 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5257 static CHANSERV_FUNC(cmd_csuspend)
5259 struct suspended *suspended;
5260 char reason[MAXLEN];
5261 unsigned long expiry, duration;
5262 struct userData *uData;
5266 if(IsProtected(channel->channel_info))
5268 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5272 if(argv[1][0] == '!')
5274 else if(IsSuspended(channel->channel_info))
5276 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5277 show_suspension_info(cmd, user, channel->channel_info->suspended);
5281 if(!strcmp(argv[1], "0"))
5283 else if((duration = ParseInterval(argv[1])))
5284 expiry = now + duration;
5287 reply("MSG_INVALID_DURATION", argv[1]);
5291 unsplit_string(argv + 2, argc - 2, reason);
5293 suspended = calloc(1, sizeof(*suspended));
5294 suspended->revoked = 0;
5295 suspended->issued = now;
5296 suspended->suspender = strdup(user->handle_info->handle);
5297 suspended->expires = expiry;
5298 suspended->reason = strdup(reason);
5299 suspended->cData = channel->channel_info;
5300 suspended->previous = suspended->cData->suspended;
5301 suspended->cData->suspended = suspended;
5303 if(suspended->expires)
5304 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5306 if(IsSuspended(channel->channel_info))
5308 suspended->previous->revoked = now;
5309 if(suspended->previous->expires)
5310 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5311 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5312 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5316 /* Mark all users in channel as absent. */
5317 for(uData = channel->channel_info->users; uData; uData = uData->next)
5326 /* Mark the channel as suspended, then part. */
5327 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5328 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5329 DelChannelUser(chanserv, channel, suspended->reason, 0);
5330 reply("CSMSG_SUSPENDED", channel->name);
5331 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5332 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5337 static CHANSERV_FUNC(cmd_cunsuspend)
5339 struct suspended *suspended;
5340 char message[MAXLEN];
5342 if(!IsSuspended(channel->channel_info))
5344 reply("CSMSG_NOT_SUSPENDED", channel->name);
5348 suspended = channel->channel_info->suspended;
5350 /* Expire the suspension and join ChanServ to the channel. */
5351 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5352 chanserv_expire_suspension(suspended);
5353 reply("CSMSG_UNSUSPENDED", channel->name);
5354 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5355 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5359 typedef struct chanservSearch
5364 unsigned long unvisited;
5365 unsigned long registered;
5367 unsigned long flags;
5371 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5374 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5379 search = malloc(sizeof(struct chanservSearch));
5380 memset(search, 0, sizeof(*search));
5383 for(i = 0; i < argc; i++)
5385 /* Assume all criteria require arguments. */
5388 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5392 if(!irccasecmp(argv[i], "name"))
5393 search->name = argv[++i];
5394 else if(!irccasecmp(argv[i], "registrar"))
5395 search->registrar = argv[++i];
5396 else if(!irccasecmp(argv[i], "unvisited"))
5397 search->unvisited = ParseInterval(argv[++i]);
5398 else if(!irccasecmp(argv[i], "registered"))
5399 search->registered = ParseInterval(argv[++i]);
5400 else if(!irccasecmp(argv[i], "flags"))
5403 if(!irccasecmp(argv[i], "nodelete"))
5404 search->flags |= CHANNEL_NODELETE;
5405 else if(!irccasecmp(argv[i], "suspended"))
5406 search->flags |= CHANNEL_SUSPENDED;
5407 else if(!irccasecmp(argv[i], "unreviewed"))
5408 search->flags |= CHANNEL_UNREVIEWED;
5411 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5415 else if(!irccasecmp(argv[i], "limit"))
5416 search->limit = strtoul(argv[++i], NULL, 10);
5419 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5424 if(search->name && !strcmp(search->name, "*"))
5426 if(search->registrar && !strcmp(search->registrar, "*"))
5427 search->registrar = 0;
5436 chanserv_channel_match(struct chanData *channel, search_t search)
5438 const char *name = channel->channel->name;
5439 if((search->name && !match_ircglob(name, search->name)) ||
5440 (search->registrar && !channel->registrar) ||
5441 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5442 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5443 (search->registered && (now - channel->registered) > search->registered) ||
5444 (search->flags && ((search->flags & channel->flags) != search->flags)))
5451 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5453 struct chanData *channel;
5454 unsigned int matches = 0;
5456 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5458 if(!chanserv_channel_match(channel, search))
5468 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5473 search_print(struct chanData *channel, void *data)
5475 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5478 static CHANSERV_FUNC(cmd_search)
5481 unsigned int matches;
5482 channel_search_func action;
5486 if(!irccasecmp(argv[1], "count"))
5487 action = search_count;
5488 else if(!irccasecmp(argv[1], "print"))
5489 action = search_print;
5492 reply("CSMSG_ACTION_INVALID", argv[1]);
5496 search = chanserv_search_create(user, argc - 2, argv + 2);
5500 if(action == search_count)
5501 search->limit = INT_MAX;
5503 if(action == search_print)
5504 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5506 matches = chanserv_channel_search(search, action, user);
5509 reply("MSG_MATCH_COUNT", matches);
5511 reply("MSG_NO_MATCHES");
5517 static CHANSERV_FUNC(cmd_unvisited)
5519 struct chanData *cData;
5520 unsigned long interval = chanserv_conf.channel_expire_delay;
5521 char buffer[INTERVALLEN];
5522 unsigned int limit = 25, matches = 0;
5526 interval = ParseInterval(argv[1]);
5528 limit = atoi(argv[2]);
5531 intervalString(buffer, interval, user->handle_info);
5532 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5534 for(cData = channelList; cData && matches < limit; cData = cData->next)
5536 if((now - cData->visited) < interval)
5539 intervalString(buffer, now - cData->visited, user->handle_info);
5540 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5547 static MODCMD_FUNC(chan_opt_defaulttopic)
5553 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5555 reply("CSMSG_TOPIC_LOCKED", channel->name);
5559 topic = unsplit_string(argv+1, argc-1, NULL);
5561 free(channel->channel_info->topic);
5562 if(topic[0] == '*' && topic[1] == 0)
5564 topic = channel->channel_info->topic = NULL;
5568 topic = channel->channel_info->topic = strdup(topic);
5569 if(channel->channel_info->topic_mask
5570 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5571 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5573 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5576 if(channel->channel_info->topic)
5577 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5579 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5583 static MODCMD_FUNC(chan_opt_topicmask)
5587 struct chanData *cData = channel->channel_info;
5590 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5592 reply("CSMSG_TOPIC_LOCKED", channel->name);
5596 mask = unsplit_string(argv+1, argc-1, NULL);
5598 if(cData->topic_mask)
5599 free(cData->topic_mask);
5600 if(mask[0] == '*' && mask[1] == 0)
5602 cData->topic_mask = 0;
5606 cData->topic_mask = strdup(mask);
5608 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5609 else if(!match_ircglob(cData->topic, cData->topic_mask))
5610 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5614 if(channel->channel_info->topic_mask)
5615 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5617 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5621 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5625 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5629 if(greeting[0] == '*' && greeting[1] == 0)
5633 unsigned int length = strlen(greeting);
5634 if(length > chanserv_conf.greeting_length)
5636 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5639 *data = strdup(greeting);
5648 reply(name, user_find_message(user, "MSG_NONE"));
5652 static MODCMD_FUNC(chan_opt_greeting)
5654 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5657 static MODCMD_FUNC(chan_opt_usergreeting)
5659 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5662 static MODCMD_FUNC(chan_opt_modes)
5664 struct mod_chanmode *new_modes;
5669 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5671 reply("CSMSG_NO_ACCESS");
5674 if(argv[1][0] == '*' && argv[1][1] == 0)
5676 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5678 else if(!(new_modes = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS|(IsOper(user) && IsHelping(user) ? MCP_OPERMODE : 0), 0)))
5680 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5683 else if(new_modes->argc > 1)
5685 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5686 mod_chanmode_free(new_modes);
5691 channel->channel_info->modes = *new_modes;
5692 modcmd_chanmode_announce(new_modes);
5693 mod_chanmode_free(new_modes);
5697 mod_chanmode_format(&channel->channel_info->modes, modes);
5699 reply("CSMSG_SET_MODES", modes);
5701 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5705 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5707 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5709 struct chanData *cData = channel->channel_info;
5714 /* Set flag according to value. */
5715 if(enabled_string(argv[1]))
5717 cData->flags |= mask;
5720 else if(disabled_string(argv[1]))
5722 cData->flags &= ~mask;
5727 reply("MSG_INVALID_BINARY", argv[1]);
5733 /* Find current option value. */
5734 value = (cData->flags & mask) ? 1 : 0;
5738 reply(name, user_find_message(user, "MSG_ON"));
5740 reply(name, user_find_message(user, "MSG_OFF"));
5744 static MODCMD_FUNC(chan_opt_nodelete)
5746 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5748 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5752 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5755 static MODCMD_FUNC(chan_opt_dynlimit)
5757 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5760 static MODCMD_FUNC(chan_opt_offchannel)
5762 struct chanData *cData = channel->channel_info;
5767 /* Set flag according to value. */
5768 if(enabled_string(argv[1]))
5770 if(!IsOffChannel(cData))
5771 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5772 cData->flags |= CHANNEL_OFFCHANNEL;
5775 else if(disabled_string(argv[1]))
5777 if(IsOffChannel(cData))
5779 struct mod_chanmode change;
5780 mod_chanmode_init(&change);
5782 change.args[0].mode = MODE_CHANOP;
5783 change.args[0].u.member = AddChannelUser(chanserv, channel);
5784 mod_chanmode_announce(chanserv, channel, &change);
5786 cData->flags &= ~CHANNEL_OFFCHANNEL;
5791 reply("MSG_INVALID_BINARY", argv[1]);
5797 /* Find current option value. */
5798 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5802 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5804 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5808 static MODCMD_FUNC(chan_opt_unreviewed)
5810 struct chanData *cData = channel->channel_info;
5811 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5817 /* The two directions can have different ACLs. */
5818 if(enabled_string(argv[1]))
5820 else if(disabled_string(argv[1]))
5824 reply("MSG_INVALID_BINARY", argv[1]);
5828 if (new_value != value)
5830 struct svccmd *subcmd;
5831 char subcmd_name[32];
5833 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5834 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5837 reply("MSG_COMMAND_DISABLED", subcmd_name);
5840 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5844 cData->flags |= CHANNEL_UNREVIEWED;
5847 free(cData->registrar);
5848 cData->registrar = strdup(user->handle_info->handle);
5849 cData->flags &= ~CHANNEL_UNREVIEWED;
5856 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5858 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5862 static MODCMD_FUNC(chan_opt_defaults)
5864 struct userData *uData;
5865 struct chanData *cData;
5866 const char *confirm;
5867 enum levelOption lvlOpt;
5868 enum charOption chOpt;
5870 cData = channel->channel_info;
5871 uData = GetChannelUser(cData, user->handle_info);
5872 if(!uData || (uData->access < UL_OWNER))
5874 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5877 confirm = make_confirmation_string(uData);
5878 if((argc < 2) || strcmp(argv[1], confirm))
5880 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5883 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5884 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5885 cData->modes = chanserv_conf.default_modes;
5886 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5887 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5888 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5889 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5890 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5895 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5897 struct chanData *cData = channel->channel_info;
5898 struct userData *uData;
5899 unsigned short value;
5903 if(!check_user_level(channel, user, option, 1, 1))
5905 reply("CSMSG_CANNOT_SET");
5908 value = user_level_from_name(argv[1], UL_OWNER+1);
5909 if(!value && strcmp(argv[1], "0"))
5911 reply("CSMSG_INVALID_ACCESS", argv[1]);
5914 uData = GetChannelUser(cData, user->handle_info);
5915 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5917 reply("CSMSG_BAD_SETLEVEL");
5923 if(value > cData->lvlOpts[lvlGiveOps])
5925 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5930 if(value < cData->lvlOpts[lvlGiveVoice])
5932 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5937 /* This test only applies to owners, since non-owners
5938 * trying to set an option to above their level get caught
5939 * by the CSMSG_BAD_SETLEVEL test above.
5941 if(value > uData->access)
5943 reply("CSMSG_BAD_SETTERS");
5950 cData->lvlOpts[option] = value;
5952 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5956 static MODCMD_FUNC(chan_opt_enfops)
5958 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5961 static MODCMD_FUNC(chan_opt_giveops)
5963 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5966 static MODCMD_FUNC(chan_opt_enfmodes)
5968 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5971 static MODCMD_FUNC(chan_opt_enftopic)
5973 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5976 static MODCMD_FUNC(chan_opt_pubcmd)
5978 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5981 static MODCMD_FUNC(chan_opt_setters)
5983 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5986 static MODCMD_FUNC(chan_opt_ctcpusers)
5988 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5991 static MODCMD_FUNC(chan_opt_userinfo)
5993 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5996 static MODCMD_FUNC(chan_opt_givevoice)
5998 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6001 static MODCMD_FUNC(chan_opt_topicsnarf)
6003 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6006 static MODCMD_FUNC(chan_opt_vote)
6008 return channel_level_option(lvlVote, CSFUNC_ARGS);
6011 static MODCMD_FUNC(chan_opt_inviteme)
6013 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6017 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6019 struct chanData *cData = channel->channel_info;
6020 int count = charOptions[option].count, idx;
6024 idx = atoi(argv[1]);
6026 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6028 reply("CSMSG_INVALID_NUMERIC", idx);
6029 /* Show possible values. */
6030 for(idx = 0; idx < count; idx++)
6031 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6035 cData->chOpts[option] = charOptions[option].values[idx].value;
6039 /* Find current option value. */
6042 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6046 /* Somehow, the option value is corrupt; reset it to the default. */
6047 cData->chOpts[option] = charOptions[option].default_value;
6052 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6056 static MODCMD_FUNC(chan_opt_protect)
6058 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6061 static MODCMD_FUNC(chan_opt_toys)
6063 return channel_multiple_option(chToys, CSFUNC_ARGS);
6066 static MODCMD_FUNC(chan_opt_ctcpreaction)
6068 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6071 static MODCMD_FUNC(chan_opt_topicrefresh)
6073 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6076 static struct svccmd_list set_shows_list;
6079 handle_svccmd_unbind(struct svccmd *target) {
6081 for(ii=0; ii<set_shows_list.used; ++ii)
6082 if(target == set_shows_list.list[ii])
6083 set_shows_list.used = 0;
6086 static CHANSERV_FUNC(cmd_set)
6088 struct svccmd *subcmd;
6092 /* Check if we need to (re-)initialize set_shows_list. */
6093 if(!set_shows_list.used)
6095 if(!set_shows_list.size)
6097 set_shows_list.size = chanserv_conf.set_shows->used;
6098 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6100 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6102 const char *name = chanserv_conf.set_shows->list[ii];
6103 sprintf(buf, "%s %s", argv[0], name);
6104 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6107 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6110 svccmd_list_append(&set_shows_list, subcmd);
6116 reply("CSMSG_CHANNEL_OPTIONS");
6117 for(ii = 0; ii < set_shows_list.used; ii++)
6119 subcmd = set_shows_list.list[ii];
6120 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6125 sprintf(buf, "%s %s", argv[0], argv[1]);
6126 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6129 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6132 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6134 reply("CSMSG_NO_ACCESS");
6140 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6144 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6146 struct userData *uData;
6148 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6151 reply("CSMSG_NOT_USER", channel->name);
6157 /* Just show current option value. */
6159 else if(enabled_string(argv[1]))
6161 uData->flags |= mask;
6163 else if(disabled_string(argv[1]))
6165 uData->flags &= ~mask;
6169 reply("MSG_INVALID_BINARY", argv[1]);
6173 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6177 static MODCMD_FUNC(user_opt_noautoop)
6179 struct userData *uData;
6181 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6184 reply("CSMSG_NOT_USER", channel->name);
6187 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6188 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6190 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6193 static MODCMD_FUNC(user_opt_autoinvite)
6195 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6197 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6199 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6202 static MODCMD_FUNC(user_opt_info)
6204 struct userData *uData;
6207 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6211 /* If they got past the command restrictions (which require access)
6212 * but fail this test, we have some fool with security override on.
6214 reply("CSMSG_NOT_USER", channel->name);
6221 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6222 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6224 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6227 bp = strcspn(infoline, "\001");
6230 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6235 if(infoline[0] == '*' && infoline[1] == 0)
6238 uData->info = strdup(infoline);
6241 reply("CSMSG_USET_INFO", uData->info);
6243 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6247 struct svccmd_list uset_shows_list;
6249 static CHANSERV_FUNC(cmd_uset)
6251 struct svccmd *subcmd;
6255 /* Check if we need to (re-)initialize uset_shows_list. */
6256 if(!uset_shows_list.used)
6260 "NoAutoOp", "AutoInvite", "Info"
6263 if(!uset_shows_list.size)
6265 uset_shows_list.size = ArrayLength(options);
6266 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6268 for(ii = 0; ii < ArrayLength(options); ii++)
6270 const char *name = options[ii];
6271 sprintf(buf, "%s %s", argv[0], name);
6272 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6275 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6278 svccmd_list_append(&uset_shows_list, subcmd);
6284 /* Do this so options are presented in a consistent order. */
6285 reply("CSMSG_USER_OPTIONS");
6286 for(ii = 0; ii < uset_shows_list.used; ii++)
6287 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6291 sprintf(buf, "%s %s", argv[0], argv[1]);
6292 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6295 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6299 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6302 static CHANSERV_FUNC(cmd_giveownership)
6304 struct handle_info *new_owner_hi;
6305 struct userData *new_owner;
6306 struct userData *curr_user;
6307 struct userData *invoker;
6308 struct chanData *cData = channel->channel_info;
6309 struct do_not_register *dnr;
6310 const char *confirm;
6312 unsigned short co_access;
6313 char reason[MAXLEN];
6316 curr_user = GetChannelAccess(cData, user->handle_info);
6317 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6318 if(!curr_user || (curr_user->access != UL_OWNER))
6320 struct userData *owner = NULL;
6321 for(curr_user = channel->channel_info->users;
6323 curr_user = curr_user->next)
6325 if(curr_user->access != UL_OWNER)
6329 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6336 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6338 char delay[INTERVALLEN];
6339 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6340 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6343 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6345 if(new_owner_hi == user->handle_info)
6347 reply("CSMSG_NO_TRANSFER_SELF");
6350 new_owner = GetChannelAccess(cData, new_owner_hi);
6355 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6359 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6363 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6365 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6368 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6369 if(!IsHelping(user))
6370 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6372 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6375 invoker = GetChannelUser(cData, user->handle_info);
6376 if(invoker->access <= UL_OWNER)
6378 confirm = make_confirmation_string(curr_user);
6379 if((argc < 3) || strcmp(argv[2], confirm))
6381 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6385 if(new_owner->access >= UL_COOWNER)
6386 co_access = new_owner->access;
6388 co_access = UL_COOWNER;
6389 new_owner->access = UL_OWNER;
6391 curr_user->access = co_access;
6392 cData->ownerTransfer = now;
6393 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6394 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6395 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6399 static CHANSERV_FUNC(cmd_suspend)
6401 struct handle_info *hi;
6402 struct userData *actor, *real_actor, *target;
6403 unsigned int override = 0;
6406 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6407 actor = GetChannelUser(channel->channel_info, user->handle_info);
6408 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6409 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6411 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6414 if(target->access >= actor->access)
6416 reply("MSG_USER_OUTRANKED", hi->handle);
6419 if(target->flags & USER_SUSPENDED)
6421 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6426 target->present = 0;
6429 if(!real_actor || target->access >= real_actor->access)
6430 override = CMD_LOG_OVERRIDE;
6431 target->flags |= USER_SUSPENDED;
6432 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6433 return 1 | override;
6436 static CHANSERV_FUNC(cmd_unsuspend)
6438 struct handle_info *hi;
6439 struct userData *actor, *real_actor, *target;
6440 unsigned int override = 0;
6443 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6444 actor = GetChannelUser(channel->channel_info, user->handle_info);
6445 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6446 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6448 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6451 if(target->access >= actor->access)
6453 reply("MSG_USER_OUTRANKED", hi->handle);
6456 if(!(target->flags & USER_SUSPENDED))
6458 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6461 if(!real_actor || target->access >= real_actor->access)
6462 override = CMD_LOG_OVERRIDE;
6463 target->flags &= ~USER_SUSPENDED;
6464 scan_user_presence(target, NULL);
6465 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6466 return 1 | override;
6469 static MODCMD_FUNC(cmd_deleteme)
6471 struct handle_info *hi;
6472 struct userData *target;
6473 const char *confirm_string;
6474 unsigned short access_level;
6477 hi = user->handle_info;
6478 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6480 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6483 if(target->access == UL_OWNER)
6485 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6488 confirm_string = make_confirmation_string(target);
6489 if((argc < 2) || strcmp(argv[1], confirm_string))
6491 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6494 access_level = target->access;
6495 channel_name = strdup(channel->name);
6496 del_channel_user(target, 1);
6497 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6502 static CHANSERV_FUNC(cmd_addvote)
6504 struct chanData *cData = channel->channel_info;
6505 struct userData *uData, *target;
6506 struct handle_info *hi;
6507 if (!cData) return 0;
6509 hi = user->handle_info;
6510 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6512 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6515 if(target->access < 300) {
6516 reply("CSMSG_NO_ACCESS");
6520 reply("CSMSG_ADDVOTE_FULL");
6524 msg = unsplit_string(argv + 1, argc - 1, NULL);
6525 cData->vote = strdup(msg);
6526 cData->vote_start=0;
6527 dict_delete(cData->vote_options);
6528 cData->vote_options = dict_new();
6529 dict_set_free_data(cData->vote_options, free_vote_options);
6530 for(uData = channel->channel_info->users; uData; uData = uData->next)
6535 reply("CSMSG_ADDVOTE_DONE");
6539 static CHANSERV_FUNC(cmd_delvote)
6541 struct chanData *cData = channel->channel_info;
6542 struct userData *target;
6543 struct handle_info *hi;
6544 if (!cData) return 0;
6545 hi = user->handle_info;
6546 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6548 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6551 if(target->access < 300) {
6552 reply("CSMSG_NO_ACCESS");
6556 reply("CSMSG_NO_VOTE");
6561 reply("CSMSG_DELVOTE_DONE");
6565 static CHANSERV_FUNC(cmd_addoption)
6567 struct chanData *cData = channel->channel_info;
6568 struct userData *target;
6569 struct handle_info *hi;
6570 if (!cData) return 0;
6572 hi = user->handle_info;
6573 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6575 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6578 if(target->access < 300) {
6579 reply("CSMSG_NO_ACCESS");
6583 reply("CSMSG_NO_VOTE");
6589 msg = unsplit_string(argv + 1, argc - 1, NULL);
6592 unsigned int lastid = 1;
6593 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6594 struct vote_option *cvOpt = iter_data(it);
6595 if(cvOpt->option_id > lastid)
6596 lastid = cvOpt->option_id;
6598 struct vote_option *vOpt;
6599 vOpt = calloc(1, sizeof(*vOpt));
6600 vOpt->name = strdup(msg);
6601 vOpt->option_id = (lastid + 1);
6603 sprintf(str,"%i",(lastid + 1));
6604 vOpt->option_str = strdup(str);
6606 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6608 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6612 static CHANSERV_FUNC(cmd_deloption)
6614 struct chanData *cData = channel->channel_info;
6615 struct userData *uData, *target;
6616 struct handle_info *hi;
6617 if (!cData) return 0;
6619 hi = user->handle_info;
6620 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6622 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6625 if(target->access < 300) {
6626 reply("CSMSG_NO_ACCESS");
6630 reply("CSMSG_NO_VOTE");
6633 if(cData->vote_start) {
6634 if(dict_size(cData->vote_options) < 3) {
6635 reply("CSMSG_VOTE_NEED_OPTIONS");
6640 int find_id = atoi(argv[1]);
6642 unsigned int found = 0;
6645 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6647 if (find_id == ii) {
6648 struct vote_option *vOpt = iter_data(it);
6649 found = vOpt->option_id;
6651 sprintf(str,"%i",vOpt->option_id);
6652 dict_remove(cData->vote_options, str);
6657 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6658 if(uData->votefor == found) {
6663 reply("CSMSG_DELOPTION_DONE");
6666 reply("CSMSG_DELOPTION_NONE");
6671 static CHANSERV_FUNC(cmd_vote)
6673 struct chanData *cData = channel->channel_info;
6674 struct userData *target;
6675 struct handle_info *hi;
6676 unsigned int votedfor = 0;
6677 char *votedfor_str = NULL;
6679 if (!cData || !cData->vote) {
6680 reply("CSMSG_NO_VOTE");
6683 if(argc > 1 && cData->vote_start) {
6684 hi = user->handle_info;
6685 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6687 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6690 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6691 reply("CSMSG_NO_ACCESS");
6695 reply("CSMSG_VOTE_VOTED");
6698 int find_id = atoi(argv[1]);
6701 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6703 if (find_id == ii) {
6704 struct vote_option *vOpt = iter_data(it);
6707 target->votefor = vOpt->option_id;
6708 votedfor = vOpt->option_id;
6709 votedfor_str = vOpt->name;
6713 reply("CSMSG_VOTE_INVALID");
6717 if (!cData->vote_start) {
6718 reply("CSMSG_VOTE_NOT_STARTED");
6720 reply("CSMSG_VOTE_QUESTION",cData->vote);
6722 unsigned int voteid = 0;
6725 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6726 struct vote_option *vOpt = iter_data(it);
6728 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6730 if(argc > 1 && cData->vote_start && votedfor_str) {
6731 reply("CSMSG_VOTE_DONE",votedfor_str);
6736 static CHANSERV_FUNC(cmd_startvote)
6738 struct chanData *cData = channel->channel_info;
6739 struct userData *target;
6740 struct handle_info *hi;
6741 if (!cData) return 0;
6742 hi = user->handle_info;
6743 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6745 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6748 if(target->access < 300) {
6749 reply("CSMSG_NO_ACCESS");
6753 reply("CSMSG_NO_VOTE");
6756 if(cData->vote_start) {
6757 reply("CSMSG_STARTVOTE_RUNNING");
6760 if(dict_size(cData->vote_options) < 2) {
6761 reply("CSMSG_VOTE_NEED_OPTIONS");
6764 cData->vote_start = 1;
6765 char response[MAXLEN];
6766 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6767 irc_privmsg(cmd->parent->bot, channel->name, response);
6768 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6769 irc_privmsg(cmd->parent->bot, channel->name, response);
6770 unsigned int voteid = 0;
6772 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6773 struct vote_option *vOpt = iter_data(it);
6775 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6776 irc_privmsg(cmd->parent->bot, channel->name, response);
6778 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6779 irc_privmsg(cmd->parent->bot, channel->name, response);
6780 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6781 irc_privmsg(cmd->parent->bot, channel->name, response);
6785 static CHANSERV_FUNC(cmd_endvote)
6787 struct chanData *cData = channel->channel_info;
6788 struct userData *target;
6789 struct handle_info *hi;
6790 if (!cData) return 0;
6791 hi = user->handle_info;
6792 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6794 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6797 if(target->access < 300) {
6798 reply("CSMSG_NO_ACCESS");
6802 reply("CSMSG_NO_VOTE");
6805 if(!cData->vote_start) {
6806 reply("CSMSG_ENDVOTE_STOPPED");
6809 cData->vote_start = 0;
6810 reply("CSMSG_ENDVOTE_DONE");
6814 static CHANSERV_FUNC(cmd_voteresults)
6816 struct chanData *cData = channel->channel_info;
6817 struct userData *target;
6818 struct handle_info *hi;
6819 if (!cData) return 0;
6821 reply("CSMSG_NO_VOTE");
6824 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6825 hi = user->handle_info;
6826 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6828 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6831 if(target->access < 300) {
6832 reply("CSMSG_NO_ACCESS");
6835 char response[MAXLEN];
6836 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6837 irc_privmsg(cmd->parent->bot, channel->name, response);
6838 unsigned int voteid = 0;
6840 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6841 struct vote_option *vOpt = iter_data(it);
6843 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6844 irc_privmsg(cmd->parent->bot, channel->name, response);
6847 reply("CSMSG_VOTE_QUESTION",cData->vote);
6848 unsigned int voteid = 0;
6850 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6851 struct vote_option *vOpt = iter_data(it);
6853 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6860 chanserv_refresh_topics(UNUSED_ARG(void *data))
6862 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6863 struct chanData *cData;
6866 for(cData = channelList; cData; cData = cData->next)
6868 if(IsSuspended(cData))
6870 opt = cData->chOpts[chTopicRefresh];
6873 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6876 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6877 cData->last_refresh = refresh_num;
6879 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6882 static CHANSERV_FUNC(cmd_unf)
6886 char response[MAXLEN];
6887 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6888 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6889 irc_privmsg(cmd->parent->bot, channel->name, response);
6892 reply("CSMSG_UNF_RESPONSE");
6896 static CHANSERV_FUNC(cmd_ping)
6900 char response[MAXLEN];
6901 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6902 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6903 irc_privmsg(cmd->parent->bot, channel->name, response);
6906 reply("CSMSG_PING_RESPONSE");
6910 static CHANSERV_FUNC(cmd_wut)
6914 char response[MAXLEN];
6915 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6916 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6917 irc_privmsg(cmd->parent->bot, channel->name, response);
6920 reply("CSMSG_WUT_RESPONSE");
6924 static CHANSERV_FUNC(cmd_8ball)
6926 unsigned int i, j, accum;
6931 for(i=1; i<argc; i++)
6932 for(j=0; argv[i][j]; j++)
6933 accum = (accum << 5) - accum + toupper(argv[i][j]);
6934 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6937 char response[MAXLEN];
6938 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6939 irc_privmsg(cmd->parent->bot, channel->name, response);
6942 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6946 static CHANSERV_FUNC(cmd_d)
6948 unsigned long sides, count, modifier, ii, total;
6949 char response[MAXLEN], *sep;
6953 if((count = strtoul(argv[1], &sep, 10)) < 1)
6963 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6964 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6968 else if((sep[0] == '-') && isdigit(sep[1]))
6969 modifier = strtoul(sep, NULL, 10);
6970 else if((sep[0] == '+') && isdigit(sep[1]))
6971 modifier = strtoul(sep+1, NULL, 10);
6978 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6983 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6986 for(total = ii = 0; ii < count; ++ii)
6987 total += (rand() % sides) + 1;
6990 if((count > 1) || modifier)
6992 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6993 sprintf(response, fmt, total, count, sides, modifier);
6997 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6998 sprintf(response, fmt, total, sides);
7001 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7003 send_message_type(4, user, cmd->parent->bot, "%s", response);
7007 static CHANSERV_FUNC(cmd_huggle)
7009 /* CTCP must be via PRIVMSG, never notice */
7011 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7013 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7018 chanserv_adjust_limit(void *data)
7020 struct mod_chanmode change;
7021 struct chanData *cData = data;
7022 struct chanNode *channel = cData->channel;
7025 if(IsSuspended(cData))
7028 cData->limitAdjusted = now;
7029 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7030 if(cData->modes.modes_set & MODE_LIMIT)
7032 if(limit > cData->modes.new_limit)
7033 limit = cData->modes.new_limit;
7034 else if(limit == cData->modes.new_limit)
7038 mod_chanmode_init(&change);
7039 change.modes_set = MODE_LIMIT;
7040 change.new_limit = limit;
7041 mod_chanmode_announce(chanserv, channel, &change);
7045 handle_new_channel(struct chanNode *channel)
7047 struct chanData *cData;
7049 if(!(cData = channel->channel_info))
7052 if(cData->modes.modes_set || cData->modes.modes_clear)
7053 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7055 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7056 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7059 void handle_new_channel_created(char *chan, struct userNode *user) {
7060 if(user->handle_info && chanserv_conf.new_channel_authed) {
7061 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7062 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7063 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7065 if(chanserv_conf.new_channel_msg)
7066 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7069 /* Welcome to my worst nightmare. Warning: Read (or modify)
7070 the code below at your own risk. */
7072 handle_join(struct modeNode *mNode)
7074 struct mod_chanmode change;
7075 struct userNode *user = mNode->user;
7076 struct chanNode *channel = mNode->channel;
7077 struct chanData *cData;
7078 struct userData *uData = NULL;
7079 struct banData *bData;
7080 struct handle_info *handle;
7081 unsigned int modes = 0, info = 0;
7085 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7088 cData = channel->channel_info;
7089 if(channel->members.used > cData->max) {
7090 cData->max = channel->members.used;
7091 cData->max_time = now;
7094 for(i = 0; i < channel->invited.used; i++)
7096 if(channel->invited.list[i] == user) {
7097 userList_remove(&channel->invited, user);
7101 /* Check for bans. If they're joining through a ban, one of two
7103 * 1: Join during a netburst, by riding the break. Kick them
7104 * unless they have ops or voice in the channel.
7105 * 2: They're allowed to join through the ban (an invite in
7106 * ircu2.10, or a +e on Hybrid, or something).
7107 * If they're not joining through a ban, and the banlist is not
7108 * full, see if they're on the banlist for the channel. If so,
7111 if(user->uplink->burst && !mNode->modes)
7114 for(ii = 0; ii < channel->banlist.used; ii++)
7116 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7118 /* Riding a netburst. Naughty. */
7119 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7125 mod_chanmode_init(&change);
7127 if(channel->banlist.used < MAXBANS)
7129 /* Not joining through a ban. */
7130 for(bData = cData->bans;
7131 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7132 bData = bData->next);
7136 char kick_reason[MAXLEN];
7137 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7139 bData->triggered = now;
7140 if(bData != cData->bans)
7142 /* Shuffle the ban to the head of the list. */
7144 bData->next->prev = bData->prev;
7146 bData->prev->next = bData->next;
7149 bData->next = cData->bans;
7152 cData->bans->prev = bData;
7153 cData->bans = bData;
7156 change.args[0].mode = MODE_BAN;
7157 change.args[0].u.hostmask = bData->mask;
7158 mod_chanmode_announce(chanserv, channel, &change);
7159 KickChannelUser(user, channel, chanserv, kick_reason);
7164 /* ChanServ will not modify the limits in join-flooded channels,
7165 or when there are enough slots left below the limit. */
7166 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7167 && !channel->join_flooded
7168 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7170 /* The user count has begun "bumping" into the channel limit,
7171 so set a timer to raise the limit a bit. Any previous
7172 timers are removed so three incoming users within the delay
7173 results in one limit change, not three. */
7175 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7176 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7179 if(channel->join_flooded)
7181 /* don't automatically give ops or voice during a join flood */
7183 else if(cData->lvlOpts[lvlGiveOps] == 0)
7184 modes |= MODE_CHANOP;
7185 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7186 modes |= MODE_VOICE;
7188 greeting = cData->greeting;
7189 if(user->handle_info)
7191 handle = user->handle_info;
7193 if(IsHelper(user) && !IsHelping(user))
7196 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7198 if(channel == chanserv_conf.support_channels.list[ii])
7200 HANDLE_SET_FLAG(user->handle_info, HELPING);
7206 uData = GetTrueChannelAccess(cData, handle);
7207 if(uData && !IsUserSuspended(uData))
7209 /* Ops and above were handled by the above case. */
7210 if(IsUserAutoOp(uData))
7212 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7213 modes |= MODE_CHANOP;
7214 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7215 modes |= MODE_VOICE;
7217 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7218 cData->visited = now;
7219 if(cData->user_greeting)
7220 greeting = cData->user_greeting;
7222 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7223 && ((now - uData->seen) >= chanserv_conf.info_delay)
7231 /* If user joining normally (not during burst), apply op or voice,
7232 * and send greeting/userinfo as appropriate.
7234 if(!user->uplink->burst)
7238 if(modes & MODE_CHANOP)
7239 modes &= ~MODE_VOICE;
7240 change.args[0].mode = modes;
7241 change.args[0].u.member = mNode;
7242 mod_chanmode_announce(chanserv, channel, &change);
7245 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7246 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7247 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7253 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7255 struct mod_chanmode change;
7256 struct userData *channel;
7257 unsigned int ii, jj;
7259 if(!user->handle_info)
7262 mod_chanmode_init(&change);
7264 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7266 struct chanNode *cn;
7267 struct modeNode *mn;
7268 if(IsUserSuspended(channel)
7269 || IsSuspended(channel->channel)
7270 || !(cn = channel->channel->channel))
7273 mn = GetUserMode(cn, user);
7276 if(!IsUserSuspended(channel)
7277 && IsUserAutoInvite(channel)
7278 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7280 && !user->uplink->burst)
7281 irc_invite(chanserv, user, cn);
7285 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7286 channel->channel->visited = now;
7288 if(IsUserAutoOp(channel))
7290 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7291 change.args[0].mode = MODE_CHANOP;
7292 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7293 change.args[0].mode = MODE_VOICE;
7295 change.args[0].mode = 0;
7296 change.args[0].u.member = mn;
7297 if(change.args[0].mode)
7298 mod_chanmode_announce(chanserv, cn, &change);
7301 channel->seen = now;
7302 channel->present = 1;
7305 for(ii = 0; ii < user->channels.used; ++ii)
7307 struct chanNode *chan = user->channels.list[ii]->channel;
7308 struct banData *ban;
7310 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7311 || !chan->channel_info
7312 || IsSuspended(chan->channel_info))
7314 for(jj = 0; jj < chan->banlist.used; ++jj)
7315 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7317 if(jj < chan->banlist.used)
7319 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7321 char kick_reason[MAXLEN];
7322 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7324 change.args[0].mode = MODE_BAN;
7325 change.args[0].u.hostmask = ban->mask;
7326 mod_chanmode_announce(chanserv, chan, &change);
7327 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7328 KickChannelUser(user, chan, chanserv, kick_reason);
7329 ban->triggered = now;
7334 if(IsSupportHelper(user))
7336 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7338 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7340 HANDLE_SET_FLAG(user->handle_info, HELPING);
7348 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7350 struct chanData *cData;
7351 struct userData *uData;
7353 cData = mn->channel->channel_info;
7354 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7357 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7359 /* Allow for a bit of padding so that the limit doesn't
7360 track the user count exactly, which could get annoying. */
7361 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7363 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7364 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7368 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7370 scan_user_presence(uData, mn->user);
7372 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7373 cData->visited = now;
7376 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7379 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7380 struct chanNode *channel;
7381 struct userNode *exclude;
7382 /* When looking at the channel that is being /part'ed, we
7383 * have to skip over the client that is leaving. For
7384 * other channels, we must not do that.
7386 channel = chanserv_conf.support_channels.list[ii];
7387 exclude = (channel == mn->channel) ? mn->user : NULL;
7388 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7391 if(ii == chanserv_conf.support_channels.used)
7392 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7397 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7399 struct userData *uData;
7401 if(!channel->channel_info || !kicker || IsService(kicker)
7402 || (kicker == victim) || IsSuspended(channel->channel_info)
7403 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7406 if(protect_user(victim, kicker, channel->channel_info))
7408 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7409 KickChannelUser(kicker, channel, chanserv, reason);
7412 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7417 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7419 struct chanData *cData;
7421 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7424 cData = channel->channel_info;
7425 if(bad_topic(channel, user, channel->topic))
7427 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7428 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7429 SetChannelTopic(channel, chanserv, old_topic, 1);
7430 else if(cData->topic)
7431 SetChannelTopic(channel, chanserv, cData->topic, 1);
7434 /* With topicsnarf, grab the topic and save it as the default topic. */
7435 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7438 cData->topic = strdup(channel->topic);
7444 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7446 struct mod_chanmode *bounce = NULL;
7447 unsigned int bnc, ii;
7450 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7453 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7454 && mode_lock_violated(&channel->channel_info->modes, change))
7456 char correct[MAXLEN];
7457 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7458 mod_chanmode_format(&channel->channel_info->modes, correct);
7459 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7461 for(ii = bnc = 0; ii < change->argc; ++ii)
7463 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7465 const struct userNode *victim = change->args[ii].u.member->user;
7466 if(!protect_user(victim, user, channel->channel_info))
7469 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7472 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7473 bounce->args[bnc].u.member = GetUserMode(channel, user);
7474 if(bounce->args[bnc].u.member)
7478 bounce->args[bnc].mode = MODE_CHANOP;
7479 bounce->args[bnc].u.member = change->args[ii].u.member;
7481 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7483 else if(change->args[ii].mode & MODE_CHANOP)
7485 const struct userNode *victim = change->args[ii].u.member->user;
7486 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7489 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7490 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7491 bounce->args[bnc].u.member = change->args[ii].u.member;
7494 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7496 const char *ban = change->args[ii].u.hostmask;
7497 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7500 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7501 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7502 bounce->args[bnc].u.hostmask = strdup(ban);
7504 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7509 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7510 mod_chanmode_announce(chanserv, channel, bounce);
7511 for(ii = 0; ii < change->argc; ++ii)
7512 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7513 free((char*)bounce->args[ii].u.hostmask);
7514 mod_chanmode_free(bounce);
7519 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7521 struct chanNode *channel;
7522 struct banData *bData;
7523 struct mod_chanmode change;
7524 unsigned int ii, jj;
7525 char kick_reason[MAXLEN];
7527 mod_chanmode_init(&change);
7529 change.args[0].mode = MODE_BAN;
7530 for(ii = 0; ii < user->channels.used; ++ii)
7532 channel = user->channels.list[ii]->channel;
7533 /* Need not check for bans if they're opped or voiced. */
7534 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7536 /* Need not check for bans unless channel registration is active. */
7537 if(!channel->channel_info || IsSuspended(channel->channel_info))
7539 /* Look for a matching ban already on the channel. */
7540 for(jj = 0; jj < channel->banlist.used; ++jj)
7541 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7543 /* Need not act if we found one. */
7544 if(jj < channel->banlist.used)
7546 /* Look for a matching ban in this channel. */
7547 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7549 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7551 change.args[0].u.hostmask = bData->mask;
7552 mod_chanmode_announce(chanserv, channel, &change);
7553 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7554 KickChannelUser(user, channel, chanserv, kick_reason);
7555 bData->triggered = now;
7556 break; /* we don't need to check any more bans in the channel */
7561 static void handle_rename(struct handle_info *handle, const char *old_handle)
7563 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7567 dict_remove2(handle_dnrs, old_handle, 1);
7568 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7569 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7574 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7576 struct userNode *h_user;
7578 if(handle->channels)
7580 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7581 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7583 while(handle->channels)
7584 del_channel_user(handle->channels, 1);
7589 handle_server_link(UNUSED_ARG(struct server *server))
7591 struct chanData *cData;
7593 for(cData = channelList; cData; cData = cData->next)
7595 if(!IsSuspended(cData))
7596 cData->may_opchan = 1;
7597 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7598 && !cData->channel->join_flooded
7599 && ((cData->channel->limit - cData->channel->members.used)
7600 < chanserv_conf.adjust_threshold))
7602 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7603 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7609 chanserv_conf_read(void)
7613 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7614 struct mod_chanmode *change;
7615 struct string_list *strlist;
7616 struct chanNode *chan;
7619 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7621 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7624 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7625 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7626 chanserv_conf.support_channels.used = 0;
7627 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7629 for(ii = 0; ii < strlist->used; ++ii)
7631 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7634 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7636 channelList_append(&chanserv_conf.support_channels, chan);
7639 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7642 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7645 chan = AddChannel(str, now, str2, NULL);
7647 channelList_append(&chanserv_conf.support_channels, chan);
7649 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7650 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7651 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7652 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7653 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7654 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7655 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7656 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7657 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7658 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7659 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7660 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7661 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7662 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7663 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7664 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7665 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7666 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7667 str = database_get_data(conf_node, KEY_REVOKE_MODE_A, RECDB_QSTRING);
7668 chanserv_conf.revoke_mode_a = str ? atoi(str) : 1;
7669 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7670 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7671 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7672 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7673 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7674 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7675 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7676 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7677 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7679 NickChange(chanserv, str, 0);
7680 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7681 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7682 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7683 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7684 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7685 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7686 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7687 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7688 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7689 chanserv_conf.max_owned = str ? atoi(str) : 5;
7690 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7691 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7692 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7693 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7694 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7695 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7696 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7697 chanserv_conf.new_channel_authed = (str && *str) ? str : NULL;
7698 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7699 chanserv_conf.new_channel_unauthed = (str && *str) ? str : NULL;
7700 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7701 chanserv_conf.new_channel_msg = (str && *str) ? str : NULL;
7702 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7705 safestrncpy(mode_line, str, sizeof(mode_line));
7706 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7707 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7708 && (change->argc < 2))
7710 chanserv_conf.default_modes = *change;
7711 mod_chanmode_free(change);
7713 free_string_list(chanserv_conf.set_shows);
7714 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7716 strlist = string_list_copy(strlist);
7719 static const char *list[] = {
7720 /* free form text */
7721 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7722 /* options based on user level */
7723 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7724 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7725 /* multiple choice options */
7726 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7727 /* binary options */
7728 "DynLimit", "NoDelete", "expire", "Vote",
7732 strlist = alloc_string_list(ArrayLength(list)-1);
7733 for(ii=0; list[ii]; ii++)
7734 string_list_append(strlist, strdup(list[ii]));
7736 chanserv_conf.set_shows = strlist;
7737 /* We don't look things up now, in case the list refers to options
7738 * defined by modules initialized after this point. Just mark the
7739 * function list as invalid, so it will be initialized.
7741 set_shows_list.used = 0;
7742 free_string_list(chanserv_conf.eightball);
7743 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7746 strlist = string_list_copy(strlist);
7750 strlist = alloc_string_list(4);
7751 string_list_append(strlist, strdup("Yes."));
7752 string_list_append(strlist, strdup("No."));
7753 string_list_append(strlist, strdup("Maybe so."));
7755 chanserv_conf.eightball = strlist;
7756 free_string_list(chanserv_conf.old_ban_names);
7757 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7759 strlist = string_list_copy(strlist);
7761 strlist = alloc_string_list(2);
7762 chanserv_conf.old_ban_names = strlist;
7763 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7764 off_channel = str ? atoi(str) : 0;
7768 chanserv_note_type_read(const char *key, struct record_data *rd)
7771 struct note_type *ntype;
7774 if(!(obj = GET_RECORD_OBJECT(rd)))
7776 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7779 if(!(ntype = chanserv_create_note_type(key)))
7781 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7785 /* Figure out set access */
7786 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7788 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7789 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7791 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7793 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7794 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7796 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7798 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7802 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7803 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7804 ntype->set_access.min_opserv = 0;
7807 /* Figure out visibility */
7808 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7809 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7810 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7811 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7812 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7813 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7814 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7815 ntype->visible_type = NOTE_VIS_ALL;
7817 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7819 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7820 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7824 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7826 struct vote_option *vOpt;
7829 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7831 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7835 vOpt = calloc(1, sizeof(*vOpt));
7836 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7837 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7838 vOpt->voted = str ? atoi(str) : 0;
7839 vOpt->option_id = str ? atoi(key) : 0;
7840 vOpt->option_str = strdup(key);
7841 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7845 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7847 struct handle_info *handle;
7848 struct userData *uData;
7849 char *seen, *inf, *flags, *voted, *votefor;
7850 unsigned long last_seen;
7851 unsigned short access_level;
7853 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7855 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7859 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7860 if(access_level > UL_OWNER)
7862 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7866 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7867 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7868 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7869 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7870 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7871 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7872 handle = get_handle_info(key);
7875 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7879 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7880 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7882 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7883 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7891 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7893 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7894 unsigned long set_time, triggered_time, expires_time;
7896 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7898 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7902 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7903 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7904 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7905 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7906 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7907 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7908 if (!reason || !owner)
7911 set_time = set ? strtoul(set, NULL, 0) : now;
7912 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7914 expires_time = strtoul(s_expires, NULL, 0);
7916 expires_time = set_time + atoi(s_duration);
7920 if(!reason || (expires_time && (expires_time < now)))
7923 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7926 static struct suspended *
7927 chanserv_read_suspended(dict_t obj)
7929 struct suspended *suspended = calloc(1, sizeof(*suspended));
7933 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7934 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7935 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7936 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7937 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7938 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7939 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7940 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7941 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7942 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7947 chanserv_channel_read(const char *key, struct record_data *hir)
7949 struct suspended *suspended;
7950 struct mod_chanmode *modes;
7951 struct chanNode *cNode;
7952 struct chanData *cData;
7953 struct dict *channel, *obj;
7954 char *str, *argv[10];
7958 channel = hir->d.object;
7960 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7963 cNode = AddChannel(key, now, NULL, NULL);
7966 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7969 cData = register_channel(cNode, str);
7972 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7976 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7978 enum levelOption lvlOpt;
7979 enum charOption chOpt;
7981 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7982 cData->flags = atoi(str);
7984 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7986 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7988 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7989 else if(levelOptions[lvlOpt].old_flag)
7991 if(cData->flags & levelOptions[lvlOpt].old_flag)
7992 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7994 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7998 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8000 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8002 cData->chOpts[chOpt] = str[0];
8005 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8007 enum levelOption lvlOpt;
8008 enum charOption chOpt;
8011 cData->flags = base64toint(str, 5);
8012 count = strlen(str += 5);
8013 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8016 if(levelOptions[lvlOpt].old_flag)
8018 if(cData->flags & levelOptions[lvlOpt].old_flag)
8019 lvl = levelOptions[lvlOpt].flag_value;
8021 lvl = levelOptions[lvlOpt].default_value;
8023 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8025 case 'c': lvl = UL_COOWNER; break;
8026 case 'm': lvl = UL_MASTER; break;
8027 case 'n': lvl = UL_OWNER+1; break;
8028 case 'o': lvl = UL_OP; break;
8029 case 'p': lvl = UL_PEON; break;
8030 case 'w': lvl = UL_OWNER; break;
8031 default: lvl = 0; break;
8033 cData->lvlOpts[lvlOpt] = lvl;
8035 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8036 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8039 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8041 cData->expiry = atoi(str);
8042 if(cData->expiry > 0) {
8043 if(cData->expiry > now) {
8044 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8046 timeq_add(1, chanserv_expire_channel, cData);
8053 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8055 suspended = chanserv_read_suspended(obj);
8056 cData->suspended = suspended;
8057 suspended->cData = cData;
8058 /* We could use suspended->expires and suspended->revoked to
8059 * set the CHANNEL_SUSPENDED flag, but we don't. */
8061 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8063 suspended = calloc(1, sizeof(*suspended));
8064 suspended->issued = 0;
8065 suspended->revoked = 0;
8066 suspended->suspender = strdup(str);
8067 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8068 suspended->expires = str ? atoi(str) : 0;
8069 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8070 suspended->reason = strdup(str ? str : "No reason");
8071 suspended->previous = NULL;
8072 cData->suspended = suspended;
8073 suspended->cData = cData;
8077 cData->flags &= ~CHANNEL_SUSPENDED;
8078 suspended = NULL; /* to squelch a warning */
8081 if(IsSuspended(cData)) {
8082 if(suspended->expires > now)
8083 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8084 else if(suspended->expires)
8085 cData->flags &= ~CHANNEL_SUSPENDED;
8088 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8089 struct mod_chanmode change;
8090 mod_chanmode_init(&change);
8092 change.args[0].mode = MODE_CHANOP;
8093 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8094 mod_chanmode_announce(chanserv, cNode, &change);
8097 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8098 cData->registered = str ? strtoul(str, NULL, 0) : now;
8099 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8100 cData->visited = str ? strtoul(str, NULL, 0) : now;
8101 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8102 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8103 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8104 cData->max = str ? atoi(str) : 0;
8105 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8106 cData->max_time = str ? atoi(str) : 0;
8107 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8108 cData->greeting = str ? strdup(str) : NULL;
8109 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8110 cData->user_greeting = str ? strdup(str) : NULL;
8111 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8112 cData->topic_mask = str ? strdup(str) : NULL;
8113 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8114 cData->topic = str ? strdup(str) : NULL;
8116 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8118 cData->vote = str ? strdup(str) : NULL;
8119 dict_delete(cData->vote_options);
8120 cData->vote_options = dict_new();
8121 dict_set_free_data(cData->vote_options, free_vote_options);
8122 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8123 cData->vote_start = str ? atoi(str) : 0;
8124 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8125 for(it = dict_first(obj); it; it = iter_next(it)) {
8126 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8130 if(!IsSuspended(cData)
8131 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8132 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8133 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8134 cData->modes = *modes;
8136 cData->modes.modes_set |= MODE_REGISTERED;
8137 if(cData->modes.argc > 1)
8138 cData->modes.argc = 1;
8139 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8140 mod_chanmode_free(modes);
8143 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8144 for(it = dict_first(obj); it; it = iter_next(it))
8145 user_read_helper(iter_key(it), iter_data(it), cData);
8147 if(!cData->users && !IsProtected(cData))
8149 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8150 unregister_channel(cData, "has empty user list.");
8154 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8155 for(it = dict_first(obj); it; it = iter_next(it))
8156 ban_read_helper(iter_key(it), iter_data(it), cData);
8158 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8159 for(it = dict_first(obj); it; it = iter_next(it))
8161 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8162 struct record_data *rd = iter_data(it);
8163 const char *note, *setter;
8165 if(rd->type != RECDB_OBJECT)
8167 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8171 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8173 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8175 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8179 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8180 if(!setter) setter = "<unknown>";
8181 chanserv_add_channel_note(cData, ntype, setter, note);
8189 chanserv_dnr_read(const char *key, struct record_data *hir)
8191 const char *setter, *reason, *str;
8192 struct do_not_register *dnr;
8193 unsigned long expiry;
8195 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8198 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8201 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8204 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8207 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8208 expiry = str ? strtoul(str, NULL, 0) : 0;
8209 if(expiry && expiry <= now)
8211 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8214 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8216 dnr->set = atoi(str);
8222 chanserv_saxdb_read(struct dict *database)
8224 struct dict *section;
8227 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8228 for(it = dict_first(section); it; it = iter_next(it))
8229 chanserv_note_type_read(iter_key(it), iter_data(it));
8231 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8232 for(it = dict_first(section); it; it = iter_next(it))
8233 chanserv_channel_read(iter_key(it), iter_data(it));
8235 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8236 for(it = dict_first(section); it; it = iter_next(it))
8237 chanserv_dnr_read(iter_key(it), iter_data(it));
8243 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8245 int high_present = 0;
8246 saxdb_start_record(ctx, KEY_USERS, 1);
8247 for(; uData; uData = uData->next)
8249 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8251 saxdb_start_record(ctx, uData->handle->handle, 0);
8252 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8253 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8255 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8256 if(uData->channel->vote && uData->voted)
8257 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8258 if(uData->channel->vote && uData->votefor)
8259 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8261 saxdb_write_string(ctx, KEY_INFO, uData->info);
8262 saxdb_end_record(ctx);
8264 saxdb_end_record(ctx);
8265 return high_present;
8269 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8273 saxdb_start_record(ctx, KEY_BANS, 1);
8274 for(; bData; bData = bData->next)
8276 saxdb_start_record(ctx, bData->mask, 0);
8277 saxdb_write_int(ctx, KEY_SET, bData->set);
8278 if(bData->triggered)
8279 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8281 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8283 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8285 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8286 saxdb_end_record(ctx);
8288 saxdb_end_record(ctx);
8292 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8294 saxdb_start_record(ctx, name, 0);
8295 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8296 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8298 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8300 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8302 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8304 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8305 saxdb_end_record(ctx);
8309 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8313 enum levelOption lvlOpt;
8314 enum charOption chOpt;
8317 saxdb_start_record(ctx, channel->channel->name, 1);
8319 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8320 saxdb_write_int(ctx, KEY_MAX, channel->max);
8321 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8323 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8324 if(channel->registrar)
8325 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8326 if(channel->greeting)
8327 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8328 if(channel->user_greeting)
8329 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8330 if(channel->topic_mask)
8331 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8332 if(channel->suspended)
8333 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8335 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8338 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8339 if(channel->vote_start)
8340 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8341 if (dict_size(channel->vote_options)) {
8342 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8343 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8344 struct vote_option *vOpt = iter_data(it);
8346 sprintf(str,"%i",vOpt->option_id);
8347 saxdb_start_record(ctx, str, 0);
8349 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8351 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8352 saxdb_end_record(ctx);
8354 saxdb_end_record(ctx);
8358 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8359 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8360 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8361 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8362 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8364 buf[0] = channel->chOpts[chOpt];
8366 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8368 saxdb_end_record(ctx);
8370 if(channel->modes.modes_set || channel->modes.modes_clear)
8372 mod_chanmode_format(&channel->modes, buf);
8373 saxdb_write_string(ctx, KEY_MODES, buf);
8376 high_present = chanserv_write_users(ctx, channel->users);
8377 chanserv_write_bans(ctx, channel->bans);
8379 if(dict_size(channel->notes))
8383 saxdb_start_record(ctx, KEY_NOTES, 1);
8384 for(it = dict_first(channel->notes); it; it = iter_next(it))
8386 struct note *note = iter_data(it);
8387 saxdb_start_record(ctx, iter_key(it), 0);
8388 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8389 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8390 saxdb_end_record(ctx);
8392 saxdb_end_record(ctx);
8395 if(channel->ownerTransfer)
8396 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8397 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8398 saxdb_end_record(ctx);
8402 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8406 saxdb_start_record(ctx, ntype->name, 0);
8407 switch(ntype->set_access_type)
8409 case NOTE_SET_CHANNEL_ACCESS:
8410 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8412 case NOTE_SET_CHANNEL_SETTER:
8413 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8415 case NOTE_SET_PRIVILEGED: default:
8416 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8419 switch(ntype->visible_type)
8421 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8422 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8423 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8425 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8426 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8427 saxdb_end_record(ctx);
8431 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8433 struct do_not_register *dnr;
8434 dict_iterator_t it, next;
8436 for(it = dict_first(dnrs); it; it = next)
8438 next = iter_next(it);
8439 dnr = iter_data(it);
8440 if(dnr->expires && dnr->expires <= now)
8442 dict_remove(dnrs, iter_key(it));
8445 saxdb_start_record(ctx, dnr->chan_name, 0);
8447 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8449 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8450 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8451 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8452 saxdb_end_record(ctx);
8457 chanserv_saxdb_write(struct saxdb_context *ctx)
8460 struct chanData *channel;
8463 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8464 for(it = dict_first(note_types); it; it = iter_next(it))
8465 chanserv_write_note_type(ctx, iter_data(it));
8466 saxdb_end_record(ctx);
8469 saxdb_start_record(ctx, KEY_DNR, 1);
8470 write_dnrs_helper(ctx, handle_dnrs);
8471 write_dnrs_helper(ctx, plain_dnrs);
8472 write_dnrs_helper(ctx, mask_dnrs);
8473 saxdb_end_record(ctx);
8476 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8477 for(channel = channelList; channel; channel = channel->next)
8478 chanserv_write_channel(ctx, channel);
8479 saxdb_end_record(ctx);
8485 chanserv_db_cleanup(void) {
8487 unreg_part_func(handle_part);
8489 unregister_channel(channelList, "terminating.");
8490 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8491 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8492 free(chanserv_conf.support_channels.list);
8493 dict_delete(handle_dnrs);
8494 dict_delete(plain_dnrs);
8495 dict_delete(mask_dnrs);
8496 dict_delete(note_types);
8497 free_string_list(chanserv_conf.eightball);
8498 free_string_list(chanserv_conf.old_ban_names);
8499 free_string_list(chanserv_conf.set_shows);
8500 free(set_shows_list.list);
8501 free(uset_shows_list.list);
8504 struct userData *helper = helperList;
8505 helperList = helperList->next;
8510 #if defined(GCC_VARMACROS)
8511 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8512 #elif defined(C99_VARMACROS)
8513 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8515 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8516 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8519 init_chanserv(const char *nick)
8521 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8522 conf_register_reload(chanserv_conf_read);
8526 reg_server_link_func(handle_server_link);
8527 reg_new_channel_func(handle_new_channel);
8528 reg_join_func(handle_join);
8529 reg_part_func(handle_part);
8530 reg_kick_func(handle_kick);
8531 reg_topic_func(handle_topic);
8532 reg_mode_change_func(handle_mode);
8533 reg_nick_change_func(handle_nick_change);
8534 reg_auth_func(handle_auth);
8537 reg_handle_rename_func(handle_rename);
8538 reg_unreg_func(handle_unreg);
8540 handle_dnrs = dict_new();
8541 dict_set_free_data(handle_dnrs, free);
8542 plain_dnrs = dict_new();
8543 dict_set_free_data(plain_dnrs, free);
8544 mask_dnrs = dict_new();
8545 dict_set_free_data(mask_dnrs, free);
8547 reg_svccmd_unbind_func(handle_svccmd_unbind);
8548 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8549 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8550 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8551 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8552 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8553 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8554 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8555 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8556 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8557 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8558 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8559 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8560 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8562 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8563 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8565 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8566 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8567 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8568 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8569 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8571 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8572 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8573 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8574 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8575 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8577 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8578 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8579 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8580 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8582 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8583 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8584 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8585 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8586 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8587 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8588 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8589 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8591 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8592 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8593 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8594 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8595 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8596 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8597 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8598 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8599 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8600 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8601 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8602 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8603 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8604 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8605 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8607 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8608 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8609 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8610 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8611 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8613 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8614 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8616 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8617 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8618 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8619 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8620 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8621 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8622 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8623 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8624 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8625 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8628 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8629 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8631 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8632 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8633 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8634 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8636 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8637 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8638 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8639 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8640 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8642 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8643 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8644 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8645 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8646 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8647 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8649 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8650 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8651 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8652 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8653 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8654 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8655 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8656 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED | MODCMD_REQUIRE_REGCHAN, NULL);
8658 DEFINE_COMMAND(opme, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_CHANNEL, NULL);
8660 /* Channel options */
8661 DEFINE_CHANNEL_OPTION(defaulttopic);
8662 DEFINE_CHANNEL_OPTION(topicmask);
8663 DEFINE_CHANNEL_OPTION(greeting);
8664 DEFINE_CHANNEL_OPTION(usergreeting);
8665 DEFINE_CHANNEL_OPTION(modes);
8666 DEFINE_CHANNEL_OPTION(enfops);
8667 DEFINE_CHANNEL_OPTION(giveops);
8668 DEFINE_CHANNEL_OPTION(protect);
8669 DEFINE_CHANNEL_OPTION(enfmodes);
8670 DEFINE_CHANNEL_OPTION(enftopic);
8671 DEFINE_CHANNEL_OPTION(pubcmd);
8672 DEFINE_CHANNEL_OPTION(givevoice);
8673 DEFINE_CHANNEL_OPTION(userinfo);
8674 DEFINE_CHANNEL_OPTION(dynlimit);
8675 DEFINE_CHANNEL_OPTION(topicsnarf);
8676 DEFINE_CHANNEL_OPTION(vote);
8677 DEFINE_CHANNEL_OPTION(nodelete);
8678 DEFINE_CHANNEL_OPTION(toys);
8679 DEFINE_CHANNEL_OPTION(setters);
8680 DEFINE_CHANNEL_OPTION(topicrefresh);
8681 DEFINE_CHANNEL_OPTION(ctcpusers);
8682 DEFINE_CHANNEL_OPTION(ctcpreaction);
8683 DEFINE_CHANNEL_OPTION(inviteme);
8684 DEFINE_CHANNEL_OPTION(unreviewed);
8685 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8686 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8687 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8689 DEFINE_CHANNEL_OPTION(offchannel);
8690 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8692 /* Alias set topic to set defaulttopic for compatibility. */
8693 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8696 DEFINE_USER_OPTION(noautoop);
8697 DEFINE_USER_OPTION(autoinvite);
8698 DEFINE_USER_OPTION(info);
8700 /* Alias uset autovoice to uset autoop. */
8701 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8703 note_types = dict_new();
8704 dict_set_free_data(note_types, chanserv_deref_note_type);
8707 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8708 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8709 service_register(chanserv)->trigger = '!';
8710 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8712 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8714 if(chanserv_conf.channel_expire_frequency)
8715 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8717 if(chanserv_conf.dnr_expire_frequency)
8718 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8720 if(chanserv_conf.refresh_period)
8722 unsigned long next_refresh;
8723 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8724 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8727 reg_exit_func(chanserv_db_cleanup);
8728 message_register_table(msgtab);