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 if (list->lowest != 0)
3319 lowest = list->lowest;
3320 else if (highest < 100)
3323 lowest = highest - 100;
3324 for(start = curr = 1; curr < list->table.length; )
3326 uData = list->users[curr-1];
3327 list->table.contents[curr++][0] = " ";
3328 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3331 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3333 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3334 temp = list->table.contents[--start];
3335 list->table.contents[start] = list->table.contents[0];
3336 tmp_table.contents = list->table.contents + start;
3337 tmp_table.length = curr - start;
3338 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3339 list->table.contents[start] = temp;
3341 highest = lowest - 1;
3342 lowest = (highest < 100) ? 0 : (highest - 99);
3348 def_list(struct listData *list)
3352 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3354 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3355 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3356 if(list->table.length == 1)
3358 msg = user_find_message(list->user, "MSG_NONE");
3359 send_message_type(4, list->user, list->bot, " %s", msg);
3364 userData_access_comp(const void *arg_a, const void *arg_b)
3366 const struct userData *a = *(struct userData**)arg_a;
3367 const struct userData *b = *(struct userData**)arg_b;
3369 if(a->access != b->access)
3370 res = b->access - a->access;
3372 res = irccasecmp(a->handle->handle, b->handle->handle);
3377 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3379 void (*send_list)(struct listData *);
3380 struct userData *uData;
3381 struct listData lData;
3382 unsigned int matches;
3386 lData.bot = cmd->parent->bot;
3387 lData.channel = channel;
3388 lData.lowest = lowest;
3389 lData.highest = highest;
3390 lData.search = (argc > 1) ? argv[1] : NULL;
3391 send_list = zoot_list;
3393 if(user->handle_info)
3395 switch(user->handle_info->userlist_style)
3397 case HI_STYLE_DEF: send_list = def_list; break;
3398 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3402 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3404 for(uData = channel->channel_info->users; uData; uData = uData->next)
3406 if((uData->access < lowest)
3407 || (uData->access > highest)
3408 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3410 lData.users[matches++] = uData;
3412 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3414 lData.table.length = matches+1;
3415 lData.table.width = 4;
3416 lData.table.flags = TABLE_NO_FREE;
3417 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3418 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3419 lData.table.contents[0] = ary;
3422 ary[2] = "Last Seen";
3424 for(matches = 1; matches < lData.table.length; ++matches)
3426 struct userData *uData = lData.users[matches-1];
3427 char seen[INTERVALLEN];
3429 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3430 lData.table.contents[matches] = ary;
3431 ary[0] = strtab(uData->access);
3432 ary[1] = uData->handle->handle;
3435 else if(!uData->seen)
3438 ary[2] = intervalString(seen, now - uData->seen);
3439 ary[2] = strdup(ary[2]);
3440 if(IsUserSuspended(uData))
3441 ary[3] = "Suspended";
3442 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3443 ary[3] = "Vacation";
3448 for(matches = 1; matches < lData.table.length; ++matches)
3450 free((char*)lData.table.contents[matches][2]);
3451 free(lData.table.contents[matches]);
3453 free(lData.table.contents[0]);
3454 free(lData.table.contents);
3458 static CHANSERV_FUNC(cmd_users)
3460 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3463 static CHANSERV_FUNC(cmd_wlist)
3465 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3468 static CHANSERV_FUNC(cmd_clist)
3470 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3473 static CHANSERV_FUNC(cmd_mlist)
3475 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3478 static CHANSERV_FUNC(cmd_olist)
3480 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3483 static CHANSERV_FUNC(cmd_plist)
3485 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3488 static CHANSERV_FUNC(cmd_bans)
3490 struct helpfile_table tbl;
3491 unsigned int matches = 0, timed = 0, ii;
3492 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3493 const char *msg_never, *triggered, *expires;
3494 struct banData *ban, **bans;
3501 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3503 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3505 if(search && !match_ircglobs(search, ban->mask))
3507 bans[matches++] = ban;
3512 tbl.length = matches + 1;
3513 tbl.width = 4 + timed;
3515 tbl.flags = TABLE_NO_FREE;
3516 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3517 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3518 tbl.contents[0][0] = "Mask";
3519 tbl.contents[0][1] = "Set By";
3520 tbl.contents[0][2] = "Triggered";
3523 tbl.contents[0][3] = "Expires";
3524 tbl.contents[0][4] = "Reason";
3527 tbl.contents[0][3] = "Reason";
3530 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3535 msg_never = user_find_message(user, "MSG_NEVER");
3536 for(ii = 0; ii < matches; )
3542 else if(ban->expires)
3543 expires = intervalString(e_buffer, ban->expires - now);
3545 expires = msg_never;
3548 triggered = intervalString(t_buffer, now - ban->triggered);
3550 triggered = msg_never;
3552 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3553 tbl.contents[ii][0] = ban->mask;
3554 tbl.contents[ii][1] = ban->owner;
3555 tbl.contents[ii][2] = strdup(triggered);
3558 tbl.contents[ii][3] = strdup(expires);
3559 tbl.contents[ii][4] = ban->reason;
3562 tbl.contents[ii][3] = ban->reason;
3564 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3565 reply("MSG_MATCH_COUNT", matches);
3566 for(ii = 1; ii < tbl.length; ++ii)
3568 free((char*)tbl.contents[ii][2]);
3570 free((char*)tbl.contents[ii][3]);
3571 free(tbl.contents[ii]);
3573 free(tbl.contents[0]);
3579 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3581 struct chanData *cData = channel->channel_info;
3582 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3584 if(cData->topic_mask)
3585 return !match_ircglob(new_topic, cData->topic_mask);
3586 else if(cData->topic)
3587 return irccasecmp(new_topic, cData->topic);
3592 static CHANSERV_FUNC(cmd_topic)
3594 struct chanData *cData;
3597 cData = channel->channel_info;
3602 SetChannelTopic(channel, chanserv, cData->topic, 1);
3603 reply("CSMSG_TOPIC_SET", cData->topic);
3607 reply("CSMSG_NO_TOPIC", channel->name);
3611 topic = unsplit_string(argv + 1, argc - 1, NULL);
3612 /* If they say "!topic *", use an empty topic. */
3613 if((topic[0] == '*') && (topic[1] == 0))
3615 if(bad_topic(channel, user, topic))
3617 char *topic_mask = cData->topic_mask;
3620 char new_topic[TOPICLEN+1], tchar;
3621 int pos=0, starpos=-1, dpos=0, len;
3623 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3630 len = strlen(topic);
3631 if((dpos + len) > TOPICLEN)
3632 len = TOPICLEN + 1 - dpos;
3633 memcpy(new_topic+dpos, topic, len);
3637 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3638 default: new_topic[dpos++] = tchar; break;
3641 if((dpos > TOPICLEN) || tchar)
3644 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3645 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3648 new_topic[dpos] = 0;
3649 SetChannelTopic(channel, chanserv, new_topic, 1);
3651 reply("CSMSG_TOPIC_LOCKED", channel->name);
3656 SetChannelTopic(channel, chanserv, topic, 1);
3658 if(cData->flags & CHANNEL_TOPIC_SNARF)
3660 /* Grab the topic and save it as the default topic. */
3662 cData->topic = strdup(channel->topic);
3668 static CHANSERV_FUNC(cmd_mode)
3670 struct mod_chanmode *change;
3674 change = &channel->channel_info->modes;
3675 if(change->modes_set || change->modes_clear) {
3676 modcmd_chanmode_announce(change);
3677 reply("CSMSG_DEFAULTED_MODES", channel->name);
3679 reply("CSMSG_NO_MODES", channel->name);
3683 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3686 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3690 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3691 && mode_lock_violated(&channel->channel_info->modes, change))
3694 mod_chanmode_format(&channel->channel_info->modes, modes);
3695 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3699 modcmd_chanmode_announce(change);
3700 mod_chanmode_free(change);
3701 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3705 static CHANSERV_FUNC(cmd_invite)
3707 struct userData *uData;
3708 struct userNode *invite;
3710 uData = GetChannelUser(channel->channel_info, user->handle_info);
3714 if(!(invite = GetUserH(argv[1])))
3716 reply("MSG_NICK_UNKNOWN", argv[1]);
3723 if(GetUserMode(channel, invite))
3725 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3731 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3732 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3734 irc_invite(chanserv, invite, channel);
3736 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3741 static CHANSERV_FUNC(cmd_inviteme)
3743 struct userData *uData;
3745 if(GetUserMode(channel, user))
3747 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3750 if(channel->channel_info
3751 && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3752 && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3753 && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3755 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3758 irc_invite(cmd->parent->bot, user, channel);
3763 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3766 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3768 /* We display things based on two dimensions:
3769 * - Issue time: present or absent
3770 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3771 * (in order of precedence, so something both expired and revoked
3772 * only counts as revoked)
3774 combo = (suspended->issued ? 4 : 0)
3775 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3777 case 0: /* no issue time, indefinite expiration */
3778 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3780 case 1: /* no issue time, expires in future */
3781 intervalString(buf1, suspended->expires-now);
3782 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3784 case 2: /* no issue time, expired */
3785 intervalString(buf1, now-suspended->expires);
3786 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3788 case 3: /* no issue time, revoked */
3789 intervalString(buf1, now-suspended->revoked);
3790 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3792 case 4: /* issue time set, indefinite expiration */
3793 intervalString(buf1, now-suspended->issued);
3794 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3796 case 5: /* issue time set, expires in future */
3797 intervalString(buf1, now-suspended->issued);
3798 intervalString(buf2, suspended->expires-now);
3799 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3801 case 6: /* issue time set, expired */
3802 intervalString(buf1, now-suspended->issued);
3803 intervalString(buf2, now-suspended->expires);
3804 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3806 case 7: /* issue time set, revoked */
3807 intervalString(buf1, now-suspended->issued);
3808 intervalString(buf2, now-suspended->revoked);
3809 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3812 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3817 static CHANSERV_FUNC(cmd_info)
3819 char modes[MAXLEN], buffer[INTERVALLEN];
3820 struct userData *uData, *owner;
3821 struct chanData *cData;
3822 struct do_not_register *dnr;
3827 cData = channel->channel_info;
3828 reply("CSMSG_CHANNEL_INFO", channel->name);
3830 uData = GetChannelUser(cData, user->handle_info);
3831 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3833 mod_chanmode_format(&cData->modes, modes);
3834 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3835 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3838 for(it = dict_first(cData->notes); it; it = iter_next(it))
3842 note = iter_data(it);
3843 if(!note_type_visible_to_user(cData, note->type, user))
3846 padding = PADLEN - 1 - strlen(iter_key(it));
3847 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3850 reply("CSMSG_CHANNEL_MAX", cData->max);
3851 for(owner = cData->users; owner; owner = owner->next)
3852 if(owner->access == UL_OWNER)
3853 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3854 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3855 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3856 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3857 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3859 privileged = IsStaff(user);
3860 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3861 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3863 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3864 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3866 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3868 struct suspended *suspended;
3869 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3870 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3871 show_suspension_info(cmd, user, suspended);
3873 else if(IsSuspended(cData))
3875 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3876 show_suspension_info(cmd, user, cData->suspended);
3881 static CHANSERV_FUNC(cmd_netinfo)
3883 extern time_t boot_time;
3884 extern unsigned long burst_length;
3885 char interval[INTERVALLEN];
3887 reply("CSMSG_NETWORK_INFO");
3888 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3889 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3890 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3891 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3892 reply("CSMSG_NETWORK_BANS", banCount);
3893 reply("CSMSG_CHANNEL_USERS", userCount);
3894 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3895 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3900 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3902 struct helpfile_table table;
3904 struct userNode *user;
3909 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3910 table.contents = alloca(list->used*sizeof(*table.contents));
3911 for(nn=0; nn<list->used; nn++)
3913 user = list->list[nn];
3914 if(user->modes & skip_flags)
3918 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3921 nick = alloca(strlen(user->nick)+3);
3922 sprintf(nick, "(%s)", user->nick);
3926 table.contents[table.length][0] = nick;
3929 table_send(chanserv, to->nick, 0, NULL, table);
3932 static CHANSERV_FUNC(cmd_ircops)
3934 reply("CSMSG_STAFF_OPERS");
3935 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3939 static CHANSERV_FUNC(cmd_helpers)
3941 reply("CSMSG_STAFF_HELPERS");
3942 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3946 static CHANSERV_FUNC(cmd_staff)
3948 reply("CSMSG_NETWORK_STAFF");
3949 cmd_ircops(CSFUNC_ARGS);
3950 cmd_helpers(CSFUNC_ARGS);
3954 static CHANSERV_FUNC(cmd_peek)
3956 struct modeNode *mn;
3957 char modes[MODELEN];
3959 struct helpfile_table table;
3961 irc_make_chanmode(channel, modes);
3963 reply("CSMSG_PEEK_INFO", channel->name);
3964 reply("CSMSG_PEEK_TOPIC", channel->topic);
3965 reply("CSMSG_PEEK_MODES", modes);
3966 reply("CSMSG_PEEK_USERS", channel->members.used);
3970 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3971 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3972 for(n = 0; n < channel->members.used; n++)
3974 mn = channel->members.list[n];
3975 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3977 table.contents[table.length] = alloca(sizeof(**table.contents));
3978 table.contents[table.length][0] = mn->user->nick;
3983 reply("CSMSG_PEEK_OPS");
3984 table_send(chanserv, user->nick, 0, NULL, table);
3987 reply("CSMSG_PEEK_NO_OPS");
3991 static MODCMD_FUNC(cmd_wipeinfo)
3993 struct handle_info *victim;
3994 struct userData *ud, *actor;
3997 actor = GetChannelUser(channel->channel_info, user->handle_info);
3998 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4000 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4002 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4005 if((ud->access >= actor->access) && (ud != actor))
4007 reply("MSG_USER_OUTRANKED", victim->handle);
4013 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4017 static CHANSERV_FUNC(cmd_resync)
4019 struct mod_chanmode *changes;
4020 struct chanData *cData = channel->channel_info;
4021 unsigned int ii, used;
4023 changes = mod_chanmode_alloc(channel->members.used * 2);
4024 for(ii = used = 0; ii < channel->members.used; ++ii)
4026 struct modeNode *mn = channel->members.list[ii];
4027 struct userData *uData;
4028 if(IsService(mn->user))
4030 /* must not change modes for this user */
4032 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4036 changes->args[used].mode = MODE_REMOVE | mn->modes;
4037 changes->args[used++].member = mn;
4040 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4042 if(mn->modes & MODE_CHANOP)
4044 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4045 changes->args[used++].member = mn;
4047 if(!(mn->modes & MODE_VOICE))
4049 changes->args[used].mode = MODE_VOICE;
4050 changes->args[used++].member = mn;
4055 if(!(mn->modes & MODE_CHANOP))
4057 changes->args[used].mode = MODE_CHANOP;
4058 changes->args[used++].member = mn;
4062 changes->argc = used;
4063 modcmd_chanmode_announce(changes);
4064 mod_chanmode_free(changes);
4065 reply("CSMSG_RESYNCED_USERS", channel->name);
4069 static CHANSERV_FUNC(cmd_seen)
4071 struct userData *uData;
4072 struct handle_info *handle;
4073 char seen[INTERVALLEN];
4077 if(!irccasecmp(argv[1], chanserv->nick))
4079 reply("CSMSG_IS_CHANSERV");
4083 if(!(handle = get_handle_info(argv[1])))
4085 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4089 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4091 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4096 reply("CSMSG_USER_PRESENT", handle->handle);
4097 else if(uData->seen)
4098 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4100 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4102 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4103 reply("CSMSG_USER_VACATION", handle->handle);
4108 static MODCMD_FUNC(cmd_names)
4110 struct userNode *targ;
4111 struct userData *targData;
4112 unsigned int ii, pos;
4115 for(ii=pos=0; ii<channel->members.used; ++ii)
4117 targ = channel->members.list[ii]->user;
4118 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4121 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4124 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4128 if(IsUserSuspended(targData))
4130 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4133 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4134 reply("CSMSG_END_NAMES", channel->name);
4139 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4141 switch(ntype->visible_type)
4143 case NOTE_VIS_ALL: return 1;
4144 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4145 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4150 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4152 struct userData *uData;
4154 switch(ntype->set_access_type)
4156 case NOTE_SET_CHANNEL_ACCESS:
4157 if(!user->handle_info)
4159 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4161 return uData->access >= ntype->set_access.min_ulevel;
4162 case NOTE_SET_CHANNEL_SETTER:
4163 return check_user_level(channel, user, lvlSetters, 1, 0);
4164 case NOTE_SET_PRIVILEGED: default:
4165 return IsHelping(user);
4169 static CHANSERV_FUNC(cmd_note)
4171 struct chanData *cData;
4173 struct note_type *ntype;
4175 cData = channel->channel_info;
4178 reply("CSMSG_NOT_REGISTERED", channel->name);
4182 /* If no arguments, show all visible notes for the channel. */
4188 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4190 note = iter_data(it);
4191 if(!note_type_visible_to_user(cData, note->type, user))
4194 reply("CSMSG_NOTELIST_HEADER", channel->name);
4195 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4198 reply("CSMSG_NOTELIST_END", channel->name);
4200 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4202 /* If one argument, show the named note. */
4205 if((note = dict_find(cData->notes, argv[1], NULL))
4206 && note_type_visible_to_user(cData, note->type, user))
4208 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4210 else if((ntype = dict_find(note_types, argv[1], NULL))
4211 && note_type_visible_to_user(NULL, ntype, user))
4213 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4218 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4222 /* Assume they're trying to set a note. */
4226 ntype = dict_find(note_types, argv[1], NULL);
4229 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4232 else if(note_type_settable_by_user(channel, ntype, user))
4234 note_text = unsplit_string(argv+2, argc-2, NULL);
4235 if((note = dict_find(cData->notes, argv[1], NULL)))
4236 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4237 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4238 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4240 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4242 /* The note is viewable to staff only, so return 0
4243 to keep the invocation from getting logged (or
4244 regular users can see it in !events). */
4250 reply("CSMSG_NO_ACCESS");
4257 static CHANSERV_FUNC(cmd_delnote)
4262 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4263 || !note_type_settable_by_user(channel, note->type, user))
4265 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4268 dict_remove(channel->channel_info->notes, note->type->name);
4269 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4273 static CHANSERV_FUNC(cmd_events)
4275 struct logSearch discrim;
4276 struct logReport report;
4277 unsigned int matches, limit;
4279 limit = (argc > 1) ? atoi(argv[1]) : 10;
4280 if(limit < 1 || limit > 200) limit = 10;
4282 memset(&discrim, 0, sizeof(discrim));
4283 discrim.masks.bot = chanserv;
4284 discrim.masks.channel_name = channel->name;
4285 if(argc > 2) discrim.masks.command = argv[2];
4286 discrim.limit = limit;
4287 discrim.max_time = INT_MAX;
4288 discrim.severities = 1 << LOG_COMMAND;
4289 report.reporter = chanserv;
4291 reply("CSMSG_EVENT_SEARCH_RESULTS");
4292 matches = log_entry_search(&discrim, log_report_entry, &report);
4294 reply("MSG_MATCH_COUNT", matches);
4296 reply("MSG_NO_MATCHES");
4300 static CHANSERV_FUNC(cmd_say)
4306 msg = unsplit_string(argv + 1, argc - 1, NULL);
4307 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4309 else if(GetUserH(argv[1]))
4312 msg = unsplit_string(argv + 2, argc - 2, NULL);
4313 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4317 reply("You must specify the name of a channel or user.");
4323 static CHANSERV_FUNC(cmd_emote)
4329 /* CTCP is so annoying. */
4330 msg = unsplit_string(argv + 1, argc - 1, NULL);
4331 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4333 else if(GetUserH(argv[1]))
4335 msg = unsplit_string(argv + 2, argc - 2, NULL);
4336 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4340 reply("You must specify the name of a channel or user.");
4346 struct channelList *
4347 chanserv_support_channels(void)
4349 return &chanserv_conf.support_channels;
4352 static CHANSERV_FUNC(cmd_expire)
4354 int channel_count = registered_channels;
4355 expire_channels(NULL);
4356 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4361 chanserv_expire_suspension(void *data)
4363 struct suspended *suspended = data;
4364 struct chanNode *channel;
4365 struct mod_chanmode change;
4367 if(!suspended->expires || (now < suspended->expires))
4368 suspended->revoked = now;
4369 channel = suspended->cData->channel;
4370 suspended->cData->channel = channel;
4371 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4372 change.modes_set = change.modes_clear = 0;
4374 change.args[0].mode = MODE_CHANOP;
4375 change.args[0].member = AddChannelUser(chanserv, channel);
4376 mod_chanmode_announce(chanserv, channel, &change);
4379 static CHANSERV_FUNC(cmd_csuspend)
4381 struct suspended *suspended;
4382 char reason[MAXLEN];
4383 time_t expiry, duration;
4384 struct userData *uData;
4388 if(IsProtected(channel->channel_info))
4390 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4394 if(argv[1][0] == '!')
4396 else if(IsSuspended(channel->channel_info))
4398 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4399 show_suspension_info(cmd, user, channel->channel_info->suspended);
4403 if(!strcmp(argv[1], "0"))
4405 else if((duration = ParseInterval(argv[1])))
4406 expiry = now + duration;
4409 reply("MSG_INVALID_DURATION", argv[1]);
4413 unsplit_string(argv + 2, argc - 2, reason);
4415 suspended = calloc(1, sizeof(*suspended));
4416 suspended->revoked = 0;
4417 suspended->issued = now;
4418 suspended->suspender = strdup(user->handle_info->handle);
4419 suspended->expires = expiry;
4420 suspended->reason = strdup(reason);
4421 suspended->cData = channel->channel_info;
4422 suspended->previous = suspended->cData->suspended;
4423 suspended->cData->suspended = suspended;
4425 if(suspended->expires)
4426 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4428 if(IsSuspended(channel->channel_info))
4430 suspended->previous->revoked = now;
4431 if(suspended->previous->expires)
4432 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4433 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4434 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4438 /* Mark all users in channel as absent. */
4439 for(uData = channel->channel_info->users; uData; uData = uData->next)
4448 /* Mark the channel as suspended, then part. */
4449 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4450 DelChannelUser(chanserv, channel, suspended->reason, 0);
4451 reply("CSMSG_SUSPENDED", channel->name);
4452 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4453 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4458 static CHANSERV_FUNC(cmd_cunsuspend)
4460 struct suspended *suspended;
4461 char message[MAXLEN];
4463 if(!IsSuspended(channel->channel_info))
4465 reply("CSMSG_NOT_SUSPENDED", channel->name);
4469 suspended = channel->channel_info->suspended;
4471 /* Expire the suspension and join ChanServ to the channel. */
4472 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4473 chanserv_expire_suspension(suspended);
4474 reply("CSMSG_UNSUSPENDED", channel->name);
4475 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4476 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4480 typedef struct chanservSearch
4488 unsigned long flags;
4492 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4495 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4500 search = malloc(sizeof(struct chanservSearch));
4501 memset(search, 0, sizeof(*search));
4504 for(i = 0; i < argc; i++)
4506 /* Assume all criteria require arguments. */
4509 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4513 if(!irccasecmp(argv[i], "name"))
4514 search->name = argv[++i];
4515 else if(!irccasecmp(argv[i], "registrar"))
4516 search->registrar = argv[++i];
4517 else if(!irccasecmp(argv[i], "unvisited"))
4518 search->unvisited = ParseInterval(argv[++i]);
4519 else if(!irccasecmp(argv[i], "registered"))
4520 search->registered = ParseInterval(argv[++i]);
4521 else if(!irccasecmp(argv[i], "flags"))
4524 if(!irccasecmp(argv[i], "nodelete"))
4525 search->flags |= CHANNEL_NODELETE;
4526 else if(!irccasecmp(argv[i], "suspended"))
4527 search->flags |= CHANNEL_SUSPENDED;
4530 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4534 else if(!irccasecmp(argv[i], "limit"))
4535 search->limit = strtoul(argv[++i], NULL, 10);
4538 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4543 if(search->name && !strcmp(search->name, "*"))
4545 if(search->registrar && !strcmp(search->registrar, "*"))
4546 search->registrar = 0;
4555 chanserv_channel_match(struct chanData *channel, search_t search)
4557 const char *name = channel->channel->name;
4558 if((search->name && !match_ircglob(name, search->name)) ||
4559 (search->registrar && !channel->registrar) ||
4560 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4561 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4562 (search->registered && (now - channel->registered) > search->registered) ||
4563 (search->flags && ((search->flags & channel->flags) != search->flags)))
4570 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4572 struct chanData *channel;
4573 unsigned int matches = 0;
4575 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4577 if(!chanserv_channel_match(channel, search))
4587 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4592 search_print(struct chanData *channel, void *data)
4594 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4597 static CHANSERV_FUNC(cmd_search)
4600 unsigned int matches;
4601 channel_search_func action;
4605 if(!irccasecmp(argv[1], "count"))
4606 action = search_count;
4607 else if(!irccasecmp(argv[1], "print"))
4608 action = search_print;
4611 reply("CSMSG_ACTION_INVALID", argv[1]);
4615 search = chanserv_search_create(user, argc - 2, argv + 2);
4619 if(action == search_count)
4620 search->limit = INT_MAX;
4622 if(action == search_print)
4623 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4625 matches = chanserv_channel_search(search, action, user);
4628 reply("MSG_MATCH_COUNT", matches);
4630 reply("MSG_NO_MATCHES");
4636 static CHANSERV_FUNC(cmd_unvisited)
4638 struct chanData *cData;
4639 time_t interval = chanserv_conf.channel_expire_delay;
4640 char buffer[INTERVALLEN];
4641 unsigned int limit = 25, matches = 0;
4645 interval = ParseInterval(argv[1]);
4647 limit = atoi(argv[2]);
4650 intervalString(buffer, interval);
4651 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4653 for(cData = channelList; cData && matches < limit; cData = cData->next)
4655 if((now - cData->visited) < interval)
4658 intervalString(buffer, now - cData->visited);
4659 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4666 static MODCMD_FUNC(chan_opt_defaulttopic)
4672 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4674 reply("CSMSG_TOPIC_LOCKED", channel->name);
4678 topic = unsplit_string(argv+1, argc-1, NULL);
4680 free(channel->channel_info->topic);
4681 if(topic[0] == '*' && topic[1] == 0)
4683 topic = channel->channel_info->topic = NULL;
4687 topic = channel->channel_info->topic = strdup(topic);
4688 if(channel->channel_info->topic_mask
4689 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4690 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4692 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4695 if(channel->channel_info->topic)
4696 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4698 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4702 static MODCMD_FUNC(chan_opt_topicmask)
4706 struct chanData *cData = channel->channel_info;
4709 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4711 reply("CSMSG_TOPIC_LOCKED", channel->name);
4715 mask = unsplit_string(argv+1, argc-1, NULL);
4717 if(cData->topic_mask)
4718 free(cData->topic_mask);
4719 if(mask[0] == '*' && mask[1] == 0)
4721 cData->topic_mask = 0;
4725 cData->topic_mask = strdup(mask);
4727 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4728 else if(!match_ircglob(cData->topic, cData->topic_mask))
4729 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4733 if(channel->channel_info->topic_mask)
4734 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4736 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4740 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4744 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4748 if(greeting[0] == '*' && greeting[1] == 0)
4752 unsigned int length = strlen(greeting);
4753 if(length > chanserv_conf.greeting_length)
4755 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4758 *data = strdup(greeting);
4767 reply(name, user_find_message(user, "MSG_NONE"));
4771 static MODCMD_FUNC(chan_opt_greeting)
4773 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4776 static MODCMD_FUNC(chan_opt_usergreeting)
4778 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4781 static MODCMD_FUNC(chan_opt_modes)
4783 struct mod_chanmode *new_modes;
4784 char modes[MODELEN];
4788 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4790 reply("CSMSG_NO_ACCESS");
4793 if(argv[1][0] == '*' && argv[1][1] == 0)
4795 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4797 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4799 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4802 else if(new_modes->argc > 1)
4804 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4805 mod_chanmode_free(new_modes);
4810 channel->channel_info->modes = *new_modes;
4811 modcmd_chanmode_announce(new_modes);
4812 mod_chanmode_free(new_modes);
4816 mod_chanmode_format(&channel->channel_info->modes, modes);
4818 reply("CSMSG_SET_MODES", modes);
4820 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4824 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4826 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4828 struct chanData *cData = channel->channel_info;
4833 /* Set flag according to value. */
4834 if(enabled_string(argv[1]))
4836 cData->flags |= mask;
4839 else if(disabled_string(argv[1]))
4841 cData->flags &= ~mask;
4846 reply("MSG_INVALID_BINARY", argv[1]);
4852 /* Find current option value. */
4853 value = (cData->flags & mask) ? 1 : 0;
4857 reply(name, user_find_message(user, "MSG_ON"));
4859 reply(name, user_find_message(user, "MSG_OFF"));
4863 static MODCMD_FUNC(chan_opt_nodelete)
4865 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4867 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4871 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4874 static MODCMD_FUNC(chan_opt_userinfo)
4876 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4879 static MODCMD_FUNC(chan_opt_voice)
4881 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4884 static MODCMD_FUNC(chan_opt_dynlimit)
4886 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4889 static MODCMD_FUNC(chan_opt_topicsnarf)
4891 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4893 reply("CSMSG_TOPIC_LOCKED", channel->name);
4896 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4899 static MODCMD_FUNC(chan_opt_peoninvite)
4901 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4904 static MODCMD_FUNC(chan_opt_defaults)
4906 struct userData *uData;
4907 struct chanData *cData;
4908 const char *confirm;
4909 enum levelOption lvlOpt;
4910 enum charOption chOpt;
4912 cData = channel->channel_info;
4913 uData = GetChannelUser(cData, user->handle_info);
4914 if(!uData || (uData->access < UL_OWNER))
4916 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4919 confirm = make_confirmation_string(uData);
4920 if((argc < 2) || strcmp(argv[1], confirm))
4922 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4925 cData->flags = CHANNEL_DEFAULT_FLAGS;
4926 cData->modes = chanserv_conf.default_modes;
4927 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4928 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4929 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4930 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4931 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4936 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4938 struct chanData *cData = channel->channel_info;
4939 struct userData *uData;
4940 unsigned short value;
4944 if(!check_user_level(channel, user, option, 1, 1))
4946 reply("CSMSG_CANNOT_SET");
4949 value = user_level_from_name(argv[1], UL_OWNER+1);
4950 if(!value && !isdigit(argv[1][0]))
4952 reply("CSMSG_INVALID_ACCESS", argv[1]);
4955 uData = GetChannelUser(cData, user->handle_info);
4956 if(!uData || (uData->access < value))
4958 reply("CSMSG_BAD_SETLEVEL");
4961 cData->lvlOpts[option] = value;
4963 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4967 static MODCMD_FUNC(chan_opt_enfops)
4969 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4972 static MODCMD_FUNC(chan_opt_giveops)
4974 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4977 static MODCMD_FUNC(chan_opt_enfmodes)
4979 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4982 static MODCMD_FUNC(chan_opt_enftopic)
4984 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4987 static MODCMD_FUNC(chan_opt_pubcmd)
4989 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4992 static MODCMD_FUNC(chan_opt_setters)
4994 return channel_level_option(lvlSetters, CSFUNC_ARGS);
4997 static MODCMD_FUNC(chan_opt_ctcpusers)
4999 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5003 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5005 struct chanData *cData = channel->channel_info;
5006 int count = charOptions[option].count, index;
5010 index = atoi(argv[1]);
5012 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5014 reply("CSMSG_INVALID_NUMERIC", index);
5015 /* Show possible values. */
5016 for(index = 0; index < count; index++)
5017 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5021 cData->chOpts[option] = charOptions[option].values[index].value;
5025 /* Find current option value. */
5028 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5032 /* Somehow, the option value is corrupt; reset it to the default. */
5033 cData->chOpts[option] = charOptions[option].default_value;
5038 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5042 static MODCMD_FUNC(chan_opt_protect)
5044 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5047 static MODCMD_FUNC(chan_opt_toys)
5049 return channel_multiple_option(chToys, CSFUNC_ARGS);
5052 static MODCMD_FUNC(chan_opt_ctcpreaction)
5054 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5057 static MODCMD_FUNC(chan_opt_topicrefresh)
5059 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5062 static struct svccmd_list set_shows_list;
5065 handle_svccmd_unbind(struct svccmd *target) {
5067 for(ii=0; ii<set_shows_list.used; ++ii)
5068 if(target == set_shows_list.list[ii])
5069 set_shows_list.used = 0;
5072 static CHANSERV_FUNC(cmd_set)
5074 struct svccmd *subcmd;
5078 /* Check if we need to (re-)initialize set_shows_list. */
5079 if(!set_shows_list.used)
5081 if(!set_shows_list.size)
5083 set_shows_list.size = chanserv_conf.set_shows->used;
5084 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5086 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5088 const char *name = chanserv_conf.set_shows->list[ii];
5089 sprintf(buf, "%s %s", argv[0], name);
5090 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5093 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5096 svccmd_list_append(&set_shows_list, subcmd);
5102 reply("CSMSG_CHANNEL_OPTIONS");
5103 for(ii = 0; ii < set_shows_list.used; ii++)
5105 subcmd = set_shows_list.list[ii];
5106 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5111 sprintf(buf, "%s %s", argv[0], argv[1]);
5112 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5115 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5118 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5120 reply("CSMSG_NO_ACCESS");
5124 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5128 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5130 struct userData *uData;
5132 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5135 reply("CSMSG_NOT_USER", channel->name);
5141 /* Just show current option value. */
5143 else if(enabled_string(argv[1]))
5145 uData->flags |= mask;
5147 else if(disabled_string(argv[1]))
5149 uData->flags &= ~mask;
5153 reply("MSG_INVALID_BINARY", argv[1]);
5157 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5161 static MODCMD_FUNC(user_opt_noautoop)
5163 struct userData *uData;
5165 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5168 reply("CSMSG_NOT_USER", channel->name);
5171 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5172 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5174 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5177 static MODCMD_FUNC(user_opt_autoinvite)
5179 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5182 static MODCMD_FUNC(user_opt_info)
5184 struct userData *uData;
5187 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5191 /* If they got past the command restrictions (which require access)
5192 * but fail this test, we have some fool with security override on.
5194 reply("CSMSG_NOT_USER", channel->name);
5200 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5203 if(infoline[0] == '*' && infoline[1] == 0)
5206 uData->info = strdup(infoline);
5209 reply("CSMSG_USET_INFO", uData->info);
5211 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5215 struct svccmd_list uset_shows_list;
5217 static CHANSERV_FUNC(cmd_uset)
5219 struct svccmd *subcmd;
5223 /* Check if we need to (re-)initialize uset_shows_list. */
5224 if(!uset_shows_list.used)
5228 "NoAutoOp", "AutoInvite", "Info"
5231 if(!uset_shows_list.size)
5233 uset_shows_list.size = ArrayLength(options);
5234 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5236 for(ii = 0; ii < ArrayLength(options); ii++)
5238 const char *name = options[ii];
5239 sprintf(buf, "%s %s", argv[0], name);
5240 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5243 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5246 svccmd_list_append(&uset_shows_list, subcmd);
5252 /* Do this so options are presented in a consistent order. */
5253 reply("CSMSG_USER_OPTIONS");
5254 for(ii = 0; ii < uset_shows_list.used; ii++)
5255 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5259 sprintf(buf, "%s %s", argv[0], argv[1]);
5260 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5263 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5267 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5270 static CHANSERV_FUNC(cmd_giveownership)
5272 struct handle_info *new_owner_hi;
5273 struct userData *new_owner, *curr_user;
5274 struct chanData *cData = channel->channel_info;
5275 struct do_not_register *dnr;
5277 unsigned short co_access;
5278 char reason[MAXLEN];
5281 curr_user = GetChannelAccess(cData, user->handle_info);
5282 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5283 if(!curr_user || (curr_user->access != UL_OWNER))
5285 struct userData *owner = NULL;
5286 for(curr_user = channel->channel_info->users;
5288 curr_user = curr_user->next)
5290 if(curr_user->access != UL_OWNER)
5294 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5300 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5302 if(new_owner_hi == user->handle_info)
5304 reply("CSMSG_NO_TRANSFER_SELF");
5307 new_owner = GetChannelAccess(cData, new_owner_hi);
5310 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5313 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5315 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5318 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5319 if(!IsHelping(user))
5320 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5322 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5325 if(new_owner->access >= UL_COOWNER)
5326 co_access = new_owner->access;
5328 co_access = UL_COOWNER;
5329 new_owner->access = UL_OWNER;
5331 curr_user->access = co_access;
5332 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5333 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5334 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5338 static CHANSERV_FUNC(cmd_suspend)
5340 struct handle_info *hi;
5341 struct userData *self, *target;
5344 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5345 self = GetChannelUser(channel->channel_info, user->handle_info);
5346 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5348 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5351 if(target->access >= self->access)
5353 reply("MSG_USER_OUTRANKED", hi->handle);
5356 if(target->flags & USER_SUSPENDED)
5358 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5363 target->present = 0;
5366 target->flags |= USER_SUSPENDED;
5367 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5371 static CHANSERV_FUNC(cmd_unsuspend)
5373 struct handle_info *hi;
5374 struct userData *self, *target;
5377 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5378 self = GetChannelUser(channel->channel_info, user->handle_info);
5379 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5381 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5384 if(target->access >= self->access)
5386 reply("MSG_USER_OUTRANKED", hi->handle);
5389 if(!(target->flags & USER_SUSPENDED))
5391 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5394 target->flags &= ~USER_SUSPENDED;
5395 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5399 static MODCMD_FUNC(cmd_deleteme)
5401 struct handle_info *hi;
5402 struct userData *target;
5403 const char *confirm_string;
5404 unsigned short access;
5407 hi = user->handle_info;
5408 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5410 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5413 if(target->access == UL_OWNER)
5415 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5418 confirm_string = make_confirmation_string(target);
5419 if((argc < 2) || strcmp(argv[1], confirm_string))
5421 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5424 access = target->access;
5425 channel_name = strdup(channel->name);
5426 del_channel_user(target, 1);
5427 reply("CSMSG_DELETED_YOU", access, channel_name);
5433 chanserv_refresh_topics(UNUSED_ARG(void *data))
5435 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5436 struct chanData *cData;
5439 for(cData = channelList; cData; cData = cData->next)
5441 if(IsSuspended(cData))
5443 opt = cData->chOpts[chTopicRefresh];
5446 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5449 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5450 cData->last_refresh = refresh_num;
5452 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5455 static CHANSERV_FUNC(cmd_unf)
5459 char response[MAXLEN];
5460 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5461 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5462 irc_privmsg(cmd->parent->bot, channel->name, response);
5465 reply("CSMSG_UNF_RESPONSE");
5469 static CHANSERV_FUNC(cmd_ping)
5473 char response[MAXLEN];
5474 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5475 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5476 irc_privmsg(cmd->parent->bot, channel->name, response);
5479 reply("CSMSG_PING_RESPONSE");
5483 static CHANSERV_FUNC(cmd_wut)
5487 char response[MAXLEN];
5488 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5489 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5490 irc_privmsg(cmd->parent->bot, channel->name, response);
5493 reply("CSMSG_WUT_RESPONSE");
5497 static CHANSERV_FUNC(cmd_8ball)
5499 unsigned int i, j, accum;
5504 for(i=1; i<argc; i++)
5505 for(j=0; argv[i][j]; j++)
5506 accum = (accum << 5) - accum + toupper(argv[i][j]);
5507 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5510 char response[MAXLEN];
5511 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5512 irc_privmsg(cmd->parent->bot, channel->name, response);
5515 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5519 static CHANSERV_FUNC(cmd_d)
5521 unsigned long sides, count, modifier, ii, total;
5522 char response[MAXLEN], *sep;
5526 if((count = strtoul(argv[1], &sep, 10)) <= 1)
5534 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5535 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5539 else if((sep[0] == '-') && isdigit(sep[1]))
5540 modifier = strtoul(sep, NULL, 10);
5541 else if((sep[0] == '+') && isdigit(sep[1]))
5542 modifier = strtoul(sep+1, NULL, 10);
5549 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5554 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5557 for(total = ii = 0; ii < count; ++ii)
5558 total += (rand() % sides) + 1;
5561 if((count > 1) || modifier)
5563 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5564 sprintf(response, fmt, total, count, sides, modifier);
5568 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5569 sprintf(response, fmt, total, sides);
5572 send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5574 send_message_type(4, user, cmd->parent->bot, "%s", response);
5578 static CHANSERV_FUNC(cmd_huggle)
5580 char response[MAXLEN];
5582 /* CTCP must be via PRIVMSG, never notice */
5585 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5586 sprintf(response, fmt, user->nick);
5587 irc_privmsg(cmd->parent->bot, channel->name, response);
5591 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5592 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5598 chanserv_adjust_limit(void *data)
5600 struct mod_chanmode change;
5601 struct chanData *cData = data;
5602 struct chanNode *channel = cData->channel;
5605 if(IsSuspended(cData))
5608 cData->limitAdjusted = now;
5609 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5610 if(cData->modes.modes_set & MODE_LIMIT)
5612 if(limit > cData->modes.new_limit)
5613 limit = cData->modes.new_limit;
5614 else if(limit == cData->modes.new_limit)
5618 change.modes_set = MODE_LIMIT;
5619 change.modes_clear = 0;
5620 change.new_limit = limit;
5622 mod_chanmode_announce(chanserv, channel, &change);
5626 handle_new_channel(struct chanNode *channel)
5628 struct chanData *cData;
5630 if(!(cData = channel->channel_info))
5633 if(cData->modes.modes_set || cData->modes.modes_clear)
5634 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5636 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5637 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5640 /* Welcome to my worst nightmare. Warning: Read (or modify)
5641 the code below at your own risk. */
5643 handle_join(struct modeNode *mNode)
5645 struct mod_chanmode change;
5646 struct userNode *user = mNode->user;
5647 struct chanNode *channel = mNode->channel;
5648 struct chanData *cData;
5649 struct userData *uData = NULL;
5650 struct banData *bData;
5651 struct handle_info *handle;
5652 unsigned int modes = 0, info = 0;
5655 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5658 cData = channel->channel_info;
5659 if(channel->members.used > cData->max)
5660 cData->max = channel->members.used;
5662 /* Check for bans. If they're joining through a ban, one of two
5664 * 1: Join during a netburst, by riding the break. Kick them
5665 * unless they have ops or voice in the channel.
5666 * 2: They're allowed to join through the ban (an invite in
5667 * ircu2.10, or a +e on Hybrid, or something).
5668 * If they're not joining through a ban, and the banlist is not
5669 * full, see if they're on the banlist for the channel. If so,
5672 if(user->uplink->burst && !mNode->modes)
5675 for(ii = 0; ii < channel->banlist.used; ii++)
5677 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5679 /* Riding a netburst. Naughty. */
5680 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5686 change.modes_set = change.modes_clear = 0;
5688 if(channel->banlist.used < MAXBANS)
5690 /* Not joining through a ban. */
5691 for(bData = cData->bans;
5692 bData && !user_matches_glob(user, bData->mask, 1);
5693 bData = bData->next);
5697 char kick_reason[MAXLEN];
5698 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5700 bData->triggered = now;
5701 if(bData != cData->bans)
5703 /* Shuffle the ban to the head of the list. */
5704 if(bData->next) bData->next->prev = bData->prev;
5705 if(bData->prev) bData->prev->next = bData->next;
5708 bData->next = cData->bans;
5711 cData->bans->prev = bData;
5712 cData->bans = bData;
5715 change.args[0].mode = MODE_BAN;
5716 change.args[0].hostmask = bData->mask;
5717 mod_chanmode_announce(chanserv, channel, &change);
5718 KickChannelUser(user, channel, chanserv, kick_reason);
5723 /* ChanServ will not modify the limits in join-flooded channels.
5724 It will also skip DynLimit processing when the user (or srvx)
5725 is bursting in, because there are likely more incoming. */
5726 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5727 && !user->uplink->burst
5728 && !channel->join_flooded
5729 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5731 /* The user count has begun "bumping" into the channel limit,
5732 so set a timer to raise the limit a bit. Any previous
5733 timers are removed so three incoming users within the delay
5734 results in one limit change, not three. */
5736 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5737 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5740 if(cData->lvlOpts[lvlGiveOps] == 0)
5741 modes |= MODE_CHANOP;
5742 else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5743 modes |= MODE_VOICE;
5745 greeting = cData->greeting;
5746 if(user->handle_info)
5748 handle = user->handle_info;
5750 if(IsHelper(user) && !IsHelping(user))
5753 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5755 if(channel == chanserv_conf.support_channels.list[ii])
5757 HANDLE_SET_FLAG(user->handle_info, HELPING);
5763 uData = GetTrueChannelAccess(cData, handle);
5764 if(uData && !IsUserSuspended(uData))
5766 /* Ops and above were handled by the above case. */
5767 if(IsUserAutoOp(uData))
5769 if(uData->access < cData->lvlOpts[lvlGiveOps])
5770 modes |= MODE_VOICE;
5772 modes |= MODE_CHANOP;
5774 if(uData->access >= UL_PRESENT)
5775 cData->visited = now;
5780 if(cData->user_greeting)
5781 greeting = cData->user_greeting;
5783 && (cData->flags & CHANNEL_INFO_LINES)
5784 && ((now - uData->seen) >= chanserv_conf.info_delay)
5789 if(!user->uplink->burst)
5793 change.args[0].mode = modes;
5794 change.args[0].member = mNode;
5795 mod_chanmode_announce(chanserv, channel, &change);
5797 if(greeting && !user->uplink->burst)
5798 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5800 send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5806 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5808 struct mod_chanmode change;
5809 struct userData *channel;
5810 unsigned int ii, jj;
5812 if(!user->handle_info)
5815 change.modes_set = change.modes_clear = 0;
5817 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5819 struct chanNode *cn;
5820 struct modeNode *mn;
5821 if(IsSuspended(channel->channel) || !(cn = channel->channel->channel))
5824 mn = GetUserMode(cn, user);
5827 if(!IsUserSuspended(channel)
5828 && IsUserAutoInvite(channel)
5829 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5831 irc_invite(chanserv, user, cn);
5835 if(channel->access >= UL_PRESENT)
5836 channel->channel->visited = now;
5838 if(IsUserAutoOp(channel))
5840 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5841 change.args[0].mode = MODE_CHANOP;
5843 change.args[0].mode = MODE_VOICE;
5844 change.args[0].member = mn;
5845 mod_chanmode_announce(chanserv, cn, &change);
5848 channel->seen = now;
5849 channel->present = 1;
5852 for(ii = 0; ii < user->channels.used; ++ii)
5854 struct chanNode *channel = user->channels.list[ii]->channel;
5855 struct banData *ban;
5857 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5858 || !channel->channel_info)
5860 for(jj = 0; jj < channel->banlist.used; ++jj)
5861 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5863 if(jj < channel->banlist.used)
5865 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5867 char kick_reason[MAXLEN];
5868 if(!user_matches_glob(user, ban->mask, 1))
5870 change.args[0].mode = MODE_BAN;
5871 change.args[0].hostmask = ban->mask;
5872 mod_chanmode_announce(chanserv, channel, &change);
5873 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5874 KickChannelUser(user, channel, chanserv, kick_reason);
5875 ban->triggered = now;
5880 if(IsSupportHelper(user))
5882 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5884 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5886 HANDLE_SET_FLAG(user->handle_info, HELPING);
5894 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5896 struct chanData *cData;
5897 struct userData *uData;
5898 struct handle_info *handle;
5900 cData = channel->channel_info;
5901 if(!cData || IsSuspended(cData) || IsLocal(user)) return;
5903 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5905 /* Allow for a bit of padding so that the limit doesn't
5906 track the user count exactly, which could get annoying. */
5907 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5909 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5910 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5914 if((handle = user->handle_info) && (uData = GetTrueChannelAccess(cData, handle)))
5917 scan_handle_presence(channel, handle, user);
5920 if(IsHelping(user) && IsSupportHelper(user))
5922 unsigned int ii, jj;
5923 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5925 for(jj = 0; jj < user->channels.used; ++jj)
5926 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5928 if(jj < user->channels.used)
5931 if(ii == chanserv_conf.support_channels.used)
5932 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5937 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5939 char *reason = "CSMSG_USER_PROTECTED";
5941 if(!channel->channel_info || !kicker || IsService(kicker)
5942 || (kicker == victim) || IsSuspended(channel->channel_info)
5943 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5946 if(protect_user(victim, kicker, channel->channel_info))
5947 KickChannelUser(kicker, channel, chanserv, reason);
5951 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5953 struct chanData *cData;
5955 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5957 cData = channel->channel_info;
5958 if(bad_topic(channel, user, channel->topic))
5960 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5961 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5962 SetChannelTopic(channel, chanserv, old_topic, 1);
5963 else if(cData->topic)
5964 SetChannelTopic(channel, chanserv, cData->topic, 1);
5967 /* With topicsnarf, grab the topic and save it as the default topic. */
5968 if(cData->flags & CHANNEL_TOPIC_SNARF)
5971 cData->topic = strdup(channel->topic);
5977 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5979 struct mod_chanmode *bounce = NULL;
5980 unsigned int bnc, ii;
5983 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5986 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5987 && mode_lock_violated(&channel->channel_info->modes, change))
5989 char correct[MAXLEN];
5990 bounce = mod_chanmode_alloc(change->argc + 1);
5991 *bounce = channel->channel_info->modes;
5992 mod_chanmode_format(&channel->channel_info->modes, correct);
5993 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5995 for(ii = bnc = 0; ii < change->argc; ++ii)
5997 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5999 const struct userNode *victim = change->args[ii].member->user;
6000 if(!protect_user(victim, user, channel->channel_info))
6003 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6006 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6007 bounce->args[bnc].member = GetUserMode(channel, user);
6008 if(bounce->args[bnc].member)
6011 bounce->args[bnc].mode = MODE_CHANOP;
6012 bounce->args[bnc].member = change->args[ii].member;
6014 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6016 else if(change->args[ii].mode & MODE_CHANOP)
6018 const struct userNode *victim = change->args[ii].member->user;
6019 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6022 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6023 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6024 bounce->args[bnc].member = change->args[ii].member;
6027 else if(change->args[ii].mode & MODE_BAN)
6029 const char *ban = change->args[ii].hostmask;
6030 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6033 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6034 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6035 bounce->args[bnc].hostmask = ban;
6037 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", remove);
6042 if((bounce->argc = bnc))
6043 mod_chanmode_announce(chanserv, channel, bounce);
6044 mod_chanmode_free(bounce);
6049 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6051 struct chanNode *channel;
6052 struct banData *bData;
6053 struct mod_chanmode change;
6054 unsigned int ii, jj;
6055 char kick_reason[MAXLEN];
6057 change.modes_set = change.modes_clear = 0;
6059 change.args[0].mode = MODE_BAN;
6060 for(ii = 0; ii < user->channels.used; ++ii)
6062 channel = user->channels.list[ii]->channel;
6063 /* Need not check for bans if they're opped or voiced. */
6064 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6066 /* Need not check for bans unless channel registration is active. */
6067 if(!channel->channel_info || IsSuspended(channel->channel_info))
6069 /* Look for a matching ban already on the channel. */
6070 for(jj = 0; jj < channel->banlist.used; ++jj)
6071 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6073 /* Need not act if we found one. */
6074 if(jj < channel->banlist.used)
6076 /* Look for a matching ban in this channel. */
6077 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6079 if(!user_matches_glob(user, bData->mask, 1))
6081 change.args[0].hostmask = bData->mask;
6082 mod_chanmode_announce(chanserv, channel, &change);
6083 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6084 KickChannelUser(user, channel, chanserv, kick_reason);
6085 bData->triggered = now;
6086 break; /* we don't need to check any more bans in the channel */
6091 static void handle_rename(struct handle_info *handle, const char *old_handle)
6093 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6097 dict_remove2(handle_dnrs, old_handle, 1);
6098 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6099 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6104 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6106 struct userNode *h_user;
6108 if(handle->channels)
6110 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6111 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6113 while(handle->channels)
6114 del_channel_user(handle->channels, 1);
6119 handle_server_link(UNUSED_ARG(struct server *server))
6121 struct chanData *cData;
6123 for(cData = channelList; cData; cData = cData->next)
6125 if(!IsSuspended(cData))
6126 cData->may_opchan = 1;
6127 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6128 && !cData->channel->join_flooded
6129 && ((cData->channel->limit - cData->channel->members.used)
6130 < chanserv_conf.adjust_threshold))
6132 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6133 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6139 chanserv_conf_read(void)
6143 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6144 struct mod_chanmode *change;
6145 struct string_list *strlist;
6146 struct chanNode *chan;
6149 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6151 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6154 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6155 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6156 chanserv_conf.support_channels.used = 0;
6157 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6159 for(ii = 0; ii < strlist->used; ++ii)
6161 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6164 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6166 channelList_append(&chanserv_conf.support_channels, chan);
6169 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6172 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6175 chan = AddChannel(str, now, str2, NULL);
6177 channelList_append(&chanserv_conf.support_channels, chan);
6179 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6180 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6181 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6182 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6183 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6184 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6185 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6186 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6187 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6188 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6189 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6190 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6191 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6192 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6193 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6194 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6195 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6196 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6197 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6198 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6199 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6201 NickChange(chanserv, str, 0);
6202 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6203 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6204 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6205 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6206 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6207 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6208 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6209 chanserv_conf.max_owned = str ? atoi(str) : 5;
6210 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6211 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6212 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6213 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6214 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6215 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6216 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6219 safestrncpy(mode_line, str, sizeof(mode_line));
6220 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6221 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6223 chanserv_conf.default_modes = *change;
6224 mod_chanmode_free(change);
6226 free_string_list(chanserv_conf.set_shows);
6227 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6229 strlist = string_list_copy(strlist);
6232 static const char *list[] = {
6233 /* multiple choice options */
6234 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6235 "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6236 "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6237 /* binary options */
6238 "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6242 strlist = alloc_string_list(ArrayLength(list)-1);
6243 for(ii=0; list[ii]; ii++)
6244 string_list_append(strlist, strdup(list[ii]));
6246 chanserv_conf.set_shows = strlist;
6247 /* We don't look things up now, in case the list refers to options
6248 * defined by modules initialized after this point. Just mark the
6249 * function list as invalid, so it will be initialized.
6251 set_shows_list.used = 0;
6252 free_string_list(chanserv_conf.eightball);
6253 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6256 strlist = string_list_copy(strlist);
6260 strlist = alloc_string_list(4);
6261 string_list_append(strlist, strdup("Yes."));
6262 string_list_append(strlist, strdup("No."));
6263 string_list_append(strlist, strdup("Maybe so."));
6265 chanserv_conf.eightball = strlist;
6266 free_string_list(chanserv_conf.old_ban_names);
6267 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6269 strlist = string_list_copy(strlist);
6271 strlist = alloc_string_list(2);
6272 chanserv_conf.old_ban_names = strlist;
6276 chanserv_note_type_read(const char *key, struct record_data *rd)
6279 struct note_type *ntype;
6282 if(!(obj = GET_RECORD_OBJECT(rd)))
6284 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6287 if(!(ntype = chanserv_create_note_type(key)))
6289 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6293 /* Figure out set access */
6294 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6296 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6297 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6299 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6301 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6302 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6304 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6306 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6310 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6311 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6312 ntype->set_access.min_opserv = 0;
6315 /* Figure out visibility */
6316 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6317 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6318 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6319 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6320 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6321 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6322 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6323 ntype->visible_type = NOTE_VIS_ALL;
6325 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6327 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6328 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6332 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6334 struct handle_info *handle;
6335 struct userData *uData;
6336 char *seen, *inf, *flags;
6338 unsigned short access;
6340 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6342 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6346 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6347 if(access > UL_OWNER)
6349 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6353 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6354 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6355 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6356 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6357 handle = get_handle_info(key);
6360 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6364 uData = add_channel_user(chan, handle, access, last_seen, inf);
6365 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6369 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6371 struct banData *bData;
6372 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6373 time_t set_time, triggered_time, expires_time;
6375 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6377 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6381 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6382 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6383 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6384 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6385 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6386 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6388 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6389 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6391 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6393 expires_time = set_time + atoi(s_duration);
6397 if(expires_time && (expires_time < now))
6400 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6403 static struct suspended *
6404 chanserv_read_suspended(dict_t obj)
6406 struct suspended *suspended = calloc(1, sizeof(*suspended));
6410 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6411 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6412 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6413 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6414 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6415 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6416 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6417 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6418 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6419 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6424 chanserv_channel_read(const char *key, struct record_data *hir)
6426 struct suspended *suspended;
6427 struct mod_chanmode *modes;
6428 struct chanNode *cNode;
6429 struct chanData *cData;
6430 struct dict *channel, *obj;
6431 char *str, *argv[10];
6435 channel = hir->d.object;
6437 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6440 cNode = AddChannel(key, now, NULL, NULL);
6443 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6446 cData = register_channel(cNode, str);
6449 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6453 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6455 enum levelOption lvlOpt;
6456 enum charOption chOpt;
6457 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6459 if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6461 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6463 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6465 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6467 cData->chOpts[chOpt] = str[0];
6469 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6470 cData->flags = atoi(str);
6472 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6474 enum levelOption lvlOpt;
6475 enum charOption chOpt;
6478 cData->flags = base64toint(str, 5);
6479 count = strlen(str += 5);
6480 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6483 switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6485 case 'c': lvl = UL_COOWNER; break;
6486 case 'm': lvl = UL_MASTER; break;
6487 case 'n': lvl = UL_OWNER+1; break;
6488 case 'o': lvl = UL_OP; break;
6489 case 'p': lvl = UL_PEON; break;
6490 case 'w': lvl = UL_OWNER; break;
6491 default: lvl = 0; break;
6493 cData->lvlOpts[lvlOpt] = lvl;
6495 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6496 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6499 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6501 suspended = chanserv_read_suspended(obj);
6502 cData->suspended = suspended;
6503 suspended->cData = cData;
6504 /* We could use suspended->expires and suspended->revoked to
6505 * set the CHANNEL_SUSPENDED flag, but we don't. */
6507 else if(cData->flags & CHANNEL_SUSPENDED)
6509 suspended = calloc(1, sizeof(*suspended));
6510 suspended->issued = 0;
6511 suspended->revoked = 0;
6512 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6513 suspended->expires = str ? atoi(str) : 0;
6514 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6515 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6516 suspended->reason = strdup(str ? str : "No reason");
6517 suspended->previous = NULL;
6518 cData->suspended = suspended;
6519 suspended->cData = cData;
6524 if((cData->flags & CHANNEL_SUSPENDED)
6525 && suspended->expires
6526 && (suspended->expires <= now))
6528 cData->flags &= ~CHANNEL_SUSPENDED;
6531 if(!(cData->flags & CHANNEL_SUSPENDED))
6533 struct mod_chanmode change;
6534 change.modes_set = change.modes_clear = 0;
6536 change.args[0].mode = MODE_CHANOP;
6537 change.args[0].member = AddChannelUser(chanserv, cNode);
6538 mod_chanmode_announce(chanserv, cNode, &change);
6540 else if(suspended->expires > now)
6542 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6545 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6546 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6547 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6548 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6549 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6550 cData->max = str ? atoi(str) : 0;
6551 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6552 cData->greeting = str ? strdup(str) : NULL;
6553 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6554 cData->user_greeting = str ? strdup(str) : NULL;
6555 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6556 cData->topic_mask = str ? strdup(str) : NULL;
6557 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6558 cData->topic = str ? strdup(str) : NULL;
6560 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6561 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6562 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6563 cData->modes = *modes;
6564 if(cData->modes.argc > 1)
6565 cData->modes.argc = 1;
6566 if(!IsSuspended(cData))
6567 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6568 mod_chanmode_free(modes);
6571 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6572 for(it = dict_first(obj); it; it = iter_next(it))
6573 user_read_helper(iter_key(it), iter_data(it), cData);
6575 if(!cData->users && !IsProtected(cData))
6577 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6578 unregister_channel(cData, "has empty user list.");
6582 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6583 for(it = dict_first(obj); it; it = iter_next(it))
6584 ban_read_helper(iter_key(it), iter_data(it), cData);
6586 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6587 for(it = dict_first(obj); it; it = iter_next(it))
6589 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6590 struct record_data *rd = iter_data(it);
6591 const char *note, *setter;
6593 if(rd->type != RECDB_OBJECT)
6595 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6599 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6601 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6603 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6607 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6608 if(!setter) setter = "<unknown>";
6609 chanserv_add_channel_note(cData, ntype, setter, note);
6617 chanserv_dnr_read(const char *key, struct record_data *hir)
6619 const char *setter, *reason, *str;
6620 struct do_not_register *dnr;
6622 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6625 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6628 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6631 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6634 dnr = chanserv_add_dnr(key, setter, reason);
6637 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6639 dnr->set = atoi(str);
6645 chanserv_saxdb_read(struct dict *database)
6647 struct dict *section;
6650 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6651 for(it = dict_first(section); it; it = iter_next(it))
6652 chanserv_note_type_read(iter_key(it), iter_data(it));
6654 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6655 for(it = dict_first(section); it; it = iter_next(it))
6656 chanserv_channel_read(iter_key(it), iter_data(it));
6658 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6659 for(it = dict_first(section); it; it = iter_next(it))
6660 chanserv_dnr_read(iter_key(it), iter_data(it));
6666 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6668 int high_present = 0;
6669 saxdb_start_record(ctx, KEY_USERS, 1);
6670 for(; uData; uData = uData->next)
6672 if((uData->access >= UL_PRESENT) && uData->present)
6674 saxdb_start_record(ctx, uData->handle->handle, 0);
6675 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6676 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6678 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6680 saxdb_write_string(ctx, KEY_INFO, uData->info);
6681 saxdb_end_record(ctx);
6683 saxdb_end_record(ctx);
6684 return high_present;
6688 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6692 saxdb_start_record(ctx, KEY_BANS, 1);
6693 for(; bData; bData = bData->next)
6695 saxdb_start_record(ctx, bData->mask, 0);
6696 saxdb_write_int(ctx, KEY_SET, bData->set);
6697 if(bData->triggered)
6698 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6700 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6702 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6704 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6705 saxdb_end_record(ctx);
6707 saxdb_end_record(ctx);
6711 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6713 saxdb_start_record(ctx, name, 0);
6714 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6715 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6717 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6719 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6721 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6723 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6724 saxdb_end_record(ctx);
6728 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6732 enum levelOption lvlOpt;
6733 enum charOption chOpt;
6735 saxdb_start_record(ctx, channel->channel->name, 1);
6737 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6738 saxdb_write_int(ctx, KEY_MAX, channel->max);
6740 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6741 if(channel->registrar)
6742 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6743 if(channel->greeting)
6744 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6745 if(channel->user_greeting)
6746 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6747 if(channel->topic_mask)
6748 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6749 if(channel->suspended)
6750 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6752 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6753 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6754 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6755 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6756 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6758 buf[0] = channel->chOpts[chOpt];
6760 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6762 saxdb_end_record(ctx);
6764 if(channel->modes.modes_set || channel->modes.modes_clear)
6766 mod_chanmode_format(&channel->modes, buf);
6767 saxdb_write_string(ctx, KEY_MODES, buf);
6770 high_present = chanserv_write_users(ctx, channel->users);
6771 chanserv_write_bans(ctx, channel->bans);
6773 if(dict_size(channel->notes))
6777 saxdb_start_record(ctx, KEY_NOTES, 1);
6778 for(it = dict_first(channel->notes); it; it = iter_next(it))
6780 struct note *note = iter_data(it);
6781 saxdb_start_record(ctx, iter_key(it), 0);
6782 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6783 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6784 saxdb_end_record(ctx);
6786 saxdb_end_record(ctx);
6789 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6790 saxdb_end_record(ctx);
6794 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6798 saxdb_start_record(ctx, ntype->name, 0);
6799 switch(ntype->set_access_type)
6801 case NOTE_SET_CHANNEL_ACCESS:
6802 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6804 case NOTE_SET_CHANNEL_SETTER:
6805 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6807 case NOTE_SET_PRIVILEGED: default:
6808 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6811 switch(ntype->visible_type)
6813 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6814 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6815 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6817 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6818 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6819 saxdb_end_record(ctx);
6823 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6825 struct do_not_register *dnr;
6828 for(it = dict_first(dnrs); it; it = iter_next(it))
6830 dnr = iter_data(it);
6831 saxdb_start_record(ctx, dnr->chan_name, 0);
6833 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6834 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6835 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6836 saxdb_end_record(ctx);
6841 chanserv_saxdb_write(struct saxdb_context *ctx)
6844 struct chanData *channel;
6847 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6848 for(it = dict_first(note_types); it; it = iter_next(it))
6849 chanserv_write_note_type(ctx, iter_data(it));
6850 saxdb_end_record(ctx);
6853 saxdb_start_record(ctx, KEY_DNR, 1);
6854 write_dnrs_helper(ctx, handle_dnrs);
6855 write_dnrs_helper(ctx, plain_dnrs);
6856 write_dnrs_helper(ctx, mask_dnrs);
6857 saxdb_end_record(ctx);
6860 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6861 for(channel = channelList; channel; channel = channel->next)
6862 chanserv_write_channel(ctx, channel);
6863 saxdb_end_record(ctx);
6869 chanserv_db_cleanup(void) {
6871 unreg_part_func(handle_part);
6873 unregister_channel(channelList, "terminating.");
6874 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6875 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6876 free(chanserv_conf.support_channels.list);
6877 dict_delete(handle_dnrs);
6878 dict_delete(plain_dnrs);
6879 dict_delete(mask_dnrs);
6880 dict_delete(note_types);
6881 free_string_list(chanserv_conf.eightball);
6882 free_string_list(chanserv_conf.old_ban_names);
6883 free_string_list(chanserv_conf.set_shows);
6884 free(set_shows_list.list);
6885 free(uset_shows_list.list);
6888 struct userData *helper = helperList;
6889 helperList = helperList->next;
6894 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6895 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6896 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6899 init_chanserv(const char *nick)
6901 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6902 conf_register_reload(chanserv_conf_read);
6904 reg_server_link_func(handle_server_link);
6906 reg_new_channel_func(handle_new_channel);
6907 reg_join_func(handle_join);
6908 reg_part_func(handle_part);
6909 reg_kick_func(handle_kick);
6910 reg_topic_func(handle_topic);
6911 reg_mode_change_func(handle_mode);
6912 reg_nick_change_func(handle_nick_change);
6914 reg_auth_func(handle_auth);
6915 reg_handle_rename_func(handle_rename);
6916 reg_unreg_func(handle_unreg);
6918 handle_dnrs = dict_new();
6919 dict_set_free_data(handle_dnrs, free);
6920 plain_dnrs = dict_new();
6921 dict_set_free_data(plain_dnrs, free);
6922 mask_dnrs = dict_new();
6923 dict_set_free_data(mask_dnrs, free);
6925 reg_svccmd_unbind_func(handle_svccmd_unbind);
6926 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6927 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6928 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6929 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6930 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6931 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6932 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6933 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6934 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6936 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6937 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6939 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6940 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6941 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6942 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6943 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6945 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6946 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6947 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6948 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6949 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6951 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6952 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6953 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6954 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6956 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6957 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6958 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6959 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6960 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6961 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6962 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6963 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6965 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6966 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6967 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6968 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6969 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6970 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6971 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6972 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6973 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6974 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6975 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6976 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6977 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6978 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6980 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
6981 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6982 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6983 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6984 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6986 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6987 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6989 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6990 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6991 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6992 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6993 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6994 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6995 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6996 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6997 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6998 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7000 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7001 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7003 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7004 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7005 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7006 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7008 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7009 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7010 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7011 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7012 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7014 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7015 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7016 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7017 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7018 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7019 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7021 /* Channel options */
7022 DEFINE_CHANNEL_OPTION(defaulttopic);
7023 DEFINE_CHANNEL_OPTION(topicmask);
7024 DEFINE_CHANNEL_OPTION(greeting);
7025 DEFINE_CHANNEL_OPTION(usergreeting);
7026 DEFINE_CHANNEL_OPTION(modes);
7027 DEFINE_CHANNEL_OPTION(enfops);
7028 DEFINE_CHANNEL_OPTION(giveops);
7029 DEFINE_CHANNEL_OPTION(protect);
7030 DEFINE_CHANNEL_OPTION(enfmodes);
7031 DEFINE_CHANNEL_OPTION(enftopic);
7032 DEFINE_CHANNEL_OPTION(pubcmd);
7033 DEFINE_CHANNEL_OPTION(voice);
7034 DEFINE_CHANNEL_OPTION(userinfo);
7035 DEFINE_CHANNEL_OPTION(dynlimit);
7036 DEFINE_CHANNEL_OPTION(topicsnarf);
7037 DEFINE_CHANNEL_OPTION(nodelete);
7038 DEFINE_CHANNEL_OPTION(toys);
7039 DEFINE_CHANNEL_OPTION(setters);
7040 DEFINE_CHANNEL_OPTION(topicrefresh);
7041 DEFINE_CHANNEL_OPTION(ctcpusers);
7042 DEFINE_CHANNEL_OPTION(ctcpreaction);
7043 DEFINE_CHANNEL_OPTION(peoninvite);
7044 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7046 /* Alias set topic to set defaulttopic for compatibility. */
7047 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7050 DEFINE_USER_OPTION(noautoop);
7051 DEFINE_USER_OPTION(autoinvite);
7052 DEFINE_USER_OPTION(info);
7054 /* Alias uset autovoice to uset autoop. */
7055 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7057 note_types = dict_new();
7058 dict_set_free_data(note_types, chanserv_deref_note_type);
7061 chanserv = AddService(nick, "Channel Services");
7062 service_register(chanserv, '!');
7063 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7065 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7067 if(chanserv_conf.channel_expire_frequency)
7068 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7070 if(chanserv_conf.refresh_period)
7072 time_t next_refresh;
7073 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7074 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7077 reg_exit_func(chanserv_db_cleanup);
7078 message_register_table(msgtab);