1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for opserv_bad_channel() */
27 #define CHANSERV_CONF_NAME "services/chanserv"
29 /* ChanServ options */
30 #define KEY_SUPPORT_CHANNEL "support_channel"
31 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
32 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
33 #define KEY_INFO_DELAY "info_delay"
34 #define KEY_MAX_GREETLEN "max_greetlen"
35 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
36 #define KEY_ADJUST_DELAY "adjust_delay"
37 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
38 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
39 #define KEY_MAX_CHAN_USERS "max_chan_users"
40 #define KEY_MAX_CHAN_BANS "max_chan_bans"
41 #define KEY_NICK "nick"
42 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
43 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
44 #define KEY_SWITCH_TIMEOUT "switch_timeout"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
56 /* ChanServ database */
57 #define KEY_CHANNELS "channels"
58 #define KEY_NOTE_TYPES "note_types"
60 /* Note type parameters */
61 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
62 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
63 #define KEY_NOTE_SETTER_ACCESS "setter_access"
64 #define KEY_NOTE_VISIBILITY "visibility"
65 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
66 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
67 #define KEY_NOTE_VIS_ALL "all"
68 #define KEY_NOTE_MAX_LENGTH "max_length"
69 #define KEY_NOTE_SETTER "setter"
70 #define KEY_NOTE_NOTE "note"
72 /* Do-not-register channels */
74 #define KEY_DNR_SET "set"
75 #define KEY_DNR_SETTER "setter"
76 #define KEY_DNR_REASON "reason"
79 #define KEY_REGISTERED "registered"
80 #define KEY_REGISTRAR "registrar"
81 #define KEY_SUSPENDED "suspended"
82 #define KEY_PREVIOUS "previous"
83 #define KEY_SUSPENDER "suspender"
84 #define KEY_ISSUED "issued"
85 #define KEY_REVOKED "revoked"
86 #define KEY_SUSPEND_EXPIRES "suspend_expires"
87 #define KEY_SUSPEND_REASON "suspend_reason"
88 #define KEY_VISITED "visited"
89 #define KEY_TOPIC "topic"
90 #define KEY_GREETING "greeting"
91 #define KEY_USER_GREETING "user_greeting"
92 #define KEY_MODES "modes"
93 #define KEY_FLAGS "flags"
94 #define KEY_OPTIONS "options"
95 #define KEY_USERS "users"
96 #define KEY_BANS "bans"
98 #define KEY_NOTES "notes"
99 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_LEVEL "level"
103 #define KEY_INFO "info"
104 #define KEY_SEEN "seen"
107 #define KEY_OWNER "owner"
108 #define KEY_REASON "reason"
109 #define KEY_SET "set"
110 #define KEY_DURATION "duration"
111 #define KEY_EXPIRES "expires"
112 #define KEY_TRIGGERED "triggered"
114 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_INFO_LINES)
115 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
117 /* Administrative messages */
118 static const struct message_entry msgtab[] = {
119 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
121 /* Channel registration */
122 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
123 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
124 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
125 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
126 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
127 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
129 /* Do-not-register channels */
130 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
131 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
132 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
133 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
134 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
135 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
136 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
137 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
138 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
139 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
140 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
142 /* Channel unregistration */
143 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
144 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
145 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
146 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
149 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
150 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
152 /* Channel merging */
153 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
154 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
155 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
156 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
157 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
159 /* Handle unregistration */
160 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
163 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
164 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
165 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
166 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
167 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
168 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
169 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
170 { "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." },
171 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
172 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
173 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
174 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
175 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
177 /* Removing yourself from a channel. */
178 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
179 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
180 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
182 /* User management */
183 { "CSMSG_ADDED_USER", "Added new %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "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." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "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" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
241 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
242 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
243 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
244 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
245 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
246 { "CSMSG_SET_MODES", "$bModes $b %s" },
247 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
248 { "CSMSG_SET_USERINFO", "$bUserInfo $b %s" },
249 { "CSMSG_SET_VOICE", "$bVoice $b %s" },
250 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
251 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %s" },
252 { "CSMSG_SET_PEONINVITE", "$bPeonInvite $b %s" },
253 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
254 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
255 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
256 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
257 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
258 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
259 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
260 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
261 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
262 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
263 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
264 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
265 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
266 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
267 { "CSMSG_USET_INFO", "$bInfo $b %s" },
269 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
270 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
271 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
272 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
273 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
274 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
275 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
276 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
277 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
278 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
279 { "CSMSG_PROTECT_NONE", "No users will be protected." },
280 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
281 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
282 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
283 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
284 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
285 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
286 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
287 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
288 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
289 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
290 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
291 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
293 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
294 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
295 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
296 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
297 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
299 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
300 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
301 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
303 /* Channel userlist */
304 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
305 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
306 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
307 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
309 /* Channel note list */
310 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
311 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
312 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
313 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
314 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
315 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
316 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
317 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
318 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
319 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
320 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
321 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
322 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
323 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
324 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
326 /* Channel [un]suspension */
327 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
328 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
329 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
330 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
331 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
332 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
333 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
335 /* Access information */
336 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
337 { "CSMSG_ACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
338 { "CSMSG_SQUAT_ACCESS", "You do not have access to any channels." },
339 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
340 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
341 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
342 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
343 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
344 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
345 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
346 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
348 /* Seen information */
349 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
350 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
351 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
352 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
354 /* Names information */
355 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
356 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
358 /* Channel information */
359 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
360 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
361 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
362 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
363 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
364 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
365 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
366 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
367 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
368 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
369 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
370 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
371 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
372 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
373 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
374 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
375 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
378 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
379 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
381 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
382 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
383 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
384 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
385 { "CSMSG_PEEK_OPS", "$bOps:$b" },
386 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
388 /* Network information */
389 { "CSMSG_NETWORK_INFO", "Network Information:" },
390 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
391 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
392 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
393 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
394 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
395 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
396 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
399 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
400 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
401 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
403 /* Channel searches */
404 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
405 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
406 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
407 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
409 /* Channel configuration */
410 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
411 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
412 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
415 { "CSMSG_USER_OPTIONS", "User Options:" },
416 { "CSMSG_USER_PROTECTED", "That user is protected." },
419 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
420 { "CSMSG_PING_RESPONSE", "Pong!" },
421 { "CSMSG_WUT_RESPONSE", "wut" },
422 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
423 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
424 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
425 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
426 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
427 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
428 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
431 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
435 /* eject_user and unban_user flags */
436 #define ACTION_KICK 0x0001
437 #define ACTION_BAN 0x0002
438 #define ACTION_ADD_BAN 0x0004
439 #define ACTION_ADD_TIMED_BAN 0x0008
440 #define ACTION_UNBAN 0x0010
441 #define ACTION_DEL_BAN 0x0020
443 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
444 #define MODELEN 40 + KEYLEN
448 #define CSFUNC_ARGS user, channel, argc, argv, cmd
450 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
451 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
452 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
453 reply("MSG_MISSING_PARAMS", argv[0]); \
457 DECLARE_LIST(dnrList, struct do_not_register *);
458 DEFINE_LIST(dnrList, struct do_not_register *);
460 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
462 struct userNode *chanserv;
464 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
465 static struct log_type *CS_LOG;
469 struct channelList support_channels;
470 struct mod_chanmode default_modes;
472 unsigned long db_backup_frequency;
473 unsigned long channel_expire_frequency;
476 unsigned int adjust_delay;
477 long channel_expire_delay;
478 unsigned int nodelete_level;
480 unsigned int adjust_threshold;
481 int join_flood_threshold;
483 unsigned int greeting_length;
484 unsigned int refresh_period;
486 unsigned int max_owned;
487 unsigned int max_chan_users;
488 unsigned int max_chan_bans;
490 struct string_list *set_shows;
491 struct string_list *eightball;
492 struct string_list *old_ban_names;
494 const char *ctcp_short_ban_duration;
495 const char *ctcp_long_ban_duration;
497 const char *irc_operator_epithet;
498 const char *network_helper_epithet;
499 const char *support_helper_epithet;
504 struct userNode *user;
505 struct userNode *bot;
506 struct chanNode *channel;
508 unsigned short lowest;
509 unsigned short highest;
510 struct userData **users;
511 struct helpfile_table table;
514 enum note_access_type
516 NOTE_SET_CHANNEL_ACCESS,
517 NOTE_SET_CHANNEL_SETTER,
521 enum note_visible_type
524 NOTE_VIS_CHANNEL_USERS,
530 enum note_access_type set_access_type;
532 unsigned int min_opserv;
533 unsigned short min_ulevel;
535 enum note_visible_type visible_type;
536 unsigned int max_length;
543 struct note_type *type;
544 char setter[NICKSERV_HANDLE_LEN+1];
548 static unsigned int registered_channels;
549 static unsigned int banCount;
551 static const struct {
554 unsigned short level;
557 { "peon", "Peon", UL_PEON, '+' },
558 { "op", "Op", UL_OP, '@' },
559 { "master", "Master", UL_MASTER, '%' },
560 { "coowner", "Coowner", UL_COOWNER, '*' },
561 { "owner", "Owner", UL_OWNER, '!' },
562 { "helper", "BUG:", UL_HELPER, 'X' }
565 static const struct {
568 unsigned short default_value;
569 unsigned int old_idx;
571 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2 },
572 { "CSMSG_SET_ENFOPS", "enfops", 300, 1 },
573 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3 },
574 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4 },
575 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5 },
576 { "CSMSG_SET_SETTERS", "setters", 400, 7 },
577 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9 }
580 struct charOptionValues {
583 } protectValues[] = {
584 { 'a', "CSMSG_PROTECT_ALL" },
585 { 'e', "CSMSG_PROTECT_EQUAL" },
586 { 'l', "CSMSG_PROTECT_LOWER" },
587 { 'n', "CSMSG_PROTECT_NONE" }
589 { 'd', "CSMSG_TOYS_DISABLED" },
590 { 'n', "CSMSG_TOYS_PRIVATE" },
591 { 'p', "CSMSG_TOYS_PUBLIC" }
592 }, topicRefreshValues[] = {
593 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
594 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
595 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
596 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
597 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
598 }, ctcpReactionValues[] = {
599 { 'k', "CSMSG_CTCPREACTION_KICK" },
600 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
601 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
602 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
605 static const struct {
609 unsigned int old_idx;
611 struct charOptionValues *values;
613 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
614 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
615 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
616 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
619 struct userData *helperList;
620 struct chanData *channelList;
621 static struct module *chanserv_module;
622 static unsigned int userCount;
624 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
625 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
626 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
629 user_level_from_name(const char *name, unsigned short clamp_level)
631 unsigned int level = 0, ii;
634 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
635 if(!irccasecmp(name, accessLevels[ii].name))
636 level = accessLevels[ii].level;
637 if(level > clamp_level)
643 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
646 *minl = strtoul(arg, &sep, 10);
654 *maxl = strtoul(sep+1, &sep, 10);
662 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
664 struct userData *uData, **head;
666 if(!channel || !handle)
669 if(override && HANDLE_FLAGGED(handle, HELPING)
670 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
672 for(uData = helperList;
673 uData && uData->handle != handle;
674 uData = uData->next);
678 uData = calloc(1, sizeof(struct userData));
679 uData->handle = handle;
681 uData->access = UL_HELPER;
687 uData->next = helperList;
689 helperList->prev = uData;
697 for(uData = channel->users; uData; uData = uData->next)
698 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
701 head = &(channel->users);
704 if(uData && (uData != *head))
706 /* Shuffle the user to the head of whatever list he was in. */
708 uData->next->prev = uData->prev;
710 uData->prev->next = uData->next;
716 (**head).prev = uData;
723 /* Returns non-zero if user has at least the minimum access.
724 * exempt_owner is set when handling !set, so the owner can set things
727 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
729 struct userData *uData;
730 struct chanData *cData = channel->channel_info;
731 unsigned short minimum = cData->lvlOpts[opt];
734 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
737 if(minimum <= uData->access)
739 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
744 /* Scan for other users authenticated to the same handle
745 still in the channel. If so, keep them listed as present.
747 user is optional, if not null, it skips checking that userNode
748 (for the handle_part function) */
750 scan_handle_presence(struct chanNode *channel, struct handle_info *handle, struct userNode *user)
752 struct userData *uData;
754 if(!channel->channel_info || IsSuspended(channel->channel_info))
757 uData = GetTrueChannelAccess(channel->channel_info, handle);
760 struct modeNode *mn = find_handle_in_channel(channel, handle, user);
773 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
775 unsigned int eflags, argc;
777 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
779 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
780 if(!channel->channel_info
781 || IsSuspended(channel->channel_info)
783 || !ircncasecmp(text, "ACTION ", 7))
785 /* Figure out the minimum level needed to CTCP the channel */
786 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
788 /* We need to enforce against them; do so. */
791 argv[1] = user->nick;
793 if(GetUserMode(channel, user))
794 eflags |= ACTION_KICK;
795 switch(channel->channel_info->chOpts[chCTCPReaction]) {
796 default: case 'k': /* just do the kick */ break;
798 eflags |= ACTION_BAN;
801 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
802 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
805 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
806 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
809 argv[argc++] = bad_ctcp_reason;
810 eject_user(chanserv, channel, argc, argv, NULL, eflags);
814 chanserv_create_note_type(const char *name)
816 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
817 strcpy(ntype->name, name);
819 dict_insert(note_types, ntype->name, ntype);
824 chanserv_deref_note_type(void *data)
826 struct note_type *ntype = data;
828 if(--ntype->refs > 0)
834 chanserv_flush_note_type(struct note_type *ntype)
836 struct chanData *cData;
837 for(cData = channelList; cData; cData = cData->next)
838 dict_remove(cData->notes, ntype->name);
842 chanserv_truncate_notes(struct note_type *ntype)
844 struct chanData *cData;
846 unsigned int size = sizeof(*note) + ntype->max_length;
848 for(cData = channelList; cData; cData = cData->next) {
849 note = dict_find(cData->notes, ntype->name, NULL);
852 if(strlen(note->note) <= ntype->max_length)
854 dict_remove2(cData->notes, ntype->name, 1);
855 note = realloc(note, size);
856 note->note[ntype->max_length] = 0;
857 dict_insert(cData->notes, ntype->name, note);
861 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
864 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
867 unsigned int len = strlen(text);
869 if(len > type->max_length) len = type->max_length;
870 note = calloc(1, sizeof(*note) + len);
872 strncpy(note->setter, setter, sizeof(note->setter)-1);
873 memcpy(note->note, text, len);
875 dict_insert(channel->notes, type->name, note);
881 chanserv_free_note(void *data)
883 struct note *note = data;
885 chanserv_deref_note_type(note->type);
886 assert(note->type->refs > 0); /* must use delnote to remove the type */
890 static MODCMD_FUNC(cmd_createnote) {
891 struct note_type *ntype;
892 unsigned int arg = 1, existed = 0, max_length;
894 if((ntype = dict_find(note_types, argv[1], NULL)))
897 ntype = chanserv_create_note_type(argv[arg]);
898 if(!irccasecmp(argv[++arg], "privileged"))
901 ntype->set_access_type = NOTE_SET_PRIVILEGED;
902 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
904 else if(!irccasecmp(argv[arg], "channel"))
906 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
909 reply("CSMSG_INVALID_ACCESS", argv[arg]);
912 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
913 ntype->set_access.min_ulevel = ulvl;
915 else if(!irccasecmp(argv[arg], "setter"))
917 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
921 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
925 if(!irccasecmp(argv[++arg], "privileged"))
926 ntype->visible_type = NOTE_VIS_PRIVILEGED;
927 else if(!irccasecmp(argv[arg], "channel_users"))
928 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
929 else if(!irccasecmp(argv[arg], "all"))
930 ntype->visible_type = NOTE_VIS_ALL;
932 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
936 if((arg+1) >= argc) {
937 reply("MSG_MISSING_PARAMS", argv[0]);
940 max_length = strtoul(argv[++arg], NULL, 0);
941 if(max_length < 20 || max_length > 450)
943 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
946 if(existed && (max_length < ntype->max_length))
948 ntype->max_length = max_length;
949 chanserv_truncate_notes(ntype);
951 ntype->max_length = max_length;
954 reply("CSMSG_NOTE_MODIFIED", ntype->name);
956 reply("CSMSG_NOTE_CREATED", ntype->name);
961 dict_remove(note_types, ntype->name);
965 static MODCMD_FUNC(cmd_removenote) {
966 struct note_type *ntype;
969 ntype = dict_find(note_types, argv[1], NULL);
970 force = (argc > 2) && !irccasecmp(argv[2], "force");
973 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
980 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
983 chanserv_flush_note_type(ntype);
985 dict_remove(note_types, argv[1]);
986 reply("CSMSG_NOTE_DELETED", argv[1]);
991 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
995 if(orig->modes_set & change->modes_clear)
997 if(orig->modes_clear & change->modes_set)
999 if((orig->modes_set & MODE_KEY)
1000 && strcmp(orig->new_key, change->new_key))
1002 if((orig->modes_set & MODE_LIMIT)
1003 && (orig->new_limit != change->new_limit))
1008 static char max_length_text[MAXLEN+1][16];
1010 static struct helpfile_expansion
1011 chanserv_expand_variable(const char *variable)
1013 struct helpfile_expansion exp;
1015 if(!irccasecmp(variable, "notes"))
1018 exp.type = HF_TABLE;
1019 exp.value.table.length = 1;
1020 exp.value.table.width = 3;
1021 exp.value.table.flags = 0;
1022 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1023 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1024 exp.value.table.contents[0][0] = "Note Type";
1025 exp.value.table.contents[0][1] = "Visibility";
1026 exp.value.table.contents[0][2] = "Max Length";
1027 for(it=dict_first(note_types); it; it=iter_next(it))
1029 struct note_type *ntype = iter_data(it);
1032 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1033 row = exp.value.table.length++;
1034 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1035 exp.value.table.contents[row][0] = ntype->name;
1036 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1037 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1039 if(!max_length_text[ntype->max_length][0])
1040 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1041 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1046 exp.type = HF_STRING;
1047 exp.value.str = NULL;
1051 static struct chanData*
1052 register_channel(struct chanNode *cNode, char *registrar)
1054 struct chanData *channel;
1055 enum levelOption lvlOpt;
1056 enum charOption chOpt;
1058 channel = calloc(1, sizeof(struct chanData));
1060 channel->notes = dict_new();
1061 dict_set_free_data(channel->notes, chanserv_free_note);
1063 channel->registrar = strdup(registrar);
1064 channel->registered = now;
1065 channel->visited = now;
1066 channel->limitAdjusted = now;
1067 channel->flags = CHANNEL_DEFAULT_FLAGS;
1068 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1069 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1070 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1071 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1073 channel->prev = NULL;
1074 channel->next = channelList;
1077 channelList->prev = channel;
1078 channelList = channel;
1079 registered_channels++;
1081 channel->channel = cNode;
1083 cNode->channel_info = channel;
1088 static struct userData*
1089 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1091 struct userData *ud;
1093 if(access > UL_OWNER)
1096 ud = calloc(1, sizeof(*ud));
1097 ud->channel = channel;
1098 ud->handle = handle;
1100 ud->access = access;
1101 ud->info = info ? strdup(info) : NULL;
1104 ud->next = channel->users;
1106 channel->users->prev = ud;
1107 channel->users = ud;
1109 channel->userCount++;
1113 ud->u_next = ud->handle->channels;
1115 ud->u_next->u_prev = ud;
1116 ud->handle->channels = ud;
1121 static void unregister_channel(struct chanData *channel, const char *reason);
1124 del_channel_user(struct userData *user, int do_gc)
1126 struct chanData *channel = user->channel;
1128 channel->userCount--;
1132 user->prev->next = user->next;
1134 channel->users = user->next;
1136 user->next->prev = user->prev;
1139 user->u_prev->u_next = user->u_next;
1141 user->handle->channels = user->u_next;
1143 user->u_next->u_prev = user->u_prev;
1147 if(do_gc && !channel->users && !IsProtected(channel))
1148 unregister_channel(channel, "lost all users.");
1151 static void expire_ban(void *data);
1153 static struct banData*
1154 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1157 unsigned int ii, l1, l2;
1162 bd = malloc(sizeof(struct banData));
1164 bd->channel = channel;
1166 bd->triggered = triggered;
1167 bd->expires = expires;
1169 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1171 extern const char *hidden_host_suffix;
1172 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1176 l2 = strlen(old_name);
1179 if(irccasecmp(mask + l1 - l2, old_name))
1181 new_mask = alloca(MAXLEN);
1182 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1185 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1187 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1188 bd->reason = reason ? strdup(reason) : NULL;
1191 timeq_add(expires, expire_ban, bd);
1194 bd->next = channel->bans;
1196 channel->bans->prev = bd;
1198 channel->banCount++;
1205 del_channel_ban(struct banData *ban)
1207 ban->channel->banCount--;
1211 ban->prev->next = ban->next;
1213 ban->channel->bans = ban->next;
1216 ban->next->prev = ban->prev;
1219 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1228 expire_ban(void *data)
1230 struct banData *bd = data;
1231 if(!IsSuspended(bd->channel))
1233 struct banList bans;
1234 struct mod_chanmode change;
1236 bans = bd->channel->channel->banlist;
1237 change.modes_set = change.modes_clear = 0;
1239 for(ii=0; ii<bans.used; ii++)
1241 if(!strcmp(bans.list[ii]->ban, bd->mask))
1244 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1245 change.args[0].hostmask = bd->mask;
1246 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1252 del_channel_ban(bd);
1255 static void chanserv_expire_suspension(void *data);
1258 unregister_channel(struct chanData *channel, const char *reason)
1260 char msgbuf[MAXLEN];
1262 /* After channel unregistration, the following must be cleaned
1264 - Channel information.
1267 - Channel suspension data.
1268 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1274 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1276 while(channel->users)
1277 del_channel_user(channel->users, 0);
1279 while(channel->bans)
1280 del_channel_ban(channel->bans);
1282 if(channel->topic) free(channel->topic);
1283 if(channel->registrar) free(channel->registrar);
1284 if(channel->greeting) free(channel->greeting);
1285 if(channel->user_greeting) free(channel->user_greeting);
1286 if(channel->topic_mask) free(channel->topic_mask);
1288 if(channel->prev) channel->prev->next = channel->next;
1289 else channelList = channel->next;
1291 if(channel->next) channel->next->prev = channel->prev;
1293 if(channel->suspended)
1295 struct chanNode *cNode = channel->channel;
1296 struct suspended *suspended, *next_suspended;
1298 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1300 next_suspended = suspended->previous;
1301 free(suspended->suspender);
1302 free(suspended->reason);
1303 if(suspended->expires)
1304 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1309 cNode->channel_info = NULL;
1311 channel->channel->channel_info = NULL;
1314 dict_delete(channel->notes);
1315 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1316 if(!IsSuspended(channel))
1317 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1318 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1319 UnlockChannel(channel->channel);
1321 registered_channels--;
1325 expire_channels(UNUSED_ARG(void *data))
1327 struct chanData *channel, *next;
1328 struct userData *user;
1329 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1331 intervalString(delay, chanserv_conf.channel_expire_delay);
1332 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1334 for(channel = channelList; channel; channel = next)
1336 next = channel->next;
1338 /* See if the channel can be expired. */
1339 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1340 || IsProtected(channel))
1343 /* Make sure there are no high-ranking users still in the channel. */
1344 for(user=channel->users; user; user=user->next)
1345 if(user->present && (user->access >= UL_PRESENT))
1350 /* Unregister the channel */
1351 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1352 unregister_channel(channel, "registration expired.");
1355 if(chanserv_conf.channel_expire_frequency)
1356 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1360 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1362 char protect = channel->chOpts[chProtect];
1363 struct userData *cs_victim, *cs_aggressor;
1365 /* Don't protect if no one is to be protected, someone is attacking
1366 himself, or if the aggressor is an IRC Operator. */
1367 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1370 /* Don't protect if the victim isn't authenticated (because they
1371 can't be a channel user), unless we are to protect non-users
1373 cs_victim = GetChannelAccess(channel, victim->handle_info);
1374 if(protect != 'a' && !cs_victim)
1377 /* Protect if the aggressor isn't a user because at this point,
1378 the aggressor can only be less than or equal to the victim. */
1379 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1383 /* If the aggressor was a user, then the victim can't be helped. */
1390 if(cs_victim->access > cs_aggressor->access)
1395 if(cs_victim->access >= cs_aggressor->access)
1404 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1406 struct chanData *cData = channel->channel_info;
1407 struct userData *cs_victim;
1409 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1410 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1411 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1413 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1421 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1423 if(IsService(victim))
1425 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1429 if(protect_user(victim, user, channel->channel_info))
1431 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1438 static struct do_not_register *
1439 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1441 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1442 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1443 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1444 strcpy(dnr->reason, reason);
1446 if(dnr->chan_name[0] == '*')
1447 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1448 else if(strpbrk(dnr->chan_name, "*?"))
1449 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1451 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1455 static struct dnrList
1456 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1458 struct dnrList list;
1460 struct do_not_register *dnr;
1462 dnrList_init(&list);
1463 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1464 dnrList_append(&list, dnr);
1465 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1466 dnrList_append(&list, dnr);
1468 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1469 if(match_ircglob(chan_name, iter_key(it)))
1470 dnrList_append(&list, iter_data(it));
1475 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1477 struct dnrList list;
1478 struct do_not_register *dnr;
1480 char buf[INTERVALLEN];
1482 list = chanserv_find_dnrs(chan_name, handle);
1483 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1485 dnr = list.list[ii];
1488 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1489 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1492 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1495 reply("CSMSG_MORE_DNRS", list.used - ii);
1500 struct do_not_register *
1501 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1503 struct do_not_register *dnr;
1506 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1510 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1512 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1513 if(match_ircglob(chan_name, iter_key(it)))
1514 return iter_data(it);
1519 static CHANSERV_FUNC(cmd_noregister)
1522 struct do_not_register *dnr;
1523 char buf[INTERVALLEN];
1524 unsigned int matches;
1530 reply("CSMSG_DNR_SEARCH_RESULTS");
1532 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1534 dnr = iter_data(it);
1536 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1538 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1541 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1543 dnr = iter_data(it);
1545 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1547 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1550 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1552 dnr = iter_data(it);
1554 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1556 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1561 reply("MSG_MATCH_COUNT", matches);
1563 reply("MSG_NO_MATCHES");
1569 if(!IsChannelName(target) && (*target != '*'))
1571 reply("CSMSG_NOT_DNR", target);
1577 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1578 if((*target == '*') && !get_handle_info(target + 1))
1580 reply("MSG_HANDLE_UNKNOWN", target + 1);
1583 chanserv_add_dnr(target, user->handle_info->handle, reason);
1584 reply("CSMSG_NOREGISTER_CHANNEL", target);
1588 reply("CSMSG_DNR_SEARCH_RESULTS");
1590 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1592 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1594 reply("MSG_NO_MATCHES");
1598 static CHANSERV_FUNC(cmd_allowregister)
1600 const char *chan_name = argv[1];
1602 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1604 dict_remove(handle_dnrs, chan_name+1);
1605 reply("CSMSG_DNR_REMOVED", chan_name);
1607 else if(dict_find(plain_dnrs, chan_name, NULL))
1609 dict_remove(plain_dnrs, chan_name);
1610 reply("CSMSG_DNR_REMOVED", chan_name);
1612 else if(dict_find(mask_dnrs, chan_name, NULL))
1614 dict_remove(mask_dnrs, chan_name);
1615 reply("CSMSG_DNR_REMOVED", chan_name);
1619 reply("CSMSG_NO_SUCH_DNR", chan_name);
1626 chanserv_get_owned_count(struct handle_info *hi)
1628 struct userData *cList;
1631 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1632 if(cList->access == UL_OWNER)
1637 static CHANSERV_FUNC(cmd_register)
1639 struct mod_chanmode *change;
1640 struct handle_info *handle;
1641 struct chanData *cData;
1642 struct modeNode *mn;
1643 char reason[MAXLEN];
1645 unsigned int new_channel, force=0;
1646 struct do_not_register *dnr;
1650 if(channel->channel_info)
1652 reply("CSMSG_ALREADY_REGGED", channel->name);
1656 if(channel->bad_channel)
1658 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1662 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1664 reply("CSMSG_MUST_BE_OPPED", channel->name);
1669 chan_name = channel->name;
1673 if((argc < 2) || !IsChannelName(argv[1]))
1675 reply("MSG_NOT_CHANNEL_NAME");
1679 if(opserv_bad_channel(argv[1]))
1681 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1686 chan_name = argv[1];
1689 if(argc >= (new_channel+2))
1691 if(!IsHelping(user))
1693 reply("CSMSG_PROXY_FORBIDDEN");
1697 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1699 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1700 dnr = chanserv_is_dnr(chan_name, handle);
1704 handle = user->handle_info;
1705 dnr = chanserv_is_dnr(chan_name, handle);
1709 if(!IsHelping(user))
1710 reply("CSMSG_DNR_CHANNEL", chan_name);
1712 chanserv_show_dnrs(user, cmd, chan_name, handle);
1716 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1718 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1723 channel = AddChannel(argv[1], now, NULL, NULL);
1725 cData = register_channel(channel, user->handle_info->handle);
1726 add_channel_user(cData, handle, UL_OWNER, 0, NULL);
1727 scan_handle_presence(channel, handle, NULL);
1728 cData->modes = chanserv_conf.default_modes;
1729 change = mod_chanmode_dup(&cData->modes, 1);
1730 change->args[change->argc].mode = MODE_CHANOP;
1731 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1733 mod_chanmode_announce(chanserv, channel, change);
1734 mod_chanmode_free(change);
1736 /* Initialize the channel's max user record. */
1737 cData->max = channel->members.used;
1739 if(handle != user->handle_info)
1740 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1742 reply("CSMSG_REG_SUCCESS", channel->name);
1744 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1745 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1750 make_confirmation_string(struct userData *uData)
1752 static char strbuf[16];
1757 for(src = uData->handle->handle; *src; )
1758 accum = accum * 31 + toupper(*src++);
1760 for(src = uData->channel->channel->name; *src; )
1761 accum = accum * 31 + toupper(*src++);
1762 sprintf(strbuf, "%08x", accum);
1766 static CHANSERV_FUNC(cmd_unregister)
1769 char reason[MAXLEN];
1770 struct chanData *cData;
1771 struct userData *uData;
1773 cData = channel->channel_info;
1776 reply("CSMSG_NOT_REGISTERED", channel->name);
1780 uData = GetChannelUser(cData, user->handle_info);
1781 if(!uData || (uData->access < UL_OWNER))
1783 reply("CSMSG_NO_ACCESS");
1787 if(IsProtected(cData))
1789 reply("CSMSG_UNREG_NODELETE", channel->name);
1793 if(!IsHelping(user))
1795 const char *confirm_string;
1796 if(IsSuspended(cData))
1798 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1801 confirm_string = make_confirmation_string(uData);
1802 if((argc < 2) || strcmp(argv[1], confirm_string))
1804 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1809 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1810 name = strdup(channel->name);
1811 unregister_channel(cData, reason);
1812 reply("CSMSG_UNREG_SUCCESS", name);
1817 static CHANSERV_FUNC(cmd_move)
1819 struct chanNode *target;
1820 struct modeNode *mn;
1821 struct userData *uData;
1822 char reason[MAXLEN];
1823 struct do_not_register *dnr;
1827 if(IsProtected(channel->channel_info))
1829 reply("CSMSG_MOVE_NODELETE", channel->name);
1833 if(!IsChannelName(argv[1]))
1835 reply("MSG_NOT_CHANNEL_NAME");
1839 if(opserv_bad_channel(argv[1]))
1841 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1845 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1847 for(uData = channel->channel_info->users; uData; uData = uData->next)
1849 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1851 if(!IsHelping(user))
1852 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1854 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1860 if(!(target = GetChannel(argv[1])))
1862 target = AddChannel(argv[1], now, NULL, NULL);
1863 LockChannel(target);
1864 if(!IsSuspended(channel->channel_info))
1865 AddChannelUser(chanserv, target);
1867 else if(target->channel_info)
1869 reply("CSMSG_ALREADY_REGGED", target->name);
1872 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1873 && !IsHelping(user))
1875 reply("CSMSG_MUST_BE_OPPED", target->name);
1878 else if(!IsSuspended(channel->channel_info))
1880 struct mod_chanmode change;
1881 change.modes_set = change.modes_clear = 0;
1883 change.args[0].mode = MODE_CHANOP;
1884 change.args[0].member = AddChannelUser(chanserv, target);
1885 mod_chanmode_announce(chanserv, target, &change);
1888 /* Move the channel_info to the target channel; it
1889 shouldn't be necessary to clear timeq callbacks
1890 for the old channel. */
1891 target->channel_info = channel->channel_info;
1892 target->channel_info->channel = target;
1893 channel->channel_info = NULL;
1895 reply("CSMSG_MOVE_SUCCESS", target->name);
1897 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1898 if(!IsSuspended(target->channel_info))
1900 char reason2[MAXLEN];
1901 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1902 DelChannelUser(chanserv, channel, reason2, 0);
1904 UnlockChannel(channel);
1905 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1910 merge_users(struct chanData *source, struct chanData *target)
1912 struct userData *suData, *tuData, *next;
1918 /* Insert the source's users into the scratch area. */
1919 for(suData = source->users; suData; suData = suData->next)
1920 dict_insert(merge, suData->handle->handle, suData);
1922 /* Iterate through the target's users, looking for
1923 users common to both channels. The lower access is
1924 removed from either the scratch area or target user
1926 for(tuData = target->users; tuData; tuData = next)
1928 struct userData *choice;
1930 next = tuData->next;
1932 /* If a source user exists with the same handle as a target
1933 channel's user, resolve the conflict by removing one. */
1934 suData = dict_find(merge, tuData->handle->handle, NULL);
1938 /* Pick the data we want to keep. */
1939 /* If the access is the same, use the later seen time. */
1940 if(suData->access == tuData->access)
1941 choice = (suData->seen > tuData->seen) ? suData : tuData;
1942 else /* Otherwise, keep the higher access level. */
1943 choice = (suData->access > tuData->access) ? suData : tuData;
1945 /* Remove the user that wasn't picked. */
1946 if(choice == tuData)
1948 dict_remove(merge, suData->handle->handle);
1949 del_channel_user(suData, 0);
1952 del_channel_user(tuData, 0);
1955 /* Move the remaining users to the target channel. */
1956 for(it = dict_first(merge); it; it = iter_next(it))
1958 suData = iter_data(it);
1960 /* Insert the user into the target channel's linked list. */
1961 suData->prev = NULL;
1962 suData->next = target->users;
1963 suData->channel = target;
1966 target->users->prev = suData;
1967 target->users = suData;
1969 /* Update the user counts for the target channel; the
1970 source counts are left alone. */
1971 target->userCount++;
1974 /* Possible to assert (source->users == NULL) here. */
1975 source->users = NULL;
1980 merge_bans(struct chanData *source, struct chanData *target)
1982 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1984 /* Hold on to the original head of the target ban list
1985 to avoid comparing source bans with source bans. */
1986 tFront = target->bans;
1988 /* Perform a totally expensive O(n*m) merge, ick. */
1989 for(sbData = source->bans; sbData; sbData = sNext)
1991 /* Flag to track whether the ban's been moved
1992 to the destination yet. */
1995 /* Possible to assert (sbData->prev == NULL) here. */
1996 sNext = sbData->next;
1998 for(tbData = tFront; tbData; tbData = tNext)
2000 tNext = tbData->next;
2002 /* Perform two comparisons between each source
2003 and target ban, conflicts are resolved by
2004 keeping the broader ban and copying the later
2005 expiration and triggered time. */
2006 if(match_ircglobs(tbData->mask, sbData->mask))
2008 /* There is a broader ban in the target channel that
2009 overrides one in the source channel; remove the
2010 source ban and break. */
2011 if(sbData->expires > tbData->expires)
2012 tbData->expires = sbData->expires;
2013 if(sbData->triggered > tbData->triggered)
2014 tbData->triggered = sbData->triggered;
2015 del_channel_ban(sbData);
2018 else if(match_ircglobs(sbData->mask, tbData->mask))
2020 /* There is a broader ban in the source channel that
2021 overrides one in the target channel; remove the
2022 target ban, fall through and move the source over. */
2023 if(tbData->expires > sbData->expires)
2024 sbData->expires = tbData->expires;
2025 if(tbData->triggered > sbData->triggered)
2026 sbData->triggered = tbData->triggered;
2027 if(tbData == tFront)
2029 del_channel_ban(tbData);
2032 /* Source bans can override multiple target bans, so
2033 we allow a source to run through this loop multiple
2034 times, but we can only move it once. */
2039 /* Remove the source ban from the source ban list. */
2041 sbData->next->prev = sbData->prev;
2043 /* Modify the source ban's associated channel. */
2044 sbData->channel = target;
2046 /* Insert the ban into the target channel's linked list. */
2047 sbData->prev = NULL;
2048 sbData->next = target->bans;
2051 target->bans->prev = sbData;
2052 target->bans = sbData;
2054 /* Update the user counts for the target channel. */
2059 /* Possible to assert (source->bans == NULL) here. */
2060 source->bans = NULL;
2064 merge_data(struct chanData *source, struct chanData *target)
2066 if(source->visited > target->visited)
2067 target->visited = source->visited;
2071 merge_channel(struct chanData *source, struct chanData *target)
2073 merge_users(source, target);
2074 merge_bans(source, target);
2075 merge_data(source, target);
2078 static CHANSERV_FUNC(cmd_merge)
2080 struct userData *target_user;
2081 struct chanNode *target;
2082 char reason[MAXLEN];
2086 /* Make sure the target channel exists and is registered to the user
2087 performing the command. */
2088 if(!(target = GetChannel(argv[1])))
2090 reply("MSG_INVALID_CHANNEL");
2094 if(!target->channel_info)
2096 reply("CSMSG_NOT_REGISTERED", target->name);
2100 if(IsProtected(channel->channel_info))
2102 reply("CSMSG_MERGE_NODELETE");
2106 if(IsSuspended(target->channel_info))
2108 reply("CSMSG_MERGE_SUSPENDED");
2112 if(channel == target)
2114 reply("CSMSG_MERGE_SELF");
2118 target_user = GetChannelUser(target->channel_info, user->handle_info);
2119 if(!target_user || (target_user->access < UL_OWNER))
2121 reply("CSMSG_MERGE_NOT_OWNER");
2125 /* Merge the channel structures and associated data. */
2126 merge_channel(channel->channel_info, target->channel_info);
2127 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2128 unregister_channel(channel->channel_info, reason);
2129 reply("CSMSG_MERGE_SUCCESS", target->name);
2133 static CHANSERV_FUNC(cmd_opchan)
2135 struct mod_chanmode change;
2136 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2138 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2141 channel->channel_info->may_opchan = 0;
2142 change.modes_set = change.modes_clear = 0;
2144 change.args[0].mode = MODE_CHANOP;
2145 change.args[0].member = GetUserMode(channel, chanserv);
2146 mod_chanmode_announce(chanserv, channel, &change);
2147 reply("CSMSG_OPCHAN_DONE", channel->name);
2151 static CHANSERV_FUNC(cmd_adduser)
2153 struct userData *actee;
2154 struct userData *actor;
2155 struct handle_info *handle;
2156 unsigned short access;
2160 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2162 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2166 access = user_level_from_name(argv[1], UL_OWNER);
2169 reply("CSMSG_INVALID_ACCESS", argv[1]);
2173 actor = GetChannelUser(channel->channel_info, user->handle_info);
2174 if(actor->access <= access)
2176 reply("CSMSG_NO_BUMP_ACCESS");
2180 if(!(handle = modcmd_get_handle_info(user, argv[2])))
2183 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2185 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2189 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2190 scan_handle_presence(channel, handle, NULL);
2191 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2195 static CHANSERV_FUNC(cmd_clvl)
2197 struct handle_info *handle;
2198 struct userData *victim;
2199 struct userData *actor;
2200 unsigned short new_access;
2201 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2205 actor = GetChannelUser(channel->channel_info, user->handle_info);
2207 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2210 if(handle == user->handle_info && !privileged)
2212 reply("CSMSG_NO_SELF_CLVL");
2216 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2218 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2222 if(actor->access <= victim->access && !privileged)
2224 reply("MSG_USER_OUTRANKED", handle->handle);
2228 new_access = user_level_from_name(argv[2], UL_OWNER);
2232 reply("CSMSG_INVALID_ACCESS", argv[2]);
2236 if(new_access >= actor->access && !privileged)
2238 reply("CSMSG_NO_BUMP_ACCESS");
2242 victim->access = new_access;
2243 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2247 static CHANSERV_FUNC(cmd_deluser)
2249 struct handle_info *handle;
2250 struct userData *victim;
2251 struct userData *actor;
2252 unsigned short access;
2257 actor = GetChannelUser(channel->channel_info, user->handle_info);
2259 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2262 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2264 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2270 access = user_level_from_name(argv[1], UL_OWNER);
2273 reply("CSMSG_INVALID_ACCESS", argv[1]);
2276 if(access != victim->access)
2278 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2284 access = victim->access;
2287 if((actor->access <= victim->access) && !IsHelping(user))
2289 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2293 chan_name = strdup(channel->name);
2294 del_channel_user(victim, 1);
2295 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2301 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2303 struct userData *actor, *uData, *next;
2305 actor = GetChannelUser(channel->channel_info, user->handle_info);
2307 if(min_access > max_access)
2309 reply("CSMSG_BAD_RANGE", min_access, max_access);
2313 if((actor->access <= max_access) && !IsHelping(user))
2315 reply("CSMSG_NO_ACCESS");
2319 for(uData = channel->channel_info->users; uData; uData = next)
2323 if((uData->access >= min_access)
2324 && (uData->access <= max_access)
2325 && match_ircglob(uData->handle->handle, mask))
2326 del_channel_user(uData, 1);
2329 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2333 static CHANSERV_FUNC(cmd_mdelowner)
2335 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2338 static CHANSERV_FUNC(cmd_mdelcoowner)
2340 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2343 static CHANSERV_FUNC(cmd_mdelmaster)
2345 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2348 static CHANSERV_FUNC(cmd_mdelop)
2350 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2353 static CHANSERV_FUNC(cmd_mdelpeon)
2355 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2359 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2361 struct banData *bData, *next;
2362 char interval[INTERVALLEN];
2367 limit = now - duration;
2368 for(bData = channel->channel_info->bans; bData; bData = next)
2372 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2375 del_channel_ban(bData);
2379 intervalString(interval, duration);
2380 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2385 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2387 struct userData *actor, *uData, *next;
2388 char interval[INTERVALLEN];
2392 actor = GetChannelUser(channel->channel_info, user->handle_info);
2393 if(min_access > max_access)
2395 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2399 if((actor->access <= max_access) && !IsHelping(user))
2401 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2406 limit = now - duration;
2407 for(uData = channel->channel_info->users; uData; uData = next)
2411 if((uData->seen > limit) || uData->present)
2414 if(((uData->access >= min_access) && (uData->access <= max_access))
2415 || (max_access && (uData->access < actor->access)))
2417 del_channel_user(uData, 1);
2422 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2426 static CHANSERV_FUNC(cmd_trim)
2428 unsigned long duration;
2429 unsigned short min_level, max_level;
2433 duration = ParseInterval(argv[2]);
2436 reply("CSMSG_CANNOT_TRIM");
2440 if(!irccasecmp(argv[1], "bans"))
2442 cmd_trim_bans(user, channel, duration);
2445 else if(!irccasecmp(argv[1], "users"))
2447 cmd_trim_users(user, channel, 0, 0, duration);
2450 else if(parse_level_range(&min_level, &max_level, argv[1]))
2452 cmd_trim_users(user, channel, min_level, max_level, duration);
2455 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2457 cmd_trim_users(user, channel, min_level, min_level, duration);
2462 reply("CSMSG_INVALID_TRIM", argv[1]);
2467 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2468 to the user. cmd_all takes advantage of this. */
2469 static CHANSERV_FUNC(cmd_up)
2471 struct mod_chanmode change;
2472 struct userData *uData;
2475 change.modes_set = change.modes_clear = 0;
2477 change.args[0].member = GetUserMode(channel, user);
2478 if(!change.args[0].member)
2481 reply("MSG_CHANNEL_ABSENT", channel->name);
2485 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2489 reply("CSMSG_GODMODE_UP", argv[0]);
2492 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2494 change.args[0].mode = MODE_CHANOP;
2495 errmsg = "CSMSG_ALREADY_OPPED";
2499 change.args[0].mode = MODE_VOICE;
2500 errmsg = "CSMSG_ALREADY_VOICED";
2502 change.args[0].mode &= ~change.args[0].member->modes;
2503 if(!change.args[0].mode)
2506 reply(errmsg, channel->name);
2509 modcmd_chanmode_announce(&change);
2513 static CHANSERV_FUNC(cmd_down)
2515 struct mod_chanmode change;
2517 change.modes_set = change.modes_clear = 0;
2519 change.args[0].member = GetUserMode(channel, user);
2520 if(!change.args[0].member)
2523 reply("MSG_CHANNEL_ABSENT", channel->name);
2527 if(!change.args[0].member->modes)
2530 reply("CSMSG_ALREADY_DOWN", channel->name);
2534 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2535 modcmd_chanmode_announce(&change);
2539 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)
2541 struct userData *cList;
2543 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2545 if(IsSuspended(cList->channel)
2546 || IsUserSuspended(cList)
2547 || !GetUserMode(cList->channel->channel, user))
2550 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2556 static CHANSERV_FUNC(cmd_upall)
2558 return cmd_all(CSFUNC_ARGS, cmd_up);
2561 static CHANSERV_FUNC(cmd_downall)
2563 return cmd_all(CSFUNC_ARGS, cmd_down);
2566 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2567 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2570 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)
2572 unsigned int ii, valid;
2573 struct userNode *victim;
2574 struct mod_chanmode *change;
2576 change = mod_chanmode_alloc(argc - 1);
2578 for(ii=valid=0; ++ii < argc; )
2580 if(!(victim = GetUserH(argv[ii])))
2582 change->args[valid].mode = mode;
2583 change->args[valid].member = GetUserMode(channel, victim);
2584 if(!change->args[valid].member)
2586 if(validate && !validate(user, channel, victim))
2591 change->argc = valid;
2592 if(valid < (argc-1))
2593 reply("CSMSG_PROCESS_FAILED");
2596 modcmd_chanmode_announce(change);
2597 reply(action, channel->name);
2599 mod_chanmode_free(change);
2603 static CHANSERV_FUNC(cmd_op)
2605 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2608 static CHANSERV_FUNC(cmd_deop)
2610 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2613 static CHANSERV_FUNC(cmd_voice)
2615 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2618 static CHANSERV_FUNC(cmd_devoice)
2620 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2624 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2630 for(ii=0; ii<channel->members.used; ii++)
2632 struct modeNode *mn = channel->members.list[ii];
2634 if(IsService(mn->user))
2637 if(!user_matches_glob(mn->user, ban, 1))
2640 if(protect_user(mn->user, user, channel->channel_info))
2644 victims[(*victimCount)++] = mn;
2650 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2652 struct userNode *victim;
2653 struct modeNode **victims;
2654 unsigned int offset, n, victimCount, duration = 0;
2655 char *reason = "Bye.", *ban, *name;
2656 char interval[INTERVALLEN];
2658 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2659 REQUIRE_PARAMS(offset);
2662 reason = unsplit_string(argv + offset, argc - offset, NULL);
2663 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2665 /* Truncate the reason to a length of TOPICLEN, as
2666 the ircd does; however, leave room for an ellipsis
2667 and the kicker's nick. */
2668 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2672 if((victim = GetUserH(argv[1])))
2674 victims = alloca(sizeof(victims[0]));
2675 victims[0] = GetUserMode(channel, victim);
2676 /* XXX: The comparison with ACTION_KICK is just because all
2677 * other actions can work on users outside the channel, and we
2678 * want to allow those (e.g. unbans) in that case. If we add
2679 * some other ejection action for in-channel users, change
2681 victimCount = victims[0] ? 1 : 0;
2683 if(IsService(victim))
2685 reply("MSG_SERVICE_IMMUNE", victim->nick);
2689 if((action == ACTION_KICK) && !victimCount)
2691 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2695 if(protect_user(victim, user, channel->channel_info))
2697 reply("CSMSG_USER_PROTECTED", victim->nick);
2701 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2702 name = victim->nick;
2706 if(!is_ircmask(argv[1]))
2708 reply("MSG_NICK_UNKNOWN", argv[1]);
2712 victims = alloca(sizeof(victims[0]) * channel->members.used);
2714 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2716 reply("CSMSG_MASK_PROTECTED", argv[1]);
2720 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2722 reply("CSMSG_LAME_MASK", argv[1]);
2726 if((action == ACTION_KICK) && (victimCount == 0))
2728 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2732 name = ban = strdup(argv[1]);
2735 /* Truncate the ban in place if necessary; we must ensure
2736 that 'ban' is a valid ban mask before sanitizing it. */
2737 sanitize_ircmask(ban);
2739 if(action & ACTION_ADD_BAN)
2741 struct banData *bData, *next;
2743 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2745 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2750 if(action & ACTION_ADD_TIMED_BAN)
2752 duration = ParseInterval(argv[2]);
2756 reply("CSMSG_DURATION_TOO_LOW");
2760 else if(duration > (86400 * 365 * 2))
2762 reply("CSMSG_DURATION_TOO_HIGH");
2768 for(bData = channel->channel_info->bans; bData; bData = next)
2770 if(match_ircglobs(bData->mask, ban))
2772 int exact = !irccasecmp(bData->mask, ban);
2774 /* The ban is redundant; there is already a ban
2775 with the same effect in place. */
2779 free(bData->reason);
2780 bData->reason = strdup(reason);
2781 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2782 reply("CSMSG_REASON_CHANGE", ban);
2786 if(exact && bData->expires)
2790 /* If the ban matches an existing one exactly,
2791 extend the expiration time if the provided
2792 duration is longer. */
2793 if(duration && ((time_t)(now + duration) > bData->expires))
2795 bData->expires = now + duration;
2806 /* Delete the expiration timeq entry and
2807 requeue if necessary. */
2808 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2811 timeq_add(bData->expires, expire_ban, bData);
2814 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2816 reply("CSMSG_BAN_ADDED", name, channel->name);
2821 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2828 if(match_ircglobs(ban, bData->mask))
2830 /* The ban we are adding makes previously existing
2831 bans redundant; silently remove them. */
2832 del_channel_ban(bData);
2836 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);
2838 name = ban = strdup(bData->mask);
2842 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2844 extern const char *hidden_host_suffix;
2845 const char *old_name = chanserv_conf.old_ban_names->list[n];
2847 unsigned int l1, l2;
2850 l2 = strlen(old_name);
2853 if(irccasecmp(ban + l1 - l2, old_name))
2855 new_mask = malloc(MAXLEN);
2856 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2858 name = ban = new_mask;
2863 if(action & ACTION_BAN)
2865 unsigned int exists;
2866 struct mod_chanmode *change;
2868 if(channel->banlist.used >= MAXBANS)
2870 reply("CSMSG_BANLIST_FULL", channel->name);
2875 exists = ChannelBanExists(channel, ban);
2876 change = mod_chanmode_alloc(victimCount + 1);
2877 for(n = 0; n < victimCount; ++n)
2879 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2880 change->args[n].member = victims[n];
2884 change->args[n].mode = MODE_BAN;
2885 change->args[n++].hostmask = ban;
2889 modcmd_chanmode_announce(change);
2891 mod_chanmode_announce(chanserv, channel, change);
2892 mod_chanmode_free(change);
2894 if(exists && (action == ACTION_BAN))
2896 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2902 if(action & ACTION_KICK)
2904 char kick_reason[MAXLEN];
2905 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2907 for(n = 0; n < victimCount; n++)
2908 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2913 /* No response, since it was automated. */
2915 else if(action & ACTION_ADD_BAN)
2918 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2920 reply("CSMSG_BAN_ADDED", name, channel->name);
2922 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2923 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2924 else if(action & ACTION_BAN)
2925 reply("CSMSG_BAN_DONE", name, channel->name);
2926 else if(action & ACTION_KICK && victimCount)
2927 reply("CSMSG_KICK_DONE", name, channel->name);
2933 static CHANSERV_FUNC(cmd_kickban)
2935 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2938 static CHANSERV_FUNC(cmd_kick)
2940 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2943 static CHANSERV_FUNC(cmd_ban)
2945 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2948 static CHANSERV_FUNC(cmd_addban)
2950 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2953 static CHANSERV_FUNC(cmd_addtimedban)
2955 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2958 static struct mod_chanmode *
2959 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2961 struct mod_chanmode *change;
2962 unsigned char *match;
2963 unsigned int ii, count;
2965 match = alloca(bans->used);
2968 for(ii = count = 0; ii < bans->used; ++ii)
2970 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2977 for(ii = count = 0; ii < bans->used; ++ii)
2979 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
2986 change = mod_chanmode_alloc(count);
2987 for(ii = count = 0; ii < bans->used; ++ii)
2991 change->args[count].mode = MODE_REMOVE | MODE_BAN;
2992 change->args[count++].hostmask = bans->list[ii]->ban;
2998 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3000 struct userNode *actee;
3006 /* may want to allow a comma delimited list of users... */
3007 if(!(actee = GetUserH(argv[1])))
3009 if(!is_ircmask(argv[1]))
3011 reply("MSG_NICK_UNKNOWN", argv[1]);
3015 mask = strdup(argv[1]);
3018 /* We don't sanitize the mask here because ircu
3020 if(action & ACTION_UNBAN)
3022 struct mod_chanmode *change;
3023 change = find_matching_bans(&channel->banlist, actee, mask);
3026 modcmd_chanmode_announce(change);
3027 mod_chanmode_free(change);
3032 if(action & ACTION_DEL_BAN)
3034 struct banData *ban, *next;
3036 ban = channel->channel_info->bans;
3040 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3043 for( ; ban && !match_ircglobs(mask, ban->mask);
3048 del_channel_ban(ban);
3055 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3057 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3063 static CHANSERV_FUNC(cmd_unban)
3065 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3068 static CHANSERV_FUNC(cmd_delban)
3070 /* it doesn't necessarily have to remove the channel ban - may want
3071 to make that an option. */
3072 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3075 static CHANSERV_FUNC(cmd_unbanme)
3077 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3078 long flags = ACTION_UNBAN;
3080 /* remove permanent bans if the user has the proper access. */
3081 if(uData->access >= UL_MASTER)
3082 flags |= ACTION_DEL_BAN;
3084 argv[1] = user->nick;
3085 return unban_user(user, channel, 2, argv, cmd, flags);
3088 static CHANSERV_FUNC(cmd_unbanall)
3090 struct mod_chanmode *change;
3093 if(!channel->banlist.used)
3095 reply("CSMSG_NO_BANS", channel->name);
3099 change = mod_chanmode_alloc(channel->banlist.used);
3100 for(ii=0; ii<channel->banlist.used; ii++)
3102 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3103 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3105 modcmd_chanmode_announce(change);
3106 mod_chanmode_free(change);
3107 reply("CSMSG_BANS_REMOVED", channel->name);
3111 static CHANSERV_FUNC(cmd_open)
3113 struct mod_chanmode *change;
3115 change = find_matching_bans(&channel->banlist, user, NULL);
3117 change = mod_chanmode_alloc(0);
3118 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3119 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3120 && channel->channel_info->modes.modes_set)
3121 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3122 modcmd_chanmode_announce(change);
3123 reply("CSMSG_CHANNEL_OPENED", channel->name);
3124 mod_chanmode_free(change);
3128 static CHANSERV_FUNC(cmd_access)
3130 struct userNode *target;
3131 struct handle_info *target_handle;
3132 struct userData *uData;
3134 char prefix[MAXLEN];
3138 struct userData *uData;
3139 const char *chanName;
3142 target_handle = user->handle_info;
3145 reply("MSG_AUTHENTICATE");
3150 if(!IsHelping(user))
3152 reply("CSMSG_ACCESS_SELF_ONLY", argv[0]);
3156 if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3160 if(!target_handle->channels)
3162 reply("CSMSG_SQUAT_ACCESS");
3165 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3166 for(uData = target_handle->channels; uData; uData = uData->u_next)
3168 struct chanData *cData = uData->channel;
3170 if(uData->access > UL_OWNER)
3172 if(IsProtected(cData) && hide && !GetTrueChannelAccess(cData, user->handle_info))
3174 chanName = cData->channel->name;
3176 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3178 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3186 target_handle = target->handle_info;
3188 else if((target = GetUserH(argv[1])))
3190 target_handle = target->handle_info;
3192 else if(argv[1][0] == '*')
3194 if(!(target_handle = get_handle_info(argv[1]+1)))
3196 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3202 reply("MSG_NICK_UNKNOWN", argv[1]);
3206 assert(target || target_handle);
3208 if(target == chanserv)
3210 reply("CSMSG_IS_CHANSERV");
3218 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3223 reply("MSG_USER_AUTHENTICATE", target->nick);
3226 reply("MSG_AUTHENTICATE");
3232 const char *epithet = NULL, *type = NULL;
3235 epithet = chanserv_conf.irc_operator_epithet;
3238 else if(IsNetworkHelper(target))
3240 epithet = chanserv_conf.network_helper_epithet;
3241 type = "network helper";
3243 else if(IsSupportHelper(target))
3245 epithet = chanserv_conf.support_helper_epithet;
3246 type = "support helper";
3250 if(target_handle->epithet)
3251 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3253 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3255 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3259 sprintf(prefix, "%s", target_handle->handle);
3262 if(!channel->channel_info)
3264 reply("CSMSG_NOT_REGISTERED", channel->name);
3268 helping = HANDLE_FLAGGED(target_handle, HELPING)
3269 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3270 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3272 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3273 /* To prevent possible information leaks, only show infolines
3274 * if the requestor is in the channel or it's their own
3276 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3278 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3280 /* Likewise, only say it's suspended if the user has active
3281 * access in that channel or it's their own entry. */
3282 if(IsUserSuspended(uData)
3283 && (GetChannelUser(channel->channel_info, user->handle_info)
3284 || (user->handle_info == uData->handle)))
3286 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3291 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3298 zoot_list(struct listData *list)
3300 struct userData *uData;
3301 unsigned int start, curr, highest, lowest;
3302 struct helpfile_table tmp_table;
3303 const char **temp, *msg;
3305 if(list->table.length == 1)
3308 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3310 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3311 msg = user_find_message(list->user, "MSG_NONE");
3312 send_message_type(4, list->user, list->bot, " %s", msg);
3314 tmp_table.width = list->table.width;
3315 tmp_table.flags = list->table.flags;
3316 list->table.contents[0][0] = " ";
3317 highest = list->highest;
3318 lowest = (highest < 100) ? 0 : (highest - 100);
3319 for(start = curr = 1; curr < list->table.length; )
3321 uData = list->users[curr-1];
3322 list->table.contents[curr++][0] = " ";
3323 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3326 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3328 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3329 temp = list->table.contents[--start];
3330 list->table.contents[start] = list->table.contents[0];
3331 tmp_table.contents = list->table.contents + start;
3332 tmp_table.length = curr - start;
3333 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3334 list->table.contents[start] = temp;
3336 highest = lowest - 1;
3337 lowest = (highest < 100) ? 0 : (highest - 99);
3343 def_list(struct listData *list)
3347 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3349 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3350 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3351 if(list->table.length == 1)
3353 msg = user_find_message(list->user, "MSG_NONE");
3354 send_message_type(4, list->user, list->bot, " %s", msg);
3359 userData_access_comp(const void *arg_a, const void *arg_b)
3361 const struct userData *a = *(struct userData**)arg_a;
3362 const struct userData *b = *(struct userData**)arg_b;
3364 if(a->access != b->access)
3365 res = b->access - a->access;
3367 res = irccasecmp(a->handle->handle, b->handle->handle);
3372 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3374 void (*send_list)(struct listData *);
3375 struct userData *uData;
3376 struct listData lData;
3377 unsigned int matches;
3381 lData.bot = cmd->parent->bot;
3382 lData.channel = channel;
3383 lData.lowest = lowest;
3384 lData.highest = highest;
3385 lData.search = (argc > 1) ? argv[1] : NULL;
3386 send_list = zoot_list;
3388 if(user->handle_info)
3390 switch(user->handle_info->userlist_style)
3392 case HI_STYLE_DEF: send_list = def_list; break;
3393 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3397 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3399 for(uData = channel->channel_info->users; uData; uData = uData->next)
3401 if((uData->access < lowest)
3402 || (uData->access > highest)
3403 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3405 lData.users[matches++] = uData;
3407 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3409 lData.table.length = matches+1;
3410 lData.table.width = 4;
3411 lData.table.flags = TABLE_NO_FREE;
3412 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3413 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3414 lData.table.contents[0] = ary;
3417 ary[2] = "Last Seen";
3419 for(matches = 1; matches < lData.table.length; ++matches)
3421 struct userData *uData = lData.users[matches-1];
3422 char seen[INTERVALLEN];
3424 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3425 lData.table.contents[matches] = ary;
3426 ary[0] = strtab(uData->access);
3427 ary[1] = uData->handle->handle;
3430 else if(!uData->seen)
3433 ary[2] = intervalString(seen, now - uData->seen);
3434 ary[2] = strdup(ary[2]);
3435 if(IsUserSuspended(uData))
3436 ary[3] = "Suspended";
3437 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3438 ary[3] = "Vacation";
3443 for(matches = 1; matches < lData.table.length; ++matches)
3445 free((char*)lData.table.contents[matches][2]);
3446 free(lData.table.contents[matches]);
3448 free(lData.table.contents[0]);
3449 free(lData.table.contents);
3453 static CHANSERV_FUNC(cmd_users)
3455 return cmd_list_users(CSFUNC_ARGS, 0, UL_OWNER);
3458 static CHANSERV_FUNC(cmd_wlist)
3460 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3463 static CHANSERV_FUNC(cmd_clist)
3465 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3467 static CHANSERV_FUNC(cmd_mlist)
3469 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3471 static CHANSERV_FUNC(cmd_olist)
3473 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3475 static CHANSERV_FUNC(cmd_plist)
3477 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3480 static CHANSERV_FUNC(cmd_bans)
3482 struct helpfile_table tbl;
3483 unsigned int matches = 0, timed = 0, ii;
3484 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3485 const char *msg_never, *triggered, *expires;
3486 struct banData *ban, **bans;
3493 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3495 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3497 if(search && !match_ircglobs(search, ban->mask))
3499 bans[matches++] = ban;
3504 tbl.length = matches + 1;
3505 tbl.width = 4 + timed;
3507 tbl.flags = TABLE_NO_FREE;
3508 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3509 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3510 tbl.contents[0][0] = "Mask";
3511 tbl.contents[0][1] = "Set By";
3512 tbl.contents[0][2] = "Triggered";
3515 tbl.contents[0][3] = "Expires";
3516 tbl.contents[0][4] = "Reason";
3519 tbl.contents[0][3] = "Reason";
3522 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3527 msg_never = user_find_message(user, "MSG_NEVER");
3528 for(ii = 0; ii < matches; )
3534 else if(ban->expires)
3535 expires = intervalString(e_buffer, ban->expires - now);
3537 expires = msg_never;
3540 triggered = intervalString(t_buffer, now - ban->triggered);
3542 triggered = msg_never;
3544 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3545 tbl.contents[ii][0] = ban->mask;
3546 tbl.contents[ii][1] = ban->owner;
3547 tbl.contents[ii][2] = strdup(triggered);
3550 tbl.contents[ii][3] = strdup(expires);
3551 tbl.contents[ii][4] = ban->reason;
3554 tbl.contents[ii][3] = ban->reason;
3556 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3557 reply("MSG_MATCH_COUNT", matches);
3558 for(ii = 1; ii < tbl.length; ++ii)
3560 free((char*)tbl.contents[ii][2]);
3562 free((char*)tbl.contents[ii][3]);
3563 free(tbl.contents[ii]);
3565 free(tbl.contents[0]);
3571 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3573 struct chanData *cData = channel->channel_info;
3574 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3576 if(cData->topic_mask)
3577 return !match_ircglob(new_topic, cData->topic_mask);
3578 else if(cData->topic)
3579 return irccasecmp(new_topic, cData->topic);
3584 static CHANSERV_FUNC(cmd_topic)
3586 struct chanData *cData;
3589 cData = channel->channel_info;
3594 SetChannelTopic(channel, chanserv, cData->topic, 1);
3595 reply("CSMSG_TOPIC_SET", cData->topic);
3599 reply("CSMSG_NO_TOPIC", channel->name);
3603 topic = unsplit_string(argv + 1, argc - 1, NULL);
3604 /* If they say "!topic *", use an empty topic. */
3605 if((topic[0] == '*') && (topic[1] == 0))
3607 if(bad_topic(channel, user, topic))
3609 char *topic_mask = cData->topic_mask;
3612 char new_topic[TOPICLEN+1], tchar;
3613 int pos=0, starpos=-1, dpos=0, len;
3615 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3622 len = strlen(topic);
3623 if((dpos + len) > TOPICLEN)
3624 len = TOPICLEN + 1 - dpos;
3625 memcpy(new_topic+dpos, topic, len);
3629 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3630 default: new_topic[dpos++] = tchar; break;
3633 if((dpos > TOPICLEN) || tchar)
3636 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3637 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3640 new_topic[dpos] = 0;
3641 SetChannelTopic(channel, chanserv, new_topic, 1);
3643 reply("CSMSG_TOPIC_LOCKED", channel->name);
3648 SetChannelTopic(channel, chanserv, topic, 1);
3650 if(cData->flags & CHANNEL_TOPIC_SNARF)
3652 /* Grab the topic and save it as the default topic. */
3654 cData->topic = strdup(channel->topic);
3660 static CHANSERV_FUNC(cmd_mode)
3662 struct mod_chanmode *change;
3666 change = &channel->channel_info->modes;
3667 if(change->modes_set || change->modes_clear) {
3668 modcmd_chanmode_announce(change);
3669 reply("CSMSG_DEFAULTED_MODES", channel->name);
3671 reply("CSMSG_NO_MODES", channel->name);
3675 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3678 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3682 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3683 && mode_lock_violated(&channel->channel_info->modes, change))
3686 mod_chanmode_format(&channel->channel_info->modes, modes);
3687 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3691 modcmd_chanmode_announce(change);
3692 mod_chanmode_free(change);
3693 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3697 static CHANSERV_FUNC(cmd_invite)
3699 struct userData *uData;
3700 struct userNode *invite;
3702 uData = GetChannelUser(channel->channel_info, user->handle_info);
3706 if(!(invite = GetUserH(argv[1])))
3708 reply("MSG_NICK_UNKNOWN", argv[1]);
3715 if(GetUserMode(channel, invite))
3717 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3723 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3724 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3726 irc_invite(chanserv, invite, channel);
3728 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3733 static CHANSERV_FUNC(cmd_inviteme)
3735 struct userData *uData;
3737 if(GetUserMode(channel, user))
3739 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3742 if(channel->channel_info
3743 && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3744 && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3745 && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3747 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3750 irc_invite(cmd->parent->bot, user, channel);
3755 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3758 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3760 /* We display things based on two dimensions:
3761 * - Issue time: present or absent
3762 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3763 * (in order of precedence, so something both expired and revoked
3764 * only counts as revoked)
3766 combo = (suspended->issued ? 4 : 0)
3767 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3769 case 0: /* no issue time, indefinite expiration */
3770 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3772 case 1: /* no issue time, expires in future */
3773 intervalString(buf1, suspended->expires-now);
3774 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3776 case 2: /* no issue time, expired */
3777 intervalString(buf1, now-suspended->expires);
3778 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3780 case 3: /* no issue time, revoked */
3781 intervalString(buf1, now-suspended->revoked);
3782 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3784 case 4: /* issue time set, indefinite expiration */
3785 intervalString(buf1, now-suspended->issued);
3786 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3788 case 5: /* issue time set, expires in future */
3789 intervalString(buf1, now-suspended->issued);
3790 intervalString(buf2, suspended->expires-now);
3791 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3793 case 6: /* issue time set, expired */
3794 intervalString(buf1, now-suspended->issued);
3795 intervalString(buf2, now-suspended->expires);
3796 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3798 case 7: /* issue time set, revoked */
3799 intervalString(buf1, now-suspended->issued);
3800 intervalString(buf2, now-suspended->revoked);
3801 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3804 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3809 static CHANSERV_FUNC(cmd_info)
3811 char modes[MAXLEN], buffer[INTERVALLEN];
3812 struct userData *uData, *owner;
3813 struct chanData *cData;
3814 struct do_not_register *dnr;
3819 cData = channel->channel_info;
3820 reply("CSMSG_CHANNEL_INFO", channel->name);
3822 uData = GetChannelUser(cData, user->handle_info);
3823 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3825 mod_chanmode_format(&cData->modes, modes);
3826 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3827 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3830 for(it = dict_first(cData->notes); it; it = iter_next(it))
3834 note = iter_data(it);
3835 if(!note_type_visible_to_user(cData, note->type, user))
3838 padding = PADLEN - 1 - strlen(iter_key(it));
3839 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3842 reply("CSMSG_CHANNEL_MAX", cData->max);
3843 for(owner = cData->users; owner; owner = owner->next)
3844 if(owner->access == UL_OWNER)
3845 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3846 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3847 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3848 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3849 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3851 privileged = IsStaff(user);
3852 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3853 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3855 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3856 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3858 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3860 struct suspended *suspended;
3861 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3862 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3863 show_suspension_info(cmd, user, suspended);
3865 else if(IsSuspended(cData))
3867 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3868 show_suspension_info(cmd, user, cData->suspended);
3873 static CHANSERV_FUNC(cmd_netinfo)
3875 extern time_t boot_time;
3876 extern unsigned long burst_length;
3877 char interval[INTERVALLEN];
3879 reply("CSMSG_NETWORK_INFO");
3880 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3881 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3882 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3883 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3884 reply("CSMSG_NETWORK_BANS", banCount);
3885 reply("CSMSG_CHANNEL_USERS", userCount);
3886 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3887 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3892 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3894 struct helpfile_table table;
3896 struct userNode *user;
3901 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3902 table.contents = alloca(list->used*sizeof(*table.contents));
3903 for(nn=0; nn<list->used; nn++)
3905 user = list->list[nn];
3906 if(user->modes & skip_flags)
3910 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3913 nick = alloca(strlen(user->nick)+3);
3914 sprintf(nick, "(%s)", user->nick);
3918 table.contents[table.length][0] = nick;
3921 table_send(chanserv, to->nick, 0, NULL, table);
3924 static CHANSERV_FUNC(cmd_ircops)
3926 reply("CSMSG_STAFF_OPERS");
3927 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3931 static CHANSERV_FUNC(cmd_helpers)
3933 reply("CSMSG_STAFF_HELPERS");
3934 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3938 static CHANSERV_FUNC(cmd_staff)
3940 reply("CSMSG_NETWORK_STAFF");
3941 cmd_ircops(CSFUNC_ARGS);
3942 cmd_helpers(CSFUNC_ARGS);
3946 static CHANSERV_FUNC(cmd_peek)
3948 struct modeNode *mn;
3949 char modes[MODELEN];
3951 struct helpfile_table table;
3953 irc_make_chanmode(channel, modes);
3955 reply("CSMSG_PEEK_INFO", channel->name);
3956 reply("CSMSG_PEEK_TOPIC", channel->topic);
3957 reply("CSMSG_PEEK_MODES", modes);
3958 reply("CSMSG_PEEK_USERS", channel->members.used);
3962 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3963 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3964 for(n = 0; n < channel->members.used; n++)
3966 mn = channel->members.list[n];
3967 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3969 table.contents[table.length] = alloca(sizeof(**table.contents));
3970 table.contents[table.length][0] = mn->user->nick;
3975 reply("CSMSG_PEEK_OPS");
3976 table_send(chanserv, user->nick, 0, NULL, table);
3979 reply("CSMSG_PEEK_NO_OPS");
3983 static MODCMD_FUNC(cmd_wipeinfo)
3985 struct handle_info *victim;
3986 struct userData *ud, *actor;
3989 actor = GetChannelUser(channel->channel_info, user->handle_info);
3990 if(!(victim = modcmd_get_handle_info(user, argv[1])))
3992 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
3994 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
3997 if((ud->access >= actor->access) && (ud != actor))
3999 reply("MSG_USER_OUTRANKED", victim->handle);
4005 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4009 static CHANSERV_FUNC(cmd_resync)
4011 struct mod_chanmode *changes;
4012 struct chanData *cData = channel->channel_info;
4013 unsigned int ii, used;
4015 changes = mod_chanmode_alloc(channel->members.used * 2);
4016 for(ii = used = 0; ii < channel->members.used; ++ii)
4018 struct modeNode *mn = channel->members.list[ii];
4019 struct userData *uData;
4020 if(IsService(mn->user))
4022 /* must not change modes for this user */
4024 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4028 changes->args[used].mode = MODE_REMOVE | mn->modes;
4029 changes->args[used++].member = mn;
4032 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4034 if(mn->modes & MODE_CHANOP)
4036 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4037 changes->args[used++].member = mn;
4039 if(!(mn->modes & MODE_VOICE))
4041 changes->args[used].mode = MODE_VOICE;
4042 changes->args[used++].member = mn;
4047 if(!(mn->modes & MODE_CHANOP))
4049 changes->args[used].mode = MODE_CHANOP;
4050 changes->args[used++].member = mn;
4054 changes->argc = used;
4055 modcmd_chanmode_announce(changes);
4056 mod_chanmode_free(changes);
4057 reply("CSMSG_RESYNCED_USERS", channel->name);
4061 static CHANSERV_FUNC(cmd_seen)
4063 struct userData *uData;
4064 struct handle_info *handle;
4065 char seen[INTERVALLEN];
4069 if(!irccasecmp(argv[1], chanserv->nick))
4071 reply("CSMSG_IS_CHANSERV");
4075 if(!(handle = get_handle_info(argv[1])))
4077 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4081 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4083 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4088 reply("CSMSG_USER_PRESENT", handle->handle);
4089 else if(uData->seen)
4090 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4092 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4094 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4095 reply("CSMSG_USER_VACATION", handle->handle);
4100 static MODCMD_FUNC(cmd_names)
4102 struct userNode *targ;
4103 struct userData *targData;
4104 unsigned int ii, pos;
4107 for(ii=pos=0; ii<channel->members.used; ++ii)
4109 targ = channel->members.list[ii]->user;
4110 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4113 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4116 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4120 if(IsUserSuspended(targData))
4122 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4125 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4126 reply("CSMSG_END_NAMES", channel->name);
4131 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4133 switch(ntype->visible_type)
4135 case NOTE_VIS_ALL: return 1;
4136 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4137 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4142 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4144 struct userData *uData;
4146 switch(ntype->set_access_type)
4148 case NOTE_SET_CHANNEL_ACCESS:
4149 if(!user->handle_info)
4151 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4153 return uData->access >= ntype->set_access.min_ulevel;
4154 case NOTE_SET_CHANNEL_SETTER:
4155 return check_user_level(channel, user, lvlSetters, 1, 0);
4156 case NOTE_SET_PRIVILEGED: default:
4157 return IsHelping(user);
4161 static CHANSERV_FUNC(cmd_note)
4163 struct chanData *cData;
4165 struct note_type *ntype;
4167 cData = channel->channel_info;
4170 reply("CSMSG_NOT_REGISTERED", channel->name);
4174 /* If no arguments, show all visible notes for the channel. */
4180 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4182 note = iter_data(it);
4183 if(!note_type_visible_to_user(cData, note->type, user))
4186 reply("CSMSG_NOTELIST_HEADER", channel->name);
4187 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4190 reply("CSMSG_NOTELIST_END", channel->name);
4192 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4194 /* If one argument, show the named note. */
4197 if((note = dict_find(cData->notes, argv[1], NULL))
4198 && note_type_visible_to_user(cData, note->type, user))
4200 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4202 else if((ntype = dict_find(note_types, argv[1], NULL))
4203 && note_type_visible_to_user(NULL, ntype, user))
4205 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4210 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4214 /* Assume they're trying to set a note. */
4218 ntype = dict_find(note_types, argv[1], NULL);
4221 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4224 else if(note_type_settable_by_user(channel, ntype, user))
4226 note_text = unsplit_string(argv+2, argc-2, NULL);
4227 if((note = dict_find(cData->notes, argv[1], NULL)))
4228 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4229 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4230 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4232 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4234 /* The note is viewable to staff only, so return 0
4235 to keep the invocation from getting logged (or
4236 regular users can see it in !events). */
4242 reply("CSMSG_NO_ACCESS");
4249 static CHANSERV_FUNC(cmd_delnote)
4254 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4255 || !note_type_settable_by_user(channel, note->type, user))
4257 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4260 dict_remove(channel->channel_info->notes, note->type->name);
4261 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4265 static CHANSERV_FUNC(cmd_events)
4267 struct logSearch discrim;
4268 struct logReport report;
4269 unsigned int matches, limit;
4271 limit = (argc > 1) ? atoi(argv[1]) : 10;
4272 if(limit < 1 || limit > 200) limit = 10;
4274 memset(&discrim, 0, sizeof(discrim));
4275 discrim.masks.bot = chanserv;
4276 discrim.masks.channel_name = channel->name;
4277 if(argc > 2) discrim.masks.command = argv[2];
4278 discrim.limit = limit;
4279 discrim.max_time = INT_MAX;
4280 discrim.severities = 1 << LOG_COMMAND;
4281 report.reporter = chanserv;
4283 reply("CSMSG_EVENT_SEARCH_RESULTS");
4284 matches = log_entry_search(&discrim, log_report_entry, &report);
4286 reply("MSG_MATCH_COUNT", matches);
4288 reply("MSG_NO_MATCHES");
4292 static CHANSERV_FUNC(cmd_say)
4298 msg = unsplit_string(argv + 1, argc - 1, NULL);
4299 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4301 else if(GetUserH(argv[1]))
4304 msg = unsplit_string(argv + 2, argc - 2, NULL);
4305 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4309 reply("You must specify the name of a channel or user.");
4315 static CHANSERV_FUNC(cmd_emote)
4321 /* CTCP is so annoying. */
4322 msg = unsplit_string(argv + 1, argc - 1, NULL);
4323 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4325 else if(GetUserH(argv[1]))
4327 msg = unsplit_string(argv + 2, argc - 2, NULL);
4328 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4332 reply("You must specify the name of a channel or user.");
4338 struct channelList *
4339 chanserv_support_channels(void)
4341 return &chanserv_conf.support_channels;
4344 static CHANSERV_FUNC(cmd_expire)
4346 int channel_count = registered_channels;
4347 expire_channels(NULL);
4348 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4353 chanserv_expire_suspension(void *data)
4355 struct suspended *suspended = data;
4356 struct chanNode *channel;
4357 struct mod_chanmode change;
4359 if(!suspended->expires || (now < suspended->expires))
4360 suspended->revoked = now;
4361 channel = suspended->cData->channel;
4362 suspended->cData->channel = channel;
4363 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4364 change.modes_set = change.modes_clear = 0;
4366 change.args[0].mode = MODE_CHANOP;
4367 change.args[0].member = AddChannelUser(chanserv, channel);
4368 mod_chanmode_announce(chanserv, channel, &change);
4371 static CHANSERV_FUNC(cmd_csuspend)
4373 struct suspended *suspended;
4374 char reason[MAXLEN];
4375 time_t expiry, duration;
4376 struct userData *uData;
4380 if(IsProtected(channel->channel_info))
4382 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4386 if(argv[1][0] == '!')
4388 else if(IsSuspended(channel->channel_info))
4390 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4391 show_suspension_info(cmd, user, channel->channel_info->suspended);
4395 if(!strcmp(argv[1], "0"))
4397 else if((duration = ParseInterval(argv[1])))
4398 expiry = now + duration;
4401 reply("MSG_INVALID_DURATION", argv[1]);
4405 unsplit_string(argv + 2, argc - 2, reason);
4407 suspended = calloc(1, sizeof(*suspended));
4408 suspended->revoked = 0;
4409 suspended->issued = now;
4410 suspended->suspender = strdup(user->handle_info->handle);
4411 suspended->expires = expiry;
4412 suspended->reason = strdup(reason);
4413 suspended->cData = channel->channel_info;
4414 suspended->previous = suspended->cData->suspended;
4415 suspended->cData->suspended = suspended;
4417 if(suspended->expires)
4418 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4420 if(IsSuspended(channel->channel_info))
4422 suspended->previous->revoked = now;
4423 if(suspended->previous->expires)
4424 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4425 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4426 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4430 /* Mark all users in channel as absent. */
4431 for(uData = channel->channel_info->users; uData; uData = uData->next)
4440 /* Mark the channel as suspended, then part. */
4441 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4442 DelChannelUser(chanserv, channel, suspended->reason, 0);
4443 reply("CSMSG_SUSPENDED", channel->name);
4444 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4445 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4450 static CHANSERV_FUNC(cmd_cunsuspend)
4452 struct suspended *suspended;
4453 char message[MAXLEN];
4455 if(!IsSuspended(channel->channel_info))
4457 reply("CSMSG_NOT_SUSPENDED", channel->name);
4461 suspended = channel->channel_info->suspended;
4463 /* Expire the suspension and join ChanServ to the channel. */
4464 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4465 chanserv_expire_suspension(suspended);
4466 reply("CSMSG_UNSUSPENDED", channel->name);
4467 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4468 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4472 typedef struct chanservSearch
4480 unsigned long flags;
4484 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4487 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4492 search = malloc(sizeof(struct chanservSearch));
4493 memset(search, 0, sizeof(*search));
4496 for(i = 0; i < argc; i++)
4498 /* Assume all criteria require arguments. */
4501 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4505 if(!irccasecmp(argv[i], "name"))
4506 search->name = argv[++i];
4507 else if(!irccasecmp(argv[i], "registrar"))
4508 search->registrar = argv[++i];
4509 else if(!irccasecmp(argv[i], "unvisited"))
4510 search->unvisited = ParseInterval(argv[++i]);
4511 else if(!irccasecmp(argv[i], "registered"))
4512 search->registered = ParseInterval(argv[++i]);
4513 else if(!irccasecmp(argv[i], "flags"))
4516 if(!irccasecmp(argv[i], "nodelete"))
4517 search->flags |= CHANNEL_NODELETE;
4518 else if(!irccasecmp(argv[i], "suspended"))
4519 search->flags |= CHANNEL_SUSPENDED;
4522 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4526 else if(!irccasecmp(argv[i], "limit"))
4527 search->limit = strtoul(argv[++i], NULL, 10);
4530 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4535 if(search->name && !strcmp(search->name, "*"))
4537 if(search->registrar && !strcmp(search->registrar, "*"))
4538 search->registrar = 0;
4547 chanserv_channel_match(struct chanData *channel, search_t search)
4549 const char *name = channel->channel->name;
4550 if((search->name && !match_ircglob(name, search->name)) ||
4551 (search->registrar && !channel->registrar) ||
4552 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4553 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4554 (search->registered && (now - channel->registered) > search->registered) ||
4555 (search->flags && ((search->flags & channel->flags) != search->flags)))
4562 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4564 struct chanData *channel;
4565 unsigned int matches = 0;
4567 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4569 if(!chanserv_channel_match(channel, search))
4579 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4584 search_print(struct chanData *channel, void *data)
4586 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4589 static CHANSERV_FUNC(cmd_search)
4592 unsigned int matches;
4593 channel_search_func action;
4597 if(!irccasecmp(argv[1], "count"))
4598 action = search_count;
4599 else if(!irccasecmp(argv[1], "print"))
4600 action = search_print;
4603 reply("CSMSG_ACTION_INVALID", argv[1]);
4607 search = chanserv_search_create(user, argc - 2, argv + 2);
4611 if(action == search_count)
4612 search->limit = INT_MAX;
4614 if(action == search_print)
4615 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4617 matches = chanserv_channel_search(search, action, user);
4620 reply("MSG_MATCH_COUNT", matches);
4622 reply("MSG_NO_MATCHES");
4628 static CHANSERV_FUNC(cmd_unvisited)
4630 struct chanData *cData;
4631 time_t interval = chanserv_conf.channel_expire_delay;
4632 char buffer[INTERVALLEN];
4633 unsigned int limit = 25, matches = 0;
4637 interval = ParseInterval(argv[1]);
4639 limit = atoi(argv[2]);
4642 intervalString(buffer, interval);
4643 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4645 for(cData = channelList; cData && matches < limit; cData = cData->next)
4647 if((now - cData->visited) < interval)
4650 intervalString(buffer, now - cData->visited);
4651 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4658 static MODCMD_FUNC(chan_opt_defaulttopic)
4664 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4666 reply("CSMSG_TOPIC_LOCKED", channel->name);
4670 topic = unsplit_string(argv+1, argc-1, NULL);
4672 free(channel->channel_info->topic);
4673 if(topic[0] == '*' && topic[1] == 0)
4675 topic = channel->channel_info->topic = NULL;
4679 topic = channel->channel_info->topic = strdup(topic);
4680 if(channel->channel_info->topic_mask
4681 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4682 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4684 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4687 if(channel->channel_info->topic)
4688 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4690 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4694 static MODCMD_FUNC(chan_opt_topicmask)
4698 struct chanData *cData = channel->channel_info;
4701 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4703 reply("CSMSG_TOPIC_LOCKED", channel->name);
4707 mask = unsplit_string(argv+1, argc-1, NULL);
4709 if(cData->topic_mask)
4710 free(cData->topic_mask);
4711 if(mask[0] == '*' && mask[1] == 0)
4713 cData->topic_mask = 0;
4717 cData->topic_mask = strdup(mask);
4719 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4720 else if(!match_ircglob(cData->topic, cData->topic_mask))
4721 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4725 if(channel->channel_info->topic_mask)
4726 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4728 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4732 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4736 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4740 if(greeting[0] == '*' && greeting[1] == 0)
4744 unsigned int length = strlen(greeting);
4745 if(length > chanserv_conf.greeting_length)
4747 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4750 *data = strdup(greeting);
4759 reply(name, user_find_message(user, "MSG_NONE"));
4763 static MODCMD_FUNC(chan_opt_greeting)
4765 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4768 static MODCMD_FUNC(chan_opt_usergreeting)
4770 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4773 static MODCMD_FUNC(chan_opt_modes)
4775 struct mod_chanmode *new_modes;
4776 char modes[MODELEN];
4780 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4782 reply("CSMSG_NO_ACCESS");
4785 if(argv[1][0] == '*' && argv[1][1] == 0)
4787 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4789 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4791 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4794 else if(new_modes->argc > 1)
4796 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4797 mod_chanmode_free(new_modes);
4802 channel->channel_info->modes = *new_modes;
4803 modcmd_chanmode_announce(new_modes);
4804 mod_chanmode_free(new_modes);
4808 mod_chanmode_format(&channel->channel_info->modes, modes);
4810 reply("CSMSG_SET_MODES", modes);
4812 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4816 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4818 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4820 struct chanData *cData = channel->channel_info;
4825 /* Set flag according to value. */
4826 if(enabled_string(argv[1]))
4828 cData->flags |= mask;
4831 else if(disabled_string(argv[1]))
4833 cData->flags &= ~mask;
4838 reply("MSG_INVALID_BINARY", argv[1]);
4844 /* Find current option value. */
4845 value = (cData->flags & mask) ? 1 : 0;
4849 reply(name, user_find_message(user, "MSG_ON"));
4851 reply(name, user_find_message(user, "MSG_OFF"));
4855 static MODCMD_FUNC(chan_opt_nodelete)
4857 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4859 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4863 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4866 static MODCMD_FUNC(chan_opt_userinfo)
4868 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4871 static MODCMD_FUNC(chan_opt_voice)
4873 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4876 static MODCMD_FUNC(chan_opt_dynlimit)
4878 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4881 static MODCMD_FUNC(chan_opt_topicsnarf)
4883 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4885 reply("CSMSG_TOPIC_LOCKED", channel->name);
4888 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4891 static MODCMD_FUNC(chan_opt_peoninvite)
4893 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4896 static MODCMD_FUNC(chan_opt_defaults)
4898 struct userData *uData;
4899 struct chanData *cData;
4900 const char *confirm;
4901 enum levelOption lvlOpt;
4902 enum charOption chOpt;
4904 cData = channel->channel_info;
4905 uData = GetChannelUser(cData, user->handle_info);
4906 if(!uData || (uData->access < UL_OWNER))
4908 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4911 confirm = make_confirmation_string(uData);
4912 if((argc < 2) || strcmp(argv[1], confirm))
4914 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4917 cData->flags = CHANNEL_DEFAULT_FLAGS;
4918 cData->modes = chanserv_conf.default_modes;
4919 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4920 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4921 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4922 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4923 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4928 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4930 struct chanData *cData = channel->channel_info;
4931 struct userData *uData;
4932 unsigned short value;
4936 if(!check_user_level(channel, user, option, 1, 1))
4938 reply("CSMSG_CANNOT_SET");
4941 value = user_level_from_name(argv[1], UL_OWNER+1);
4942 if(!value && !isdigit(argv[1][0]))
4944 reply("CSMSG_INVALID_ACCESS", argv[1]);
4947 uData = GetChannelUser(cData, user->handle_info);
4948 if(!uData || (uData->access < value))
4950 reply("CSMSG_BAD_SETLEVEL");
4953 cData->lvlOpts[option] = value;
4955 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4959 static MODCMD_FUNC(chan_opt_enfops)
4961 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4964 static MODCMD_FUNC(chan_opt_giveops)
4966 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4969 static MODCMD_FUNC(chan_opt_enfmodes)
4971 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4974 static MODCMD_FUNC(chan_opt_enftopic)
4976 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4979 static MODCMD_FUNC(chan_opt_pubcmd)
4981 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4984 static MODCMD_FUNC(chan_opt_setters)
4986 return channel_level_option(lvlSetters, CSFUNC_ARGS);
4989 static MODCMD_FUNC(chan_opt_ctcpusers)
4991 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
4995 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4997 struct chanData *cData = channel->channel_info;
4998 int count = charOptions[option].count, index;
5002 index = atoi(argv[1]);
5004 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5006 reply("CSMSG_INVALID_NUMERIC", index);
5007 /* Show possible values. */
5008 for(index = 0; index < count; index++)
5009 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5013 cData->chOpts[option] = charOptions[option].values[index].value;
5017 /* Find current option value. */
5020 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5024 /* Somehow, the option value is corrupt; reset it to the default. */
5025 cData->chOpts[option] = charOptions[option].default_value;
5030 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5034 static MODCMD_FUNC(chan_opt_protect)
5036 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5039 static MODCMD_FUNC(chan_opt_toys)
5041 return channel_multiple_option(chToys, CSFUNC_ARGS);
5044 static MODCMD_FUNC(chan_opt_ctcpreaction)
5046 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5049 static MODCMD_FUNC(chan_opt_topicrefresh)
5051 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5054 static struct svccmd_list set_shows_list;
5057 handle_svccmd_unbind(struct svccmd *target) {
5059 for(ii=0; ii<set_shows_list.used; ++ii)
5060 if(target == set_shows_list.list[ii])
5061 set_shows_list.used = 0;
5064 static CHANSERV_FUNC(cmd_set)
5066 struct svccmd *subcmd;
5070 /* Check if we need to (re-)initialize set_shows_list. */
5071 if(!set_shows_list.used)
5073 if(!set_shows_list.size)
5075 set_shows_list.size = chanserv_conf.set_shows->used;
5076 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5078 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5080 const char *name = chanserv_conf.set_shows->list[ii];
5081 sprintf(buf, "%s %s", argv[0], name);
5082 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5085 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5088 svccmd_list_append(&set_shows_list, subcmd);
5094 reply("CSMSG_CHANNEL_OPTIONS");
5095 for(ii = 0; ii < set_shows_list.used; ii++)
5097 subcmd = set_shows_list.list[ii];
5098 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5103 sprintf(buf, "%s %s", argv[0], argv[1]);
5104 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5107 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5110 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5112 reply("CSMSG_NO_ACCESS");
5116 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5120 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5122 struct userData *uData;
5124 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5127 reply("CSMSG_NOT_USER", channel->name);
5133 /* Just show current option value. */
5135 else if(enabled_string(argv[1]))
5137 uData->flags |= mask;
5139 else if(disabled_string(argv[1]))
5141 uData->flags &= ~mask;
5145 reply("MSG_INVALID_BINARY", argv[1]);
5149 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5153 static MODCMD_FUNC(user_opt_noautoop)
5155 struct userData *uData;
5157 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5160 reply("CSMSG_NOT_USER", channel->name);
5163 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5164 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5166 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5169 static MODCMD_FUNC(user_opt_autoinvite)
5171 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5174 static MODCMD_FUNC(user_opt_info)
5176 struct userData *uData;
5179 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5183 /* If they got past the command restrictions (which require access)
5184 * but fail this test, we have some fool with security override on.
5186 reply("CSMSG_NOT_USER", channel->name);
5192 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5195 if(infoline[0] == '*' && infoline[1] == 0)
5198 uData->info = strdup(infoline);
5201 reply("CSMSG_USET_INFO", uData->info);
5203 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5207 struct svccmd_list uset_shows_list;
5209 static CHANSERV_FUNC(cmd_uset)
5211 struct svccmd *subcmd;
5215 /* Check if we need to (re-)initialize uset_shows_list. */
5216 if(!uset_shows_list.used)
5220 "NoAutoOp", "AutoInvite", "Info"
5223 if(!uset_shows_list.size)
5225 uset_shows_list.size = ArrayLength(options);
5226 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5228 for(ii = 0; ii < ArrayLength(options); ii++)
5230 const char *name = options[ii];
5231 sprintf(buf, "%s %s", argv[0], name);
5232 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5235 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5238 svccmd_list_append(&uset_shows_list, subcmd);
5244 /* Do this so options are presented in a consistent order. */
5245 reply("CSMSG_USER_OPTIONS");
5246 for(ii = 0; ii < uset_shows_list.used; ii++)
5247 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5251 sprintf(buf, "%s %s", argv[0], argv[1]);
5252 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5255 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5259 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5262 static CHANSERV_FUNC(cmd_giveownership)
5264 struct handle_info *new_owner_hi;
5265 struct userData *new_owner, *curr_user;
5266 struct chanData *cData = channel->channel_info;
5267 struct do_not_register *dnr;
5269 unsigned short co_access;
5270 char reason[MAXLEN];
5273 curr_user = GetChannelAccess(cData, user->handle_info);
5274 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5275 if(!curr_user || (curr_user->access != UL_OWNER))
5277 struct userData *owner = NULL;
5278 for(curr_user = channel->channel_info->users;
5280 curr_user = curr_user->next)
5282 if(curr_user->access != UL_OWNER)
5286 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5292 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5294 if(new_owner_hi == user->handle_info)
5296 reply("CSMSG_NO_TRANSFER_SELF");
5299 new_owner = GetChannelAccess(cData, new_owner_hi);
5302 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5305 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5307 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5310 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5311 if(!IsHelping(user))
5312 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5314 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5317 if(new_owner->access >= UL_COOWNER)
5318 co_access = new_owner->access;
5320 co_access = UL_COOWNER;
5321 new_owner->access = UL_OWNER;
5323 curr_user->access = co_access;
5324 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5325 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5326 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5330 static CHANSERV_FUNC(cmd_suspend)
5332 struct handle_info *hi;
5333 struct userData *self, *target;
5336 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5337 self = GetChannelUser(channel->channel_info, user->handle_info);
5338 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5340 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5343 if(target->access >= self->access)
5345 reply("MSG_USER_OUTRANKED", hi->handle);
5348 if(target->flags & USER_SUSPENDED)
5350 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5355 target->present = 0;
5358 target->flags |= USER_SUSPENDED;
5359 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5363 static CHANSERV_FUNC(cmd_unsuspend)
5365 struct handle_info *hi;
5366 struct userData *self, *target;
5369 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5370 self = GetChannelUser(channel->channel_info, user->handle_info);
5371 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5373 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5376 if(target->access >= self->access)
5378 reply("MSG_USER_OUTRANKED", hi->handle);
5381 if(!(target->flags & USER_SUSPENDED))
5383 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5386 target->flags &= ~USER_SUSPENDED;
5387 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5391 static MODCMD_FUNC(cmd_deleteme)
5393 struct handle_info *hi;
5394 struct userData *target;
5395 const char *confirm_string;
5396 unsigned short access;
5399 hi = user->handle_info;
5400 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5402 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5405 if(target->access == UL_OWNER)
5407 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5410 confirm_string = make_confirmation_string(target);
5411 if((argc < 2) || strcmp(argv[1], confirm_string))
5413 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5416 access = target->access;
5417 channel_name = strdup(channel->name);
5418 del_channel_user(target, 1);
5419 reply("CSMSG_DELETED_YOU", access, channel_name);
5425 chanserv_refresh_topics(UNUSED_ARG(void *data))
5427 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5428 struct chanData *cData;
5431 for(cData = channelList; cData; cData = cData->next)
5433 if(IsSuspended(cData))
5435 opt = cData->chOpts[chTopicRefresh];
5438 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5441 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5442 cData->last_refresh = refresh_num;
5444 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5447 static CHANSERV_FUNC(cmd_unf)
5451 char response[MAXLEN];
5452 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5453 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5454 irc_privmsg(cmd->parent->bot, channel->name, response);
5457 reply("CSMSG_UNF_RESPONSE");
5461 static CHANSERV_FUNC(cmd_ping)
5465 char response[MAXLEN];
5466 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5467 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5468 irc_privmsg(cmd->parent->bot, channel->name, response);
5471 reply("CSMSG_PING_RESPONSE");
5475 static CHANSERV_FUNC(cmd_wut)
5479 char response[MAXLEN];
5480 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5481 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5482 irc_privmsg(cmd->parent->bot, channel->name, response);
5485 reply("CSMSG_WUT_RESPONSE");
5489 static CHANSERV_FUNC(cmd_8ball)
5491 unsigned int i, j, accum;
5496 for(i=1; i<argc; i++)
5497 for(j=0; argv[i][j]; j++)
5498 accum = (accum << 5) - accum + toupper(argv[i][j]);
5499 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5502 char response[MAXLEN];
5503 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5504 irc_privmsg(cmd->parent->bot, channel->name, response);
5507 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5511 static CHANSERV_FUNC(cmd_d)
5513 unsigned long sides, count, modifier, ii, total;
5514 char response[MAXLEN], *sep;
5518 if((count = strtoul(argv[1], &sep, 10)) <= 1)
5526 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5527 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5531 else if((sep[0] == '-') && isdigit(sep[1]))
5532 modifier = strtoul(sep, NULL, 10);
5533 else if((sep[0] == '+') && isdigit(sep[1]))
5534 modifier = strtoul(sep+1, NULL, 10);
5541 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5546 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5549 for(total = ii = 0; ii < count; ++ii)
5550 total += (rand() % sides) + 1;
5553 if((count > 1) || modifier)
5555 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5556 sprintf(response, fmt, total, count, sides, modifier);
5560 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5561 sprintf(response, fmt, total, sides);
5564 send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5566 send_message_type(4, user, cmd->parent->bot, "%s", response);
5570 static CHANSERV_FUNC(cmd_huggle)
5572 char response[MAXLEN];
5574 /* CTCP must be via PRIVMSG, never notice */
5577 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5578 sprintf(response, fmt, user->nick);
5579 irc_privmsg(cmd->parent->bot, channel->name, response);
5583 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5584 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5590 chanserv_adjust_limit(void *data)
5592 struct mod_chanmode change;
5593 struct chanData *cData = data;
5594 struct chanNode *channel = cData->channel;
5597 if(IsSuspended(cData))
5600 cData->limitAdjusted = now;
5601 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5602 if(cData->modes.modes_set & MODE_LIMIT)
5604 if(limit > cData->modes.new_limit)
5605 limit = cData->modes.new_limit;
5606 else if(limit == cData->modes.new_limit)
5610 change.modes_set = MODE_LIMIT;
5611 change.modes_clear = 0;
5612 change.new_limit = limit;
5614 mod_chanmode_announce(chanserv, channel, &change);
5618 handle_new_channel(struct chanNode *channel)
5620 struct chanData *cData;
5622 if(!(cData = channel->channel_info))
5625 if(cData->modes.modes_set || cData->modes.modes_clear)
5626 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5628 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5629 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5632 /* Welcome to my worst nightmare. Warning: Read (or modify)
5633 the code below at your own risk. */
5635 handle_join(struct modeNode *mNode)
5637 struct mod_chanmode change;
5638 struct userNode *user = mNode->user;
5639 struct chanNode *channel = mNode->channel;
5640 struct chanData *cData;
5641 struct userData *uData = NULL;
5642 struct banData *bData;
5643 struct handle_info *handle;
5644 unsigned int modes = 0, info = 0;
5647 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5650 cData = channel->channel_info;
5651 if(channel->members.used > cData->max)
5652 cData->max = channel->members.used;
5654 /* Check for bans. If they're joining through a ban, one of two
5656 * 1: Join during a netburst, by riding the break. Kick them
5657 * unless they have ops or voice in the channel.
5658 * 2: They're allowed to join through the ban (an invite in
5659 * ircu2.10, or a +e on Hybrid, or something).
5660 * If they're not joining through a ban, and the banlist is not
5661 * full, see if they're on the banlist for the channel. If so,
5664 if(user->uplink->burst && !mNode->modes)
5667 for(ii = 0; ii < channel->banlist.used; ii++)
5669 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5671 /* Riding a netburst. Naughty. */
5672 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5678 change.modes_set = change.modes_clear = 0;
5680 if(channel->banlist.used < MAXBANS)
5682 /* Not joining through a ban. */
5683 for(bData = cData->bans;
5684 bData && !user_matches_glob(user, bData->mask, 1);
5685 bData = bData->next);
5689 char kick_reason[MAXLEN];
5690 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5692 bData->triggered = now;
5693 if(bData != cData->bans)
5695 /* Shuffle the ban to the head of the list. */
5696 if(bData->next) bData->next->prev = bData->prev;
5697 if(bData->prev) bData->prev->next = bData->next;
5700 bData->next = cData->bans;
5703 cData->bans->prev = bData;
5704 cData->bans = bData;
5707 change.args[0].mode = MODE_BAN;
5708 change.args[0].hostmask = bData->mask;
5709 mod_chanmode_announce(chanserv, channel, &change);
5710 KickChannelUser(user, channel, chanserv, kick_reason);
5715 /* ChanServ will not modify the limits in join-flooded channels.
5716 It will also skip DynLimit processing when the user (or srvx)
5717 is bursting in, because there are likely more incoming. */
5718 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5719 && !user->uplink->burst
5720 && !channel->join_flooded
5721 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5723 /* The user count has begun "bumping" into the channel limit,
5724 so set a timer to raise the limit a bit. Any previous
5725 timers are removed so three incoming users within the delay
5726 results in one limit change, not three. */
5728 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5729 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5732 if(cData->lvlOpts[lvlGiveOps] == 0)
5733 modes |= MODE_CHANOP;
5734 else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5735 modes |= MODE_VOICE;
5737 greeting = cData->greeting;
5738 if(user->handle_info)
5740 handle = user->handle_info;
5742 if(IsHelper(user) && !IsHelping(user))
5745 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5747 if(channel == chanserv_conf.support_channels.list[ii])
5749 HANDLE_SET_FLAG(user->handle_info, HELPING);
5755 uData = GetTrueChannelAccess(cData, handle);
5756 if(uData && !IsUserSuspended(uData))
5758 /* Ops and above were handled by the above case. */
5759 if(IsUserAutoOp(uData))
5761 if(uData->access < cData->lvlOpts[lvlGiveOps])
5762 modes |= MODE_VOICE;
5764 modes |= MODE_CHANOP;
5766 if(uData->access >= UL_PRESENT)
5767 cData->visited = now;
5772 if(cData->user_greeting)
5773 greeting = cData->user_greeting;
5775 && (cData->flags & CHANNEL_INFO_LINES)
5776 && ((now - uData->seen) >= chanserv_conf.info_delay)
5781 if(!user->uplink->burst)
5785 change.args[0].mode = modes;
5786 change.args[0].member = mNode;
5787 mod_chanmode_announce(chanserv, channel, &change);
5789 if(greeting && !user->uplink->burst)
5790 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5792 send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5798 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5800 struct mod_chanmode change;
5801 struct userData *channel;
5802 unsigned int ii, jj;
5804 if(!user->handle_info)
5807 change.modes_set = change.modes_clear = 0;
5809 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5811 struct chanNode *cn;
5812 struct modeNode *mn;
5813 if(IsSuspended(channel->channel) || !(cn = channel->channel->channel))
5816 mn = GetUserMode(cn, user);
5819 if(!IsUserSuspended(channel)
5820 && IsUserAutoInvite(channel)
5821 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5823 irc_invite(chanserv, user, cn);
5827 if(channel->access >= UL_PRESENT)
5828 channel->channel->visited = now;
5830 if(IsUserAutoOp(channel))
5832 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5833 change.args[0].mode = MODE_CHANOP;
5835 change.args[0].mode = MODE_VOICE;
5836 change.args[0].member = mn;
5837 mod_chanmode_announce(chanserv, cn, &change);
5840 channel->seen = now;
5841 channel->present = 1;
5844 for(ii = 0; ii < user->channels.used; ++ii)
5846 struct chanNode *channel = user->channels.list[ii]->channel;
5847 struct banData *ban;
5849 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5850 || !channel->channel_info)
5852 for(jj = 0; jj < channel->banlist.used; ++jj)
5853 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5855 if(jj < channel->banlist.used)
5857 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5859 char kick_reason[MAXLEN];
5860 if(!user_matches_glob(user, ban->mask, 1))
5862 change.args[0].mode = MODE_BAN;
5863 change.args[0].hostmask = ban->mask;
5864 mod_chanmode_announce(chanserv, channel, &change);
5865 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5866 KickChannelUser(user, channel, chanserv, kick_reason);
5867 ban->triggered = now;
5872 if(IsSupportHelper(user))
5874 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5876 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5878 HANDLE_SET_FLAG(user->handle_info, HELPING);
5886 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5888 struct chanData *cData;
5889 struct userData *uData;
5890 struct handle_info *handle;
5892 cData = channel->channel_info;
5893 if(!cData || IsSuspended(cData) || IsLocal(user)) return;
5895 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5897 /* Allow for a bit of padding so that the limit doesn't
5898 track the user count exactly, which could get annoying. */
5899 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5901 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5902 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5906 if((handle = user->handle_info) && (uData = GetTrueChannelAccess(cData, handle)))
5909 scan_handle_presence(channel, handle, user);
5912 if(IsHelping(user) && IsSupportHelper(user))
5914 unsigned int ii, jj;
5915 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5917 for(jj = 0; jj < user->channels.used; ++jj)
5918 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5920 if(jj < user->channels.used)
5923 if(ii == chanserv_conf.support_channels.used)
5924 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5929 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5931 char *reason = "CSMSG_USER_PROTECTED";
5933 if(!channel->channel_info || !kicker || IsService(kicker)
5934 || (kicker == victim) || IsSuspended(channel->channel_info)
5935 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5938 if(protect_user(victim, kicker, channel->channel_info))
5939 KickChannelUser(kicker, channel, chanserv, reason);
5943 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5945 struct chanData *cData;
5947 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5949 cData = channel->channel_info;
5950 if(bad_topic(channel, user, channel->topic))
5952 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5953 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5954 SetChannelTopic(channel, chanserv, old_topic, 1);
5955 else if(cData->topic)
5956 SetChannelTopic(channel, chanserv, cData->topic, 1);
5959 /* With topicsnarf, grab the topic and save it as the default topic. */
5960 if(cData->flags & CHANNEL_TOPIC_SNARF)
5963 cData->topic = strdup(channel->topic);
5969 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5971 struct mod_chanmode *bounce = NULL;
5972 unsigned int bnc, ii;
5975 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5978 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5979 && mode_lock_violated(&channel->channel_info->modes, change))
5981 char correct[MAXLEN];
5982 bounce = mod_chanmode_alloc(change->argc + 1);
5983 *bounce = channel->channel_info->modes;
5984 mod_chanmode_format(&channel->channel_info->modes, correct);
5985 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5987 for(ii = bnc = 0; ii < change->argc; ++ii)
5989 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5991 const struct userNode *victim = change->args[ii].member->user;
5992 if(!protect_user(victim, user, channel->channel_info))
5995 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
5998 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
5999 bounce->args[bnc].member = GetUserMode(channel, user);
6000 if(bounce->args[bnc].member)
6003 bounce->args[bnc].mode = MODE_CHANOP;
6004 bounce->args[bnc].member = change->args[ii].member;
6006 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6008 else if(change->args[ii].mode & MODE_CHANOP)
6010 const struct userNode *victim = change->args[ii].member->user;
6011 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6014 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6015 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6016 bounce->args[bnc].member = change->args[ii].member;
6019 else if(change->args[ii].mode & MODE_BAN)
6021 const char *ban = change->args[ii].hostmask;
6022 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6025 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6026 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6027 bounce->args[bnc].hostmask = ban;
6029 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", remove);
6034 if((bounce->argc = bnc))
6035 mod_chanmode_announce(chanserv, channel, bounce);
6036 mod_chanmode_free(bounce);
6041 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6043 struct chanNode *channel;
6044 struct banData *bData;
6045 struct mod_chanmode change;
6046 unsigned int ii, jj;
6047 char kick_reason[MAXLEN];
6049 change.modes_set = change.modes_clear = 0;
6051 change.args[0].mode = MODE_BAN;
6052 for(ii = 0; ii < user->channels.used; ++ii)
6054 channel = user->channels.list[ii]->channel;
6055 /* Need not check for bans if they're opped or voiced. */
6056 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6058 /* Need not check for bans unless channel registration is active. */
6059 if(!channel->channel_info || IsSuspended(channel->channel_info))
6061 /* Look for a matching ban already on the channel. */
6062 for(jj = 0; jj < channel->banlist.used; ++jj)
6063 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6065 /* Need not act if we found one. */
6066 if(jj < channel->banlist.used)
6068 /* Look for a matching ban in this channel. */
6069 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6071 if(!user_matches_glob(user, bData->mask, 1))
6073 change.args[0].hostmask = bData->mask;
6074 mod_chanmode_announce(chanserv, channel, &change);
6075 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6076 KickChannelUser(user, channel, chanserv, kick_reason);
6077 bData->triggered = now;
6078 break; /* we don't need to check any more bans in the channel */
6083 static void handle_rename(struct handle_info *handle, const char *old_handle)
6085 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6089 dict_remove2(handle_dnrs, old_handle, 1);
6090 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6091 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6096 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6098 struct userNode *h_user;
6100 if(handle->channels)
6102 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6103 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6105 while(handle->channels)
6106 del_channel_user(handle->channels, 1);
6111 handle_server_link(UNUSED_ARG(struct server *server))
6113 struct chanData *cData;
6115 for(cData = channelList; cData; cData = cData->next)
6117 if(!IsSuspended(cData))
6118 cData->may_opchan = 1;
6119 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6120 && !cData->channel->join_flooded
6121 && ((cData->channel->limit - cData->channel->members.used)
6122 < chanserv_conf.adjust_threshold))
6124 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6125 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6131 chanserv_conf_read(void)
6135 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6136 struct mod_chanmode *change;
6137 struct string_list *strlist;
6138 struct chanNode *chan;
6141 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6143 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6146 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6147 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6148 chanserv_conf.support_channels.used = 0;
6149 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6151 for(ii = 0; ii < strlist->used; ++ii)
6153 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6156 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6158 channelList_append(&chanserv_conf.support_channels, chan);
6161 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6164 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6167 chan = AddChannel(str, now, str2, NULL);
6169 channelList_append(&chanserv_conf.support_channels, chan);
6171 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6172 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6173 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6174 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6175 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6176 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6177 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6178 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6179 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6180 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6181 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6182 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6183 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6184 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6185 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6186 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6187 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6188 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6189 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6190 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6191 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6192 if(str) NickChange(chanserv, str, 0);
6193 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6194 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6195 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6196 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6197 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6198 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6199 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6200 chanserv_conf.max_owned = str ? atoi(str) : 5;
6201 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6202 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6203 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6204 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6205 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6206 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6207 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6210 safestrncpy(mode_line, str, sizeof(mode_line));
6211 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6212 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6214 chanserv_conf.default_modes = *change;
6215 mod_chanmode_free(change);
6217 free_string_list(chanserv_conf.set_shows);
6218 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6220 strlist = string_list_copy(strlist);
6223 static const char *list[] = {
6224 /* multiple choice options */
6225 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6226 "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6227 "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6228 /* binary options */
6229 "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6233 strlist = alloc_string_list(ArrayLength(list)-1);
6234 for(ii=0; list[ii]; ii++)
6235 string_list_append(strlist, strdup(list[ii]));
6237 chanserv_conf.set_shows = strlist;
6238 /* We don't look things up now, in case the list refers to options
6239 * defined by modules initialized after this point. Just mark the
6240 * function list as invalid, so it will be initialized.
6242 set_shows_list.used = 0;
6243 free_string_list(chanserv_conf.eightball);
6244 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6247 strlist = string_list_copy(strlist);
6251 strlist = alloc_string_list(4);
6252 string_list_append(strlist, strdup("Yes."));
6253 string_list_append(strlist, strdup("No."));
6254 string_list_append(strlist, strdup("Maybe so."));
6256 chanserv_conf.eightball = strlist;
6257 free_string_list(chanserv_conf.old_ban_names);
6258 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6260 strlist = string_list_copy(strlist);
6262 strlist = alloc_string_list(2);
6263 chanserv_conf.old_ban_names = strlist;
6267 chanserv_note_type_read(const char *key, struct record_data *rd)
6270 struct note_type *ntype;
6273 if(!(obj = GET_RECORD_OBJECT(rd)))
6275 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6278 if(!(ntype = chanserv_create_note_type(key)))
6280 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6284 /* Figure out set access */
6285 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6287 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6288 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6290 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6292 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6293 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6295 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6297 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6301 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6302 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6303 ntype->set_access.min_opserv = 0;
6306 /* Figure out visibility */
6307 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6308 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6309 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6310 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6311 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6312 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6313 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6314 ntype->visible_type = NOTE_VIS_ALL;
6316 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6318 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6319 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6323 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6325 struct handle_info *handle;
6326 struct userData *uData;
6327 char *seen, *inf, *flags;
6329 unsigned short access;
6331 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6333 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6337 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6338 if(access > UL_OWNER)
6340 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6344 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6345 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6346 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6347 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6348 handle = get_handle_info(key);
6351 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6355 uData = add_channel_user(chan, handle, access, last_seen, inf);
6356 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6360 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6362 struct banData *bData;
6363 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6364 time_t set_time, triggered_time, expires_time;
6366 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6368 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6372 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6373 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6374 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6375 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6376 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6377 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6379 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6380 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6382 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6384 expires_time = set_time + atoi(s_duration);
6388 if(expires_time && (expires_time < now))
6391 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6394 static struct suspended *
6395 chanserv_read_suspended(dict_t obj)
6397 struct suspended *suspended = calloc(1, sizeof(*suspended));
6401 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6402 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6403 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6404 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6405 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6406 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6407 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6408 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6409 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6410 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6415 chanserv_channel_read(const char *key, struct record_data *hir)
6417 struct suspended *suspended;
6418 struct mod_chanmode *modes;
6419 struct chanNode *cNode;
6420 struct chanData *cData;
6421 struct dict *channel, *obj;
6422 char *str, *argv[10];
6426 channel = hir->d.object;
6428 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6431 cNode = AddChannel(key, now, NULL, NULL);
6434 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6437 cData = register_channel(cNode, str);
6440 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6444 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6446 enum levelOption lvlOpt;
6447 enum charOption chOpt;
6448 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6450 if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6452 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6454 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6456 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6458 cData->chOpts[chOpt] = str[0];
6460 if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6461 cData->flags = atoi(str);
6463 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6465 enum levelOption lvlOpt;
6466 enum charOption chOpt;
6469 cData->flags = base64toint(str, 5);
6470 count = strlen(str += 5);
6471 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6474 switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6476 case 'c': lvl = UL_COOWNER; break;
6477 case 'm': lvl = UL_MASTER; break;
6478 case 'n': lvl = UL_OWNER+1; break;
6479 case 'o': lvl = UL_OP; break;
6480 case 'p': lvl = UL_PEON; break;
6481 case 'w': lvl = UL_OWNER; break;
6482 default: lvl = 0; break;
6484 cData->lvlOpts[lvlOpt] = lvl;
6486 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6487 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6490 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6492 suspended = chanserv_read_suspended(obj);
6493 cData->suspended = suspended;
6494 suspended->cData = cData;
6495 /* We could use suspended->expires and suspended->revoked to
6496 * set the CHANNEL_SUSPENDED flag, but we don't. */
6498 else if(cData->flags & CHANNEL_SUSPENDED)
6500 suspended = calloc(1, sizeof(*suspended));
6501 suspended->issued = 0;
6502 suspended->revoked = 0;
6503 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6504 suspended->expires = str ? atoi(str) : 0;
6505 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6506 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6507 suspended->reason = strdup(str ? str : "No reason");
6508 suspended->previous = NULL;
6509 cData->suspended = suspended;
6510 suspended->cData = cData;
6515 if((cData->flags & CHANNEL_SUSPENDED)
6516 && suspended->expires
6517 && (suspended->expires <= now))
6519 cData->flags &= ~CHANNEL_SUSPENDED;
6522 if((cData->flags & CHANNEL_SUSPENDED) && (suspended->expires > now))
6524 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6528 struct mod_chanmode change;
6529 change.modes_set = change.modes_clear = 0;
6531 change.args[0].mode = MODE_CHANOP;
6532 change.args[0].member = AddChannelUser(chanserv, cNode);
6533 mod_chanmode_announce(chanserv, cNode, &change);
6536 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6537 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6538 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6539 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6540 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6541 cData->max = str ? atoi(str) : 0;
6542 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6543 cData->greeting = str ? strdup(str) : NULL;
6544 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6545 cData->user_greeting = str ? strdup(str) : NULL;
6546 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6547 cData->topic_mask = str ? strdup(str) : NULL;
6548 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6549 cData->topic = str ? strdup(str) : NULL;
6551 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6552 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6553 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6554 cData->modes = *modes;
6555 if(cData->modes.argc > 1)
6556 cData->modes.argc = 1;
6557 if(!IsSuspended(cData))
6558 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6559 mod_chanmode_free(modes);
6562 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6563 for(it = dict_first(obj); it; it = iter_next(it))
6564 user_read_helper(iter_key(it), iter_data(it), cData);
6566 if(!cData->users && !IsProtected(cData))
6568 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6569 unregister_channel(cData, "has empty user list.");
6573 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6574 for(it = dict_first(obj); it; it = iter_next(it))
6575 ban_read_helper(iter_key(it), iter_data(it), cData);
6577 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6578 for(it = dict_first(obj); it; it = iter_next(it))
6580 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6581 struct record_data *rd = iter_data(it);
6582 const char *note, *setter;
6584 if(rd->type != RECDB_OBJECT)
6586 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6590 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6592 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6594 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6598 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6599 if(!setter) setter = "<unknown>";
6600 chanserv_add_channel_note(cData, ntype, setter, note);
6608 chanserv_dnr_read(const char *key, struct record_data *hir)
6610 const char *setter, *reason, *str;
6611 struct do_not_register *dnr;
6613 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6616 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6619 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6622 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6625 dnr = chanserv_add_dnr(key, setter, reason);
6628 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6630 dnr->set = atoi(str);
6636 chanserv_saxdb_read(struct dict *database)
6638 struct dict *section;
6641 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6642 for(it = dict_first(section); it; it = iter_next(it))
6643 chanserv_note_type_read(iter_key(it), iter_data(it));
6645 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6646 for(it = dict_first(section); it; it = iter_next(it))
6647 chanserv_channel_read(iter_key(it), iter_data(it));
6649 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6650 for(it = dict_first(section); it; it = iter_next(it))
6651 chanserv_dnr_read(iter_key(it), iter_data(it));
6657 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6659 int high_present = 0;
6660 saxdb_start_record(ctx, KEY_USERS, 1);
6661 for(; uData; uData = uData->next)
6663 if((uData->access >= UL_PRESENT) && uData->present)
6665 saxdb_start_record(ctx, uData->handle->handle, 0);
6666 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6667 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6669 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6671 saxdb_write_string(ctx, KEY_INFO, uData->info);
6672 saxdb_end_record(ctx);
6674 saxdb_end_record(ctx);
6675 return high_present;
6679 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6683 saxdb_start_record(ctx, KEY_BANS, 1);
6684 for(; bData; bData = bData->next)
6686 saxdb_start_record(ctx, bData->mask, 0);
6687 saxdb_write_int(ctx, KEY_SET, bData->set);
6688 if(bData->triggered)
6689 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6691 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6693 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6695 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6696 saxdb_end_record(ctx);
6698 saxdb_end_record(ctx);
6702 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6704 saxdb_start_record(ctx, name, 0);
6705 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6706 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6708 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6710 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6712 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6714 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6715 saxdb_end_record(ctx);
6719 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6723 enum levelOption lvlOpt;
6724 enum charOption chOpt;
6726 saxdb_start_record(ctx, channel->channel->name, 1);
6728 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6729 saxdb_write_int(ctx, KEY_MAX, channel->max);
6731 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6732 if(channel->registrar)
6733 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6734 if(channel->greeting)
6735 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6736 if(channel->user_greeting)
6737 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6738 if(channel->topic_mask)
6739 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6740 if(channel->suspended)
6741 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6743 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6744 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6745 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6746 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6747 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6749 buf[0] = channel->chOpts[chOpt];
6751 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6753 saxdb_end_record(ctx);
6755 if(channel->modes.modes_set || channel->modes.modes_clear)
6757 mod_chanmode_format(&channel->modes, buf);
6758 saxdb_write_string(ctx, KEY_MODES, buf);
6761 high_present = chanserv_write_users(ctx, channel->users);
6762 chanserv_write_bans(ctx, channel->bans);
6764 if(dict_size(channel->notes))
6768 saxdb_start_record(ctx, KEY_NOTES, 1);
6769 for(it = dict_first(channel->notes); it; it = iter_next(it))
6771 struct note *note = iter_data(it);
6772 saxdb_start_record(ctx, iter_key(it), 0);
6773 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6774 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6775 saxdb_end_record(ctx);
6777 saxdb_end_record(ctx);
6780 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6781 saxdb_end_record(ctx);
6785 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6789 saxdb_start_record(ctx, ntype->name, 0);
6790 switch(ntype->set_access_type)
6792 case NOTE_SET_CHANNEL_ACCESS:
6793 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6795 case NOTE_SET_CHANNEL_SETTER:
6796 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6798 case NOTE_SET_PRIVILEGED: default:
6799 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6802 switch(ntype->visible_type)
6804 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6805 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6806 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6808 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6809 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6810 saxdb_end_record(ctx);
6814 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6816 struct do_not_register *dnr;
6819 for(it = dict_first(dnrs); it; it = iter_next(it))
6821 dnr = iter_data(it);
6822 saxdb_start_record(ctx, dnr->chan_name, 0);
6824 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6825 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6826 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6827 saxdb_end_record(ctx);
6832 chanserv_saxdb_write(struct saxdb_context *ctx)
6835 struct chanData *channel;
6838 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6839 for(it = dict_first(note_types); it; it = iter_next(it))
6840 chanserv_write_note_type(ctx, iter_data(it));
6841 saxdb_end_record(ctx);
6844 saxdb_start_record(ctx, KEY_DNR, 1);
6845 write_dnrs_helper(ctx, handle_dnrs);
6846 write_dnrs_helper(ctx, plain_dnrs);
6847 write_dnrs_helper(ctx, mask_dnrs);
6848 saxdb_end_record(ctx);
6851 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6852 for(channel = channelList; channel; channel = channel->next)
6853 chanserv_write_channel(ctx, channel);
6854 saxdb_end_record(ctx);
6860 chanserv_db_cleanup(void) {
6862 unreg_part_func(handle_part);
6864 unregister_channel(channelList, "terminating.");
6865 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6866 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6867 free(chanserv_conf.support_channels.list);
6868 dict_delete(handle_dnrs);
6869 dict_delete(plain_dnrs);
6870 dict_delete(mask_dnrs);
6871 dict_delete(note_types);
6872 free_string_list(chanserv_conf.eightball);
6873 free_string_list(chanserv_conf.old_ban_names);
6874 free_string_list(chanserv_conf.set_shows);
6875 free(set_shows_list.list);
6876 free(uset_shows_list.list);
6879 struct userData *helper = helperList;
6880 helperList = helperList->next;
6885 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6886 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6887 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6890 init_chanserv(const char *nick)
6892 chanserv = AddService(nick, "Channel Services");
6893 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6894 conf_register_reload(chanserv_conf_read);
6896 reg_server_link_func(handle_server_link);
6898 reg_new_channel_func(handle_new_channel);
6899 reg_join_func(handle_join);
6900 reg_part_func(handle_part);
6901 reg_kick_func(handle_kick);
6902 reg_topic_func(handle_topic);
6903 reg_mode_change_func(handle_mode);
6904 reg_nick_change_func(handle_nick_change);
6906 reg_auth_func(handle_auth);
6907 reg_handle_rename_func(handle_rename);
6908 reg_unreg_func(handle_unreg);
6910 handle_dnrs = dict_new();
6911 dict_set_free_data(handle_dnrs, free);
6912 plain_dnrs = dict_new();
6913 dict_set_free_data(plain_dnrs, free);
6914 mask_dnrs = dict_new();
6915 dict_set_free_data(mask_dnrs, free);
6917 reg_svccmd_unbind_func(handle_svccmd_unbind);
6918 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6919 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6920 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6921 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6922 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6923 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6924 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6925 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6926 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6928 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6929 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6931 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6932 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6933 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6934 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6935 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6937 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6938 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6939 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6940 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6941 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6943 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6944 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6945 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6946 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6948 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6949 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6950 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6951 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6952 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6953 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6954 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6955 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6957 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6958 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6959 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6960 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6961 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6962 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6963 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6964 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6965 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6966 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6967 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6968 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6969 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6970 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6972 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "coowner", NULL);
6973 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6974 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6975 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6976 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6978 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6979 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6981 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6982 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6983 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6984 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6985 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6986 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6987 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6988 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6989 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6990 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6992 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
6993 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
6995 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
6996 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
6997 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
6998 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7000 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7001 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7002 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7003 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7004 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7006 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7007 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7008 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7009 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7010 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7011 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7013 /* Channel options */
7014 DEFINE_CHANNEL_OPTION(defaulttopic);
7015 DEFINE_CHANNEL_OPTION(topicmask);
7016 DEFINE_CHANNEL_OPTION(greeting);
7017 DEFINE_CHANNEL_OPTION(usergreeting);
7018 DEFINE_CHANNEL_OPTION(modes);
7019 DEFINE_CHANNEL_OPTION(enfops);
7020 DEFINE_CHANNEL_OPTION(giveops);
7021 DEFINE_CHANNEL_OPTION(protect);
7022 DEFINE_CHANNEL_OPTION(enfmodes);
7023 DEFINE_CHANNEL_OPTION(enftopic);
7024 DEFINE_CHANNEL_OPTION(pubcmd);
7025 DEFINE_CHANNEL_OPTION(voice);
7026 DEFINE_CHANNEL_OPTION(userinfo);
7027 DEFINE_CHANNEL_OPTION(dynlimit);
7028 DEFINE_CHANNEL_OPTION(topicsnarf);
7029 DEFINE_CHANNEL_OPTION(nodelete);
7030 DEFINE_CHANNEL_OPTION(toys);
7031 DEFINE_CHANNEL_OPTION(setters);
7032 DEFINE_CHANNEL_OPTION(topicrefresh);
7033 DEFINE_CHANNEL_OPTION(ctcpusers);
7034 DEFINE_CHANNEL_OPTION(ctcpreaction);
7035 DEFINE_CHANNEL_OPTION(peoninvite);
7036 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7038 /* Alias set topic to set defaulttopic for compatibility. */
7039 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7042 DEFINE_USER_OPTION(noautoop);
7043 DEFINE_USER_OPTION(autoinvite);
7044 DEFINE_USER_OPTION(info);
7046 /* Alias uset autovoice to uset autoop. */
7047 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7049 note_types = dict_new();
7050 dict_set_free_data(note_types, chanserv_deref_note_type);
7051 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7052 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7054 if(chanserv_conf.channel_expire_frequency)
7055 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7057 if(chanserv_conf.refresh_period)
7059 time_t next_refresh;
7060 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7061 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7064 reg_exit_func(chanserv_db_cleanup);
7065 message_register_table(msgtab);