1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 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() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
46 #define KEY_SWITCH_TIMEOUT "switch_timeout"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab
[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "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." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
199 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
202 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
203 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
204 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
205 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
206 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
207 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
208 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
209 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
210 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
211 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
212 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
213 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
214 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
215 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
216 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
217 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
219 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
221 /* Channel management */
222 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
223 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
224 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
226 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
227 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
228 { "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" },
229 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
230 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
231 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
232 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
234 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
235 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
236 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
237 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
238 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
239 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
240 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
241 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
242 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
243 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
244 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
245 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
246 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
247 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
248 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
249 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
250 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
251 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
252 { "CSMSG_SET_MODES", "$bModes $b %s" },
253 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
254 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
255 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
256 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
257 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
258 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
259 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
260 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
261 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
262 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
263 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
264 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
265 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
266 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
267 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
268 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
269 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
270 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
271 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
272 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
273 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
274 { "CSMSG_USET_INFO", "$bInfo $b %s" },
276 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
277 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
278 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
279 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
280 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
281 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
282 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
283 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
284 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
286 { "CSMSG_PROTECT_NONE", "No users will be protected." },
287 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
288 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
289 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
290 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
291 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
292 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
293 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
294 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
295 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
300 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
301 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
302 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
303 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
304 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
305 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
306 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
307 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
309 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
310 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
311 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
313 /* Channel userlist */
314 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
315 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
316 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
317 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
319 /* Channel note list */
320 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
321 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
322 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
323 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
324 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
325 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
326 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
327 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
328 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
329 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
330 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
331 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
332 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
333 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
334 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
336 /* Channel [un]suspension */
337 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
338 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
339 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
340 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
341 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
342 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
343 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
345 /* Access information */
346 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
347 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
348 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
349 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
350 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
351 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
352 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
353 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
354 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
355 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
356 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
358 /* Seen information */
359 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
360 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
361 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
362 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
364 /* Names information */
365 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
366 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
368 /* Channel information */
369 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
370 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
371 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
372 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
373 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
374 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
375 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
376 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
377 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
378 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
379 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
380 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
388 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
389 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
391 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
392 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
393 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
394 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
395 { "CSMSG_PEEK_OPS", "$bOps:$b" },
396 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
398 /* Network information */
399 { "CSMSG_NETWORK_INFO", "Network Information:" },
400 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
401 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
402 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
403 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
404 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
405 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
406 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
407 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
410 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
411 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
412 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
414 /* Channel searches */
415 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
416 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
417 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
418 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
420 /* Channel configuration */
421 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
422 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
423 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
426 { "CSMSG_USER_OPTIONS", "User Options:" },
427 { "CSMSG_USER_PROTECTED", "That user is protected." },
430 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
431 { "CSMSG_PING_RESPONSE", "Pong!" },
432 { "CSMSG_WUT_RESPONSE", "wut" },
433 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
434 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
435 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
436 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
437 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
438 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
439 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
442 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
446 /* eject_user and unban_user flags */
447 #define ACTION_KICK 0x0001
448 #define ACTION_BAN 0x0002
449 #define ACTION_ADD_BAN 0x0004
450 #define ACTION_ADD_TIMED_BAN 0x0008
451 #define ACTION_UNBAN 0x0010
452 #define ACTION_DEL_BAN 0x0020
454 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
455 #define MODELEN 40 + KEYLEN
459 #define CSFUNC_ARGS user, channel, argc, argv, cmd
461 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
462 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
463 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
464 reply("MSG_MISSING_PARAMS", argv[0]); \
468 DECLARE_LIST(dnrList
, struct do_not_register
*);
469 DEFINE_LIST(dnrList
, struct do_not_register
*);
471 static int eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
);
473 struct userNode
*chanserv
;
476 static dict_t plain_dnrs
, mask_dnrs
, handle_dnrs
;
477 static struct log_type
*CS_LOG
;
481 struct channelList support_channels
;
482 struct mod_chanmode default_modes
;
484 unsigned long db_backup_frequency
;
485 unsigned long channel_expire_frequency
;
488 unsigned int adjust_delay
;
489 long channel_expire_delay
;
490 unsigned int nodelete_level
;
492 unsigned int adjust_threshold
;
493 int join_flood_threshold
;
495 unsigned int greeting_length
;
496 unsigned int refresh_period
;
498 unsigned int max_owned
;
499 unsigned int max_chan_users
;
500 unsigned int max_chan_bans
;
501 unsigned int max_userinfo_length
;
503 struct string_list
*set_shows
;
504 struct string_list
*eightball
;
505 struct string_list
*old_ban_names
;
507 const char *ctcp_short_ban_duration
;
508 const char *ctcp_long_ban_duration
;
510 const char *irc_operator_epithet
;
511 const char *network_helper_epithet
;
512 const char *support_helper_epithet
;
517 struct userNode
*user
;
518 struct userNode
*bot
;
519 struct chanNode
*channel
;
521 unsigned short lowest
;
522 unsigned short highest
;
523 struct userData
**users
;
524 struct helpfile_table table
;
527 enum note_access_type
529 NOTE_SET_CHANNEL_ACCESS
,
530 NOTE_SET_CHANNEL_SETTER
,
534 enum note_visible_type
537 NOTE_VIS_CHANNEL_USERS
,
543 enum note_access_type set_access_type
;
545 unsigned int min_opserv
;
546 unsigned short min_ulevel
;
548 enum note_visible_type visible_type
;
549 unsigned int max_length
;
556 struct note_type
*type
;
557 char setter
[NICKSERV_HANDLE_LEN
+1];
561 static unsigned int registered_channels
;
562 static unsigned int banCount
;
564 static const struct {
567 unsigned short level
;
570 { "peon", "Peon", UL_PEON
, '+' },
571 { "op", "Op", UL_OP
, '@' },
572 { "manager", "Manager", UL_MANAGER
, '%' },
573 { "coowner", "Coowner", UL_COOWNER
, '*' },
574 { "owner", "Owner", UL_OWNER
, '!' },
575 { "helper", "BUG:", UL_HELPER
, 'X' }
578 static const struct {
581 unsigned short default_value
;
582 unsigned int old_idx
;
583 unsigned int old_flag
;
584 unsigned short flag_value
;
586 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL
, 0 },
587 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
588 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
589 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
590 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
591 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
592 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
593 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
594 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES
, 1 },
595 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE
, 200 },
596 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF
, 1 }
599 struct charOptionValues
{
602 } protectValues
[] = {
603 { 'a', "CSMSG_PROTECT_ALL" },
604 { 'e', "CSMSG_PROTECT_EQUAL" },
605 { 'l', "CSMSG_PROTECT_LOWER" },
606 { 'n', "CSMSG_PROTECT_NONE" }
608 { 'd', "CSMSG_TOYS_DISABLED" },
609 { 'n', "CSMSG_TOYS_PRIVATE" },
610 { 'p', "CSMSG_TOYS_PUBLIC" }
611 }, topicRefreshValues
[] = {
612 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
613 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
614 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
615 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
616 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
617 }, ctcpReactionValues
[] = {
618 { 'k', "CSMSG_CTCPREACTION_KICK" },
619 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
620 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
621 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
624 static const struct {
628 unsigned int old_idx
;
630 struct charOptionValues
*values
;
632 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues
), protectValues
},
633 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues
), toysValues
},
634 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues
), topicRefreshValues
},
635 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues
), ctcpReactionValues
}
638 struct userData
*helperList
;
639 struct chanData
*channelList
;
640 static struct module *chanserv_module
;
641 static unsigned int userCount
;
643 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
644 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
645 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
648 user_level_from_name(const char *name
, unsigned short clamp_level
)
650 unsigned int level
= 0, ii
;
652 level
= strtoul(name
, NULL
, 10);
653 else for(ii
= 0; (ii
< ArrayLength(accessLevels
)) && !level
; ++ii
)
654 if(!irccasecmp(name
, accessLevels
[ii
].name
))
655 level
= accessLevels
[ii
].level
;
656 if(level
> clamp_level
)
662 parse_level_range(unsigned short *minl
, unsigned short *maxl
, const char *arg
)
665 *minl
= strtoul(arg
, &sep
, 10);
673 *maxl
= strtoul(sep
+1, &sep
, 10);
681 _GetChannelUser(struct chanData
*channel
, struct handle_info
*handle
, int override
, int allow_suspended
)
683 struct userData
*uData
, **head
;
685 if(!channel
|| !handle
)
688 if(override
&& HANDLE_FLAGGED(handle
, HELPING
)
689 && ((handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
)))
691 for(uData
= helperList
;
692 uData
&& uData
->handle
!= handle
;
693 uData
= uData
->next
);
697 uData
= calloc(1, sizeof(struct userData
));
698 uData
->handle
= handle
;
700 uData
->access
= UL_HELPER
;
706 uData
->next
= helperList
;
708 helperList
->prev
= uData
;
716 for(uData
= channel
->users
; uData
; uData
= uData
->next
)
717 if((uData
->handle
== handle
) && (allow_suspended
|| !IsUserSuspended(uData
)))
720 head
= &(channel
->users
);
723 if(uData
&& (uData
!= *head
))
725 /* Shuffle the user to the head of whatever list he was in. */
727 uData
->next
->prev
= uData
->prev
;
729 uData
->prev
->next
= uData
->next
;
735 (**head
).prev
= uData
;
742 /* Returns non-zero if user has at least the minimum access.
743 * exempt_owner is set when handling !set, so the owner can set things
746 int check_user_level(struct chanNode
*channel
, struct userNode
*user
, enum levelOption opt
, int allow_override
, int exempt_owner
)
748 struct userData
*uData
;
749 struct chanData
*cData
= channel
->channel_info
;
750 unsigned short minimum
= cData
->lvlOpts
[opt
];
753 uData
= _GetChannelUser(cData
, user
->handle_info
, allow_override
, 0);
756 if(minimum
<= uData
->access
)
758 if((minimum
> UL_OWNER
) && (uData
->access
== UL_OWNER
) && exempt_owner
)
763 /* Scan for other users authenticated to the same handle
764 still in the channel. If so, keep them listed as present.
766 user is optional, if not null, it skips checking that userNode
767 (for the handle_part function) */
769 scan_user_presence(struct userData
*uData
, struct userNode
*user
)
773 if(IsSuspended(uData
->channel
)
774 || IsUserSuspended(uData
)
775 || !(mn
= find_handle_in_channel(uData
->channel
->channel
, uData
->handle
, user
)))
787 chanserv_ctcp_check(struct userNode
*user
, struct chanNode
*channel
, char *text
, UNUSED_ARG(struct userNode
*bot
))
789 unsigned int eflags
, argc
;
791 static char *bad_ctcp_reason
= "CTCPs to this channel are forbidden.";
793 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
794 if(!channel
->channel_info
795 || IsSuspended(channel
->channel_info
)
797 || !ircncasecmp(text
, "ACTION ", 7))
799 /* Figure out the minimum level needed to CTCP the channel */
800 if(check_user_level(channel
, user
, lvlCTCPUsers
, 1, 0))
802 /* We need to enforce against them; do so. */
805 argv
[1] = user
->nick
;
807 if(GetUserMode(channel
, user
))
808 eflags
|= ACTION_KICK
;
809 switch(channel
->channel_info
->chOpts
[chCTCPReaction
]) {
810 default: case 'k': /* just do the kick */ break;
812 eflags
|= ACTION_BAN
;
815 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
816 argv
[argc
++] = (char*)chanserv_conf
.ctcp_short_ban_duration
;
819 eflags
|= ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
;
820 argv
[argc
++] = (char*)chanserv_conf
.ctcp_long_ban_duration
;
823 argv
[argc
++] = bad_ctcp_reason
;
824 eject_user(chanserv
, channel
, argc
, argv
, NULL
, eflags
);
828 chanserv_create_note_type(const char *name
)
830 struct note_type
*ntype
= calloc(1, sizeof(*ntype
) + strlen(name
));
831 strcpy(ntype
->name
, name
);
833 dict_insert(note_types
, ntype
->name
, ntype
);
838 chanserv_deref_note_type(void *data
)
840 struct note_type
*ntype
= data
;
842 if(--ntype
->refs
> 0)
848 chanserv_flush_note_type(struct note_type
*ntype
)
850 struct chanData
*cData
;
851 for(cData
= channelList
; cData
; cData
= cData
->next
)
852 dict_remove(cData
->notes
, ntype
->name
);
856 chanserv_truncate_notes(struct note_type
*ntype
)
858 struct chanData
*cData
;
860 unsigned int size
= sizeof(*note
) + ntype
->max_length
;
862 for(cData
= channelList
; cData
; cData
= cData
->next
) {
863 note
= dict_find(cData
->notes
, ntype
->name
, NULL
);
866 if(strlen(note
->note
) <= ntype
->max_length
)
868 dict_remove2(cData
->notes
, ntype
->name
, 1);
869 note
= realloc(note
, size
);
870 note
->note
[ntype
->max_length
] = 0;
871 dict_insert(cData
->notes
, ntype
->name
, note
);
875 static int note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
);
878 chanserv_add_channel_note(struct chanData
*channel
, struct note_type
*type
, const char *setter
, const char *text
)
881 unsigned int len
= strlen(text
);
883 if(len
> type
->max_length
) len
= type
->max_length
;
884 note
= calloc(1, sizeof(*note
) + len
);
886 strncpy(note
->setter
, setter
, sizeof(note
->setter
)-1);
887 memcpy(note
->note
, text
, len
);
889 dict_insert(channel
->notes
, type
->name
, note
);
895 chanserv_free_note(void *data
)
897 struct note
*note
= data
;
899 chanserv_deref_note_type(note
->type
);
900 assert(note
->type
->refs
> 0); /* must use delnote to remove the type */
904 static MODCMD_FUNC(cmd_createnote
) {
905 struct note_type
*ntype
;
906 unsigned int arg
= 1, existed
= 0, max_length
;
908 if((ntype
= dict_find(note_types
, argv
[1], NULL
)))
911 ntype
= chanserv_create_note_type(argv
[arg
]);
912 if(!irccasecmp(argv
[++arg
], "privileged"))
915 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
916 ntype
->set_access
.min_opserv
= strtoul(argv
[arg
], NULL
, 0);
918 else if(!irccasecmp(argv
[arg
], "channel"))
920 unsigned short ulvl
= user_level_from_name(argv
[++arg
], UL_OWNER
);
923 reply("CSMSG_INVALID_ACCESS", argv
[arg
]);
926 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
927 ntype
->set_access
.min_ulevel
= ulvl
;
929 else if(!irccasecmp(argv
[arg
], "setter"))
931 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
935 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
939 if(!irccasecmp(argv
[++arg
], "privileged"))
940 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
941 else if(!irccasecmp(argv
[arg
], "channel_users"))
942 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
943 else if(!irccasecmp(argv
[arg
], "all"))
944 ntype
->visible_type
= NOTE_VIS_ALL
;
946 reply("CSMSG_BAD_NOTE_ACCESS", argv
[arg
]);
950 if((arg
+1) >= argc
) {
951 reply("MSG_MISSING_PARAMS", argv
[0]);
954 max_length
= strtoul(argv
[++arg
], NULL
, 0);
955 if(max_length
< 20 || max_length
> 450)
957 reply("CSMSG_BAD_MAX_LENGTH", argv
[arg
]);
960 if(existed
&& (max_length
< ntype
->max_length
))
962 ntype
->max_length
= max_length
;
963 chanserv_truncate_notes(ntype
);
965 ntype
->max_length
= max_length
;
968 reply("CSMSG_NOTE_MODIFIED", ntype
->name
);
970 reply("CSMSG_NOTE_CREATED", ntype
->name
);
975 dict_remove(note_types
, ntype
->name
);
979 static MODCMD_FUNC(cmd_removenote
) {
980 struct note_type
*ntype
;
983 ntype
= dict_find(note_types
, argv
[1], NULL
);
984 force
= (argc
> 2) && !irccasecmp(argv
[2], "force");
987 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
994 reply("CSMSG_NOTE_TYPE_USED", ntype
->name
);
997 chanserv_flush_note_type(ntype
);
999 dict_remove(note_types
, argv
[1]);
1000 reply("CSMSG_NOTE_DELETED", argv
[1]);
1005 mode_lock_violated(const struct mod_chanmode
*orig
, const struct mod_chanmode
*change
)
1009 if(orig
->modes_set
& change
->modes_clear
)
1011 if(orig
->modes_clear
& change
->modes_set
)
1013 if((orig
->modes_set
& MODE_KEY
) && (change
->modes_set
& MODE_KEY
)
1014 && strcmp(orig
->new_key
, change
->new_key
))
1016 if((orig
->modes_set
& MODE_LIMIT
) && (change
->modes_set
& MODE_LIMIT
)
1017 && (orig
->new_limit
!= change
->new_limit
))
1022 static char max_length_text
[MAXLEN
+1][16];
1024 static struct helpfile_expansion
1025 chanserv_expand_variable(const char *variable
)
1027 struct helpfile_expansion exp
;
1029 if(!irccasecmp(variable
, "notes"))
1032 exp
.type
= HF_TABLE
;
1033 exp
.value
.table
.length
= 1;
1034 exp
.value
.table
.width
= 3;
1035 exp
.value
.table
.flags
= 0;
1036 exp
.value
.table
.contents
= calloc(dict_size(note_types
)+1, sizeof(char**));
1037 exp
.value
.table
.contents
[0] = calloc(exp
.value
.table
.width
, sizeof(char*));
1038 exp
.value
.table
.contents
[0][0] = "Note Type";
1039 exp
.value
.table
.contents
[0][1] = "Visibility";
1040 exp
.value
.table
.contents
[0][2] = "Max Length";
1041 for(it
=dict_first(note_types
); it
; it
=iter_next(it
))
1043 struct note_type
*ntype
= iter_data(it
);
1046 if(!note_type_visible_to_user(NULL
, ntype
, message_dest
)) continue;
1047 row
= exp
.value
.table
.length
++;
1048 exp
.value
.table
.contents
[row
] = calloc(exp
.value
.table
.width
, sizeof(char*));
1049 exp
.value
.table
.contents
[row
][0] = ntype
->name
;
1050 exp
.value
.table
.contents
[row
][1] = (ntype
->visible_type
== NOTE_VIS_ALL
) ? "all" :
1051 (ntype
->visible_type
== NOTE_VIS_CHANNEL_USERS
) ? "chan users" :
1053 if(!max_length_text
[ntype
->max_length
][0])
1054 snprintf(max_length_text
[ntype
->max_length
], sizeof(max_length_text
[ntype
->max_length
]), "%u", ntype
->max_length
);
1055 exp
.value
.table
.contents
[row
][2] = max_length_text
[ntype
->max_length
];
1060 exp
.type
= HF_STRING
;
1061 exp
.value
.str
= NULL
;
1065 static struct chanData
*
1066 register_channel(struct chanNode
*cNode
, char *registrar
)
1068 struct chanData
*channel
;
1069 enum levelOption lvlOpt
;
1070 enum charOption chOpt
;
1072 channel
= calloc(1, sizeof(struct chanData
));
1074 channel
->notes
= dict_new();
1075 dict_set_free_data(channel
->notes
, chanserv_free_note
);
1077 channel
->registrar
= strdup(registrar
);
1078 channel
->registered
= now
;
1079 channel
->visited
= now
;
1080 channel
->limitAdjusted
= now
;
1081 channel
->flags
= CHANNEL_DEFAULT_FLAGS
;
1082 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
1083 channel
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
1084 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
1085 channel
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
1087 channel
->prev
= NULL
;
1088 channel
->next
= channelList
;
1091 channelList
->prev
= channel
;
1092 channelList
= channel
;
1093 registered_channels
++;
1095 channel
->channel
= cNode
;
1097 cNode
->channel_info
= channel
;
1102 static struct userData
*
1103 add_channel_user(struct chanData
*channel
, struct handle_info
*handle
, unsigned short access
, time_t seen
, const char *info
)
1105 struct userData
*ud
;
1107 if(access
> UL_OWNER
)
1110 ud
= calloc(1, sizeof(*ud
));
1111 ud
->channel
= channel
;
1112 ud
->handle
= handle
;
1114 ud
->access
= access
;
1115 ud
->info
= info
? strdup(info
) : NULL
;
1118 ud
->next
= channel
->users
;
1120 channel
->users
->prev
= ud
;
1121 channel
->users
= ud
;
1123 channel
->userCount
++;
1127 ud
->u_next
= ud
->handle
->channels
;
1129 ud
->u_next
->u_prev
= ud
;
1130 ud
->handle
->channels
= ud
;
1135 static void unregister_channel(struct chanData
*channel
, const char *reason
);
1138 del_channel_user(struct userData
*user
, int do_gc
)
1140 struct chanData
*channel
= user
->channel
;
1142 channel
->userCount
--;
1146 user
->prev
->next
= user
->next
;
1148 channel
->users
= user
->next
;
1150 user
->next
->prev
= user
->prev
;
1153 user
->u_prev
->u_next
= user
->u_next
;
1155 user
->handle
->channels
= user
->u_next
;
1157 user
->u_next
->u_prev
= user
->u_prev
;
1161 if(do_gc
&& !channel
->users
&& !IsProtected(channel
))
1162 unregister_channel(channel
, "lost all users.");
1165 static void expire_ban(void *data
);
1167 static struct banData
*
1168 add_channel_ban(struct chanData
*channel
, const char *mask
, char *owner
, time_t set
, time_t triggered
, time_t expires
, char *reason
)
1171 unsigned int ii
, l1
, l2
;
1176 bd
= malloc(sizeof(struct banData
));
1178 bd
->channel
= channel
;
1180 bd
->triggered
= triggered
;
1181 bd
->expires
= expires
;
1183 for(ii
= 0; ii
< chanserv_conf
.old_ban_names
->used
; ++ii
)
1185 extern const char *hidden_host_suffix
;
1186 const char *old_name
= chanserv_conf
.old_ban_names
->list
[ii
];
1190 l2
= strlen(old_name
);
1193 if(irccasecmp(mask
+ l1
- l2
, old_name
))
1195 new_mask
= alloca(MAXLEN
);
1196 sprintf(new_mask
, "%.*s%s", l1
-l2
, mask
, hidden_host_suffix
);
1199 safestrncpy(bd
->mask
, mask
, sizeof(bd
->mask
));
1201 safestrncpy(bd
->owner
, owner
, sizeof(bd
->owner
));
1202 bd
->reason
= reason
? strdup(reason
) : NULL
;
1205 timeq_add(expires
, expire_ban
, bd
);
1208 bd
->next
= channel
->bans
;
1210 channel
->bans
->prev
= bd
;
1212 channel
->banCount
++;
1219 del_channel_ban(struct banData
*ban
)
1221 ban
->channel
->banCount
--;
1225 ban
->prev
->next
= ban
->next
;
1227 ban
->channel
->bans
= ban
->next
;
1230 ban
->next
->prev
= ban
->prev
;
1233 timeq_del(0, expire_ban
, ban
, TIMEQ_IGNORE_WHEN
);
1242 expire_ban(void *data
)
1244 struct banData
*bd
= data
;
1245 if(!IsSuspended(bd
->channel
))
1247 struct banList bans
;
1248 struct mod_chanmode change
;
1250 bans
= bd
->channel
->channel
->banlist
;
1251 mod_chanmode_init(&change
);
1252 for(ii
=0; ii
<bans
.used
; ii
++)
1254 if(!strcmp(bans
.list
[ii
]->ban
, bd
->mask
))
1257 change
.args
[0].mode
= MODE_REMOVE
|MODE_BAN
;
1258 change
.args
[0].hostmask
= bd
->mask
;
1259 mod_chanmode_announce(chanserv
, bd
->channel
->channel
, &change
);
1265 del_channel_ban(bd
);
1268 static void chanserv_expire_suspension(void *data
);
1271 unregister_channel(struct chanData
*channel
, const char *reason
)
1273 struct mod_chanmode change
;
1274 char msgbuf
[MAXLEN
];
1276 /* After channel unregistration, the following must be cleaned
1278 - Channel information.
1281 - Channel suspension data.
1282 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1288 timeq_del(0, NULL
, channel
, TIMEQ_IGNORE_FUNC
| TIMEQ_IGNORE_WHEN
);
1290 mod_chanmode_init(&change
);
1291 change
.modes_clear
|= MODE_REGISTERED
;
1292 mod_chanmode_announce(chanserv
, channel
->channel
, &change
);
1294 while(channel
->users
)
1295 del_channel_user(channel
->users
, 0);
1297 while(channel
->bans
)
1298 del_channel_ban(channel
->bans
);
1300 free(channel
->topic
);
1301 free(channel
->registrar
);
1302 free(channel
->greeting
);
1303 free(channel
->user_greeting
);
1304 free(channel
->topic_mask
);
1307 channel
->prev
->next
= channel
->next
;
1309 channelList
= channel
->next
;
1312 channel
->next
->prev
= channel
->prev
;
1314 if(channel
->suspended
)
1316 struct chanNode
*cNode
= channel
->channel
;
1317 struct suspended
*suspended
, *next_suspended
;
1319 for(suspended
= channel
->suspended
; suspended
; suspended
= next_suspended
)
1321 next_suspended
= suspended
->previous
;
1322 free(suspended
->suspender
);
1323 free(suspended
->reason
);
1324 if(suspended
->expires
)
1325 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
1330 cNode
->channel_info
= NULL
;
1332 channel
->channel
->channel_info
= NULL
;
1334 dict_delete(channel
->notes
);
1335 sprintf(msgbuf
, "%s %s", channel
->channel
->name
, reason
);
1336 if(!IsSuspended(channel
))
1337 DelChannelUser(chanserv
, channel
->channel
, msgbuf
, 0);
1338 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, msgbuf
);
1339 UnlockChannel(channel
->channel
);
1341 registered_channels
--;
1345 expire_channels(UNUSED_ARG(void *data
))
1347 struct chanData
*channel
, *next
;
1348 struct userData
*user
;
1349 char delay
[INTERVALLEN
], reason
[INTERVALLEN
+ 64];
1351 intervalString(delay
, chanserv_conf
.channel_expire_delay
, NULL
);
1352 sprintf(reason
, "Channel registration automatically expired after %s of disuse.", delay
);
1354 for(channel
= channelList
; channel
; channel
= next
)
1356 next
= channel
->next
;
1358 /* See if the channel can be expired. */
1359 if(((now
- channel
->visited
) <= chanserv_conf
.channel_expire_delay
)
1360 || IsProtected(channel
))
1363 /* Make sure there are no high-ranking users still in the channel. */
1364 for(user
=channel
->users
; user
; user
=user
->next
)
1365 if(user
->present
&& (user
->access
>= UL_PRESENT
))
1370 /* Unregister the channel */
1371 log_module(CS_LOG
, LOG_INFO
, "(%s) Channel registration expired.", channel
->channel
->name
);
1372 unregister_channel(channel
, "registration expired.");
1375 if(chanserv_conf
.channel_expire_frequency
)
1376 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
1380 protect_user(const struct userNode
*victim
, const struct userNode
*aggressor
, struct chanData
*channel
)
1382 char protect
= channel
->chOpts
[chProtect
];
1383 struct userData
*cs_victim
, *cs_aggressor
;
1385 /* Don't protect if no one is to be protected, someone is attacking
1386 himself, or if the aggressor is an IRC Operator. */
1387 if(protect
== 'n' || victim
== aggressor
|| IsOper(aggressor
))
1390 /* Don't protect if the victim isn't authenticated (because they
1391 can't be a channel user), unless we are to protect non-users
1393 cs_victim
= GetChannelAccess(channel
, victim
->handle_info
);
1394 if(protect
!= 'a' && !cs_victim
)
1397 /* Protect if the aggressor isn't a user because at this point,
1398 the aggressor can only be less than or equal to the victim. */
1399 cs_aggressor
= GetChannelAccess(channel
, aggressor
->handle_info
);
1403 /* If the aggressor was a user, then the victim can't be helped. */
1410 if(cs_victim
->access
> cs_aggressor
->access
)
1415 if(cs_victim
->access
>= cs_aggressor
->access
)
1424 validate_op(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1426 struct chanData
*cData
= channel
->channel_info
;
1427 struct userData
*cs_victim
;
1429 if((!(cs_victim
= GetChannelUser(cData
, victim
->handle_info
))
1430 || (cs_victim
->access
< cData
->lvlOpts
[lvlGiveOps
]))
1431 && !check_user_level(channel
, user
, lvlEnfOps
, 0, 0))
1433 send_message(user
, chanserv
, "CSMSG_OPBY_LOCKED");
1441 validate_deop(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
)
1443 if(IsService(victim
))
1445 send_message(user
, chanserv
, "MSG_SERVICE_IMMUNE", victim
->nick
);
1449 if(protect_user(victim
, user
, channel
->channel_info
))
1451 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
1458 static struct do_not_register
*
1459 chanserv_add_dnr(const char *chan_name
, const char *setter
, const char *reason
)
1461 struct do_not_register
*dnr
= calloc(1, sizeof(*dnr
)+strlen(reason
));
1462 safestrncpy(dnr
->chan_name
, chan_name
, sizeof(dnr
->chan_name
));
1463 safestrncpy(dnr
->setter
, setter
, sizeof(dnr
->setter
));
1464 strcpy(dnr
->reason
, reason
);
1466 if(dnr
->chan_name
[0] == '*')
1467 dict_insert(handle_dnrs
, dnr
->chan_name
+1, dnr
);
1468 else if(strpbrk(dnr
->chan_name
, "*?"))
1469 dict_insert(mask_dnrs
, dnr
->chan_name
, dnr
);
1471 dict_insert(plain_dnrs
, dnr
->chan_name
, dnr
);
1475 static struct dnrList
1476 chanserv_find_dnrs(const char *chan_name
, struct handle_info
*handle
)
1478 struct dnrList list
;
1480 struct do_not_register
*dnr
;
1482 dnrList_init(&list
);
1483 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1484 dnrList_append(&list
, dnr
);
1485 if(chan_name
&& (dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1486 dnrList_append(&list
, dnr
);
1488 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1489 if(match_ircglob(chan_name
, iter_key(it
)))
1490 dnrList_append(&list
, iter_data(it
));
1495 chanserv_show_dnrs(struct userNode
*user
, struct svccmd
*cmd
, const char *chan_name
, struct handle_info
*handle
)
1497 struct dnrList list
;
1498 struct do_not_register
*dnr
;
1500 char buf
[INTERVALLEN
];
1502 list
= chanserv_find_dnrs(chan_name
, handle
);
1503 for(ii
= 0; (ii
< list
.used
) && (ii
< 10); ++ii
)
1505 dnr
= list
.list
[ii
];
1508 strftime(buf
, sizeof(buf
), "%Y %b %d", localtime(&dnr
->set
));
1509 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, buf
, dnr
->setter
, dnr
->reason
);
1512 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1515 reply("CSMSG_MORE_DNRS", list
.used
- ii
);
1520 struct do_not_register
*
1521 chanserv_is_dnr(const char *chan_name
, struct handle_info
*handle
)
1523 struct do_not_register
*dnr
;
1526 if(handle
&& (dnr
= dict_find(handle_dnrs
, handle
->handle
, NULL
)))
1530 if((dnr
= dict_find(plain_dnrs
, chan_name
, NULL
)))
1532 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1533 if(match_ircglob(chan_name
, iter_key(it
)))
1534 return iter_data(it
);
1539 static CHANSERV_FUNC(cmd_noregister
)
1542 struct do_not_register
*dnr
;
1543 char buf
[INTERVALLEN
];
1544 unsigned int matches
;
1550 reply("CSMSG_DNR_SEARCH_RESULTS");
1552 for(it
= dict_first(handle_dnrs
); it
; it
= iter_next(it
))
1554 dnr
= iter_data(it
);
1556 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1558 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1561 for(it
= dict_first(plain_dnrs
); it
; it
= iter_next(it
))
1563 dnr
= iter_data(it
);
1565 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1567 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1570 for(it
= dict_first(mask_dnrs
); it
; it
= iter_next(it
))
1572 dnr
= iter_data(it
);
1574 reply("CSMSG_DNR_INFO_SET", dnr
->chan_name
, intervalString(buf
, now
- dnr
->set
, user
->handle_info
), dnr
->setter
, dnr
->reason
);
1576 reply("CSMSG_DNR_INFO", dnr
->chan_name
, dnr
->setter
, dnr
->reason
);
1581 reply("MSG_MATCH_COUNT", matches
);
1583 reply("MSG_NO_MATCHES");
1589 if(!IsChannelName(target
) && (*target
!= '*'))
1591 reply("CSMSG_NOT_DNR", target
);
1597 const char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
1598 if((*target
== '*') && !get_handle_info(target
+ 1))
1600 reply("MSG_HANDLE_UNKNOWN", target
+ 1);
1603 chanserv_add_dnr(target
, user
->handle_info
->handle
, reason
);
1604 reply("CSMSG_NOREGISTER_CHANNEL", target
);
1608 reply("CSMSG_DNR_SEARCH_RESULTS");
1610 matches
= chanserv_show_dnrs(user
, cmd
, NULL
, get_handle_info(target
+ 1));
1612 matches
= chanserv_show_dnrs(user
, cmd
, target
, NULL
);
1614 reply("MSG_NO_MATCHES");
1618 static CHANSERV_FUNC(cmd_allowregister
)
1620 const char *chan_name
= argv
[1];
1622 if((chan_name
[0] == '*') && dict_find(handle_dnrs
, chan_name
+1, NULL
))
1624 dict_remove(handle_dnrs
, chan_name
+1);
1625 reply("CSMSG_DNR_REMOVED", chan_name
);
1627 else if(dict_find(plain_dnrs
, chan_name
, NULL
))
1629 dict_remove(plain_dnrs
, chan_name
);
1630 reply("CSMSG_DNR_REMOVED", chan_name
);
1632 else if(dict_find(mask_dnrs
, chan_name
, NULL
))
1634 dict_remove(mask_dnrs
, chan_name
);
1635 reply("CSMSG_DNR_REMOVED", chan_name
);
1639 reply("CSMSG_NO_SUCH_DNR", chan_name
);
1646 chanserv_get_owned_count(struct handle_info
*hi
)
1648 struct userData
*cList
;
1651 for(owned
=0, cList
=hi
->channels
; cList
; cList
=cList
->u_next
)
1652 if(cList
->access
== UL_OWNER
)
1657 static CHANSERV_FUNC(cmd_register
)
1659 struct mod_chanmode
*change
;
1660 struct handle_info
*handle
;
1661 struct chanData
*cData
;
1662 struct modeNode
*mn
;
1663 char reason
[MAXLEN
];
1665 unsigned int new_channel
, force
=0;
1666 struct do_not_register
*dnr
;
1670 if(channel
->channel_info
)
1672 reply("CSMSG_ALREADY_REGGED", channel
->name
);
1676 if(channel
->bad_channel
)
1678 reply("CSMSG_ILLEGAL_CHANNEL", channel
->name
);
1682 if(!IsHelping(user
) && (!(mn
= GetUserMode(channel
, user
)) || !(mn
->modes
& MODE_CHANOP
)))
1684 reply("CSMSG_MUST_BE_OPPED", channel
->name
);
1689 chan_name
= channel
->name
;
1693 if((argc
< 2) || !IsChannelName(argv
[1]))
1695 reply("MSG_NOT_CHANNEL_NAME");
1699 if(opserv_bad_channel(argv
[1]))
1701 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1706 chan_name
= argv
[1];
1709 if(argc
>= (new_channel
+2))
1711 if(!IsHelping(user
))
1713 reply("CSMSG_PROXY_FORBIDDEN");
1717 if(!(handle
= modcmd_get_handle_info(user
, argv
[new_channel
+1])))
1719 force
= (argc
> (new_channel
+2)) && !irccasecmp(argv
[new_channel
+2], "force");
1720 dnr
= chanserv_is_dnr(chan_name
, handle
);
1724 handle
= user
->handle_info
;
1725 dnr
= chanserv_is_dnr(chan_name
, handle
);
1729 if(!IsHelping(user
))
1730 reply("CSMSG_DNR_CHANNEL", chan_name
);
1732 chanserv_show_dnrs(user
, cmd
, chan_name
, handle
);
1736 if((chanserv_get_owned_count(handle
) >= chanserv_conf
.max_owned
) && !force
)
1738 reply("CSMSG_OWN_TOO_MANY", handle
->handle
, chanserv_conf
.max_owned
);
1743 channel
= AddChannel(argv
[1], now
, NULL
, NULL
);
1745 cData
= register_channel(channel
, user
->handle_info
->handle
);
1746 scan_user_presence(add_channel_user(cData
, handle
, UL_OWNER
, 0, NULL
), NULL
);
1747 cData
->modes
= chanserv_conf
.default_modes
;
1748 change
= mod_chanmode_dup(&cData
->modes
, 1);
1749 change
->args
[change
->argc
].mode
= MODE_CHANOP
;
1750 change
->args
[change
->argc
].member
= AddChannelUser(chanserv
, channel
);
1752 mod_chanmode_announce(chanserv
, channel
, change
);
1753 mod_chanmode_free(change
);
1755 /* Initialize the channel's max user record. */
1756 cData
->max
= channel
->members
.used
;
1758 if(handle
!= user
->handle_info
)
1759 reply("CSMSG_PROXY_SUCCESS", handle
->handle
, channel
->name
);
1761 reply("CSMSG_REG_SUCCESS", channel
->name
);
1763 sprintf(reason
, "%s registered to %s by %s.", channel
->name
, handle
->handle
, user
->handle_info
->handle
);
1764 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1769 make_confirmation_string(struct userData
*uData
)
1771 static char strbuf
[16];
1776 for(src
= uData
->handle
->handle
; *src
; )
1777 accum
= accum
* 31 + toupper(*src
++);
1779 for(src
= uData
->channel
->channel
->name
; *src
; )
1780 accum
= accum
* 31 + toupper(*src
++);
1781 sprintf(strbuf
, "%08x", accum
);
1785 static CHANSERV_FUNC(cmd_unregister
)
1788 char reason
[MAXLEN
];
1789 struct chanData
*cData
;
1790 struct userData
*uData
;
1792 cData
= channel
->channel_info
;
1795 reply("CSMSG_NOT_REGISTERED", channel
->name
);
1799 uData
= GetChannelUser(cData
, user
->handle_info
);
1800 if(!uData
|| (uData
->access
< UL_OWNER
))
1802 reply("CSMSG_NO_ACCESS");
1806 if(IsProtected(cData
))
1808 reply("CSMSG_UNREG_NODELETE", channel
->name
);
1812 if(!IsHelping(user
))
1814 const char *confirm_string
;
1815 if(IsSuspended(cData
))
1817 reply("CSMSG_CHAN_SUSPENDED", channel
->name
, cData
->suspended
->reason
);
1820 confirm_string
= make_confirmation_string(uData
);
1821 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
1823 reply("CSMSG_CONFIRM_UNREG", confirm_string
);
1828 sprintf(reason
, "unregistered by %s.", user
->handle_info
->handle
);
1829 name
= strdup(channel
->name
);
1830 unregister_channel(cData
, reason
);
1831 reply("CSMSG_UNREG_SUCCESS", name
);
1836 static CHANSERV_FUNC(cmd_move
)
1838 struct chanNode
*target
;
1839 struct modeNode
*mn
;
1840 struct userData
*uData
;
1841 char reason
[MAXLEN
];
1842 struct do_not_register
*dnr
;
1846 if(IsProtected(channel
->channel_info
))
1848 reply("CSMSG_MOVE_NODELETE", channel
->name
);
1852 if(!IsChannelName(argv
[1]))
1854 reply("MSG_NOT_CHANNEL_NAME");
1858 if(opserv_bad_channel(argv
[1]))
1860 reply("CSMSG_ILLEGAL_CHANNEL", argv
[1]);
1864 if(!IsHelping(user
) || (argc
< 3) || irccasecmp(argv
[2], "force"))
1866 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
1868 if((uData
->access
== UL_OWNER
) && (dnr
= chanserv_is_dnr(argv
[1], uData
->handle
)))
1870 if(!IsHelping(user
))
1871 reply("CSMSG_DNR_CHANNEL_MOVE", argv
[1]);
1873 chanserv_show_dnrs(user
, cmd
, argv
[1], uData
->handle
);
1879 if(!(target
= GetChannel(argv
[1])))
1881 target
= AddChannel(argv
[1], now
, NULL
, NULL
);
1882 if(!IsSuspended(channel
->channel_info
))
1883 AddChannelUser(chanserv
, target
);
1885 else if(target
->channel_info
)
1887 reply("CSMSG_ALREADY_REGGED", target
->name
);
1890 else if((!(mn
= GetUserMode(target
, user
)) || !(mn
->modes
&& MODE_CHANOP
))
1891 && !IsHelping(user
))
1893 reply("CSMSG_MUST_BE_OPPED", target
->name
);
1896 else if(!IsSuspended(channel
->channel_info
))
1898 struct mod_chanmode change
;
1899 mod_chanmode_init(&change
);
1901 change
.args
[0].mode
= MODE_CHANOP
;
1902 change
.args
[0].member
= AddChannelUser(chanserv
, target
);
1903 mod_chanmode_announce(chanserv
, target
, &change
);
1906 /* Move the channel_info to the target channel; it
1907 shouldn't be necessary to clear timeq callbacks
1908 for the old channel. */
1909 target
->channel_info
= channel
->channel_info
;
1910 target
->channel_info
->channel
= target
;
1911 channel
->channel_info
= NULL
;
1913 reply("CSMSG_MOVE_SUCCESS", target
->name
);
1915 sprintf(reason
, "%s moved to %s by %s.", channel
->name
, target
->name
, user
->handle_info
->handle
);
1916 if(!IsSuspended(target
->channel_info
))
1918 char reason2
[MAXLEN
];
1919 sprintf(reason2
, "Channel moved to %s by %s.", target
->name
, user
->handle_info
->handle
);
1920 DelChannelUser(chanserv
, channel
, reason2
, 0);
1922 UnlockChannel(channel
);
1923 LockChannel(target
);
1924 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
1929 merge_users(struct chanData
*source
, struct chanData
*target
)
1931 struct userData
*suData
, *tuData
, *next
;
1937 /* Insert the source's users into the scratch area. */
1938 for(suData
= source
->users
; suData
; suData
= suData
->next
)
1939 dict_insert(merge
, suData
->handle
->handle
, suData
);
1941 /* Iterate through the target's users, looking for
1942 users common to both channels. The lower access is
1943 removed from either the scratch area or target user
1945 for(tuData
= target
->users
; tuData
; tuData
= next
)
1947 struct userData
*choice
;
1949 next
= tuData
->next
;
1951 /* If a source user exists with the same handle as a target
1952 channel's user, resolve the conflict by removing one. */
1953 suData
= dict_find(merge
, tuData
->handle
->handle
, NULL
);
1957 /* Pick the data we want to keep. */
1958 /* If the access is the same, use the later seen time. */
1959 if(suData
->access
== tuData
->access
)
1960 choice
= (suData
->seen
> tuData
->seen
) ? suData
: tuData
;
1961 else /* Otherwise, keep the higher access level. */
1962 choice
= (suData
->access
> tuData
->access
) ? suData
: tuData
;
1964 /* Remove the user that wasn't picked. */
1965 if(choice
== tuData
)
1967 dict_remove(merge
, suData
->handle
->handle
);
1968 del_channel_user(suData
, 0);
1971 del_channel_user(tuData
, 0);
1974 /* Move the remaining users to the target channel. */
1975 for(it
= dict_first(merge
); it
; it
= iter_next(it
))
1977 suData
= iter_data(it
);
1979 /* Insert the user into the target channel's linked list. */
1980 suData
->prev
= NULL
;
1981 suData
->next
= target
->users
;
1982 suData
->channel
= target
;
1985 target
->users
->prev
= suData
;
1986 target
->users
= suData
;
1988 /* Update the user counts for the target channel; the
1989 source counts are left alone. */
1990 target
->userCount
++;
1993 /* Possible to assert (source->users == NULL) here. */
1994 source
->users
= NULL
;
1999 merge_bans(struct chanData
*source
, struct chanData
*target
)
2001 struct banData
*sbData
, *tbData
, *sNext
, *tNext
, *tFront
;
2003 /* Hold on to the original head of the target ban list
2004 to avoid comparing source bans with source bans. */
2005 tFront
= target
->bans
;
2007 /* Perform a totally expensive O(n*m) merge, ick. */
2008 for(sbData
= source
->bans
; sbData
; sbData
= sNext
)
2010 /* Flag to track whether the ban's been moved
2011 to the destination yet. */
2014 /* Possible to assert (sbData->prev == NULL) here. */
2015 sNext
= sbData
->next
;
2017 for(tbData
= tFront
; tbData
; tbData
= tNext
)
2019 tNext
= tbData
->next
;
2021 /* Perform two comparisons between each source
2022 and target ban, conflicts are resolved by
2023 keeping the broader ban and copying the later
2024 expiration and triggered time. */
2025 if(match_ircglobs(tbData
->mask
, sbData
->mask
))
2027 /* There is a broader ban in the target channel that
2028 overrides one in the source channel; remove the
2029 source ban and break. */
2030 if(sbData
->expires
> tbData
->expires
)
2031 tbData
->expires
= sbData
->expires
;
2032 if(sbData
->triggered
> tbData
->triggered
)
2033 tbData
->triggered
= sbData
->triggered
;
2034 del_channel_ban(sbData
);
2037 else if(match_ircglobs(sbData
->mask
, tbData
->mask
))
2039 /* There is a broader ban in the source channel that
2040 overrides one in the target channel; remove the
2041 target ban, fall through and move the source over. */
2042 if(tbData
->expires
> sbData
->expires
)
2043 sbData
->expires
= tbData
->expires
;
2044 if(tbData
->triggered
> sbData
->triggered
)
2045 sbData
->triggered
= tbData
->triggered
;
2046 if(tbData
== tFront
)
2048 del_channel_ban(tbData
);
2051 /* Source bans can override multiple target bans, so
2052 we allow a source to run through this loop multiple
2053 times, but we can only move it once. */
2058 /* Remove the source ban from the source ban list. */
2060 sbData
->next
->prev
= sbData
->prev
;
2062 /* Modify the source ban's associated channel. */
2063 sbData
->channel
= target
;
2065 /* Insert the ban into the target channel's linked list. */
2066 sbData
->prev
= NULL
;
2067 sbData
->next
= target
->bans
;
2070 target
->bans
->prev
= sbData
;
2071 target
->bans
= sbData
;
2073 /* Update the user counts for the target channel. */
2078 /* Possible to assert (source->bans == NULL) here. */
2079 source
->bans
= NULL
;
2083 merge_data(struct chanData
*source
, struct chanData
*target
)
2085 if(source
->visited
> target
->visited
)
2086 target
->visited
= source
->visited
;
2090 merge_channel(struct chanData
*source
, struct chanData
*target
)
2092 merge_users(source
, target
);
2093 merge_bans(source
, target
);
2094 merge_data(source
, target
);
2097 static CHANSERV_FUNC(cmd_merge
)
2099 struct userData
*target_user
;
2100 struct chanNode
*target
;
2101 char reason
[MAXLEN
];
2105 /* Make sure the target channel exists and is registered to the user
2106 performing the command. */
2107 if(!(target
= GetChannel(argv
[1])))
2109 reply("MSG_INVALID_CHANNEL");
2113 if(!target
->channel_info
)
2115 reply("CSMSG_NOT_REGISTERED", target
->name
);
2119 if(IsProtected(channel
->channel_info
))
2121 reply("CSMSG_MERGE_NODELETE");
2125 if(IsSuspended(target
->channel_info
))
2127 reply("CSMSG_MERGE_SUSPENDED");
2131 if(channel
== target
)
2133 reply("CSMSG_MERGE_SELF");
2137 target_user
= GetChannelUser(target
->channel_info
, user
->handle_info
);
2138 if(!target_user
|| (target_user
->access
< UL_OWNER
))
2140 reply("CSMSG_MERGE_NOT_OWNER");
2144 /* Merge the channel structures and associated data. */
2145 merge_channel(channel
->channel_info
, target
->channel_info
);
2146 sprintf(reason
, "merged into %s by %s.", target
->name
, user
->handle_info
->handle
);
2147 unregister_channel(channel
->channel_info
, reason
);
2148 reply("CSMSG_MERGE_SUCCESS", target
->name
);
2152 static CHANSERV_FUNC(cmd_opchan
)
2154 struct mod_chanmode change
;
2155 if(!IsHelping(user
) && !channel
->channel_info
->may_opchan
)
2157 reply("CSMSG_ALREADY_OPCHANNED", channel
->name
);
2160 channel
->channel_info
->may_opchan
= 0;
2161 mod_chanmode_init(&change
);
2163 change
.args
[0].mode
= MODE_CHANOP
;
2164 change
.args
[0].member
= GetUserMode(channel
, chanserv
);
2165 mod_chanmode_announce(chanserv
, channel
, &change
);
2166 reply("CSMSG_OPCHAN_DONE", channel
->name
);
2170 static CHANSERV_FUNC(cmd_adduser
)
2172 struct userData
*actee
;
2173 struct userData
*actor
;
2174 struct handle_info
*handle
;
2175 unsigned short access
;
2179 if(channel
->channel_info
->userCount
>= chanserv_conf
.max_chan_users
)
2181 reply("CSMSG_MAXIMUM_USERS", chanserv_conf
.max_chan_users
);
2185 access
= user_level_from_name(argv
[2], UL_OWNER
);
2188 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2192 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2193 if(actor
->access
<= access
)
2195 reply("CSMSG_NO_BUMP_ACCESS");
2199 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2202 if((actee
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2204 reply("CSMSG_USER_EXISTS", handle
->handle
, channel
->name
, actee
->access
);
2208 actee
= add_channel_user(channel
->channel_info
, handle
, access
, 0, NULL
);
2209 scan_user_presence(actee
, NULL
);
2210 reply("CSMSG_ADDED_USER", handle
->handle
, channel
->name
, access
);
2214 static CHANSERV_FUNC(cmd_clvl
)
2216 struct handle_info
*handle
;
2217 struct userData
*victim
;
2218 struct userData
*actor
;
2219 unsigned short new_access
;
2220 int privileged
= IsHelping(user
) && ((user
->handle_info
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
2224 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2226 if(!(handle
= modcmd_get_handle_info(user
, argv
[1])))
2229 if(handle
== user
->handle_info
&& !privileged
)
2231 reply("CSMSG_NO_SELF_CLVL");
2235 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2237 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2241 if(actor
->access
<= victim
->access
&& !privileged
)
2243 reply("MSG_USER_OUTRANKED", handle
->handle
);
2247 new_access
= user_level_from_name(argv
[2], UL_OWNER
);
2251 reply("CSMSG_INVALID_ACCESS", argv
[2]);
2255 if(new_access
>= actor
->access
&& !privileged
)
2257 reply("CSMSG_NO_BUMP_ACCESS");
2261 victim
->access
= new_access
;
2262 reply("CSMSG_CHANGED_ACCESS", handle
->handle
, new_access
, channel
->name
);
2266 static CHANSERV_FUNC(cmd_deluser
)
2268 struct handle_info
*handle
;
2269 struct userData
*victim
;
2270 struct userData
*actor
;
2271 unsigned short access
;
2276 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2278 if(!(handle
= modcmd_get_handle_info(user
, argv
[argc
-1])))
2281 if(!(victim
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
2283 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
2289 access
= user_level_from_name(argv
[1], UL_OWNER
);
2292 reply("CSMSG_INVALID_ACCESS", argv
[1]);
2295 if(access
!= victim
->access
)
2297 reply("CSMSG_INCORRECT_ACCESS", handle
->handle
, victim
->access
, argv
[1]);
2303 access
= victim
->access
;
2306 if((actor
->access
<= victim
->access
) && !IsHelping(user
))
2308 reply("MSG_USER_OUTRANKED", victim
->handle
->handle
);
2312 chan_name
= strdup(channel
->name
);
2313 del_channel_user(victim
, 1);
2314 reply("CSMSG_DELETED_USER", handle
->handle
, access
, chan_name
);
2320 cmd_mdel_user(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, char *mask
, struct svccmd
*cmd
)
2322 struct userData
*actor
, *uData
, *next
;
2324 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2326 if(min_access
> max_access
)
2328 reply("CSMSG_BAD_RANGE", min_access
, max_access
);
2332 if((actor
->access
<= max_access
) && !IsHelping(user
))
2334 reply("CSMSG_NO_ACCESS");
2338 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2342 if((uData
->access
>= min_access
)
2343 && (uData
->access
<= max_access
)
2344 && match_ircglob(uData
->handle
->handle
, mask
))
2345 del_channel_user(uData
, 1);
2348 reply("CSMSG_DELETED_USERS", mask
, min_access
, max_access
, channel
->name
);
2352 static CHANSERV_FUNC(cmd_mdelowner
)
2354 return cmd_mdel_user(user
, channel
, UL_OWNER
, UL_OWNER
, argv
[1], cmd
);
2357 static CHANSERV_FUNC(cmd_mdelcoowner
)
2359 return cmd_mdel_user(user
, channel
, UL_COOWNER
, UL_COOWNER
, argv
[1], cmd
);
2362 static CHANSERV_FUNC(cmd_mdelmanager
)
2364 return cmd_mdel_user(user
, channel
, UL_MANAGER
, UL_MANAGER
, argv
[1], cmd
);
2367 static CHANSERV_FUNC(cmd_mdelop
)
2369 return cmd_mdel_user(user
, channel
, UL_OP
, UL_OP
, argv
[1], cmd
);
2372 static CHANSERV_FUNC(cmd_mdelpeon
)
2374 return cmd_mdel_user(user
, channel
, UL_PEON
, UL_PEON
, argv
[1], cmd
);
2378 cmd_trim_bans(struct userNode
*user
, struct chanNode
*channel
, unsigned long duration
)
2380 struct banData
*bData
, *next
;
2381 char interval
[INTERVALLEN
];
2386 limit
= now
- duration
;
2387 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2391 if((bData
->triggered
&& bData
->triggered
>= limit
) || (bData
->set
&& bData
->set
>= limit
))
2394 del_channel_ban(bData
);
2398 intervalString(interval
, duration
, user
->handle_info
);
2399 send_message(user
, chanserv
, "CSMSG_TRIMMED_BANS", count
, channel
->name
, interval
);
2404 cmd_trim_users(struct userNode
*user
, struct chanNode
*channel
, unsigned short min_access
, unsigned short max_access
, unsigned long duration
)
2406 struct userData
*actor
, *uData
, *next
;
2407 char interval
[INTERVALLEN
];
2411 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
2412 if(min_access
> max_access
)
2414 send_message(user
, chanserv
, "CSMSG_BAD_RANGE", min_access
, max_access
);
2418 if((actor
->access
<= max_access
) && !IsHelping(user
))
2420 send_message(user
, chanserv
, "CSMSG_NO_ACCESS");
2425 limit
= now
- duration
;
2426 for(uData
= channel
->channel_info
->users
; uData
; uData
= next
)
2430 if((uData
->seen
> limit
) || uData
->present
)
2433 if(((uData
->access
>= min_access
) && (uData
->access
<= max_access
))
2434 || (!max_access
&& (uData
->access
< actor
->access
)))
2436 del_channel_user(uData
, 1);
2444 max_access
= UL_OWNER
;
2446 send_message(user
, chanserv
, "CSMSG_TRIMMED_USERS", count
, min_access
, max_access
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2450 static CHANSERV_FUNC(cmd_trim
)
2452 unsigned long duration
;
2453 unsigned short min_level
, max_level
;
2457 duration
= ParseInterval(argv
[2]);
2460 reply("CSMSG_CANNOT_TRIM");
2464 if(!irccasecmp(argv
[1], "bans"))
2466 cmd_trim_bans(user
, channel
, duration
);
2469 else if(!irccasecmp(argv
[1], "users"))
2471 cmd_trim_users(user
, channel
, 0, 0, duration
);
2474 else if(parse_level_range(&min_level
, &max_level
, argv
[1]))
2476 cmd_trim_users(user
, channel
, min_level
, max_level
, duration
);
2479 else if((min_level
= user_level_from_name(argv
[1], UL_OWNER
)))
2481 cmd_trim_users(user
, channel
, min_level
, min_level
, duration
);
2486 reply("CSMSG_INVALID_TRIM", argv
[1]);
2491 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2492 to the user. cmd_all takes advantage of this. */
2493 static CHANSERV_FUNC(cmd_up
)
2495 struct mod_chanmode change
;
2496 struct userData
*uData
;
2499 mod_chanmode_init(&change
);
2501 change
.args
[0].member
= GetUserMode(channel
, user
);
2502 if(!change
.args
[0].member
)
2505 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2509 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
2513 reply("CSMSG_GODMODE_UP", argv
[0]);
2516 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveOps
])
2518 change
.args
[0].mode
= MODE_CHANOP
;
2519 errmsg
= "CSMSG_ALREADY_OPPED";
2521 else if(uData
->access
>= channel
->channel_info
->lvlOpts
[lvlGiveVoice
])
2523 change
.args
[0].mode
= MODE_VOICE
;
2524 errmsg
= "CSMSG_ALREADY_VOICED";
2529 reply("CSMSG_NO_ACCESS");
2532 change
.args
[0].mode
&= ~change
.args
[0].member
->modes
;
2533 if(!change
.args
[0].mode
)
2536 reply(errmsg
, channel
->name
);
2539 modcmd_chanmode_announce(&change
);
2543 static CHANSERV_FUNC(cmd_down
)
2545 struct mod_chanmode change
;
2547 mod_chanmode_init(&change
);
2549 change
.args
[0].member
= GetUserMode(channel
, user
);
2550 if(!change
.args
[0].member
)
2553 reply("MSG_CHANNEL_ABSENT", channel
->name
);
2557 if(!change
.args
[0].member
->modes
)
2560 reply("CSMSG_ALREADY_DOWN", channel
->name
);
2564 change
.args
[0].mode
= MODE_REMOVE
| change
.args
[0].member
->modes
;
2565 modcmd_chanmode_announce(&change
);
2569 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
)
2571 struct userData
*cList
;
2573 for(cList
= user
->handle_info
->channels
; cList
; cList
= cList
->u_next
)
2575 if(IsSuspended(cList
->channel
)
2576 || IsUserSuspended(cList
)
2577 || !GetUserMode(cList
->channel
->channel
, user
))
2580 mcmd(user
, cList
->channel
->channel
, 0, NULL
, cmd
);
2586 static CHANSERV_FUNC(cmd_upall
)
2588 return cmd_all(CSFUNC_ARGS
, cmd_up
);
2591 static CHANSERV_FUNC(cmd_downall
)
2593 return cmd_all(CSFUNC_ARGS
, cmd_down
);
2596 typedef int validate_func_t(struct userNode
*user
, struct chanNode
*channel
, struct userNode
*victim
);
2597 typedef void process_func_t(unsigned int num
, struct userNode
**newops
, struct chanNode
*channel
, struct userNode
*who
, int announce
);
2600 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
)
2602 unsigned int ii
, valid
;
2603 struct userNode
*victim
;
2604 struct mod_chanmode
*change
;
2606 change
= mod_chanmode_alloc(argc
- 1);
2608 for(ii
=valid
=0; ++ii
< argc
; )
2610 if(!(victim
= GetUserH(argv
[ii
])))
2612 change
->args
[valid
].mode
= mode
;
2613 change
->args
[valid
].member
= GetUserMode(channel
, victim
);
2614 if(!change
->args
[valid
].member
)
2616 if(validate
&& !validate(user
, channel
, victim
))
2621 change
->argc
= valid
;
2622 if(valid
< (argc
-1))
2623 reply("CSMSG_PROCESS_FAILED");
2626 modcmd_chanmode_announce(change
);
2627 reply(action
, channel
->name
);
2629 mod_chanmode_free(change
);
2633 static CHANSERV_FUNC(cmd_op
)
2635 return modify_users(CSFUNC_ARGS
, validate_op
, MODE_CHANOP
, "CSMSG_OPPED_USERS");
2638 static CHANSERV_FUNC(cmd_deop
)
2640 return modify_users(CSFUNC_ARGS
, validate_deop
, MODE_REMOVE
|MODE_CHANOP
, "CSMSG_DEOPPED_USERS");
2643 static CHANSERV_FUNC(cmd_voice
)
2645 return modify_users(CSFUNC_ARGS
, NULL
, MODE_VOICE
, "CSMSG_VOICED_USERS");
2648 static CHANSERV_FUNC(cmd_devoice
)
2650 return modify_users(CSFUNC_ARGS
, NULL
, MODE_REMOVE
|MODE_VOICE
, "CSMSG_DEVOICED_USERS");
2654 bad_channel_ban(struct chanNode
*channel
, struct userNode
*user
, const char *ban
, int *victimCount
, struct modeNode
**victims
)
2660 for(ii
=0; ii
<channel
->members
.used
; ii
++)
2662 struct modeNode
*mn
= channel
->members
.list
[ii
];
2664 if(IsService(mn
->user
))
2667 if(!user_matches_glob(mn
->user
, ban
, 1))
2670 if(protect_user(mn
->user
, user
, channel
->channel_info
))
2674 victims
[(*victimCount
)++] = mn
;
2680 eject_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
2682 struct userNode
*victim
;
2683 struct modeNode
**victims
;
2684 unsigned int offset
, n
, victimCount
, duration
= 0;
2685 char *reason
= "Bye.", *ban
, *name
;
2686 char interval
[INTERVALLEN
];
2688 offset
= (action
& ACTION_ADD_TIMED_BAN
) ? 3 : 2;
2689 REQUIRE_PARAMS(offset
);
2692 reason
= unsplit_string(argv
+ offset
, argc
- offset
, NULL
);
2693 if(strlen(reason
) > (TOPICLEN
- (NICKLEN
+ 3)))
2695 /* Truncate the reason to a length of TOPICLEN, as
2696 the ircd does; however, leave room for an ellipsis
2697 and the kicker's nick. */
2698 sprintf(reason
+ (TOPICLEN
- (NICKLEN
+ 6)), "...");
2702 if((victim
= GetUserH(argv
[1])))
2704 victims
= alloca(sizeof(victims
[0]));
2705 victims
[0] = GetUserMode(channel
, victim
);
2706 /* XXX: The comparison with ACTION_KICK is just because all
2707 * other actions can work on users outside the channel, and we
2708 * want to allow those (e.g. unbans) in that case. If we add
2709 * some other ejection action for in-channel users, change
2711 victimCount
= victims
[0] ? 1 : 0;
2713 if(IsService(victim
))
2715 reply("MSG_SERVICE_IMMUNE", victim
->nick
);
2719 if((action
== ACTION_KICK
) && !victimCount
)
2721 reply("MSG_CHANNEL_USER_ABSENT", victim
->nick
, channel
->name
);
2725 if(protect_user(victim
, user
, channel
->channel_info
))
2727 reply("CSMSG_USER_PROTECTED", victim
->nick
);
2731 ban
= generate_hostmask(victim
, GENMASK_STRICT_HOST
|GENMASK_ANY_IDENT
);
2732 name
= victim
->nick
;
2736 if(!is_ircmask(argv
[1]))
2738 reply("MSG_NICK_UNKNOWN", argv
[1]);
2742 victims
= alloca(sizeof(victims
[0]) * channel
->members
.used
);
2744 if(bad_channel_ban(channel
, user
, argv
[1], &victimCount
, victims
))
2746 reply("CSMSG_MASK_PROTECTED", argv
[1]);
2750 if((victimCount
> 4) && ((victimCount
* 3) > channel
->members
.used
) && !IsOper(user
))
2752 reply("CSMSG_LAME_MASK", argv
[1]);
2756 if((action
== ACTION_KICK
) && (victimCount
== 0))
2758 reply("CSMSG_NO_MATCHING_USERS", channel
->name
, argv
[1]);
2762 name
= ban
= strdup(argv
[1]);
2765 /* Truncate the ban in place if necessary; we must ensure
2766 that 'ban' is a valid ban mask before sanitizing it. */
2767 sanitize_ircmask(ban
);
2769 if(action
& ACTION_ADD_BAN
)
2771 struct banData
*bData
, *next
;
2773 if(channel
->channel_info
->banCount
>= chanserv_conf
.max_chan_bans
)
2775 reply("CSMSG_MAXIMUM_BANS", chanserv_conf
.max_chan_bans
);
2780 if(action
& ACTION_ADD_TIMED_BAN
)
2782 duration
= ParseInterval(argv
[2]);
2786 reply("CSMSG_DURATION_TOO_LOW");
2790 else if(duration
> (86400 * 365 * 2))
2792 reply("CSMSG_DURATION_TOO_HIGH");
2798 for(bData
= channel
->channel_info
->bans
; bData
; bData
= next
)
2800 if(match_ircglobs(bData
->mask
, ban
))
2802 int exact
= !irccasecmp(bData
->mask
, ban
);
2804 /* The ban is redundant; there is already a ban
2805 with the same effect in place. */
2809 free(bData
->reason
);
2810 bData
->reason
= strdup(reason
);
2811 safestrncpy(bData
->owner
, (user
->handle_info
? user
->handle_info
->handle
: user
->nick
), sizeof(bData
->owner
));
2813 reply("CSMSG_REASON_CHANGE", ban
);
2817 if(exact
&& bData
->expires
)
2821 /* If the ban matches an existing one exactly,
2822 extend the expiration time if the provided
2823 duration is longer. */
2824 if(duration
&& ((time_t)(now
+ duration
) > bData
->expires
))
2826 bData
->expires
= now
+ duration
;
2837 /* Delete the expiration timeq entry and
2838 requeue if necessary. */
2839 timeq_del(0, expire_ban
, bData
, TIMEQ_IGNORE_WHEN
);
2842 timeq_add(bData
->expires
, expire_ban
, bData
);
2846 /* automated kickban */
2849 reply("CSMSG_BAN_EXTENDED", ban
, intervalString(interval
, duration
, user
->handle_info
));
2851 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2857 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2864 if(match_ircglobs(ban
, bData
->mask
))
2866 /* The ban we are adding makes previously existing
2867 bans redundant; silently remove them. */
2868 del_channel_ban(bData
);
2872 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
);
2874 name
= ban
= strdup(bData
->mask
);
2878 for(n
= 0; n
< chanserv_conf
.old_ban_names
->used
; ++n
)
2880 extern const char *hidden_host_suffix
;
2881 const char *old_name
= chanserv_conf
.old_ban_names
->list
[n
];
2883 unsigned int l1
, l2
;
2886 l2
= strlen(old_name
);
2889 if(irccasecmp(ban
+ l1
- l2
, old_name
))
2891 new_mask
= malloc(MAXLEN
);
2892 sprintf(new_mask
, "%.*s%s", l1
-l2
, ban
, hidden_host_suffix
);
2894 name
= ban
= new_mask
;
2899 if(action
& ACTION_BAN
)
2901 unsigned int exists
;
2902 struct mod_chanmode
*change
;
2904 if(channel
->banlist
.used
>= MAXBANS
)
2907 reply("CSMSG_BANLIST_FULL", channel
->name
);
2912 exists
= ChannelBanExists(channel
, ban
);
2913 change
= mod_chanmode_alloc(victimCount
+ 1);
2914 for(n
= 0; n
< victimCount
; ++n
)
2916 change
->args
[n
].mode
= MODE_REMOVE
|MODE_CHANOP
|MODE_VOICE
;
2917 change
->args
[n
].member
= victims
[n
];
2921 change
->args
[n
].mode
= MODE_BAN
;
2922 change
->args
[n
++].hostmask
= ban
;
2926 modcmd_chanmode_announce(change
);
2928 mod_chanmode_announce(chanserv
, channel
, change
);
2929 mod_chanmode_free(change
);
2931 if(exists
&& (action
== ACTION_BAN
))
2934 reply("CSMSG_REDUNDANT_BAN", name
, channel
->name
);
2940 if(action
& ACTION_KICK
)
2942 char kick_reason
[MAXLEN
];
2943 sprintf(kick_reason
, "(%s) %s", user
->nick
, reason
);
2945 for(n
= 0; n
< victimCount
; n
++)
2946 KickChannelUser(victims
[n
]->user
, channel
, chanserv
, kick_reason
);
2951 /* No response, since it was automated. */
2953 else if(action
& ACTION_ADD_BAN
)
2956 reply("CSMSG_TIMED_BAN_ADDED", name
, channel
->name
, intervalString(interval
, duration
, user
->handle_info
));
2958 reply("CSMSG_BAN_ADDED", name
, channel
->name
);
2960 else if((action
& (ACTION_BAN
| ACTION_KICK
)) == (ACTION_BAN
| ACTION_KICK
))
2961 reply("CSMSG_KICK_BAN_DONE", name
, channel
->name
);
2962 else if(action
& ACTION_BAN
)
2963 reply("CSMSG_BAN_DONE", name
, channel
->name
);
2964 else if(action
& ACTION_KICK
&& victimCount
)
2965 reply("CSMSG_KICK_DONE", name
, channel
->name
);
2971 static CHANSERV_FUNC(cmd_kickban
)
2973 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
);
2976 static CHANSERV_FUNC(cmd_kick
)
2978 return eject_user(CSFUNC_ARGS
, ACTION_KICK
);
2981 static CHANSERV_FUNC(cmd_ban
)
2983 return eject_user(CSFUNC_ARGS
, ACTION_BAN
);
2986 static CHANSERV_FUNC(cmd_addban
)
2988 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
);
2991 static CHANSERV_FUNC(cmd_addtimedban
)
2993 return eject_user(CSFUNC_ARGS
, ACTION_KICK
| ACTION_BAN
| ACTION_ADD_BAN
| ACTION_ADD_TIMED_BAN
);
2996 static struct mod_chanmode
*
2997 find_matching_bans(struct banList
*bans
, struct userNode
*actee
, const char *mask
)
2999 struct mod_chanmode
*change
;
3000 unsigned char *match
;
3001 unsigned int ii
, count
;
3003 match
= alloca(bans
->used
);
3006 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3008 match
[ii
] = user_matches_glob(actee
, bans
->list
[ii
]->ban
, 1);
3015 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3017 match
[ii
] = match_ircglobs(mask
, bans
->list
[ii
]->ban
);
3024 change
= mod_chanmode_alloc(count
);
3025 for(ii
= count
= 0; ii
< bans
->used
; ++ii
)
3029 change
->args
[count
].mode
= MODE_REMOVE
| MODE_BAN
;
3030 change
->args
[count
++].hostmask
= bans
->list
[ii
]->ban
;
3036 unban_user(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, int action
)
3038 struct userNode
*actee
;
3044 /* may want to allow a comma delimited list of users... */
3045 if(!(actee
= GetUserH(argv
[1])))
3047 if(!is_ircmask(argv
[1]))
3049 reply("MSG_NICK_UNKNOWN", argv
[1]);
3053 mask
= strdup(argv
[1]);
3056 /* We don't sanitize the mask here because ircu
3058 if(action
& ACTION_UNBAN
)
3060 struct mod_chanmode
*change
;
3061 change
= find_matching_bans(&channel
->banlist
, actee
, mask
);
3064 modcmd_chanmode_announce(change
);
3065 mod_chanmode_free(change
);
3070 if(action
& ACTION_DEL_BAN
)
3072 struct banData
*ban
, *next
;
3074 ban
= channel
->channel_info
->bans
;
3078 for( ; ban
&& !user_matches_glob(actee
, ban
->mask
, 1);
3081 for( ; ban
&& !match_ircglobs(mask
, ban
->mask
);
3086 del_channel_ban(ban
);
3093 reply("CSMSG_BAN_NOT_FOUND", actee
? actee
->nick
: mask
);
3095 reply("CSMSG_BAN_REMOVED", actee
? actee
->nick
: mask
);
3101 static CHANSERV_FUNC(cmd_unban
)
3103 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
);
3106 static CHANSERV_FUNC(cmd_delban
)
3108 /* it doesn't necessarily have to remove the channel ban - may want
3109 to make that an option. */
3110 return unban_user(CSFUNC_ARGS
, ACTION_UNBAN
| ACTION_DEL_BAN
);
3113 static CHANSERV_FUNC(cmd_unbanme
)
3115 struct userData
*uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3116 long flags
= ACTION_UNBAN
;
3118 /* remove permanent bans if the user has the proper access. */
3119 if(uData
->access
>= UL_MANAGER
)
3120 flags
|= ACTION_DEL_BAN
;
3122 argv
[1] = user
->nick
;
3123 return unban_user(user
, channel
, 2, argv
, cmd
, flags
);
3126 static CHANSERV_FUNC(cmd_unbanall
)
3128 struct mod_chanmode
*change
;
3131 if(!channel
->banlist
.used
)
3133 reply("CSMSG_NO_BANS", channel
->name
);
3137 change
= mod_chanmode_alloc(channel
->banlist
.used
);
3138 for(ii
=0; ii
<channel
->banlist
.used
; ii
++)
3140 change
->args
[ii
].mode
= MODE_REMOVE
| MODE_BAN
;
3141 change
->args
[ii
].hostmask
= channel
->banlist
.list
[ii
]->ban
;
3143 modcmd_chanmode_announce(change
);
3144 mod_chanmode_free(change
);
3145 reply("CSMSG_BANS_REMOVED", channel
->name
);
3149 static CHANSERV_FUNC(cmd_open
)
3151 struct mod_chanmode
*change
;
3153 change
= find_matching_bans(&channel
->banlist
, user
, NULL
);
3155 change
= mod_chanmode_alloc(0);
3156 change
->modes_clear
|= MODE_INVITEONLY
| MODE_LIMIT
| MODE_KEY
;
3157 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3158 && channel
->channel_info
->modes
.modes_set
)
3159 change
->modes_clear
&= ~channel
->channel_info
->modes
.modes_set
;
3160 modcmd_chanmode_announce(change
);
3161 reply("CSMSG_CHANNEL_OPENED", channel
->name
);
3162 mod_chanmode_free(change
);
3166 static CHANSERV_FUNC(cmd_myaccess
)
3168 static struct string_buffer sbuf
;
3169 struct handle_info
*target_handle
;
3170 struct userData
*uData
;
3173 target_handle
= user
->handle_info
;
3174 else if(!IsHelping(user
))
3176 reply("CSMSG_MYACCESS_SELF_ONLY", argv
[0]);
3179 else if(!(target_handle
= modcmd_get_handle_info(user
, argv
[1])))
3182 if(!target_handle
->channels
)
3184 reply("CSMSG_SQUAT_ACCESS", target_handle
->handle
);
3188 reply("CSMSG_INFOLINE_LIST", target_handle
->handle
);
3189 for(uData
= target_handle
->channels
; uData
; uData
= uData
->u_next
)
3191 struct chanData
*cData
= uData
->channel
;
3193 if(uData
->access
> UL_OWNER
)
3195 if(IsProtected(cData
)
3196 && (target_handle
!= user
->handle_info
)
3197 && !GetTrueChannelAccess(cData
, user
->handle_info
))
3200 string_buffer_append_printf(&sbuf
, "[%s (%d", cData
->channel
->name
, uData
->access
);
3201 if(uData
->flags
!= USER_AUTO_OP
)
3202 string_buffer_append(&sbuf
, ',');
3203 if(IsUserSuspended(uData
))
3204 string_buffer_append(&sbuf
, 's');
3205 if(IsUserAutoOp(uData
))
3207 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
3208 string_buffer_append(&sbuf
, 'o');
3209 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
3210 string_buffer_append(&sbuf
, 'v');
3212 if(IsUserAutoInvite(uData
) && (uData
->access
>= cData
->lvlOpts
[lvlInviteMe
]))
3213 string_buffer_append(&sbuf
, 'i');
3215 string_buffer_append_printf(&sbuf
, ")] %s", uData
->info
);
3217 string_buffer_append_string(&sbuf
, ")]");
3218 string_buffer_append(&sbuf
, '\0');
3219 send_message_type(4, user
, cmd
->parent
->bot
, sbuf
.list
);
3225 static CHANSERV_FUNC(cmd_access
)
3227 struct userNode
*target
;
3228 struct handle_info
*target_handle
;
3229 struct userData
*uData
;
3231 char prefix
[MAXLEN
];
3236 target_handle
= target
->handle_info
;
3238 else if((target
= GetUserH(argv
[1])))
3240 target_handle
= target
->handle_info
;
3242 else if(argv
[1][0] == '*')
3244 if(!(target_handle
= get_handle_info(argv
[1]+1)))
3246 reply("MSG_HANDLE_UNKNOWN", argv
[1]+1);
3252 reply("MSG_NICK_UNKNOWN", argv
[1]);
3256 assert(target
|| target_handle
);
3258 if(target
== chanserv
)
3260 reply("CSMSG_IS_CHANSERV");
3268 reply("CSMSG_LAZY_SMURF_TARGET", target
->nick
, chanserv_conf
.irc_operator_epithet
);
3273 reply("MSG_USER_AUTHENTICATE", target
->nick
);
3276 reply("MSG_AUTHENTICATE");
3282 const char *epithet
= NULL
, *type
= NULL
;
3285 epithet
= chanserv_conf
.irc_operator_epithet
;
3288 else if(IsNetworkHelper(target
))
3290 epithet
= chanserv_conf
.network_helper_epithet
;
3291 type
= "network helper";
3293 else if(IsSupportHelper(target
))
3295 epithet
= chanserv_conf
.support_helper_epithet
;
3296 type
= "support helper";
3300 if(target_handle
->epithet
)
3301 reply("CSMSG_SMURF_TARGET", target
->nick
, target_handle
->epithet
, type
);
3303 reply("CSMSG_SMURF_TARGET", target
->nick
, epithet
, type
);
3305 sprintf(prefix
, "%s (%s)", target
->nick
, target_handle
->handle
);
3309 sprintf(prefix
, "%s", target_handle
->handle
);
3312 if(!channel
->channel_info
)
3314 reply("CSMSG_NOT_REGISTERED", channel
->name
);
3318 helping
= HANDLE_FLAGGED(target_handle
, HELPING
)
3319 && ((target_handle
->opserv_level
>= chanserv_conf
.nodelete_level
) || !IsProtected(channel
->channel_info
));
3320 if((uData
= GetTrueChannelAccess(channel
->channel_info
, target_handle
)))
3322 reply((helping
? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix
, uData
->access
, channel
->name
);
3323 /* To prevent possible information leaks, only show infolines
3324 * if the requestor is in the channel or it's their own
3326 if(uData
->info
&& (GetUserMode(channel
, user
) || (target_handle
== user
->handle_info
)))
3328 send_message_type(4, user
, cmd
->parent
->bot
, "[%s] %s", (target
? target
->nick
: target_handle
->handle
), uData
->info
);
3330 /* Likewise, only say it's suspended if the user has active
3331 * access in that channel or it's their own entry. */
3332 if(IsUserSuspended(uData
)
3333 && (GetChannelUser(channel
->channel_info
, user
->handle_info
)
3334 || (user
->handle_info
== uData
->handle
)))
3336 reply("CSMSG_USER_SUSPENDED", (target
? target
->nick
: target_handle
->handle
), channel
->name
);
3341 reply((helping
? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix
, channel
->name
);
3348 zoot_list(struct listData
*list
)
3350 struct userData
*uData
;
3351 unsigned int start
, curr
, highest
, lowest
;
3352 struct helpfile_table tmp_table
;
3353 const char **temp
, *msg
;
3355 if(list
->table
.length
== 1)
3358 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3360 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3361 msg
= user_find_message(list
->user
, "MSG_NONE");
3362 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3364 tmp_table
.width
= list
->table
.width
;
3365 tmp_table
.flags
= list
->table
.flags
;
3366 list
->table
.contents
[0][0] = " ";
3367 highest
= list
->highest
;
3368 if(list
->lowest
!= 0)
3369 lowest
= list
->lowest
;
3370 else if(highest
< 100)
3373 lowest
= highest
- 100;
3374 for(start
= curr
= 1; curr
< list
->table
.length
; )
3376 uData
= list
->users
[curr
-1];
3377 list
->table
.contents
[curr
++][0] = " ";
3378 if((curr
== list
->table
.length
) || (list
->users
[curr
-1]->access
< lowest
))
3381 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, lowest
, highest
, list
->search
);
3383 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, lowest
, highest
);
3384 temp
= list
->table
.contents
[--start
];
3385 list
->table
.contents
[start
] = list
->table
.contents
[0];
3386 tmp_table
.contents
= list
->table
.contents
+ start
;
3387 tmp_table
.length
= curr
- start
;
3388 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, tmp_table
);
3389 list
->table
.contents
[start
] = temp
;
3391 highest
= lowest
- 1;
3392 lowest
= (highest
< 100) ? 0 : (highest
- 99);
3398 def_list(struct listData
*list
)
3402 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_SEARCH_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
, list
->search
);
3404 send_message(list
->user
, list
->bot
, "CSMSG_ACCESS_ALL_HEADER", list
->channel
->name
, list
->lowest
, list
->highest
);
3405 table_send(list
->bot
, list
->user
->nick
, 0, NULL
, list
->table
);
3406 if(list
->table
.length
== 1)
3408 msg
= user_find_message(list
->user
, "MSG_NONE");
3409 send_message_type(4, list
->user
, list
->bot
, " %s", msg
);
3414 userData_access_comp(const void *arg_a
, const void *arg_b
)
3416 const struct userData
*a
= *(struct userData
**)arg_a
;
3417 const struct userData
*b
= *(struct userData
**)arg_b
;
3419 if(a
->access
!= b
->access
)
3420 res
= b
->access
- a
->access
;
3422 res
= irccasecmp(a
->handle
->handle
, b
->handle
->handle
);
3427 cmd_list_users(struct userNode
*user
, struct chanNode
*channel
, unsigned int argc
, char *argv
[], struct svccmd
*cmd
, unsigned short lowest
, unsigned short highest
)
3429 void (*send_list
)(struct listData
*);
3430 struct userData
*uData
;
3431 struct listData lData
;
3432 unsigned int matches
;
3436 lData
.bot
= cmd
->parent
->bot
;
3437 lData
.channel
= channel
;
3438 lData
.lowest
= lowest
;
3439 lData
.highest
= highest
;
3440 lData
.search
= (argc
> 1) ? argv
[1] : NULL
;
3441 send_list
= def_list
;
3442 (void)zoot_list
; /* since it doesn't show user levels */
3444 if(user
->handle_info
)
3446 switch(user
->handle_info
->userlist_style
)
3448 case HI_STYLE_DEF
: send_list
= def_list
; break;
3449 case HI_STYLE_ZOOT
: send_list
= def_list
; break;
3453 lData
.users
= alloca(channel
->channel_info
->userCount
* sizeof(struct userData
*));
3455 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
3457 if((uData
->access
< lowest
)
3458 || (uData
->access
> highest
)
3459 || (lData
.search
&& !match_ircglob(uData
->handle
->handle
, lData
.search
)))
3461 lData
.users
[matches
++] = uData
;
3463 qsort(lData
.users
, matches
, sizeof(lData
.users
[0]), userData_access_comp
);
3465 lData
.table
.length
= matches
+1;
3466 lData
.table
.width
= 4;
3467 lData
.table
.flags
= TABLE_NO_FREE
;
3468 lData
.table
.contents
= malloc(lData
.table
.length
*sizeof(*lData
.table
.contents
));
3469 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3470 lData
.table
.contents
[0] = ary
;
3473 ary
[2] = "Last Seen";
3475 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3477 struct userData
*uData
= lData
.users
[matches
-1];
3478 char seen
[INTERVALLEN
];
3480 ary
= malloc(lData
.table
.width
*sizeof(**lData
.table
.contents
));
3481 lData
.table
.contents
[matches
] = ary
;
3482 ary
[0] = strtab(uData
->access
);
3483 ary
[1] = uData
->handle
->handle
;
3486 else if(!uData
->seen
)
3489 ary
[2] = intervalString(seen
, now
- uData
->seen
, user
->handle_info
);
3490 ary
[2] = strdup(ary
[2]);
3491 if(IsUserSuspended(uData
))
3492 ary
[3] = "Suspended";
3493 else if(HANDLE_FLAGGED(uData
->handle
, FROZEN
))
3494 ary
[3] = "Vacation";
3499 for(matches
= 1; matches
< lData
.table
.length
; ++matches
)
3501 free((char*)lData
.table
.contents
[matches
][2]);
3502 free(lData
.table
.contents
[matches
]);
3504 free(lData
.table
.contents
[0]);
3505 free(lData
.table
.contents
);
3509 static CHANSERV_FUNC(cmd_users
)
3511 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OWNER
);
3514 static CHANSERV_FUNC(cmd_wlist
)
3516 return cmd_list_users(CSFUNC_ARGS
, UL_OWNER
, UL_OWNER
);
3519 static CHANSERV_FUNC(cmd_clist
)
3521 return cmd_list_users(CSFUNC_ARGS
, UL_COOWNER
, UL_OWNER
-1);
3524 static CHANSERV_FUNC(cmd_mlist
)
3526 return cmd_list_users(CSFUNC_ARGS
, UL_MANAGER
, UL_COOWNER
-1);
3529 static CHANSERV_FUNC(cmd_olist
)
3531 return cmd_list_users(CSFUNC_ARGS
, UL_OP
, UL_MANAGER
-1);
3534 static CHANSERV_FUNC(cmd_plist
)
3536 return cmd_list_users(CSFUNC_ARGS
, 1, UL_OP
-1);
3539 static CHANSERV_FUNC(cmd_bans
)
3541 struct helpfile_table tbl
;
3542 unsigned int matches
= 0, timed
= 0, ii
;
3543 char t_buffer
[INTERVALLEN
], e_buffer
[INTERVALLEN
], *search
;
3544 const char *msg_never
, *triggered
, *expires
;
3545 struct banData
*ban
, **bans
;
3552 bans
= alloca(channel
->channel_info
->banCount
* sizeof(struct banData
*));
3554 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
3556 if(search
&& !match_ircglobs(search
, ban
->mask
))
3558 bans
[matches
++] = ban
;
3563 tbl
.length
= matches
+ 1;
3564 tbl
.width
= 4 + timed
;
3566 tbl
.flags
= TABLE_NO_FREE
;
3567 tbl
.contents
= malloc(tbl
.length
* sizeof(tbl
.contents
[0]));
3568 tbl
.contents
[0] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3569 tbl
.contents
[0][0] = "Mask";
3570 tbl
.contents
[0][1] = "Set By";
3571 tbl
.contents
[0][2] = "Triggered";
3574 tbl
.contents
[0][3] = "Expires";
3575 tbl
.contents
[0][4] = "Reason";
3578 tbl
.contents
[0][3] = "Reason";
3581 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3583 free(tbl
.contents
[0]);
3588 msg_never
= user_find_message(user
, "MSG_NEVER");
3589 for(ii
= 0; ii
< matches
; )
3595 else if(ban
->expires
)
3596 expires
= intervalString(e_buffer
, ban
->expires
- now
, user
->handle_info
);
3598 expires
= msg_never
;
3601 triggered
= intervalString(t_buffer
, now
- ban
->triggered
, user
->handle_info
);
3603 triggered
= msg_never
;
3605 tbl
.contents
[++ii
] = malloc(tbl
.width
* sizeof(tbl
.contents
[0][0]));
3606 tbl
.contents
[ii
][0] = ban
->mask
;
3607 tbl
.contents
[ii
][1] = ban
->owner
;
3608 tbl
.contents
[ii
][2] = strdup(triggered
);
3611 tbl
.contents
[ii
][3] = strdup(expires
);
3612 tbl
.contents
[ii
][4] = ban
->reason
;
3615 tbl
.contents
[ii
][3] = ban
->reason
;
3617 table_send(cmd
->parent
->bot
, user
->nick
, 0, NULL
, tbl
);
3618 reply("MSG_MATCH_COUNT", matches
);
3619 for(ii
= 1; ii
< tbl
.length
; ++ii
)
3621 free((char*)tbl
.contents
[ii
][2]);
3623 free((char*)tbl
.contents
[ii
][3]);
3624 free(tbl
.contents
[ii
]);
3626 free(tbl
.contents
[0]);
3632 bad_topic(struct chanNode
*channel
, struct userNode
*user
, const char *new_topic
)
3634 struct chanData
*cData
= channel
->channel_info
;
3635 if(check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
3637 if(cData
->topic_mask
)
3638 return !match_ircglob(new_topic
, cData
->topic_mask
);
3639 else if(cData
->topic
)
3640 return irccasecmp(new_topic
, cData
->topic
);
3645 static CHANSERV_FUNC(cmd_topic
)
3647 struct chanData
*cData
;
3650 cData
= channel
->channel_info
;
3655 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
3656 reply("CSMSG_TOPIC_SET", cData
->topic
);
3660 reply("CSMSG_NO_TOPIC", channel
->name
);
3664 topic
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
3665 /* If they say "!topic *", use an empty topic. */
3666 if((topic
[0] == '*') && (topic
[1] == 0))
3668 if(bad_topic(channel
, user
, topic
))
3670 char *topic_mask
= cData
->topic_mask
;
3673 char new_topic
[TOPICLEN
+1], tchar
;
3674 int pos
=0, starpos
=-1, dpos
=0, len
;
3676 while((tchar
= topic_mask
[pos
++]) && (dpos
<= TOPICLEN
))
3683 len
= strlen(topic
);
3684 if((dpos
+ len
) > TOPICLEN
)
3685 len
= TOPICLEN
+ 1 - dpos
;
3686 memcpy(new_topic
+dpos
, topic
, len
);
3690 case '\\': tchar
= topic_mask
[pos
++]; /* and fall through */
3691 default: new_topic
[dpos
++] = tchar
; break;
3694 if((dpos
> TOPICLEN
) || tchar
)
3697 reply("CSMSG_TOPICMASK_CONFLICT1", channel
->name
, topic_mask
);
3698 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN
);
3701 new_topic
[dpos
] = 0;
3702 SetChannelTopic(channel
, chanserv
, new_topic
, 1);
3704 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
3709 SetChannelTopic(channel
, chanserv
, topic
, 1);
3711 if(check_user_level(channel
, user
, lvlTopicSnarf
, 1, 0))
3713 /* Grab the topic and save it as the default topic. */
3715 cData
->topic
= strdup(channel
->topic
);
3721 static CHANSERV_FUNC(cmd_mode
)
3723 struct mod_chanmode
*change
;
3727 change
= &channel
->channel_info
->modes
;
3728 if(change
->modes_set
|| change
->modes_clear
) {
3729 modcmd_chanmode_announce(change
);
3730 reply("CSMSG_DEFAULTED_MODES", channel
->name
);
3732 reply("CSMSG_NO_MODES", channel
->name
);
3736 change
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
);
3739 reply("MSG_INVALID_MODES", unsplit_string(argv
+1, argc
-1, NULL
));
3743 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
3744 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
3747 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
3748 reply("CSMSG_MODE_LOCKED", modes
, channel
->name
);
3752 modcmd_chanmode_announce(change
);
3753 mod_chanmode_free(change
);
3754 reply("CSMSG_MODES_SET", unsplit_string(argv
+1, argc
-1, NULL
));
3758 static CHANSERV_FUNC(cmd_invite
)
3760 struct userData
*uData
;
3761 struct userNode
*invite
;
3763 uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
3767 if(!(invite
= GetUserH(argv
[1])))
3769 reply("MSG_NICK_UNKNOWN", argv
[1]);
3776 if(GetUserMode(channel
, invite
))
3778 reply("CSMSG_ALREADY_PRESENT", invite
->nick
, channel
->name
);
3786 char *reason
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
3787 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU_REASON", user
->nick
, channel
->name
, reason
);
3790 send_message(invite
, chanserv
, "CSMSG_INVITING_YOU", user
->nick
, channel
->name
);
3792 irc_invite(chanserv
, invite
, channel
);
3794 reply("CSMSG_INVITED_USER", argv
[1], channel
->name
);
3799 static CHANSERV_FUNC(cmd_inviteme
)
3801 if(GetUserMode(channel
, user
))
3803 reply("CSMSG_YOU_ALREADY_PRESENT", channel
->name
);
3806 if(channel
->channel_info
3807 && !check_user_level(channel
, user
, lvlInviteMe
, 1, 0))
3809 reply("CSMSG_LOW_CHANNEL_ACCESS", channel
->name
);
3812 irc_invite(cmd
->parent
->bot
, user
, channel
);
3817 show_suspension_info(struct svccmd
*cmd
, struct userNode
*user
, struct suspended
*suspended
)
3820 char buf1
[INTERVALLEN
], buf2
[INTERVALLEN
];
3822 /* We display things based on two dimensions:
3823 * - Issue time: present or absent
3824 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3825 * (in order of precedence, so something both expired and revoked
3826 * only counts as revoked)
3828 combo
= (suspended
->issued
? 4 : 0)
3829 + (suspended
->revoked
? 3 : suspended
->expires
? ((suspended
->expires
< now
) ? 2 : 1) : 0);
3831 case 0: /* no issue time, indefinite expiration */
3832 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended
->suspender
, suspended
->reason
);
3834 case 1: /* no issue time, expires in future */
3835 intervalString(buf1
, suspended
->expires
-now
, user
->handle_info
);
3836 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended
->suspender
, buf1
, suspended
->reason
);
3838 case 2: /* no issue time, expired */
3839 intervalString(buf1
, now
-suspended
->expires
, user
->handle_info
);
3840 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended
->suspender
, buf1
, suspended
->reason
);
3842 case 3: /* no issue time, revoked */
3843 intervalString(buf1
, now
-suspended
->revoked
, user
->handle_info
);
3844 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended
->suspender
, buf1
, suspended
->reason
);
3846 case 4: /* issue time set, indefinite expiration */
3847 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3848 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1
, suspended
->suspender
, suspended
->reason
);
3850 case 5: /* issue time set, expires in future */
3851 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3852 intervalString(buf2
, suspended
->expires
-now
, user
->handle_info
);
3853 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3855 case 6: /* issue time set, expired */
3856 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3857 intervalString(buf2
, now
-suspended
->expires
, user
->handle_info
);
3858 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3860 case 7: /* issue time set, revoked */
3861 intervalString(buf1
, now
-suspended
->issued
, user
->handle_info
);
3862 intervalString(buf2
, now
-suspended
->revoked
, user
->handle_info
);
3863 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1
, suspended
->suspender
, buf2
, suspended
->reason
);
3866 log_module(CS_LOG
, LOG_ERROR
, "Invalid combo value %d in show_suspension_info()", combo
);
3871 static CHANSERV_FUNC(cmd_info
)
3873 char modes
[MAXLEN
], buffer
[INTERVALLEN
];
3874 struct userData
*uData
, *owner
;
3875 struct chanData
*cData
;
3876 struct do_not_register
*dnr
;
3881 cData
= channel
->channel_info
;
3882 reply("CSMSG_CHANNEL_INFO", channel
->name
);
3884 uData
= GetChannelUser(cData
, user
->handle_info
);
3885 if(uData
&& (uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
3887 mod_chanmode_format(&cData
->modes
, modes
);
3888 reply("CSMSG_CHANNEL_TOPIC", cData
->topic
);
3889 reply("CSMSG_CHANNEL_MODES", modes
[0] ? modes
: user_find_message(user
, "MSG_NONE"));
3892 for(it
= dict_first(cData
->notes
); it
; it
= iter_next(it
))
3896 note
= iter_data(it
);
3897 if(!note_type_visible_to_user(cData
, note
->type
, user
))
3900 padding
= PADLEN
- 1 - strlen(iter_key(it
));
3901 reply("CSMSG_CHANNEL_NOTE", iter_key(it
), padding
> 0 ? padding
: 1, "", note
->note
);
3904 reply("CSMSG_CHANNEL_MAX", cData
->max
);
3905 for(owner
= cData
->users
; owner
; owner
= owner
->next
)
3906 if(owner
->access
== UL_OWNER
)
3907 reply("CSMSG_CHANNEL_OWNER", owner
->handle
->handle
);
3908 reply("CSMSG_CHANNEL_USERS", cData
->userCount
);
3909 reply("CSMSG_CHANNEL_BANS", cData
->banCount
);
3910 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer
, now
- cData
->visited
, user
->handle_info
));
3911 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer
, now
- cData
->registered
, user
->handle_info
));
3913 privileged
= IsStaff(user
);
3914 if(((uData
&& uData
->access
>= UL_COOWNER
) || privileged
) && cData
->registrar
)
3915 reply("CSMSG_CHANNEL_REGISTRAR", cData
->registrar
);
3917 if(privileged
&& (dnr
= chanserv_is_dnr(channel
->name
, NULL
)))
3918 chanserv_show_dnrs(user
, cmd
, channel
->name
, NULL
);
3920 if(cData
->suspended
&& ((uData
&& (uData
->access
>= UL_COOWNER
)) || IsHelping(user
)))
3922 struct suspended
*suspended
;
3923 reply((IsSuspended(cData
) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel
->name
);
3924 for(suspended
= cData
->suspended
; suspended
; suspended
= suspended
->previous
)
3925 show_suspension_info(cmd
, user
, suspended
);
3927 else if(IsSuspended(cData
))
3929 reply("CSMSG_CHANNEL_SUSPENDED", channel
->name
);
3930 show_suspension_info(cmd
, user
, cData
->suspended
);
3935 static CHANSERV_FUNC(cmd_netinfo
)
3937 extern time_t boot_time
;
3938 extern unsigned long burst_length
;
3939 char interval
[INTERVALLEN
];
3941 reply("CSMSG_NETWORK_INFO");
3942 reply("CSMSG_NETWORK_SERVERS", dict_size(servers
));
3943 reply("CSMSG_NETWORK_USERS", dict_size(clients
));
3944 reply("CSMSG_NETWORK_OPERS", curr_opers
.used
);
3945 reply("CSMSG_NETWORK_CHANNELS", registered_channels
);
3946 reply("CSMSG_NETWORK_BANS", banCount
);
3947 reply("CSMSG_NETWORK_CHANUSERS", userCount
);
3948 reply("CSMSG_SERVICES_UPTIME", intervalString(interval
, time(NULL
) - boot_time
, user
->handle_info
));
3949 reply("CSMSG_BURST_LENGTH", intervalString(interval
, burst_length
, user
->handle_info
));
3954 send_staff_list(struct userNode
*to
, struct userList
*list
, int skip_flags
)
3956 struct helpfile_table table
;
3958 struct userNode
*user
;
3963 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
3964 table
.contents
= alloca(list
->used
*sizeof(*table
.contents
));
3965 for(nn
=0; nn
<list
->used
; nn
++)
3967 user
= list
->list
[nn
];
3968 if(user
->modes
& skip_flags
)
3972 table
.contents
[table
.length
] = alloca(table
.width
*sizeof(**table
.contents
));
3975 nick
= alloca(strlen(user
->nick
)+3);
3976 sprintf(nick
, "(%s)", user
->nick
);
3980 table
.contents
[table
.length
][0] = nick
;
3983 table_send(chanserv
, to
->nick
, 0, NULL
, table
);
3986 static CHANSERV_FUNC(cmd_ircops
)
3988 reply("CSMSG_STAFF_OPERS");
3989 send_staff_list(user
, &curr_opers
, FLAGS_SERVICE
);
3993 static CHANSERV_FUNC(cmd_helpers
)
3995 reply("CSMSG_STAFF_HELPERS");
3996 send_staff_list(user
, &curr_helpers
, FLAGS_OPER
);
4000 static CHANSERV_FUNC(cmd_staff
)
4002 reply("CSMSG_NETWORK_STAFF");
4003 cmd_ircops(CSFUNC_ARGS
);
4004 cmd_helpers(CSFUNC_ARGS
);
4008 static CHANSERV_FUNC(cmd_peek
)
4010 struct modeNode
*mn
;
4011 char modes
[MODELEN
];
4013 struct helpfile_table table
;
4015 irc_make_chanmode(channel
, modes
);
4017 reply("CSMSG_PEEK_INFO", channel
->name
);
4018 reply("CSMSG_PEEK_TOPIC", channel
->topic
);
4019 reply("CSMSG_PEEK_MODES", modes
);
4020 reply("CSMSG_PEEK_USERS", channel
->members
.used
);
4024 table
.flags
= TABLE_REPEAT_ROWS
| TABLE_NO_FREE
| TABLE_NO_HEADERS
;
4025 table
.contents
= alloca(channel
->members
.used
*sizeof(*table
.contents
));
4026 for(n
= 0; n
< channel
->members
.used
; n
++)
4028 mn
= channel
->members
.list
[n
];
4029 if(!(mn
->modes
& MODE_CHANOP
) || IsLocal(mn
->user
))
4031 table
.contents
[table
.length
] = alloca(sizeof(**table
.contents
));
4032 table
.contents
[table
.length
][0] = mn
->user
->nick
;
4037 reply("CSMSG_PEEK_OPS");
4038 table_send(chanserv
, user
->nick
, 0, NULL
, table
);
4041 reply("CSMSG_PEEK_NO_OPS");
4045 static MODCMD_FUNC(cmd_wipeinfo
)
4047 struct handle_info
*victim
;
4048 struct userData
*ud
, *actor
;
4051 actor
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
4052 if(!(victim
= modcmd_get_handle_info(user
, argv
[1])))
4054 if(!(ud
= GetTrueChannelAccess(channel
->channel_info
, victim
)))
4056 reply("CSMSG_NO_CHAN_USER", argv
[1], channel
->name
);
4059 if((ud
->access
>= actor
->access
) && (ud
!= actor
))
4061 reply("MSG_USER_OUTRANKED", victim
->handle
);
4067 reply("CSMSG_WIPED_INFO_LINE", argv
[1], channel
->name
);
4071 static CHANSERV_FUNC(cmd_resync
)
4073 struct mod_chanmode
*changes
;
4074 struct chanData
*cData
= channel
->channel_info
;
4075 unsigned int ii
, used
;
4077 changes
= mod_chanmode_alloc(channel
->members
.used
* 2);
4078 for(ii
= used
= 0; ii
< channel
->members
.used
; ++ii
)
4080 struct modeNode
*mn
= channel
->members
.list
[ii
];
4081 struct userData
*uData
;
4083 if(IsService(mn
->user
))
4086 uData
= GetChannelAccess(cData
, mn
->user
->handle_info
);
4087 if(!cData
->lvlOpts
[lvlGiveOps
]
4088 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveOps
]))
4090 if(!(mn
->modes
& MODE_CHANOP
))
4092 changes
->args
[used
].mode
= MODE_CHANOP
;
4093 changes
->args
[used
++].member
= mn
;
4096 else if(!cData
->lvlOpts
[lvlGiveVoice
]
4097 || (uData
&& uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
]))
4099 if(mn
->modes
& MODE_CHANOP
)
4101 changes
->args
[used
].mode
= MODE_REMOVE
| (mn
->modes
& ~MODE_VOICE
);
4102 changes
->args
[used
++].member
= mn
;
4104 if(!(mn
->modes
& MODE_VOICE
))
4106 changes
->args
[used
].mode
= MODE_VOICE
;
4107 changes
->args
[used
++].member
= mn
;
4114 changes
->args
[used
].mode
= MODE_REMOVE
| mn
->modes
;
4115 changes
->args
[used
++].member
= mn
;
4119 changes
->argc
= used
;
4120 modcmd_chanmode_announce(changes
);
4121 mod_chanmode_free(changes
);
4122 reply("CSMSG_RESYNCED_USERS", channel
->name
);
4126 static CHANSERV_FUNC(cmd_seen
)
4128 struct userData
*uData
;
4129 struct handle_info
*handle
;
4130 char seen
[INTERVALLEN
];
4134 if(!irccasecmp(argv
[1], chanserv
->nick
))
4136 reply("CSMSG_IS_CHANSERV");
4140 if(!(handle
= get_handle_info(argv
[1])))
4142 reply("MSG_HANDLE_UNKNOWN", argv
[1]);
4146 if(!(uData
= GetTrueChannelAccess(channel
->channel_info
, handle
)))
4148 reply("CSMSG_NO_CHAN_USER", handle
->handle
, channel
->name
);
4153 reply("CSMSG_USER_PRESENT", handle
->handle
);
4154 else if(uData
->seen
)
4155 reply("CSMSG_USER_SEEN", handle
->handle
, channel
->name
, intervalString(seen
, now
- uData
->seen
, user
->handle_info
));
4157 reply("CSMSG_NEVER_SEEN", handle
->handle
, channel
->name
);
4159 if(!uData
->present
&& HANDLE_FLAGGED(handle
, FROZEN
))
4160 reply("CSMSG_USER_VACATION", handle
->handle
);
4165 static MODCMD_FUNC(cmd_names
)
4167 struct userNode
*targ
;
4168 struct userData
*targData
;
4169 unsigned int ii
, pos
;
4172 for(ii
=pos
=0; ii
<channel
->members
.used
; ++ii
)
4174 targ
= channel
->members
.list
[ii
]->user
;
4175 targData
= GetTrueChannelAccess(channel
->channel_info
, targ
->handle_info
);
4178 if(pos
+ strlen(targ
->nick
) + strlen(targ
->handle_info
->handle
) + 8 > sizeof(buf
))
4181 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4185 if(IsUserSuspended(targData
))
4187 pos
+= sprintf(buf
+pos
, "%d:%s(%s)", targData
->access
, targ
->nick
, targ
->handle_info
->handle
);
4190 reply("CSMSG_CHANNEL_NAMES", channel
->name
, buf
);
4191 reply("CSMSG_END_NAMES", channel
->name
);
4196 note_type_visible_to_user(struct chanData
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4198 switch(ntype
->visible_type
)
4200 case NOTE_VIS_ALL
: return 1;
4201 case NOTE_VIS_CHANNEL_USERS
: return !channel
|| !user
|| (user
->handle_info
&& GetChannelUser(channel
, user
->handle_info
));
4202 case NOTE_VIS_PRIVILEGED
: default: return user
&& (IsOper(user
) || IsSupportHelper(user
) || IsNetworkHelper(user
));
4207 note_type_settable_by_user(struct chanNode
*channel
, struct note_type
*ntype
, struct userNode
*user
)
4209 struct userData
*uData
;
4211 switch(ntype
->set_access_type
)
4213 case NOTE_SET_CHANNEL_ACCESS
:
4214 if(!user
->handle_info
)
4216 if(!(uData
= GetChannelUser(channel
->channel_info
, user
->handle_info
)))
4218 return uData
->access
>= ntype
->set_access
.min_ulevel
;
4219 case NOTE_SET_CHANNEL_SETTER
:
4220 return check_user_level(channel
, user
, lvlSetters
, 1, 0);
4221 case NOTE_SET_PRIVILEGED
: default:
4222 return IsHelping(user
) && (user
->handle_info
->opserv_level
>= ntype
->set_access
.min_opserv
);
4226 static CHANSERV_FUNC(cmd_note
)
4228 struct chanData
*cData
;
4230 struct note_type
*ntype
;
4232 cData
= channel
->channel_info
;
4235 reply("CSMSG_NOT_REGISTERED", channel
->name
);
4239 /* If no arguments, show all visible notes for the channel. */
4245 for(count
=0, it
=dict_first(cData
->notes
); it
; it
=iter_next(it
))
4247 note
= iter_data(it
);
4248 if(!note_type_visible_to_user(cData
, note
->type
, user
))
4251 reply("CSMSG_NOTELIST_HEADER", channel
->name
);
4252 reply("CSMSG_NOTE_FORMAT", iter_key(it
), note
->setter
, note
->note
);
4255 reply("CSMSG_NOTELIST_END", channel
->name
);
4257 reply("CSMSG_NOTELIST_EMPTY", channel
->name
);
4259 /* If one argument, show the named note. */
4262 if((note
= dict_find(cData
->notes
, argv
[1], NULL
))
4263 && note_type_visible_to_user(cData
, note
->type
, user
))
4265 reply("CSMSG_NOTE_FORMAT", note
->type
->name
, note
->setter
, note
->note
);
4267 else if((ntype
= dict_find(note_types
, argv
[1], NULL
))
4268 && note_type_visible_to_user(NULL
, ntype
, user
))
4270 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, ntype
->name
);
4275 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4279 /* Assume they're trying to set a note. */
4283 ntype
= dict_find(note_types
, argv
[1], NULL
);
4286 reply("CSMSG_BAD_NOTE_TYPE", argv
[1]);
4289 else if(note_type_settable_by_user(channel
, ntype
, user
))
4291 note_text
= unsplit_string(argv
+2, argc
-2, NULL
);
4292 if((note
= dict_find(cData
->notes
, argv
[1], NULL
)))
4293 reply("CSMSG_REPLACED_NOTE", ntype
->name
, channel
->name
, note
->setter
, note
->note
);
4294 chanserv_add_channel_note(cData
, ntype
, user
->handle_info
->handle
, note_text
);
4295 reply("CSMSG_NOTE_SET", ntype
->name
, channel
->name
);
4297 if(ntype
->visible_type
== NOTE_VIS_PRIVILEGED
)
4299 /* The note is viewable to staff only, so return 0
4300 to keep the invocation from getting logged (or
4301 regular users can see it in !events). */
4307 reply("CSMSG_NO_ACCESS");
4314 static CHANSERV_FUNC(cmd_delnote
)
4319 if(!(note
= dict_find(channel
->channel_info
->notes
, argv
[1], NULL
))
4320 || !note_type_settable_by_user(channel
, note
->type
, user
))
4322 reply("CSMSG_NO_SUCH_NOTE", channel
->name
, argv
[1]);
4325 dict_remove(channel
->channel_info
->notes
, note
->type
->name
);
4326 reply("CSMSG_NOTE_REMOVED", argv
[1], channel
->name
);
4330 static CHANSERV_FUNC(cmd_events
)
4332 struct logSearch discrim
;
4333 struct logReport report
;
4334 unsigned int matches
, limit
;
4336 limit
= (argc
> 1) ? atoi(argv
[1]) : 10;
4337 if(limit
< 1 || limit
> 200)
4340 memset(&discrim
, 0, sizeof(discrim
));
4341 discrim
.masks
.bot
= chanserv
;
4342 discrim
.masks
.channel_name
= channel
->name
;
4344 discrim
.masks
.command
= argv
[2];
4345 discrim
.limit
= limit
;
4346 discrim
.max_time
= INT_MAX
;
4347 discrim
.severities
= 1 << LOG_COMMAND
;
4348 report
.reporter
= chanserv
;
4350 reply("CSMSG_EVENT_SEARCH_RESULTS");
4351 matches
= log_entry_search(&discrim
, log_report_entry
, &report
);
4353 reply("MSG_MATCH_COUNT", matches
);
4355 reply("MSG_NO_MATCHES");
4359 static CHANSERV_FUNC(cmd_say
)
4365 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4366 send_channel_message(channel
, cmd
->parent
->bot
, "%s", msg
);
4368 else if(GetUserH(argv
[1]))
4371 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4372 send_target_message(5, argv
[1], cmd
->parent
->bot
, "%s", msg
);
4376 reply("MSG_NOT_TARGET_NAME");
4382 static CHANSERV_FUNC(cmd_emote
)
4388 /* CTCP is so annoying. */
4389 msg
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
4390 send_channel_message(channel
, cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4392 else if(GetUserH(argv
[1]))
4394 msg
= unsplit_string(argv
+ 2, argc
- 2, NULL
);
4395 send_target_message(5, argv
[1], cmd
->parent
->bot
, "\001ACTION %s\001", msg
);
4399 reply("MSG_NOT_TARGET_NAME");
4405 struct channelList
*
4406 chanserv_support_channels(void)
4408 return &chanserv_conf
.support_channels
;
4411 static CHANSERV_FUNC(cmd_expire
)
4413 int channel_count
= registered_channels
;
4414 expire_channels(NULL
);
4415 reply("CSMSG_CHANNELS_EXPIRED", channel_count
- registered_channels
);
4420 chanserv_expire_suspension(void *data
)
4422 struct suspended
*suspended
= data
;
4423 struct chanNode
*channel
;
4424 struct mod_chanmode change
;
4426 if(!suspended
->expires
|| (now
< suspended
->expires
))
4427 suspended
->revoked
= now
;
4428 channel
= suspended
->cData
->channel
;
4429 suspended
->cData
->channel
= channel
;
4430 suspended
->cData
->flags
&= ~CHANNEL_SUSPENDED
;
4431 mod_chanmode_init(&change
);
4433 change
.args
[0].mode
= MODE_CHANOP
;
4434 change
.args
[0].member
= AddChannelUser(chanserv
, channel
);
4435 mod_chanmode_announce(chanserv
, channel
, &change
);
4438 static CHANSERV_FUNC(cmd_csuspend
)
4440 struct suspended
*suspended
;
4441 char reason
[MAXLEN
];
4442 time_t expiry
, duration
;
4443 struct userData
*uData
;
4447 if(IsProtected(channel
->channel_info
))
4449 reply("CSMSG_SUSPEND_NODELETE", channel
->name
);
4453 if(argv
[1][0] == '!')
4455 else if(IsSuspended(channel
->channel_info
))
4457 reply("CSMSG_ALREADY_SUSPENDED", channel
->name
);
4458 show_suspension_info(cmd
, user
, channel
->channel_info
->suspended
);
4462 if(!strcmp(argv
[1], "0"))
4464 else if((duration
= ParseInterval(argv
[1])))
4465 expiry
= now
+ duration
;
4468 reply("MSG_INVALID_DURATION", argv
[1]);
4472 unsplit_string(argv
+ 2, argc
- 2, reason
);
4474 suspended
= calloc(1, sizeof(*suspended
));
4475 suspended
->revoked
= 0;
4476 suspended
->issued
= now
;
4477 suspended
->suspender
= strdup(user
->handle_info
->handle
);
4478 suspended
->expires
= expiry
;
4479 suspended
->reason
= strdup(reason
);
4480 suspended
->cData
= channel
->channel_info
;
4481 suspended
->previous
= suspended
->cData
->suspended
;
4482 suspended
->cData
->suspended
= suspended
;
4484 if(suspended
->expires
)
4485 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
4487 if(IsSuspended(channel
->channel_info
))
4489 suspended
->previous
->revoked
= now
;
4490 if(suspended
->previous
->expires
)
4491 timeq_del(suspended
->previous
->expires
, chanserv_expire_suspension
, suspended
->previous
, 0);
4492 sprintf(reason
, "%s suspension modified by %s.", channel
->name
, suspended
->suspender
);
4493 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4497 /* Mark all users in channel as absent. */
4498 for(uData
= channel
->channel_info
->users
; uData
; uData
= uData
->next
)
4507 /* Mark the channel as suspended, then part. */
4508 channel
->channel_info
->flags
|= CHANNEL_SUSPENDED
;
4509 DelChannelUser(chanserv
, channel
, suspended
->reason
, 0);
4510 reply("CSMSG_SUSPENDED", channel
->name
);
4511 sprintf(reason
, "%s suspended by %s.", channel
->name
, suspended
->suspender
);
4512 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
4517 static CHANSERV_FUNC(cmd_cunsuspend
)
4519 struct suspended
*suspended
;
4520 char message
[MAXLEN
];
4522 if(!IsSuspended(channel
->channel_info
))
4524 reply("CSMSG_NOT_SUSPENDED", channel
->name
);
4528 suspended
= channel
->channel_info
->suspended
;
4530 /* Expire the suspension and join ChanServ to the channel. */
4531 timeq_del(suspended
->expires
, chanserv_expire_suspension
, suspended
, 0);
4532 chanserv_expire_suspension(suspended
);
4533 reply("CSMSG_UNSUSPENDED", channel
->name
);
4534 sprintf(message
, "%s unsuspended by %s.", channel
->name
, user
->handle_info
->handle
);
4535 global_message(MESSAGE_RECIPIENT_OPERS
|MESSAGE_RECIPIENT_HELPERS
, message
);
4539 typedef struct chanservSearch
4547 unsigned long flags
;
4551 typedef void (*channel_search_func
)(struct chanData
*channel
, void *data
);
4554 chanserv_search_create(struct userNode
*user
, unsigned int argc
, char *argv
[])
4559 search
= malloc(sizeof(struct chanservSearch
));
4560 memset(search
, 0, sizeof(*search
));
4563 for(i
= 0; i
< argc
; i
++)
4565 /* Assume all criteria require arguments. */
4568 send_message(user
, chanserv
, "MSG_MISSING_PARAMS", argv
[i
]);
4572 if(!irccasecmp(argv
[i
], "name"))
4573 search
->name
= argv
[++i
];
4574 else if(!irccasecmp(argv
[i
], "registrar"))
4575 search
->registrar
= argv
[++i
];
4576 else if(!irccasecmp(argv
[i
], "unvisited"))
4577 search
->unvisited
= ParseInterval(argv
[++i
]);
4578 else if(!irccasecmp(argv
[i
], "registered"))
4579 search
->registered
= ParseInterval(argv
[++i
]);
4580 else if(!irccasecmp(argv
[i
], "flags"))
4583 if(!irccasecmp(argv
[i
], "nodelete"))
4584 search
->flags
|= CHANNEL_NODELETE
;
4585 else if(!irccasecmp(argv
[i
], "suspended"))
4586 search
->flags
|= CHANNEL_SUSPENDED
;
4589 send_message(user
, chanserv
, "CSMSG_INVALID_CFLAG", argv
[i
]);
4593 else if(!irccasecmp(argv
[i
], "limit"))
4594 search
->limit
= strtoul(argv
[++i
], NULL
, 10);
4597 send_message(user
, chanserv
, "MSG_INVALID_CRITERIA", argv
[i
]);
4602 if(search
->name
&& !strcmp(search
->name
, "*"))
4604 if(search
->registrar
&& !strcmp(search
->registrar
, "*"))
4605 search
->registrar
= 0;
4614 chanserv_channel_match(struct chanData
*channel
, search_t search
)
4616 const char *name
= channel
->channel
->name
;
4617 if((search
->name
&& !match_ircglob(name
, search
->name
)) ||
4618 (search
->registrar
&& !channel
->registrar
) ||
4619 (search
->registrar
&& !match_ircglob(channel
->registrar
, search
->registrar
)) ||
4620 (search
->unvisited
&& (now
- channel
->visited
) < search
->unvisited
) ||
4621 (search
->registered
&& (now
- channel
->registered
) > search
->registered
) ||
4622 (search
->flags
&& ((search
->flags
& channel
->flags
) != search
->flags
)))
4629 chanserv_channel_search(search_t search
, channel_search_func smf
, void *data
)
4631 struct chanData
*channel
;
4632 unsigned int matches
= 0;
4634 for(channel
= channelList
; channel
&& matches
< search
->limit
; channel
= channel
->next
)
4636 if(!chanserv_channel_match(channel
, search
))
4646 search_count(UNUSED_ARG(struct chanData
*channel
), UNUSED_ARG(void *data
))
4651 search_print(struct chanData
*channel
, void *data
)
4653 send_message_type(4, data
, chanserv
, "%s", channel
->channel
->name
);
4656 static CHANSERV_FUNC(cmd_search
)
4659 unsigned int matches
;
4660 channel_search_func action
;
4664 if(!irccasecmp(argv
[1], "count"))
4665 action
= search_count
;
4666 else if(!irccasecmp(argv
[1], "print"))
4667 action
= search_print
;
4670 reply("CSMSG_ACTION_INVALID", argv
[1]);
4674 search
= chanserv_search_create(user
, argc
- 2, argv
+ 2);
4678 if(action
== search_count
)
4679 search
->limit
= INT_MAX
;
4681 if(action
== search_print
)
4682 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4684 matches
= chanserv_channel_search(search
, action
, user
);
4687 reply("MSG_MATCH_COUNT", matches
);
4689 reply("MSG_NO_MATCHES");
4695 static CHANSERV_FUNC(cmd_unvisited
)
4697 struct chanData
*cData
;
4698 time_t interval
= chanserv_conf
.channel_expire_delay
;
4699 char buffer
[INTERVALLEN
];
4700 unsigned int limit
= 25, matches
= 0;
4704 interval
= ParseInterval(argv
[1]);
4706 limit
= atoi(argv
[2]);
4709 intervalString(buffer
, interval
, user
->handle_info
);
4710 reply("CSMSG_UNVISITED_HEADER", limit
, buffer
);
4712 for(cData
= channelList
; cData
&& matches
< limit
; cData
= cData
->next
)
4714 if((now
- cData
->visited
) < interval
)
4717 intervalString(buffer
, now
- cData
->visited
, user
->handle_info
);
4718 reply("CSMSG_UNVISITED_DATA", cData
->channel
->name
, buffer
);
4725 static MODCMD_FUNC(chan_opt_defaulttopic
)
4731 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4733 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4737 topic
= unsplit_string(argv
+1, argc
-1, NULL
);
4739 free(channel
->channel_info
->topic
);
4740 if(topic
[0] == '*' && topic
[1] == 0)
4742 topic
= channel
->channel_info
->topic
= NULL
;
4746 topic
= channel
->channel_info
->topic
= strdup(topic
);
4747 if(channel
->channel_info
->topic_mask
4748 && !match_ircglob(channel
->channel_info
->topic
, channel
->channel_info
->topic_mask
))
4749 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4751 SetChannelTopic(channel
, chanserv
, topic
? topic
: "", 1);
4754 if(channel
->channel_info
->topic
)
4755 reply("CSMSG_SET_DEFAULT_TOPIC", channel
->channel_info
->topic
);
4757 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user
, "MSG_NONE"));
4761 static MODCMD_FUNC(chan_opt_topicmask
)
4765 struct chanData
*cData
= channel
->channel_info
;
4768 if(!check_user_level(channel
, user
, lvlEnfTopic
, 1, 0))
4770 reply("CSMSG_TOPIC_LOCKED", channel
->name
);
4774 mask
= unsplit_string(argv
+1, argc
-1, NULL
);
4776 if(cData
->topic_mask
)
4777 free(cData
->topic_mask
);
4778 if(mask
[0] == '*' && mask
[1] == 0)
4780 cData
->topic_mask
= 0;
4784 cData
->topic_mask
= strdup(mask
);
4786 reply("CSMSG_MASK_BUT_NO_TOPIC", channel
->name
);
4787 else if(!match_ircglob(cData
->topic
, cData
->topic_mask
))
4788 reply("CSMSG_TOPIC_MISMATCH", channel
->name
);
4792 if(channel
->channel_info
->topic_mask
)
4793 reply("CSMSG_SET_TOPICMASK", channel
->channel_info
->topic_mask
);
4795 reply("CSMSG_SET_TOPICMASK", user_find_message(user
, "MSG_NONE"));
4799 int opt_greeting_common(struct userNode
*user
, struct svccmd
*cmd
, int argc
, char *argv
[], char *name
, char **data
)
4803 char *greeting
= unsplit_string(argv
+1, argc
-1, NULL
);
4807 if(greeting
[0] == '*' && greeting
[1] == 0)
4811 unsigned int length
= strlen(greeting
);
4812 if(length
> chanserv_conf
.greeting_length
)
4814 reply("CSMSG_GREETING_TOO_LONG", length
, chanserv_conf
.greeting_length
);
4817 *data
= strdup(greeting
);
4826 reply(name
, user_find_message(user
, "MSG_NONE"));
4830 static MODCMD_FUNC(chan_opt_greeting
)
4832 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_GREETING", &channel
->channel_info
->greeting
);
4835 static MODCMD_FUNC(chan_opt_usergreeting
)
4837 return opt_greeting_common(user
, cmd
, argc
, argv
, "CSMSG_SET_USERGREETING", &channel
->channel_info
->user_greeting
);
4840 static MODCMD_FUNC(chan_opt_modes
)
4842 struct mod_chanmode
*new_modes
;
4843 char modes
[MODELEN
];
4847 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0))
4849 reply("CSMSG_NO_ACCESS");
4852 if(argv
[1][0] == '*' && argv
[1][1] == 0)
4854 memset(&channel
->channel_info
->modes
, 0, sizeof(channel
->channel_info
->modes
));
4856 else if(!(new_modes
= mod_chanmode_parse(channel
, argv
+1, argc
-1, MCP_KEY_FREE
)))
4858 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4861 else if(new_modes
->argc
> 1)
4863 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv
+1, argc
-1, NULL
));
4864 mod_chanmode_free(new_modes
);
4869 channel
->channel_info
->modes
= *new_modes
;
4870 modcmd_chanmode_announce(new_modes
);
4871 mod_chanmode_free(new_modes
);
4875 mod_chanmode_format(&channel
->channel_info
->modes
, modes
);
4877 reply("CSMSG_SET_MODES", modes
);
4879 reply("CSMSG_SET_MODES", user_find_message(user
, "MSG_NONE"));
4883 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4885 channel_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
4887 struct chanData
*cData
= channel
->channel_info
;
4892 /* Set flag according to value. */
4893 if(enabled_string(argv
[1]))
4895 cData
->flags
|= mask
;
4898 else if(disabled_string(argv
[1]))
4900 cData
->flags
&= ~mask
;
4905 reply("MSG_INVALID_BINARY", argv
[1]);
4911 /* Find current option value. */
4912 value
= (cData
->flags
& mask
) ? 1 : 0;
4916 reply(name
, user_find_message(user
, "MSG_ON"));
4918 reply(name
, user_find_message(user
, "MSG_OFF"));
4922 static MODCMD_FUNC(chan_opt_nodelete
)
4924 if((argc
> 1) && (!IsOper(user
) || !user
->handle_info
|| (user
->handle_info
->opserv_level
< chanserv_conf
.nodelete_level
)))
4926 reply("MSG_SETTING_PRIVILEGED", argv
[0]);
4930 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE
);
4933 static MODCMD_FUNC(chan_opt_dynlimit
)
4935 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT
);
4938 static MODCMD_FUNC(chan_opt_offchannel
)
4940 struct chanData
*cData
= channel
->channel_info
;
4945 /* Set flag according to value. */
4946 if(enabled_string(argv
[1]))
4948 if(!IsOffChannel(cData
))
4949 DelChannelUser(chanserv
, channel
, "Going off-channel.", 0);
4950 cData
->flags
|= CHANNEL_OFFCHANNEL
;
4953 else if(disabled_string(argv
[1]))
4955 if(IsOffChannel(cData
))
4957 struct mod_chanmode change
;
4958 mod_chanmode_init(&change
);
4960 change
.args
[0].mode
= MODE_CHANOP
;
4961 change
.args
[0].member
= AddChannelUser(chanserv
, channel
);
4962 mod_chanmode_announce(chanserv
, channel
, &change
);
4964 cData
->flags
&= ~CHANNEL_OFFCHANNEL
;
4969 reply("MSG_INVALID_BINARY", argv
[1]);
4975 /* Find current option value. */
4976 value
= (cData
->flags
& CHANNEL_OFFCHANNEL
) ? 1 : 0;
4980 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_ON"));
4982 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user
, "MSG_OFF"));
4986 static MODCMD_FUNC(chan_opt_defaults
)
4988 struct userData
*uData
;
4989 struct chanData
*cData
;
4990 const char *confirm
;
4991 enum levelOption lvlOpt
;
4992 enum charOption chOpt
;
4994 cData
= channel
->channel_info
;
4995 uData
= GetChannelUser(cData
, user
->handle_info
);
4996 if(!uData
|| (uData
->access
< UL_OWNER
))
4998 reply("CSMSG_OWNER_DEFAULTS", channel
->name
);
5001 confirm
= make_confirmation_string(uData
);
5002 if((argc
< 2) || strcmp(argv
[1], confirm
))
5004 reply("CSMSG_CONFIRM_DEFAULTS", channel
->name
, confirm
);
5007 cData
->flags
= CHANNEL_DEFAULT_FLAGS
;
5008 cData
->modes
= chanserv_conf
.default_modes
;
5009 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
5010 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
5011 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
5012 cData
->chOpts
[chOpt
] = charOptions
[chOpt
].default_value
;
5013 reply("CSMSG_SETTINGS_DEFAULTED", channel
->name
);
5018 channel_level_option(enum levelOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5020 struct chanData
*cData
= channel
->channel_info
;
5021 struct userData
*uData
;
5022 unsigned short value
;
5026 if(!check_user_level(channel
, user
, option
, 1, 1))
5028 reply("CSMSG_CANNOT_SET");
5031 value
= user_level_from_name(argv
[1], UL_OWNER
+1);
5032 if(!value
&& strcmp(argv
[1], "0"))
5034 reply("CSMSG_INVALID_ACCESS", argv
[1]);
5037 uData
= GetChannelUser(cData
, user
->handle_info
);
5038 if(!uData
|| ((uData
->access
< UL_OWNER
) && (value
> uData
->access
)))
5040 reply("CSMSG_BAD_SETLEVEL");
5046 if(value
> cData
->lvlOpts
[lvlGiveOps
])
5048 reply("CSMSG_BAD_GIVEVOICE", cData
->lvlOpts
[lvlGiveOps
]);
5053 if(value
< cData
->lvlOpts
[lvlGiveVoice
])
5055 reply("CSMSG_BAD_GIVEOPS", cData
->lvlOpts
[lvlGiveVoice
]);
5060 /* This test only applies to owners, since non-owners
5061 * trying to set an option to above their level get caught
5062 * by the CSMSG_BAD_SETLEVEL test above.
5064 if(value
> uData
->access
)
5066 reply("CSMSG_BAD_SETTERS");
5073 cData
->lvlOpts
[option
] = value
;
5075 reply(levelOptions
[option
].format_name
, cData
->lvlOpts
[option
]);
5079 static MODCMD_FUNC(chan_opt_enfops
)
5081 return channel_level_option(lvlEnfOps
, CSFUNC_ARGS
);
5084 static MODCMD_FUNC(chan_opt_giveops
)
5086 return channel_level_option(lvlGiveOps
, CSFUNC_ARGS
);
5089 static MODCMD_FUNC(chan_opt_enfmodes
)
5091 return channel_level_option(lvlEnfModes
, CSFUNC_ARGS
);
5094 static MODCMD_FUNC(chan_opt_enftopic
)
5096 return channel_level_option(lvlEnfTopic
, CSFUNC_ARGS
);
5099 static MODCMD_FUNC(chan_opt_pubcmd
)
5101 return channel_level_option(lvlPubCmd
, CSFUNC_ARGS
);
5104 static MODCMD_FUNC(chan_opt_setters
)
5106 return channel_level_option(lvlSetters
, CSFUNC_ARGS
);
5109 static MODCMD_FUNC(chan_opt_ctcpusers
)
5111 return channel_level_option(lvlCTCPUsers
, CSFUNC_ARGS
);
5114 static MODCMD_FUNC(chan_opt_userinfo
)
5116 return channel_level_option(lvlUserInfo
, CSFUNC_ARGS
);
5119 static MODCMD_FUNC(chan_opt_givevoice
)
5121 return channel_level_option(lvlGiveVoice
, CSFUNC_ARGS
);
5124 static MODCMD_FUNC(chan_opt_topicsnarf
)
5126 return channel_level_option(lvlTopicSnarf
, CSFUNC_ARGS
);
5129 static MODCMD_FUNC(chan_opt_inviteme
)
5131 return channel_level_option(lvlInviteMe
, CSFUNC_ARGS
);
5135 channel_multiple_option(enum charOption option
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5137 struct chanData
*cData
= channel
->channel_info
;
5138 int count
= charOptions
[option
].count
, index
;
5142 index
= atoi(argv
[1]);
5144 if(!isdigit(argv
[1][0]) || (index
< 0) || (index
>= count
))
5146 reply("CSMSG_INVALID_NUMERIC", index
);
5147 /* Show possible values. */
5148 for(index
= 0; index
< count
; index
++)
5149 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5153 cData
->chOpts
[option
] = charOptions
[option
].values
[index
].value
;
5157 /* Find current option value. */
5160 (index
< count
) && (cData
->chOpts
[option
] != charOptions
[option
].values
[index
].value
);
5164 /* Somehow, the option value is corrupt; reset it to the default. */
5165 cData
->chOpts
[option
] = charOptions
[option
].default_value
;
5170 reply(charOptions
[option
].format_name
, index
, user_find_message(user
, charOptions
[option
].values
[index
].format_name
));
5174 static MODCMD_FUNC(chan_opt_protect
)
5176 return channel_multiple_option(chProtect
, CSFUNC_ARGS
);
5179 static MODCMD_FUNC(chan_opt_toys
)
5181 return channel_multiple_option(chToys
, CSFUNC_ARGS
);
5184 static MODCMD_FUNC(chan_opt_ctcpreaction
)
5186 return channel_multiple_option(chCTCPReaction
, CSFUNC_ARGS
);
5189 static MODCMD_FUNC(chan_opt_topicrefresh
)
5191 return channel_multiple_option(chTopicRefresh
, CSFUNC_ARGS
);
5194 static struct svccmd_list set_shows_list
;
5197 handle_svccmd_unbind(struct svccmd
*target
) {
5199 for(ii
=0; ii
<set_shows_list
.used
; ++ii
)
5200 if(target
== set_shows_list
.list
[ii
])
5201 set_shows_list
.used
= 0;
5204 static CHANSERV_FUNC(cmd_set
)
5206 struct svccmd
*subcmd
;
5210 /* Check if we need to (re-)initialize set_shows_list. */
5211 if(!set_shows_list
.used
)
5213 if(!set_shows_list
.size
)
5215 set_shows_list
.size
= chanserv_conf
.set_shows
->used
;
5216 set_shows_list
.list
= calloc(set_shows_list
.size
, sizeof(set_shows_list
.list
[0]));
5218 for(ii
= 0; ii
< chanserv_conf
.set_shows
->used
; ii
++)
5220 const char *name
= chanserv_conf
.set_shows
->list
[ii
];
5221 sprintf(buf
, "%s %s", argv
[0], name
);
5222 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5225 log_module(CS_LOG
, LOG_ERROR
, "Unable to find set option \"%s\".", name
);
5228 svccmd_list_append(&set_shows_list
, subcmd
);
5234 reply("CSMSG_CHANNEL_OPTIONS");
5235 for(ii
= 0; ii
< set_shows_list
.used
; ii
++)
5237 subcmd
= set_shows_list
.list
[ii
];
5238 subcmd
->command
->func(user
, channel
, 1, argv
+1, subcmd
);
5243 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5244 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5247 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5250 if((argc
> 2) && !check_user_level(channel
, user
, lvlSetters
, 1, 0))
5252 reply("CSMSG_NO_ACCESS");
5256 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5260 user_binary_option(char *name
, unsigned long mask
, struct userNode
*user
, struct chanNode
*channel
, int argc
, char *argv
[], struct svccmd
*cmd
)
5262 struct userData
*uData
;
5264 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5267 reply("CSMSG_NOT_USER", channel
->name
);
5273 /* Just show current option value. */
5275 else if(enabled_string(argv
[1]))
5277 uData
->flags
|= mask
;
5279 else if(disabled_string(argv
[1]))
5281 uData
->flags
&= ~mask
;
5285 reply("MSG_INVALID_BINARY", argv
[1]);
5289 reply(name
, user_find_message(user
, (uData
->flags
& mask
) ? "MSG_ON" : "MSG_OFF"));
5293 static MODCMD_FUNC(user_opt_noautoop
)
5295 struct userData
*uData
;
5297 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5300 reply("CSMSG_NOT_USER", channel
->name
);
5303 if(uData
->access
< channel
->channel_info
->lvlOpts
[lvlGiveOps
])
5304 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP
, CSFUNC_ARGS
);
5306 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP
, CSFUNC_ARGS
);
5309 static MODCMD_FUNC(user_opt_autoinvite
)
5311 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE
, CSFUNC_ARGS
);
5314 static MODCMD_FUNC(user_opt_info
)
5316 struct userData
*uData
;
5319 uData
= GetChannelAccess(channel
->channel_info
, user
->handle_info
);
5323 /* If they got past the command restrictions (which require access)
5324 * but fail this test, we have some fool with security override on.
5326 reply("CSMSG_NOT_USER", channel
->name
);
5333 infoline
= unsplit_string(argv
+ 1, argc
- 1, NULL
);
5334 if(strlen(infoline
) > chanserv_conf
.max_userinfo_length
)
5336 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf
.max_userinfo_length
);
5339 bp
= strcspn(infoline
, "\001");
5342 reply("CSMSG_BAD_INFOLINE", infoline
[bp
]);
5347 if(infoline
[0] == '*' && infoline
[1] == 0)
5350 uData
->info
= strdup(infoline
);
5353 reply("CSMSG_USET_INFO", uData
->info
);
5355 reply("CSMSG_USET_INFO", user_find_message(user
, "MSG_NONE"));
5359 struct svccmd_list uset_shows_list
;
5361 static CHANSERV_FUNC(cmd_uset
)
5363 struct svccmd
*subcmd
;
5367 /* Check if we need to (re-)initialize uset_shows_list. */
5368 if(!uset_shows_list
.used
)
5372 "NoAutoOp", "AutoInvite", "Info"
5375 if(!uset_shows_list
.size
)
5377 uset_shows_list
.size
= ArrayLength(options
);
5378 uset_shows_list
.list
= calloc(uset_shows_list
.size
, sizeof(uset_shows_list
.list
[0]));
5380 for(ii
= 0; ii
< ArrayLength(options
); ii
++)
5382 const char *name
= options
[ii
];
5383 sprintf(buf
, "%s %s", argv
[0], name
);
5384 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5387 log_module(CS_LOG
, LOG_ERROR
, "Unable to find uset option %s.", name
);
5390 svccmd_list_append(&uset_shows_list
, subcmd
);
5396 /* Do this so options are presented in a consistent order. */
5397 reply("CSMSG_USER_OPTIONS");
5398 for(ii
= 0; ii
< uset_shows_list
.used
; ii
++)
5399 uset_shows_list
.list
[ii
]->command
->func(user
, channel
, 1, argv
+1, uset_shows_list
.list
[ii
]);
5403 sprintf(buf
, "%s %s", argv
[0], argv
[1]);
5404 subcmd
= dict_find(cmd
->parent
->commands
, buf
, NULL
);
5407 reply("CSMSG_INVALID_OPTION", argv
[1], argv
[0]);
5411 return subcmd
->command
->func(user
, channel
, argc
- 1, argv
+ 1, subcmd
);
5414 static CHANSERV_FUNC(cmd_giveownership
)
5416 struct handle_info
*new_owner_hi
;
5417 struct userData
*new_owner
, *curr_user
;
5418 struct chanData
*cData
= channel
->channel_info
;
5419 struct do_not_register
*dnr
;
5421 unsigned short co_access
;
5422 char reason
[MAXLEN
];
5425 curr_user
= GetChannelAccess(cData
, user
->handle_info
);
5426 force
= IsHelping(user
) && (argc
> 2) && !irccasecmp(argv
[2], "force");
5427 if(!curr_user
|| (curr_user
->access
!= UL_OWNER
))
5429 struct userData
*owner
= NULL
;
5430 for(curr_user
= channel
->channel_info
->users
;
5432 curr_user
= curr_user
->next
)
5434 if(curr_user
->access
!= UL_OWNER
)
5438 reply("CSMSG_MULTIPLE_OWNERS", channel
->name
);
5445 if(!(new_owner_hi
= modcmd_get_handle_info(user
, argv
[1])))
5447 if(new_owner_hi
== user
->handle_info
)
5449 reply("CSMSG_NO_TRANSFER_SELF");
5452 new_owner
= GetChannelAccess(cData
, new_owner_hi
);
5455 reply("CSMSG_NO_CHAN_USER", new_owner_hi
->handle
, channel
->name
);
5458 if((chanserv_get_owned_count(new_owner_hi
) >= chanserv_conf
.max_owned
) && !force
)
5460 reply("CSMSG_OWN_TOO_MANY", new_owner_hi
->handle
, chanserv_conf
.max_owned
);
5463 if((dnr
= chanserv_is_dnr(NULL
, new_owner_hi
)) && !force
) {
5464 if(!IsHelping(user
))
5465 reply("CSMSG_DNR_ACCOUNT", new_owner_hi
->handle
);
5467 chanserv_show_dnrs(user
, cmd
, NULL
, new_owner_hi
);
5470 if(new_owner
->access
>= UL_COOWNER
)
5471 co_access
= new_owner
->access
;
5473 co_access
= UL_COOWNER
;
5474 new_owner
->access
= UL_OWNER
;
5476 curr_user
->access
= co_access
;
5477 reply("CSMSG_OWNERSHIP_GIVEN", channel
->name
, new_owner_hi
->handle
);
5478 sprintf(reason
, "%s ownership transferred to %s by %s.", channel
->name
, new_owner_hi
->handle
, user
->handle_info
->handle
);
5479 global_message(MESSAGE_RECIPIENT_OPERS
| MESSAGE_RECIPIENT_HELPERS
, reason
);
5483 static CHANSERV_FUNC(cmd_suspend
)
5485 struct handle_info
*hi
;
5486 struct userData
*self
, *target
;
5489 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5490 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5491 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5493 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5496 if(target
->access
>= self
->access
)
5498 reply("MSG_USER_OUTRANKED", hi
->handle
);
5501 if(target
->flags
& USER_SUSPENDED
)
5503 reply("CSMSG_ALREADY_SUSPENDED", hi
->handle
);
5508 target
->present
= 0;
5511 target
->flags
|= USER_SUSPENDED
;
5512 reply("CSMSG_USER_SUSPENDED", hi
->handle
, channel
->name
);
5516 static CHANSERV_FUNC(cmd_unsuspend
)
5518 struct handle_info
*hi
;
5519 struct userData
*self
, *target
;
5522 if(!(hi
= modcmd_get_handle_info(user
, argv
[1]))) return 0;
5523 self
= GetChannelUser(channel
->channel_info
, user
->handle_info
);
5524 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5526 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5529 if(target
->access
>= self
->access
)
5531 reply("MSG_USER_OUTRANKED", hi
->handle
);
5534 if(!(target
->flags
& USER_SUSPENDED
))
5536 reply("CSMSG_NOT_SUSPENDED", hi
->handle
);
5539 target
->flags
&= ~USER_SUSPENDED
;
5540 reply("CSMSG_USER_UNSUSPENDED", hi
->handle
, channel
->name
);
5544 static MODCMD_FUNC(cmd_deleteme
)
5546 struct handle_info
*hi
;
5547 struct userData
*target
;
5548 const char *confirm_string
;
5549 unsigned short access
;
5552 hi
= user
->handle_info
;
5553 if(!(target
= GetTrueChannelAccess(channel
->channel_info
, hi
)))
5555 reply("CSMSG_NO_CHAN_USER", hi
->handle
, channel
->name
);
5558 if(target
->access
== UL_OWNER
)
5560 reply("CSMSG_NO_OWNER_DELETEME", channel
->name
);
5563 confirm_string
= make_confirmation_string(target
);
5564 if((argc
< 2) || strcmp(argv
[1], confirm_string
))
5566 reply("CSMSG_CONFIRM_DELETEME", confirm_string
);
5569 access
= target
->access
;
5570 channel_name
= strdup(channel
->name
);
5571 del_channel_user(target
, 1);
5572 reply("CSMSG_DELETED_YOU", access
, channel_name
);
5578 chanserv_refresh_topics(UNUSED_ARG(void *data
))
5580 unsigned int refresh_num
= (now
- self
->link
) / chanserv_conf
.refresh_period
;
5581 struct chanData
*cData
;
5584 for(cData
= channelList
; cData
; cData
= cData
->next
)
5586 if(IsSuspended(cData
))
5588 opt
= cData
->chOpts
[chTopicRefresh
];
5591 if((refresh_num
- cData
->last_refresh
) < (unsigned int)(1 << (opt
- '1')))
5594 SetChannelTopic(cData
->channel
, chanserv
, cData
->topic
, 1);
5595 cData
->last_refresh
= refresh_num
;
5597 timeq_add(now
+ chanserv_conf
.refresh_period
, chanserv_refresh_topics
, NULL
);
5600 static CHANSERV_FUNC(cmd_unf
)
5604 char response
[MAXLEN
];
5605 const char *fmt
= user_find_message(user
, "CSMSG_UNF_RESPONSE");
5606 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5607 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5610 reply("CSMSG_UNF_RESPONSE");
5614 static CHANSERV_FUNC(cmd_ping
)
5618 char response
[MAXLEN
];
5619 const char *fmt
= user_find_message(user
, "CSMSG_PING_RESPONSE");
5620 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5621 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5624 reply("CSMSG_PING_RESPONSE");
5628 static CHANSERV_FUNC(cmd_wut
)
5632 char response
[MAXLEN
];
5633 const char *fmt
= user_find_message(user
, "CSMSG_WUT_RESPONSE");
5634 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, fmt
);
5635 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5638 reply("CSMSG_WUT_RESPONSE");
5642 static CHANSERV_FUNC(cmd_8ball
)
5644 unsigned int i
, j
, accum
;
5649 for(i
=1; i
<argc
; i
++)
5650 for(j
=0; argv
[i
][j
]; j
++)
5651 accum
= (accum
<< 5) - accum
+ toupper(argv
[i
][j
]);
5652 resp
= chanserv_conf
.eightball
->list
[accum
% chanserv_conf
.eightball
->used
];
5655 char response
[MAXLEN
];
5656 sprintf(response
, "\ 2%s\ 2: %s", user
->nick
, resp
);
5657 irc_privmsg(cmd
->parent
->bot
, channel
->name
, response
);
5660 send_message_type(4, user
, cmd
->parent
->bot
, "%s", resp
);
5664 static CHANSERV_FUNC(cmd_d
)
5666 unsigned long sides
, count
, modifier
, ii
, total
;
5667 char response
[MAXLEN
], *sep
;
5671 if((count
= strtoul(argv
[1], &sep
, 10)) < 1)
5681 else if(((sep
[0] == 'd') || (sep
[0] == 'D')) && isdigit(sep
[1])
5682 && (sides
= strtoul(sep
+1, &sep
, 10)) > 1)
5686 else if((sep
[0] == '-') && isdigit(sep
[1]))
5687 modifier
= strtoul(sep
, NULL
, 10);
5688 else if((sep
[0] == '+') && isdigit(sep
[1]))
5689 modifier
= strtoul(sep
+1, NULL
, 10);
5696 reply("CSMSG_BAD_DIE_FORMAT", argv
[1]);
5701 reply("CSMSG_BAD_DICE_COUNT", count
, 10);
5704 for(total
= ii
= 0; ii
< count
; ++ii
)
5705 total
+= (rand() % sides
) + 1;
5708 if((count
> 1) || modifier
)
5710 fmt
= user_find_message(user
, "CSMSG_DICE_ROLL");
5711 sprintf(response
, fmt
, total
, count
, sides
, modifier
);
5715 fmt
= user_find_message(user
, "CSMSG_DIE_ROLL");
5716 sprintf(response
, fmt
, total
, sides
);
5719 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5721 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5725 static CHANSERV_FUNC(cmd_huggle
)
5727 /* CTCP must be via PRIVMSG, never notice */
5729 send_target_message(1, channel
->name
, cmd
->parent
->bot
, "CSMSG_HUGGLES_HIM", user
->nick
);
5731 send_target_message(1, user
->nick
, cmd
->parent
->bot
, "CSMSG_HUGGLES_YOU");
5735 static CHANSERV_FUNC(cmd_calc
)
5737 char response
[MAXLEN
];
5740 do_math(response
, unsplit_string(argv
+ 1, argc
- 1, NULL
));
5743 send_channel_message(channel
, cmd
->parent
->bot
, "$b%s$b: %s", user
->nick
, response
);
5745 send_message_type(4, user
, cmd
->parent
->bot
, "%s", response
);
5750 chanserv_adjust_limit(void *data
)
5752 struct mod_chanmode change
;
5753 struct chanData
*cData
= data
;
5754 struct chanNode
*channel
= cData
->channel
;
5757 if(IsSuspended(cData
))
5760 cData
->limitAdjusted
= now
;
5761 limit
= channel
->members
.used
+ chanserv_conf
.adjust_threshold
+ 5;
5762 if(cData
->modes
.modes_set
& MODE_LIMIT
)
5764 if(limit
> cData
->modes
.new_limit
)
5765 limit
= cData
->modes
.new_limit
;
5766 else if(limit
== cData
->modes
.new_limit
)
5770 mod_chanmode_init(&change
);
5771 change
.modes_set
= MODE_LIMIT
;
5772 change
.new_limit
= limit
;
5773 mod_chanmode_announce(chanserv
, channel
, &change
);
5777 handle_new_channel(struct chanNode
*channel
)
5779 struct chanData
*cData
;
5781 if(!(cData
= channel
->channel_info
))
5784 if(cData
->modes
.modes_set
|| cData
->modes
.modes_clear
)
5785 mod_chanmode_announce(chanserv
, cData
->channel
, &cData
->modes
);
5787 if(self
->uplink
&& !self
->uplink
->burst
&& channel
->channel_info
->topic
)
5788 SetChannelTopic(channel
, chanserv
, channel
->channel_info
->topic
, 1);
5791 /* Welcome to my worst nightmare. Warning: Read (or modify)
5792 the code below at your own risk. */
5794 handle_join(struct modeNode
*mNode
)
5796 struct mod_chanmode change
;
5797 struct userNode
*user
= mNode
->user
;
5798 struct chanNode
*channel
= mNode
->channel
;
5799 struct chanData
*cData
;
5800 struct userData
*uData
= NULL
;
5801 struct banData
*bData
;
5802 struct handle_info
*handle
;
5803 unsigned int modes
= 0, info
= 0;
5806 if(IsLocal(user
) || !channel
->channel_info
|| IsSuspended(channel
->channel_info
))
5809 cData
= channel
->channel_info
;
5810 if(channel
->members
.used
> cData
->max
)
5811 cData
->max
= channel
->members
.used
;
5813 /* Check for bans. If they're joining through a ban, one of two
5815 * 1: Join during a netburst, by riding the break. Kick them
5816 * unless they have ops or voice in the channel.
5817 * 2: They're allowed to join through the ban (an invite in
5818 * ircu2.10, or a +e on Hybrid, or something).
5819 * If they're not joining through a ban, and the banlist is not
5820 * full, see if they're on the banlist for the channel. If so,
5823 /* This is really, really stupid. not all banned people are kicked.
5824 * sometimes we like to leave them unkicked.
5825 * I tried to explain this to the srvx developers and
5826 * got insulted.. hence one reason for this fork.
5828 if(user->uplink->burst && !mNode->modes)
5831 for(ii = 0; ii < channel->banlist.used; ii++)
5833 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5835 ** Riding a netburst. Naughty. **
5836 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5843 mod_chanmode_init(&change
);
5845 if(channel
->banlist
.used
< MAXBANS
)
5847 /* Not joining through a ban. */
5848 for(bData
= cData
->bans
;
5849 bData
&& !user_matches_glob(user
, bData
->mask
, 1);
5850 bData
= bData
->next
);
5854 char kick_reason
[MAXLEN
];
5855 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
5857 bData
->triggered
= now
;
5858 if(bData
!= cData
->bans
)
5860 /* Shuffle the ban to the head of the list. */
5862 bData
->next
->prev
= bData
->prev
;
5864 bData
->prev
->next
= bData
->next
;
5867 bData
->next
= cData
->bans
;
5870 cData
->bans
->prev
= bData
;
5871 cData
->bans
= bData
;
5874 change
.args
[0].mode
= MODE_BAN
;
5875 change
.args
[0].hostmask
= bData
->mask
;
5876 mod_chanmode_announce(chanserv
, channel
, &change
);
5877 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
5882 /* ChanServ will not modify the limits in join-flooded channels.
5883 It will also skip DynLimit processing when the user (or srvx)
5884 is bursting in, because there are likely more incoming. */
5885 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
5886 && !user
->uplink
->burst
5887 && !channel
->join_flooded
5888 && (channel
->limit
- channel
->members
.used
) < chanserv_conf
.adjust_threshold
)
5890 /* The user count has begun "bumping" into the channel limit,
5891 so set a timer to raise the limit a bit. Any previous
5892 timers are removed so three incoming users within the delay
5893 results in one limit change, not three. */
5895 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
5896 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
5899 if(channel
->join_flooded
)
5901 /* don't automatically give ops or voice during a join flood */
5903 else if(cData
->lvlOpts
[lvlGiveOps
] == 0)
5904 modes
|= MODE_CHANOP
;
5905 else if(cData
->lvlOpts
[lvlGiveVoice
] == 0)
5906 modes
|= MODE_VOICE
;
5908 greeting
= cData
->greeting
;
5909 if(user
->handle_info
)
5911 handle
= user
->handle_info
;
5913 if(IsHelper(user
) && !IsHelping(user
))
5916 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
5918 if(channel
== chanserv_conf
.support_channels
.list
[ii
])
5920 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
5926 uData
= GetTrueChannelAccess(cData
, handle
);
5927 if(uData
&& !IsUserSuspended(uData
))
5929 /* Ops and above were handled by the above case. */
5930 if(IsUserAutoOp(uData
))
5932 if(uData
->access
>= cData
->lvlOpts
[lvlGiveOps
])
5933 modes
|= MODE_CHANOP
;
5934 else if(uData
->access
>= cData
->lvlOpts
[lvlGiveVoice
])
5935 modes
|= MODE_VOICE
;
5937 if(uData
->access
>= UL_PRESENT
)
5938 cData
->visited
= now
;
5939 if(cData
->user_greeting
)
5940 greeting
= cData
->user_greeting
;
5942 && (uData
->access
>= cData
->lvlOpts
[lvlUserInfo
])
5943 && ((now
- uData
->seen
) >= chanserv_conf
.info_delay
)
5950 if(!user
->uplink
->burst
)
5954 if(modes
& MODE_CHANOP
)
5955 modes
&= ~MODE_VOICE
;
5956 change
.args
[0].mode
= modes
;
5957 change
.args
[0].member
= mNode
;
5958 mod_chanmode_announce(chanserv
, channel
, &change
);
5960 if(greeting
&& !user
->uplink
->burst
)
5961 send_message_type(4, user
, chanserv
, "(%s) %s", channel
->name
, greeting
);
5963 send_target_message(5, channel
->name
, chanserv
, "[%s] %s", user
->nick
, uData
->info
);
5969 handle_auth(struct userNode
*user
, UNUSED_ARG(struct handle_info
*old_handle
))
5971 struct mod_chanmode change
;
5972 struct userData
*channel
;
5973 unsigned int ii
, jj
;
5975 if(!user
->handle_info
)
5978 mod_chanmode_init(&change
);
5980 for(channel
= user
->handle_info
->channels
; channel
; channel
= channel
->u_next
)
5982 struct chanNode
*cn
;
5983 struct modeNode
*mn
;
5984 if(IsUserSuspended(channel
)
5985 || IsSuspended(channel
->channel
)
5986 || !(cn
= channel
->channel
->channel
))
5989 mn
= GetUserMode(cn
, user
);
5992 if(!IsUserSuspended(channel
)
5993 && IsUserAutoInvite(channel
)
5994 && (channel
->access
>= channel
->channel
->lvlOpts
[lvlInviteMe
])
5996 && !user
->uplink
->burst
)
5997 irc_invite(chanserv
, user
, cn
);
6001 if(channel
->access
>= UL_PRESENT
)
6002 channel
->channel
->visited
= now
;
6004 if(IsUserAutoOp(channel
))
6006 if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveOps
])
6007 change
.args
[0].mode
= MODE_CHANOP
;
6008 else if(channel
->access
>= cn
->channel_info
->lvlOpts
[lvlGiveVoice
])
6009 change
.args
[0].mode
= MODE_VOICE
;
6011 change
.args
[0].mode
= 0;
6012 change
.args
[0].member
= mn
;
6013 if(change
.args
[0].mode
)
6014 mod_chanmode_announce(chanserv
, cn
, &change
);
6017 channel
->seen
= now
;
6018 channel
->present
= 1;
6021 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6023 struct chanNode
*channel
= user
->channels
.list
[ii
]->channel
;
6024 struct banData
*ban
;
6026 if((user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_VOICE
))
6027 || !channel
->channel_info
)
6029 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6030 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6032 if(jj
< channel
->banlist
.used
)
6034 for(ban
= channel
->channel_info
->bans
; ban
; ban
= ban
->next
)
6036 char kick_reason
[MAXLEN
];
6037 if(!user_matches_glob(user
, ban
->mask
, 1))
6039 change
.args
[0].mode
= MODE_BAN
;
6040 change
.args
[0].hostmask
= ban
->mask
;
6041 mod_chanmode_announce(chanserv
, channel
, &change
);
6042 sprintf(kick_reason
, "(%s) %s", ban
->owner
, ban
->reason
);
6043 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6044 ban
->triggered
= now
;
6049 if(IsSupportHelper(user
))
6051 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6053 if(GetUserMode(chanserv_conf
.support_channels
.list
[ii
], user
))
6055 HANDLE_SET_FLAG(user
->handle_info
, HELPING
);
6063 handle_part(struct modeNode
*mn
, UNUSED_ARG(const char *reason
))
6065 struct chanData
*cData
;
6066 struct userData
*uData
;
6068 cData
= mn
->channel
->channel_info
;
6069 if(!cData
|| IsSuspended(cData
) || IsLocal(mn
->user
))
6072 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
) && !mn
->channel
->join_flooded
)
6074 /* Allow for a bit of padding so that the limit doesn't
6075 track the user count exactly, which could get annoying. */
6076 if((mn
->channel
->limit
- mn
->channel
->members
.used
) > chanserv_conf
.adjust_threshold
+ 5)
6078 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6079 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6083 if((uData
= GetTrueChannelAccess(cData
, mn
->user
->handle_info
)))
6085 scan_user_presence(uData
, mn
->user
);
6089 if(IsHelping(mn
->user
) && IsSupportHelper(mn
->user
))
6091 unsigned int ii
, jj
;
6092 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6094 for(jj
= 0; jj
< mn
->user
->channels
.used
; ++jj
)
6095 if(mn
->user
->channels
.list
[jj
]->channel
== chanserv_conf
.support_channels
.list
[ii
])
6097 if(jj
< mn
->user
->channels
.used
)
6100 if(ii
== chanserv_conf
.support_channels
.used
)
6101 HANDLE_CLEAR_FLAG(mn
->user
->handle_info
, HELPING
);
6106 handle_kick(struct userNode
*kicker
, struct userNode
*victim
, struct chanNode
*channel
)
6108 struct userData
*uData
;
6110 if(!channel
->channel_info
|| !kicker
|| IsService(kicker
)
6111 || (kicker
== victim
) || IsSuspended(channel
->channel_info
)
6112 || (kicker
->handle_info
&& kicker
->handle_info
== victim
->handle_info
))
6115 if(protect_user(victim
, kicker
, channel
->channel_info
))
6117 const char *reason
= user_find_message(kicker
, "CSMSG_USER_PROTECTED");
6118 KickChannelUser(kicker
, channel
, chanserv
, reason
);
6121 if((uData
= GetTrueChannelAccess(channel
->channel_info
, victim
->handle_info
)))
6126 handle_topic(struct userNode
*user
, struct chanNode
*channel
, const char *old_topic
)
6128 struct chanData
*cData
;
6130 if(!channel
->channel_info
|| !user
|| IsSuspended(channel
->channel_info
) || IsService(user
))
6133 cData
= channel
->channel_info
;
6134 if(bad_topic(channel
, user
, channel
->topic
))
6136 send_message(user
, chanserv
, "CSMSG_TOPIC_LOCKED", channel
->name
);
6137 if(cData
->topic_mask
&& match_ircglob(old_topic
, cData
->topic_mask
))
6138 SetChannelTopic(channel
, chanserv
, old_topic
, 1);
6139 else if(cData
->topic
)
6140 SetChannelTopic(channel
, chanserv
, cData
->topic
, 1);
6143 /* With topicsnarf, grab the topic and save it as the default topic. */
6144 if(check_user_level(channel
, user
, lvlTopicSnarf
, 0, 0))
6147 cData
->topic
= strdup(channel
->topic
);
6153 handle_mode(struct chanNode
*channel
, struct userNode
*user
, const struct mod_chanmode
*change
)
6155 struct mod_chanmode
*bounce
= NULL
;
6156 unsigned int bnc
, ii
;
6159 if(!channel
->channel_info
|| IsLocal(user
) || IsSuspended(channel
->channel_info
) || IsService(user
))
6162 if(!check_user_level(channel
, user
, lvlEnfModes
, 1, 0)
6163 && mode_lock_violated(&channel
->channel_info
->modes
, change
))
6165 char correct
[MAXLEN
];
6166 bounce
= mod_chanmode_dup(&channel
->channel_info
->modes
, change
->argc
+ 1);
6167 mod_chanmode_format(&channel
->channel_info
->modes
, correct
);
6168 send_message(user
, chanserv
, "CSMSG_MODE_LOCKED", correct
, channel
->name
);
6170 for(ii
= bnc
= 0; ii
< change
->argc
; ++ii
)
6172 if((change
->args
[ii
].mode
& (MODE_REMOVE
|MODE_CHANOP
)) == (MODE_REMOVE
|MODE_CHANOP
))
6174 const struct userNode
*victim
= change
->args
[ii
].member
->user
;
6175 if(!protect_user(victim
, user
, channel
->channel_info
))
6178 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6181 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6182 bounce
->args
[bnc
].member
= GetUserMode(channel
, user
);
6183 if(bounce
->args
[bnc
].member
)
6187 bounce
->args
[bnc
].mode
= MODE_CHANOP
;
6188 bounce
->args
[bnc
].member
= change
->args
[ii
].member
;
6190 send_message(user
, chanserv
, "CSMSG_USER_PROTECTED", victim
->nick
);
6192 else if(change
->args
[ii
].mode
& MODE_CHANOP
)
6194 const struct userNode
*victim
= change
->args
[ii
].member
->user
;
6195 if(IsService(victim
) || validate_op(user
, channel
, (struct userNode
*)victim
))
6198 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6199 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_CHANOP
;
6200 bounce
->args
[bnc
].member
= change
->args
[ii
].member
;
6203 else if((change
->args
[ii
].mode
& (MODE_REMOVE
| MODE_BAN
)) == MODE_BAN
)
6205 const char *ban
= change
->args
[ii
].hostmask
;
6206 if(!bad_channel_ban(channel
, user
, ban
, NULL
, NULL
))
6209 bounce
= mod_chanmode_alloc(change
->argc
+ 1 - ii
);
6210 bounce
->args
[bnc
].mode
= MODE_REMOVE
| MODE_BAN
;
6211 bounce
->args
[bnc
].hostmask
= ban
;
6213 send_message(user
, chanserv
, "CSMSG_MASK_PROTECTED", ban
);
6218 if((bounce
->argc
= bnc
) || bounce
->modes_set
|| bounce
->modes_clear
)
6219 mod_chanmode_announce(chanserv
, channel
, bounce
);
6220 mod_chanmode_free(bounce
);
6225 handle_nick_change(struct userNode
*user
, UNUSED_ARG(const char *old_nick
))
6227 struct chanNode
*channel
;
6228 struct banData
*bData
;
6229 struct mod_chanmode change
;
6230 unsigned int ii
, jj
;
6231 char kick_reason
[MAXLEN
];
6233 mod_chanmode_init(&change
);
6235 change
.args
[0].mode
= MODE_BAN
;
6236 for(ii
= 0; ii
< user
->channels
.used
; ++ii
)
6238 channel
= user
->channels
.list
[ii
]->channel
;
6239 /* Need not check for bans if they're opped or voiced. */
6240 if(user
->channels
.list
[ii
]->modes
& (MODE_CHANOP
|MODE_VOICE
))
6242 /* Need not check for bans unless channel registration is active. */
6243 if(!channel
->channel_info
|| IsSuspended(channel
->channel_info
))
6245 /* Look for a matching ban already on the channel. */
6246 for(jj
= 0; jj
< channel
->banlist
.used
; ++jj
)
6247 if(user_matches_glob(user
, channel
->banlist
.list
[jj
]->ban
, 1))
6249 /* Need not act if we found one. */
6250 if(jj
< channel
->banlist
.used
)
6252 /* Look for a matching ban in this channel. */
6253 for(bData
= channel
->channel_info
->bans
; bData
; bData
= bData
->next
)
6255 if(!user_matches_glob(user
, bData
->mask
, 1))
6257 change
.args
[0].hostmask
= bData
->mask
;
6258 mod_chanmode_announce(chanserv
, channel
, &change
);
6259 sprintf(kick_reason
, "(%s) %s", bData
->owner
, bData
->reason
);
6260 KickChannelUser(user
, channel
, chanserv
, kick_reason
);
6261 bData
->triggered
= now
;
6262 break; /* we don't need to check any more bans in the channel */
6267 static void handle_rename(struct handle_info
*handle
, const char *old_handle
)
6269 struct do_not_register
*dnr
= dict_find(handle_dnrs
, old_handle
, NULL
);
6273 dict_remove2(handle_dnrs
, old_handle
, 1);
6274 safestrncpy(dnr
->chan_name
+ 1, handle
->handle
, sizeof(dnr
->chan_name
) - 1);
6275 dict_insert(handle_dnrs
, dnr
->chan_name
+ 1, dnr
);
6280 handle_unreg(UNUSED_ARG(struct userNode
*user
), struct handle_info
*handle
)
6282 struct userNode
*h_user
;
6284 if(handle
->channels
)
6286 for(h_user
= handle
->users
; h_user
; h_user
= h_user
->next_authed
)
6287 send_message(h_user
, chanserv
, "CSMSG_HANDLE_UNREGISTERED");
6289 while(handle
->channels
)
6290 del_channel_user(handle
->channels
, 1);
6295 handle_server_link(UNUSED_ARG(struct server
*server
))
6297 struct chanData
*cData
;
6299 for(cData
= channelList
; cData
; cData
= cData
->next
)
6301 if(!IsSuspended(cData
))
6302 cData
->may_opchan
= 1;
6303 if((cData
->flags
& CHANNEL_DYNAMIC_LIMIT
)
6304 && !cData
->channel
->join_flooded
6305 && ((cData
->channel
->limit
- cData
->channel
->members
.used
)
6306 < chanserv_conf
.adjust_threshold
))
6308 timeq_del(0, chanserv_adjust_limit
, cData
, TIMEQ_IGNORE_WHEN
);
6309 timeq_add(now
+ chanserv_conf
.adjust_delay
, chanserv_adjust_limit
, cData
);
6315 chanserv_conf_read(void)
6319 char mode_line
[MAXLEN
], *modes
[MAXNUMPARAMS
];
6320 struct mod_chanmode
*change
;
6321 struct string_list
*strlist
;
6322 struct chanNode
*chan
;
6325 if(!(conf_node
= conf_get_data(CHANSERV_CONF_NAME
, RECDB_OBJECT
)))
6327 log_module(CS_LOG
, LOG_ERROR
, "Invalid config node `%s'.", CHANSERV_CONF_NAME
);
6330 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
6331 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
6332 chanserv_conf
.support_channels
.used
= 0;
6333 if((strlist
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_STRING_LIST
)))
6335 for(ii
= 0; ii
< strlist
->used
; ++ii
)
6337 const char *str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6340 chan
= AddChannel(strlist
->list
[ii
], now
, str2
, NULL
);
6342 channelList_append(&chanserv_conf
.support_channels
, chan
);
6345 else if((str
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL
, RECDB_QSTRING
)))
6348 str2
= database_get_data(conf_node
, KEY_SUPPORT_CHANNEL_MODES
, RECDB_QSTRING
);
6351 chan
= AddChannel(str
, now
, str2
, NULL
);
6353 channelList_append(&chanserv_conf
.support_channels
, chan
);
6355 str
= database_get_data(conf_node
, KEY_DB_BACKUP_FREQ
, RECDB_QSTRING
);
6356 chanserv_conf
.db_backup_frequency
= str
? ParseInterval(str
) : 7200;
6357 str
= database_get_data(conf_node
, KEY_INFO_DELAY
, RECDB_QSTRING
);
6358 chanserv_conf
.info_delay
= str
? ParseInterval(str
) : 180;
6359 str
= database_get_data(conf_node
, KEY_MAX_GREETLEN
, RECDB_QSTRING
);
6360 chanserv_conf
.greeting_length
= str
? atoi(str
) : 120;
6361 str
= database_get_data(conf_node
, KEY_ADJUST_THRESHOLD
, RECDB_QSTRING
);
6362 chanserv_conf
.adjust_threshold
= str
? atoi(str
) : 15;
6363 str
= database_get_data(conf_node
, KEY_ADJUST_DELAY
, RECDB_QSTRING
);
6364 chanserv_conf
.adjust_delay
= str
? ParseInterval(str
) : 30;
6365 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_FREQ
, RECDB_QSTRING
);
6366 chanserv_conf
.channel_expire_frequency
= str
? ParseInterval(str
) : 86400;
6367 str
= database_get_data(conf_node
, KEY_CHAN_EXPIRE_DELAY
, RECDB_QSTRING
);
6368 chanserv_conf
.channel_expire_delay
= str
? ParseInterval(str
) : 86400*30;
6369 str
= database_get_data(conf_node
, KEY_NODELETE_LEVEL
, RECDB_QSTRING
);
6370 chanserv_conf
.nodelete_level
= str
? atoi(str
) : 1;
6371 str
= database_get_data(conf_node
, KEY_MAX_CHAN_USERS
, RECDB_QSTRING
);
6372 chanserv_conf
.max_chan_users
= str
? atoi(str
) : 512;
6373 str
= database_get_data(conf_node
, KEY_MAX_CHAN_BANS
, RECDB_QSTRING
);
6374 chanserv_conf
.max_chan_bans
= str
? atoi(str
) : 512;
6375 str
= database_get_data(conf_node
, KEY_MAX_USERINFO_LENGTH
, RECDB_QSTRING
);
6376 chanserv_conf
.max_userinfo_length
= str
? atoi(str
) : 400;
6377 str
= database_get_data(conf_node
, KEY_NICK
, RECDB_QSTRING
);
6379 NickChange(chanserv
, str
, 0);
6380 str
= database_get_data(conf_node
, KEY_REFRESH_PERIOD
, RECDB_QSTRING
);
6381 chanserv_conf
.refresh_period
= str
? ParseInterval(str
) : 3*60*60;
6382 str
= database_get_data(conf_node
, KEY_CTCP_SHORT_BAN_DURATION
, RECDB_QSTRING
);
6383 chanserv_conf
.ctcp_short_ban_duration
= str
? str
: "3m";
6384 str
= database_get_data(conf_node
, KEY_CTCP_LONG_BAN_DURATION
, RECDB_QSTRING
);
6385 chanserv_conf
.ctcp_long_ban_duration
= str
? str
: "1h";
6386 str
= database_get_data(conf_node
, KEY_MAX_OWNED
, RECDB_QSTRING
);
6387 chanserv_conf
.max_owned
= str
? atoi(str
) : 5;
6388 str
= database_get_data(conf_node
, KEY_IRC_OPERATOR_EPITHET
, RECDB_QSTRING
);
6389 chanserv_conf
.irc_operator_epithet
= str
? str
: "a megalomaniacal power hungry tyrant";
6390 str
= database_get_data(conf_node
, KEY_NETWORK_HELPER_EPITHET
, RECDB_QSTRING
);
6391 chanserv_conf
.network_helper_epithet
= str
? str
: "a wannabe tyrant";
6392 str
= database_get_data(conf_node
, KEY_SUPPORT_HELPER_EPITHET
, RECDB_QSTRING
);
6393 chanserv_conf
.support_helper_epithet
= str
? str
: "a wannabe tyrant";
6394 str
= database_get_data(conf_node
, "default_modes", RECDB_QSTRING
);
6397 safestrncpy(mode_line
, str
, sizeof(mode_line
));
6398 ii
= split_line(mode_line
, 0, ArrayLength(modes
), modes
);
6399 if((change
= mod_chanmode_parse(NULL
, modes
, ii
, MCP_KEY_FREE
)) && (change
->argc
< 2))
6401 chanserv_conf
.default_modes
= *change
;
6402 mod_chanmode_free(change
);
6404 free_string_list(chanserv_conf
.set_shows
);
6405 strlist
= database_get_data(conf_node
, "set_shows", RECDB_STRING_LIST
);
6407 strlist
= string_list_copy(strlist
);
6410 static const char *list
[] = {
6411 /* free form text */
6412 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6413 /* options based on user level */
6414 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6415 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6416 /* multiple choice options */
6417 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6418 /* binary options */
6419 "DynLimit", "NoDelete",
6424 strlist
= alloc_string_list(ArrayLength(list
)-1);
6425 for(ii
=0; list
[ii
]; ii
++)
6426 string_list_append(strlist
, strdup(list
[ii
]));
6428 chanserv_conf
.set_shows
= strlist
;
6429 /* We don't look things up now, in case the list refers to options
6430 * defined by modules initialized after this point. Just mark the
6431 * function list as invalid, so it will be initialized.
6433 set_shows_list
.used
= 0;
6434 free_string_list(chanserv_conf
.eightball
);
6435 strlist
= database_get_data(conf_node
, KEY_8BALL_RESPONSES
, RECDB_STRING_LIST
);
6438 strlist
= string_list_copy(strlist
);
6442 strlist
= alloc_string_list(4);
6443 string_list_append(strlist
, strdup("Yes."));
6444 string_list_append(strlist
, strdup("No."));
6445 string_list_append(strlist
, strdup("Maybe so."));
6447 chanserv_conf
.eightball
= strlist
;
6448 free_string_list(chanserv_conf
.old_ban_names
);
6449 strlist
= database_get_data(conf_node
, KEY_OLD_BAN_NAMES
, RECDB_STRING_LIST
);
6451 strlist
= string_list_copy(strlist
);
6453 strlist
= alloc_string_list(2);
6454 chanserv_conf
.old_ban_names
= strlist
;
6455 /* the variable itself is actually declared in proto-common.c; this is equally
6457 str
= database_get_data(conf_node
, "off_channel", RECDB_QSTRING
);
6458 off_channel
= (str
&& enabled_string(str
)) ? 1 : 0;
6462 chanserv_note_type_read(const char *key
, struct record_data
*rd
)
6465 struct note_type
*ntype
;
6468 if(!(obj
= GET_RECORD_OBJECT(rd
)))
6470 log_module(CS_LOG
, LOG_ERROR
, "Invalid note type %s.", key
);
6473 if(!(ntype
= chanserv_create_note_type(key
)))
6475 log_module(CS_LOG
, LOG_ERROR
, "Memory allocation failed for note %s.", key
);
6479 /* Figure out set access */
6480 if((str
= database_get_data(obj
, KEY_NOTE_OPSERV_ACCESS
, RECDB_QSTRING
)))
6482 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6483 ntype
->set_access
.min_opserv
= strtoul(str
, NULL
, 0);
6485 else if((str
= database_get_data(obj
, KEY_NOTE_CHANNEL_ACCESS
, RECDB_QSTRING
)))
6487 ntype
->set_access_type
= NOTE_SET_CHANNEL_ACCESS
;
6488 ntype
->set_access
.min_ulevel
= strtoul(str
, NULL
, 0);
6490 else if((str
= database_get_data(obj
, KEY_NOTE_SETTER_ACCESS
, RECDB_QSTRING
)))
6492 ntype
->set_access_type
= NOTE_SET_CHANNEL_SETTER
;
6496 log_module(CS_LOG
, LOG_ERROR
, "Could not find access type for note %s; defaulting to OpServ access level 0.", key
);
6497 ntype
->set_access_type
= NOTE_SET_PRIVILEGED
;
6498 ntype
->set_access
.min_opserv
= 0;
6501 /* Figure out visibility */
6502 if(!(str
= database_get_data(obj
, KEY_NOTE_VISIBILITY
, RECDB_QSTRING
)))
6503 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6504 else if(!irccasecmp(str
, KEY_NOTE_VIS_PRIVILEGED
))
6505 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6506 else if(!irccasecmp(str
, KEY_NOTE_VIS_CHANNEL_USERS
))
6507 ntype
->visible_type
= NOTE_VIS_CHANNEL_USERS
;
6508 else if(!irccasecmp(str
, KEY_NOTE_VIS_ALL
))
6509 ntype
->visible_type
= NOTE_VIS_ALL
;
6511 ntype
->visible_type
= NOTE_VIS_PRIVILEGED
;
6513 str
= database_get_data(obj
, KEY_NOTE_MAX_LENGTH
, RECDB_QSTRING
);
6514 ntype
->max_length
= str
? strtoul(str
, NULL
, 0) : 400;
6518 user_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6520 struct handle_info
*handle
;
6521 struct userData
*uData
;
6522 char *seen
, *inf
, *flags
;
6524 unsigned short access
;
6526 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6528 log_module(CS_LOG
, LOG_ERROR
, "Invalid user in %s.", chan
->channel
->name
);
6532 access
= atoi(database_get_data(rd
->d
.object
, KEY_LEVEL
, RECDB_QSTRING
));
6533 if(access
> UL_OWNER
)
6535 log_module(CS_LOG
, LOG_ERROR
, "Invalid access level for %s in %s.", key
, chan
->channel
->name
);
6539 inf
= database_get_data(rd
->d
.object
, KEY_INFO
, RECDB_QSTRING
);
6540 seen
= database_get_data(rd
->d
.object
, KEY_SEEN
, RECDB_QSTRING
);
6541 last_seen
= seen
? (signed)strtoul(seen
, NULL
, 0) : now
;
6542 flags
= database_get_data(rd
->d
.object
, KEY_FLAGS
, RECDB_QSTRING
);
6543 handle
= get_handle_info(key
);
6546 log_module(CS_LOG
, LOG_ERROR
, "Nonexistent account %s in %s.", key
, chan
->channel
->name
);
6550 uData
= add_channel_user(chan
, handle
, access
, last_seen
, inf
);
6551 uData
->flags
= flags
? strtoul(flags
, NULL
, 0) : 0;
6555 ban_read_helper(const char *key
, struct record_data
*rd
, struct chanData
*chan
)
6557 struct banData
*bData
;
6558 char *set
, *triggered
, *s_duration
, *s_expires
, *reason
, *owner
;
6559 time_t set_time
, triggered_time
, expires_time
;
6561 if(rd
->type
!= RECDB_OBJECT
|| !dict_size(rd
->d
.object
))
6563 log_module(CS_LOG
, LOG_ERROR
, "Invalid ban in %s.", chan
->channel
->name
);
6567 set
= database_get_data(rd
->d
.object
, KEY_SET
, RECDB_QSTRING
);
6568 triggered
= database_get_data(rd
->d
.object
, KEY_TRIGGERED
, RECDB_QSTRING
);
6569 s_duration
= database_get_data(rd
->d
.object
, KEY_DURATION
, RECDB_QSTRING
);
6570 s_expires
= database_get_data(rd
->d
.object
, KEY_EXPIRES
, RECDB_QSTRING
);
6571 owner
= database_get_data(rd
->d
.object
, KEY_OWNER
, RECDB_QSTRING
);
6572 reason
= database_get_data(rd
->d
.object
, KEY_REASON
, RECDB_QSTRING
);
6574 set_time
= set
? (time_t)strtoul(set
, NULL
, 0) : now
;
6575 triggered_time
= triggered
? (time_t)strtoul(triggered
, NULL
, 0) : 0;
6577 expires_time
= (time_t)strtoul(s_expires
, NULL
, 0);
6579 expires_time
= set_time
+ atoi(s_duration
);
6583 if(expires_time
&& (expires_time
< now
))
6586 bData
= add_channel_ban(chan
, key
, owner
, set_time
, triggered_time
, expires_time
, reason
);
6589 static struct suspended
*
6590 chanserv_read_suspended(dict_t obj
)
6592 struct suspended
*suspended
= calloc(1, sizeof(*suspended
));
6596 str
= database_get_data(obj
, KEY_EXPIRES
, RECDB_QSTRING
);
6597 suspended
->expires
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6598 str
= database_get_data(obj
, KEY_REVOKED
, RECDB_QSTRING
);
6599 suspended
->revoked
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6600 str
= database_get_data(obj
, KEY_ISSUED
, RECDB_QSTRING
);
6601 suspended
->issued
= str
? (time_t)strtoul(str
, NULL
, 0) : 0;
6602 suspended
->suspender
= strdup(database_get_data(obj
, KEY_SUSPENDER
, RECDB_QSTRING
));
6603 suspended
->reason
= strdup(database_get_data(obj
, KEY_REASON
, RECDB_QSTRING
));
6604 previous
= database_get_data(obj
, KEY_PREVIOUS
, RECDB_OBJECT
);
6605 suspended
->previous
= previous
? chanserv_read_suspended(previous
) : NULL
;
6610 chanserv_channel_read(const char *key
, struct record_data
*hir
)
6612 struct suspended
*suspended
;
6613 struct mod_chanmode
*modes
;
6614 struct chanNode
*cNode
;
6615 struct chanData
*cData
;
6616 struct dict
*channel
, *obj
;
6617 char *str
, *argv
[10];
6621 channel
= hir
->d
.object
;
6623 str
= database_get_data(channel
, KEY_REGISTRAR
, RECDB_QSTRING
);
6626 cNode
= AddChannel(key
, now
, NULL
, NULL
);
6629 log_module(CS_LOG
, LOG_ERROR
, "Unable to create registered channel %s.", key
);
6632 cData
= register_channel(cNode
, str
);
6635 log_module(CS_LOG
, LOG_ERROR
, "Unable to register channel %s from database.", key
);
6639 if((obj
= database_get_data(channel
, KEY_OPTIONS
, RECDB_OBJECT
)))
6641 enum levelOption lvlOpt
;
6642 enum charOption chOpt
;
6644 if((str
= database_get_data(obj
, KEY_FLAGS
, RECDB_QSTRING
)))
6645 cData
->flags
= atoi(str
);
6647 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6649 str
= database_get_data(obj
, levelOptions
[lvlOpt
].db_name
, RECDB_QSTRING
);
6651 cData
->lvlOpts
[lvlOpt
] = user_level_from_name(str
, UL_OWNER
+1);
6652 else if(levelOptions
[lvlOpt
].old_flag
)
6654 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6655 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].flag_value
;
6657 cData
->lvlOpts
[lvlOpt
] = levelOptions
[lvlOpt
].default_value
;
6661 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6663 if(!(str
= database_get_data(obj
, charOptions
[chOpt
].db_name
, RECDB_QSTRING
)))
6665 cData
->chOpts
[chOpt
] = str
[0];
6668 else if((str
= database_get_data(channel
, KEY_FLAGS
, RECDB_QSTRING
)))
6670 enum levelOption lvlOpt
;
6671 enum charOption chOpt
;
6674 cData
->flags
= base64toint(str
, 5);
6675 count
= strlen(str
+= 5);
6676 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6679 if(levelOptions
[lvlOpt
].old_flag
)
6681 if(cData
->flags
& levelOptions
[lvlOpt
].old_flag
)
6682 lvl
= levelOptions
[lvlOpt
].flag_value
;
6684 lvl
= levelOptions
[lvlOpt
].default_value
;
6686 else switch(((count
<= levelOptions
[lvlOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[levelOptions
[lvlOpt
].old_idx
])
6688 case 'c': lvl
= UL_COOWNER
; break;
6689 case 'm': lvl
= UL_MANAGER
; break;
6690 case 'n': lvl
= UL_OWNER
+1; break;
6691 case 'o': lvl
= UL_OP
; break;
6692 case 'p': lvl
= UL_PEON
; break;
6693 case 'w': lvl
= UL_OWNER
; break;
6694 default: lvl
= 0; break;
6696 cData
->lvlOpts
[lvlOpt
] = lvl
;
6698 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6699 cData
->chOpts
[chOpt
] = ((count
<= charOptions
[chOpt
].old_idx
) ? str
: CHANNEL_DEFAULT_OPTIONS
)[charOptions
[chOpt
].old_idx
];
6702 if((obj
= database_get_data(hir
->d
.object
, KEY_SUSPENDED
, RECDB_OBJECT
)))
6704 suspended
= chanserv_read_suspended(obj
);
6705 cData
->suspended
= suspended
;
6706 suspended
->cData
= cData
;
6707 /* We could use suspended->expires and suspended->revoked to
6708 * set the CHANNEL_SUSPENDED flag, but we don't. */
6710 else if(IsSuspended(cData
))
6712 suspended
= calloc(1, sizeof(*suspended
));
6713 suspended
->issued
= 0;
6714 suspended
->revoked
= 0;
6715 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_EXPIRES
, RECDB_QSTRING
);
6716 suspended
->expires
= str
? atoi(str
) : 0;
6717 suspended
->suspender
= strdup(database_get_data(hir
->d
.object
, KEY_SUSPENDER
, RECDB_QSTRING
));
6718 str
= database_get_data(hir
->d
.object
, KEY_SUSPEND_REASON
, RECDB_QSTRING
);
6719 suspended
->reason
= strdup(str
? str
: "No reason");
6720 suspended
->previous
= NULL
;
6721 cData
->suspended
= suspended
;
6722 suspended
->cData
= cData
;
6725 suspended
= NULL
; /* to squelch a warning */
6727 if(IsSuspended(cData
)) {
6728 if(suspended
->expires
> now
)
6729 timeq_add(suspended
->expires
, chanserv_expire_suspension
, suspended
);
6730 else if(suspended
->expires
)
6731 cData
->flags
&= ~CHANNEL_SUSPENDED
;
6734 if((!off_channel
|| !IsOffChannel(cData
)) && !IsSuspended(cData
)) {
6735 struct mod_chanmode change
;
6736 mod_chanmode_init(&change
);
6738 change
.args
[0].mode
= MODE_CHANOP
;
6739 change
.args
[0].member
= AddChannelUser(chanserv
, cNode
);
6740 mod_chanmode_announce(chanserv
, cNode
, &change
);
6743 str
= database_get_data(channel
, KEY_REGISTERED
, RECDB_QSTRING
);
6744 cData
->registered
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6745 str
= database_get_data(channel
, KEY_VISITED
, RECDB_QSTRING
);
6746 cData
->visited
= str
? (time_t)strtoul(str
, NULL
, 0) : now
;
6747 str
= database_get_data(channel
, KEY_MAX
, RECDB_QSTRING
);
6748 cData
->max
= str
? atoi(str
) : 0;
6749 str
= database_get_data(channel
, KEY_GREETING
, RECDB_QSTRING
);
6750 cData
->greeting
= str
? strdup(str
) : NULL
;
6751 str
= database_get_data(channel
, KEY_USER_GREETING
, RECDB_QSTRING
);
6752 cData
->user_greeting
= str
? strdup(str
) : NULL
;
6753 str
= database_get_data(channel
, KEY_TOPIC_MASK
, RECDB_QSTRING
);
6754 cData
->topic_mask
= str
? strdup(str
) : NULL
;
6755 str
= database_get_data(channel
, KEY_TOPIC
, RECDB_QSTRING
);
6756 cData
->topic
= str
? strdup(str
) : NULL
;
6758 if(!IsSuspended(cData
)
6759 && (str
= database_get_data(channel
, KEY_MODES
, RECDB_QSTRING
))
6760 && (argc
= split_line(str
, 0, ArrayLength(argv
), argv
))
6761 && (modes
= mod_chanmode_parse(cNode
, argv
, argc
, MCP_KEY_FREE
))) {
6762 cData
->modes
= *modes
;
6763 cData
->modes
.modes_set
|= MODE_REGISTERED
;
6764 if(cData
->modes
.argc
> 1)
6765 cData
->modes
.argc
= 1;
6766 mod_chanmode_announce(chanserv
, cNode
, &cData
->modes
);
6767 mod_chanmode_free(modes
);
6770 obj
= database_get_data(channel
, KEY_USERS
, RECDB_OBJECT
);
6771 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6772 user_read_helper(iter_key(it
), iter_data(it
), cData
);
6774 if(!cData
->users
&& !IsProtected(cData
))
6776 log_module(CS_LOG
, LOG_ERROR
, "Channel %s had no users in database, unregistering it.", key
);
6777 unregister_channel(cData
, "has empty user list.");
6781 obj
= database_get_data(channel
, KEY_BANS
, RECDB_OBJECT
);
6782 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6783 ban_read_helper(iter_key(it
), iter_data(it
), cData
);
6785 obj
= database_get_data(channel
, KEY_NOTES
, RECDB_OBJECT
);
6786 for(it
= dict_first(obj
); it
; it
= iter_next(it
))
6788 struct note_type
*ntype
= dict_find(note_types
, iter_key(it
), NULL
);
6789 struct record_data
*rd
= iter_data(it
);
6790 const char *note
, *setter
;
6792 if(rd
->type
!= RECDB_OBJECT
)
6794 log_module(CS_LOG
, LOG_ERROR
, "Bad record type for note %s in channel %s.", iter_key(it
), key
);
6798 log_module(CS_LOG
, LOG_ERROR
, "Bad note type name %s in channel %s.", iter_key(it
), key
);
6800 else if(!(note
= database_get_data(rd
->d
.object
, KEY_NOTE_NOTE
, RECDB_QSTRING
)))
6802 log_module(CS_LOG
, LOG_ERROR
, "Missing note text for note %s in channel %s.", iter_key(it
), key
);
6806 setter
= database_get_data(rd
->d
.object
, KEY_NOTE_SETTER
, RECDB_QSTRING
);
6807 if(!setter
) setter
= "<unknown>";
6808 chanserv_add_channel_note(cData
, ntype
, setter
, note
);
6816 chanserv_dnr_read(const char *key
, struct record_data
*hir
)
6818 const char *setter
, *reason
, *str
;
6819 struct do_not_register
*dnr
;
6821 setter
= database_get_data(hir
->d
.object
, KEY_DNR_SETTER
, RECDB_QSTRING
);
6824 log_module(CS_LOG
, LOG_ERROR
, "Missing setter for DNR %s.", key
);
6827 reason
= database_get_data(hir
->d
.object
, KEY_DNR_REASON
, RECDB_QSTRING
);
6830 log_module(CS_LOG
, LOG_ERROR
, "Missing reason for DNR %s.", key
);
6833 dnr
= chanserv_add_dnr(key
, setter
, reason
);
6836 str
= database_get_data(hir
->d
.object
, KEY_DNR_SET
, RECDB_QSTRING
);
6838 dnr
->set
= atoi(str
);
6844 chanserv_saxdb_read(struct dict
*database
)
6846 struct dict
*section
;
6849 if((section
= database_get_data(database
, KEY_NOTE_TYPES
, RECDB_OBJECT
)))
6850 for(it
= dict_first(section
); it
; it
= iter_next(it
))
6851 chanserv_note_type_read(iter_key(it
), iter_data(it
));
6853 if((section
= database_get_data(database
, KEY_CHANNELS
, RECDB_OBJECT
)))
6854 for(it
= dict_first(section
); it
; it
= iter_next(it
))
6855 chanserv_channel_read(iter_key(it
), iter_data(it
));
6857 if((section
= database_get_data(database
, KEY_DNR
, RECDB_OBJECT
)))
6858 for(it
= dict_first(section
); it
; it
= iter_next(it
))
6859 chanserv_dnr_read(iter_key(it
), iter_data(it
));
6865 chanserv_write_users(struct saxdb_context
*ctx
, struct userData
*uData
)
6867 int high_present
= 0;
6868 saxdb_start_record(ctx
, KEY_USERS
, 1);
6869 for(; uData
; uData
= uData
->next
)
6871 if((uData
->access
>= UL_PRESENT
) && uData
->present
)
6873 saxdb_start_record(ctx
, uData
->handle
->handle
, 0);
6874 saxdb_write_int(ctx
, KEY_LEVEL
, uData
->access
);
6875 saxdb_write_int(ctx
, KEY_SEEN
, uData
->seen
);
6877 saxdb_write_int(ctx
, KEY_FLAGS
, uData
->flags
);
6879 saxdb_write_string(ctx
, KEY_INFO
, uData
->info
);
6880 saxdb_end_record(ctx
);
6882 saxdb_end_record(ctx
);
6883 return high_present
;
6887 chanserv_write_bans(struct saxdb_context
*ctx
, struct banData
*bData
)
6891 saxdb_start_record(ctx
, KEY_BANS
, 1);
6892 for(; bData
; bData
= bData
->next
)
6894 saxdb_start_record(ctx
, bData
->mask
, 0);
6895 saxdb_write_int(ctx
, KEY_SET
, bData
->set
);
6896 if(bData
->triggered
)
6897 saxdb_write_int(ctx
, KEY_TRIGGERED
, bData
->triggered
);
6899 saxdb_write_int(ctx
, KEY_EXPIRES
, bData
->expires
);
6901 saxdb_write_string(ctx
, KEY_OWNER
, bData
->owner
);
6903 saxdb_write_string(ctx
, KEY_REASON
, bData
->reason
);
6904 saxdb_end_record(ctx
);
6906 saxdb_end_record(ctx
);
6910 chanserv_write_suspended(struct saxdb_context
*ctx
, const char *name
, struct suspended
*susp
)
6912 saxdb_start_record(ctx
, name
, 0);
6913 saxdb_write_string(ctx
, KEY_SUSPENDER
, susp
->suspender
);
6914 saxdb_write_string(ctx
, KEY_REASON
, susp
->reason
);
6916 saxdb_write_int(ctx
, KEY_ISSUED
, susp
->issued
);
6918 saxdb_write_int(ctx
, KEY_EXPIRES
, susp
->expires
);
6920 saxdb_write_int(ctx
, KEY_REVOKED
, susp
->revoked
);
6922 chanserv_write_suspended(ctx
, KEY_PREVIOUS
, susp
->previous
);
6923 saxdb_end_record(ctx
);
6927 chanserv_write_channel(struct saxdb_context
*ctx
, struct chanData
*channel
)
6931 enum levelOption lvlOpt
;
6932 enum charOption chOpt
;
6934 saxdb_start_record(ctx
, channel
->channel
->name
, 1);
6936 saxdb_write_int(ctx
, KEY_REGISTERED
, channel
->registered
);
6937 saxdb_write_int(ctx
, KEY_MAX
, channel
->max
);
6939 saxdb_write_string(ctx
, KEY_TOPIC
, channel
->topic
);
6940 if(channel
->registrar
)
6941 saxdb_write_string(ctx
, KEY_REGISTRAR
, channel
->registrar
);
6942 if(channel
->greeting
)
6943 saxdb_write_string(ctx
, KEY_GREETING
, channel
->greeting
);
6944 if(channel
->user_greeting
)
6945 saxdb_write_string(ctx
, KEY_USER_GREETING
, channel
->user_greeting
);
6946 if(channel
->topic_mask
)
6947 saxdb_write_string(ctx
, KEY_TOPIC_MASK
, channel
->topic_mask
);
6948 if(channel
->suspended
)
6949 chanserv_write_suspended(ctx
, "suspended", channel
->suspended
);
6951 saxdb_start_record(ctx
, KEY_OPTIONS
, 0);
6952 saxdb_write_int(ctx
, KEY_FLAGS
, channel
->flags
);
6953 for(lvlOpt
= 0; lvlOpt
< NUM_LEVEL_OPTIONS
; ++lvlOpt
)
6954 saxdb_write_int(ctx
, levelOptions
[lvlOpt
].db_name
, channel
->lvlOpts
[lvlOpt
]);
6955 for(chOpt
= 0; chOpt
< NUM_CHAR_OPTIONS
; ++chOpt
)
6957 buf
[0] = channel
->chOpts
[chOpt
];
6959 saxdb_write_string(ctx
, charOptions
[chOpt
].db_name
, buf
);
6961 saxdb_end_record(ctx
);
6963 if(channel
->modes
.modes_set
|| channel
->modes
.modes_clear
)
6965 mod_chanmode_format(&channel
->modes
, buf
);
6966 saxdb_write_string(ctx
, KEY_MODES
, buf
);
6969 high_present
= chanserv_write_users(ctx
, channel
->users
);
6970 chanserv_write_bans(ctx
, channel
->bans
);
6972 if(dict_size(channel
->notes
))
6976 saxdb_start_record(ctx
, KEY_NOTES
, 1);
6977 for(it
= dict_first(channel
->notes
); it
; it
= iter_next(it
))
6979 struct note
*note
= iter_data(it
);
6980 saxdb_start_record(ctx
, iter_key(it
), 0);
6981 saxdb_write_string(ctx
, KEY_NOTE_SETTER
, note
->setter
);
6982 saxdb_write_string(ctx
, KEY_NOTE_NOTE
, note
->note
);
6983 saxdb_end_record(ctx
);
6985 saxdb_end_record(ctx
);
6988 saxdb_write_int(ctx
, KEY_VISITED
, high_present
? now
: channel
->visited
);
6989 saxdb_end_record(ctx
);
6993 chanserv_write_note_type(struct saxdb_context
*ctx
, struct note_type
*ntype
)
6997 saxdb_start_record(ctx
, ntype
->name
, 0);
6998 switch(ntype
->set_access_type
)
7000 case NOTE_SET_CHANNEL_ACCESS
:
7001 saxdb_write_int(ctx
, KEY_NOTE_CHANNEL_ACCESS
, ntype
->set_access
.min_ulevel
);
7003 case NOTE_SET_CHANNEL_SETTER
:
7004 saxdb_write_int(ctx
, KEY_NOTE_SETTER_ACCESS
, 1);
7006 case NOTE_SET_PRIVILEGED
: default:
7007 saxdb_write_int(ctx
, KEY_NOTE_OPSERV_ACCESS
, ntype
->set_access
.min_opserv
);
7010 switch(ntype
->visible_type
)
7012 case NOTE_VIS_ALL
: str
= KEY_NOTE_VIS_ALL
; break;
7013 case NOTE_VIS_CHANNEL_USERS
: str
= KEY_NOTE_VIS_CHANNEL_USERS
; break;
7014 case NOTE_VIS_PRIVILEGED
: default: str
= KEY_NOTE_VIS_PRIVILEGED
; break;
7016 saxdb_write_string(ctx
, KEY_NOTE_VISIBILITY
, str
);
7017 saxdb_write_int(ctx
, KEY_NOTE_MAX_LENGTH
, ntype
->max_length
);
7018 saxdb_end_record(ctx
);
7022 write_dnrs_helper(struct saxdb_context
*ctx
, struct dict
*dnrs
)
7024 struct do_not_register
*dnr
;
7027 for(it
= dict_first(dnrs
); it
; it
= iter_next(it
))
7029 dnr
= iter_data(it
);
7030 saxdb_start_record(ctx
, dnr
->chan_name
, 0);
7032 saxdb_write_int(ctx
, KEY_DNR_SET
, dnr
->set
);
7033 saxdb_write_string(ctx
, KEY_DNR_SETTER
, dnr
->setter
);
7034 saxdb_write_string(ctx
, KEY_DNR_REASON
, dnr
->reason
);
7035 saxdb_end_record(ctx
);
7040 chanserv_saxdb_write(struct saxdb_context
*ctx
)
7043 struct chanData
*channel
;
7046 saxdb_start_record(ctx
, KEY_NOTE_TYPES
, 1);
7047 for(it
= dict_first(note_types
); it
; it
= iter_next(it
))
7048 chanserv_write_note_type(ctx
, iter_data(it
));
7049 saxdb_end_record(ctx
);
7052 saxdb_start_record(ctx
, KEY_DNR
, 1);
7053 write_dnrs_helper(ctx
, handle_dnrs
);
7054 write_dnrs_helper(ctx
, plain_dnrs
);
7055 write_dnrs_helper(ctx
, mask_dnrs
);
7056 saxdb_end_record(ctx
);
7059 saxdb_start_record(ctx
, KEY_CHANNELS
, 1);
7060 for(channel
= channelList
; channel
; channel
= channel
->next
)
7061 chanserv_write_channel(ctx
, channel
);
7062 saxdb_end_record(ctx
);
7068 chanserv_db_cleanup(void) {
7070 unreg_part_func(handle_part
);
7072 unregister_channel(channelList
, "terminating.");
7073 for(ii
= 0; ii
< chanserv_conf
.support_channels
.used
; ++ii
)
7074 UnlockChannel(chanserv_conf
.support_channels
.list
[ii
]);
7075 free(chanserv_conf
.support_channels
.list
);
7076 dict_delete(handle_dnrs
);
7077 dict_delete(plain_dnrs
);
7078 dict_delete(mask_dnrs
);
7079 dict_delete(note_types
);
7080 free_string_list(chanserv_conf
.eightball
);
7081 free_string_list(chanserv_conf
.old_ban_names
);
7082 free_string_list(chanserv_conf
.set_shows
);
7083 free(set_shows_list
.list
);
7084 free(uset_shows_list
.list
);
7087 struct userData
*helper
= helperList
;
7088 helperList
= helperList
->next
;
7093 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7094 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7095 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7098 init_chanserv(const char *nick
)
7100 CS_LOG
= log_register_type("ChanServ", "file:chanserv.log");
7101 conf_register_reload(chanserv_conf_read
);
7103 reg_server_link_func(handle_server_link
);
7105 reg_new_channel_func(handle_new_channel
);
7106 reg_join_func(handle_join
);
7107 reg_part_func(handle_part
);
7108 reg_kick_func(handle_kick
);
7109 reg_topic_func(handle_topic
);
7110 reg_mode_change_func(handle_mode
);
7111 reg_nick_change_func(handle_nick_change
);
7113 reg_auth_func(handle_auth
);
7114 reg_handle_rename_func(handle_rename
);
7115 reg_unreg_func(handle_unreg
);
7117 handle_dnrs
= dict_new();
7118 dict_set_free_data(handle_dnrs
, free
);
7119 plain_dnrs
= dict_new();
7120 dict_set_free_data(plain_dnrs
, free
);
7121 mask_dnrs
= dict_new();
7122 dict_set_free_data(mask_dnrs
, free
);
7124 reg_svccmd_unbind_func(handle_svccmd_unbind
);
7125 chanserv_module
= module_register("ChanServ", CS_LOG
, "chanserv.help", chanserv_expand_variable
);
7126 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+acceptchan,+helping", NULL
);
7127 DEFINE_COMMAND(noregister
, 1, MODCMD_REQUIRE_AUTHED
, "flags", "+helping", NULL
);
7128 DEFINE_COMMAND(allowregister
, 2, 0, "template", "noregister", NULL
);
7129 DEFINE_COMMAND(move
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "template", "register", NULL
);
7130 DEFINE_COMMAND(csuspend
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7131 DEFINE_COMMAND(cunsuspend
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+helping", NULL
);
7132 DEFINE_COMMAND(createnote
, 5, 0, "level", "800", NULL
);
7133 DEFINE_COMMAND(removenote
, 2, 0, "level", "800", NULL
);
7135 DEFINE_COMMAND(unregister
, 1, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "flags", "+loghostmask", NULL
);
7136 DEFINE_COMMAND(merge
, 2, MODCMD_REQUIRE_AUTHED
|MODCMD_REQUIRE_REGCHAN
, "access", "owner", NULL
);
7138 DEFINE_COMMAND(adduser
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7139 DEFINE_COMMAND(deluser
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7140 DEFINE_COMMAND(suspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7141 DEFINE_COMMAND(unsuspend
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7142 DEFINE_COMMAND(deleteme
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7144 DEFINE_COMMAND(mdelowner
, 2, MODCMD_REQUIRE_CHANUSER
, "flags", "+helping", NULL
);
7145 DEFINE_COMMAND(mdelcoowner
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", NULL
);
7146 DEFINE_COMMAND(mdelmanager
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "coowner", NULL
);
7147 DEFINE_COMMAND(mdelop
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7148 DEFINE_COMMAND(mdelpeon
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7150 DEFINE_COMMAND(trim
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7151 DEFINE_COMMAND(opchan
, 1, MODCMD_REQUIRE_REGCHAN
|MODCMD_NEVER_CSUSPEND
, "access", "1", NULL
);
7152 DEFINE_COMMAND(clvl
, 3, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7153 DEFINE_COMMAND(giveownership
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "owner", "flags", "+loghostmask", NULL
);
7155 DEFINE_COMMAND(up
, 1, MODCMD_REQUIRE_CHANUSER
, NULL
);
7156 DEFINE_COMMAND(down
, 1, MODCMD_REQUIRE_REGCHAN
, NULL
);
7157 DEFINE_COMMAND(upall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7158 DEFINE_COMMAND(downall
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7159 DEFINE_COMMAND(op
, 2, MODCMD_REQUIRE_CHANNEL
, "access", "op", NULL
);
7160 DEFINE_COMMAND(deop
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7161 DEFINE_COMMAND(voice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7162 DEFINE_COMMAND(devoice
, 2, MODCMD_REQUIRE_CHANNEL
, "template", "op", NULL
);
7164 DEFINE_COMMAND(kickban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7165 DEFINE_COMMAND(kick
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7166 DEFINE_COMMAND(ban
, 2, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7167 DEFINE_COMMAND(unban
, 2, 0, "template", "op", NULL
);
7168 DEFINE_COMMAND(unbanall
, 1, 0, "template", "op", NULL
);
7169 DEFINE_COMMAND(unbanme
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7170 DEFINE_COMMAND(open
, 1, MODCMD_REQUIRE_CHANUSER
, "template", "op", NULL
);
7171 DEFINE_COMMAND(topic
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", "flags", "+never_csuspend", NULL
);
7172 DEFINE_COMMAND(mode
, 1, MODCMD_REQUIRE_REGCHAN
, "template", "op", NULL
);
7173 DEFINE_COMMAND(inviteme
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "1", NULL
);
7174 DEFINE_COMMAND(invite
, 1, MODCMD_REQUIRE_CHANNEL
, "access", "manager", NULL
);
7175 DEFINE_COMMAND(set
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "op", NULL
);
7176 DEFINE_COMMAND(wipeinfo
, 2, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7177 DEFINE_COMMAND(resync
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "manager", NULL
);
7179 DEFINE_COMMAND(events
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog", "access", "350", NULL
);
7180 DEFINE_COMMAND(addban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7181 DEFINE_COMMAND(addtimedban
, 3, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7182 DEFINE_COMMAND(delban
, 2, MODCMD_REQUIRE_REGCHAN
, "access", "250", NULL
);
7183 DEFINE_COMMAND(uset
, 1, MODCMD_REQUIRE_CHANUSER
, "access", "1", NULL
);
7185 DEFINE_COMMAND(bans
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "1", "flags", "+nolog", NULL
);
7186 DEFINE_COMMAND(peek
, 1, MODCMD_REQUIRE_REGCHAN
, "access", "op", "flags", "+nolog", NULL
);
7188 DEFINE_COMMAND(myaccess
, 1, MODCMD_REQUIRE_AUTHED
, NULL
);
7189 DEFINE_COMMAND(access
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7190 DEFINE_COMMAND(users
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7191 DEFINE_COMMAND(wlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7192 DEFINE_COMMAND(clist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7193 DEFINE_COMMAND(mlist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7194 DEFINE_COMMAND(olist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7195 DEFINE_COMMAND(plist
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7196 DEFINE_COMMAND(info
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7197 DEFINE_COMMAND(seen
, 2, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7198 DEFINE_COMMAND(names
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+nolog,+joinable", NULL
);
7200 DEFINE_COMMAND(note
, 1, MODCMD_REQUIRE_REGCHAN
, "flags", "+joinable,+acceptchan", NULL
);
7201 DEFINE_COMMAND(delnote
, 2, MODCMD_REQUIRE_CHANUSER
, NULL
);
7203 DEFINE_COMMAND(netinfo
, 1, 0, "flags", "+nolog", NULL
);
7204 DEFINE_COMMAND(ircops
, 1, 0, "flags", "+nolog", NULL
);
7205 DEFINE_COMMAND(helpers
, 1, 0, "flags", "+nolog", NULL
);
7206 DEFINE_COMMAND(staff
, 1, 0, "flags", "+nolog", NULL
);
7208 DEFINE_COMMAND(say
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7209 DEFINE_COMMAND(emote
, 2, 0, "flags", "+oper,+acceptchan", NULL
);
7210 DEFINE_COMMAND(expire
, 1, 0, "flags", "+oper", NULL
);
7211 DEFINE_COMMAND(search
, 3, 0, "flags", "+nolog,+helping", NULL
);
7212 DEFINE_COMMAND(unvisited
, 1, 0, "flags", "+nolog,+helping", NULL
);
7214 DEFINE_COMMAND(unf
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7215 DEFINE_COMMAND(ping
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7216 DEFINE_COMMAND(wut
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7217 DEFINE_COMMAND(8ball
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7218 DEFINE_COMMAND(d
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7219 DEFINE_COMMAND(huggle
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7220 DEFINE_COMMAND(calc
, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL
);
7222 /* Channel options */
7223 DEFINE_CHANNEL_OPTION(defaulttopic
);
7224 DEFINE_CHANNEL_OPTION(topicmask
);
7225 DEFINE_CHANNEL_OPTION(greeting
);
7226 DEFINE_CHANNEL_OPTION(usergreeting
);
7227 DEFINE_CHANNEL_OPTION(modes
);
7228 DEFINE_CHANNEL_OPTION(enfops
);
7229 DEFINE_CHANNEL_OPTION(giveops
);
7230 DEFINE_CHANNEL_OPTION(protect
);
7231 DEFINE_CHANNEL_OPTION(enfmodes
);
7232 DEFINE_CHANNEL_OPTION(enftopic
);
7233 DEFINE_CHANNEL_OPTION(pubcmd
);
7234 DEFINE_CHANNEL_OPTION(givevoice
);
7235 DEFINE_CHANNEL_OPTION(userinfo
);
7236 DEFINE_CHANNEL_OPTION(dynlimit
);
7237 DEFINE_CHANNEL_OPTION(topicsnarf
);
7238 DEFINE_CHANNEL_OPTION(nodelete
);
7239 DEFINE_CHANNEL_OPTION(toys
);
7240 DEFINE_CHANNEL_OPTION(setters
);
7241 DEFINE_CHANNEL_OPTION(topicrefresh
);
7242 DEFINE_CHANNEL_OPTION(ctcpusers
);
7243 DEFINE_CHANNEL_OPTION(ctcpreaction
);
7244 DEFINE_CHANNEL_OPTION(inviteme
);
7246 DEFINE_CHANNEL_OPTION(offchannel
);
7247 modcmd_register(chanserv_module
, "set defaults", chan_opt_defaults
, 1, 0, "access", "owner", NULL
);
7249 /* Alias set topic to set defaulttopic for compatibility. */
7250 modcmd_register(chanserv_module
, "set topic", chan_opt_defaulttopic
, 1, 0, NULL
);
7253 DEFINE_USER_OPTION(noautoop
);
7254 DEFINE_USER_OPTION(autoinvite
);
7255 DEFINE_USER_OPTION(info
);
7257 /* Alias uset autovoice to uset autoop. */
7258 modcmd_register(chanserv_module
, "uset noautovoice", user_opt_noautoop
, 1, 0, NULL
);
7260 note_types
= dict_new();
7261 dict_set_free_data(note_types
, chanserv_deref_note_type
);
7264 chanserv
= AddService(nick
, "Channel Services", NULL
);
7265 service_register(chanserv
)->trigger
= '!';
7266 reg_chanmsg_func('\001', chanserv
, chanserv_ctcp_check
);
7268 saxdb_register("ChanServ", chanserv_saxdb_read
, chanserv_saxdb_write
);
7270 if(chanserv_conf
.channel_expire_frequency
)
7271 timeq_add(now
+ chanserv_conf
.channel_expire_frequency
, expire_channels
, NULL
);
7273 if(chanserv_conf
.refresh_period
)
7275 time_t next_refresh
;
7276 next_refresh
= (now
+ chanserv_conf
.refresh_period
- 1) / chanserv_conf
.refresh_period
* chanserv_conf
.refresh_period
;
7277 timeq_add(next_refresh
, chanserv_refresh_topics
, NULL
);
7280 reg_exit_func(chanserv_db_cleanup
);
7281 message_register_table(msgtab
);