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_user_presence(struct userData *uData, struct userNode *user)
754 if(IsSuspended(uData->channel)
755 || IsUserSuspended(uData)
756 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
768 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
770 unsigned int eflags, argc;
772 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
774 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
775 if(!channel->channel_info
776 || IsSuspended(channel->channel_info)
778 || !ircncasecmp(text, "ACTION ", 7))
780 /* Figure out the minimum level needed to CTCP the channel */
781 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
783 /* We need to enforce against them; do so. */
786 argv[1] = user->nick;
788 if(GetUserMode(channel, user))
789 eflags |= ACTION_KICK;
790 switch(channel->channel_info->chOpts[chCTCPReaction]) {
791 default: case 'k': /* just do the kick */ break;
793 eflags |= ACTION_BAN;
796 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
797 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
800 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
801 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
804 argv[argc++] = bad_ctcp_reason;
805 eject_user(chanserv, channel, argc, argv, NULL, eflags);
809 chanserv_create_note_type(const char *name)
811 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
812 strcpy(ntype->name, name);
814 dict_insert(note_types, ntype->name, ntype);
819 chanserv_deref_note_type(void *data)
821 struct note_type *ntype = data;
823 if(--ntype->refs > 0)
829 chanserv_flush_note_type(struct note_type *ntype)
831 struct chanData *cData;
832 for(cData = channelList; cData; cData = cData->next)
833 dict_remove(cData->notes, ntype->name);
837 chanserv_truncate_notes(struct note_type *ntype)
839 struct chanData *cData;
841 unsigned int size = sizeof(*note) + ntype->max_length;
843 for(cData = channelList; cData; cData = cData->next) {
844 note = dict_find(cData->notes, ntype->name, NULL);
847 if(strlen(note->note) <= ntype->max_length)
849 dict_remove2(cData->notes, ntype->name, 1);
850 note = realloc(note, size);
851 note->note[ntype->max_length] = 0;
852 dict_insert(cData->notes, ntype->name, note);
856 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
859 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
862 unsigned int len = strlen(text);
864 if(len > type->max_length) len = type->max_length;
865 note = calloc(1, sizeof(*note) + len);
867 strncpy(note->setter, setter, sizeof(note->setter)-1);
868 memcpy(note->note, text, len);
870 dict_insert(channel->notes, type->name, note);
876 chanserv_free_note(void *data)
878 struct note *note = data;
880 chanserv_deref_note_type(note->type);
881 assert(note->type->refs > 0); /* must use delnote to remove the type */
885 static MODCMD_FUNC(cmd_createnote) {
886 struct note_type *ntype;
887 unsigned int arg = 1, existed = 0, max_length;
889 if((ntype = dict_find(note_types, argv[1], NULL)))
892 ntype = chanserv_create_note_type(argv[arg]);
893 if(!irccasecmp(argv[++arg], "privileged"))
896 ntype->set_access_type = NOTE_SET_PRIVILEGED;
897 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
899 else if(!irccasecmp(argv[arg], "channel"))
901 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
904 reply("CSMSG_INVALID_ACCESS", argv[arg]);
907 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
908 ntype->set_access.min_ulevel = ulvl;
910 else if(!irccasecmp(argv[arg], "setter"))
912 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
916 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
920 if(!irccasecmp(argv[++arg], "privileged"))
921 ntype->visible_type = NOTE_VIS_PRIVILEGED;
922 else if(!irccasecmp(argv[arg], "channel_users"))
923 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
924 else if(!irccasecmp(argv[arg], "all"))
925 ntype->visible_type = NOTE_VIS_ALL;
927 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
931 if((arg+1) >= argc) {
932 reply("MSG_MISSING_PARAMS", argv[0]);
935 max_length = strtoul(argv[++arg], NULL, 0);
936 if(max_length < 20 || max_length > 450)
938 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
941 if(existed && (max_length < ntype->max_length))
943 ntype->max_length = max_length;
944 chanserv_truncate_notes(ntype);
946 ntype->max_length = max_length;
949 reply("CSMSG_NOTE_MODIFIED", ntype->name);
951 reply("CSMSG_NOTE_CREATED", ntype->name);
956 dict_remove(note_types, ntype->name);
960 static MODCMD_FUNC(cmd_removenote) {
961 struct note_type *ntype;
964 ntype = dict_find(note_types, argv[1], NULL);
965 force = (argc > 2) && !irccasecmp(argv[2], "force");
968 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
975 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
978 chanserv_flush_note_type(ntype);
980 dict_remove(note_types, argv[1]);
981 reply("CSMSG_NOTE_DELETED", argv[1]);
986 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
990 if(orig->modes_set & change->modes_clear)
992 if(orig->modes_clear & change->modes_set)
994 if((orig->modes_set & MODE_KEY)
995 && strcmp(orig->new_key, change->new_key))
997 if((orig->modes_set & MODE_LIMIT)
998 && (orig->new_limit != change->new_limit))
1003 static char max_length_text[MAXLEN+1][16];
1005 static struct helpfile_expansion
1006 chanserv_expand_variable(const char *variable)
1008 struct helpfile_expansion exp;
1010 if(!irccasecmp(variable, "notes"))
1013 exp.type = HF_TABLE;
1014 exp.value.table.length = 1;
1015 exp.value.table.width = 3;
1016 exp.value.table.flags = 0;
1017 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1018 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1019 exp.value.table.contents[0][0] = "Note Type";
1020 exp.value.table.contents[0][1] = "Visibility";
1021 exp.value.table.contents[0][2] = "Max Length";
1022 for(it=dict_first(note_types); it; it=iter_next(it))
1024 struct note_type *ntype = iter_data(it);
1027 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1028 row = exp.value.table.length++;
1029 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1030 exp.value.table.contents[row][0] = ntype->name;
1031 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1032 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1034 if(!max_length_text[ntype->max_length][0])
1035 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1036 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1041 exp.type = HF_STRING;
1042 exp.value.str = NULL;
1046 static struct chanData*
1047 register_channel(struct chanNode *cNode, char *registrar)
1049 struct chanData *channel;
1050 enum levelOption lvlOpt;
1051 enum charOption chOpt;
1053 channel = calloc(1, sizeof(struct chanData));
1055 channel->notes = dict_new();
1056 dict_set_free_data(channel->notes, chanserv_free_note);
1058 channel->registrar = strdup(registrar);
1059 channel->registered = now;
1060 channel->visited = now;
1061 channel->limitAdjusted = now;
1062 channel->flags = CHANNEL_DEFAULT_FLAGS;
1063 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1064 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1065 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1066 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1068 channel->prev = NULL;
1069 channel->next = channelList;
1072 channelList->prev = channel;
1073 channelList = channel;
1074 registered_channels++;
1076 channel->channel = cNode;
1078 cNode->channel_info = channel;
1083 static struct userData*
1084 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1086 struct userData *ud;
1088 if(access > UL_OWNER)
1091 ud = calloc(1, sizeof(*ud));
1092 ud->channel = channel;
1093 ud->handle = handle;
1095 ud->access = access;
1096 ud->info = info ? strdup(info) : NULL;
1099 ud->next = channel->users;
1101 channel->users->prev = ud;
1102 channel->users = ud;
1104 channel->userCount++;
1108 ud->u_next = ud->handle->channels;
1110 ud->u_next->u_prev = ud;
1111 ud->handle->channels = ud;
1116 static void unregister_channel(struct chanData *channel, const char *reason);
1119 del_channel_user(struct userData *user, int do_gc)
1121 struct chanData *channel = user->channel;
1123 channel->userCount--;
1127 user->prev->next = user->next;
1129 channel->users = user->next;
1131 user->next->prev = user->prev;
1134 user->u_prev->u_next = user->u_next;
1136 user->handle->channels = user->u_next;
1138 user->u_next->u_prev = user->u_prev;
1142 if(do_gc && !channel->users && !IsProtected(channel))
1143 unregister_channel(channel, "lost all users.");
1146 static void expire_ban(void *data);
1148 static struct banData*
1149 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1152 unsigned int ii, l1, l2;
1157 bd = malloc(sizeof(struct banData));
1159 bd->channel = channel;
1161 bd->triggered = triggered;
1162 bd->expires = expires;
1164 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1166 extern const char *hidden_host_suffix;
1167 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1171 l2 = strlen(old_name);
1174 if(irccasecmp(mask + l1 - l2, old_name))
1176 new_mask = alloca(MAXLEN);
1177 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1180 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1182 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1183 bd->reason = reason ? strdup(reason) : NULL;
1186 timeq_add(expires, expire_ban, bd);
1189 bd->next = channel->bans;
1191 channel->bans->prev = bd;
1193 channel->banCount++;
1200 del_channel_ban(struct banData *ban)
1202 ban->channel->banCount--;
1206 ban->prev->next = ban->next;
1208 ban->channel->bans = ban->next;
1211 ban->next->prev = ban->prev;
1214 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1223 expire_ban(void *data)
1225 struct banData *bd = data;
1226 if(!IsSuspended(bd->channel))
1228 struct banList bans;
1229 struct mod_chanmode change;
1231 bans = bd->channel->channel->banlist;
1232 change.modes_set = change.modes_clear = 0;
1234 for(ii=0; ii<bans.used; ii++)
1236 if(!strcmp(bans.list[ii]->ban, bd->mask))
1239 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1240 change.args[0].hostmask = bd->mask;
1241 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1247 del_channel_ban(bd);
1250 static void chanserv_expire_suspension(void *data);
1253 unregister_channel(struct chanData *channel, const char *reason)
1255 char msgbuf[MAXLEN];
1257 /* After channel unregistration, the following must be cleaned
1259 - Channel information.
1262 - Channel suspension data.
1263 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1269 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1271 while(channel->users)
1272 del_channel_user(channel->users, 0);
1274 while(channel->bans)
1275 del_channel_ban(channel->bans);
1277 if(channel->topic) free(channel->topic);
1278 if(channel->registrar) free(channel->registrar);
1279 if(channel->greeting) free(channel->greeting);
1280 if(channel->user_greeting) free(channel->user_greeting);
1281 if(channel->topic_mask) free(channel->topic_mask);
1283 if(channel->prev) channel->prev->next = channel->next;
1284 else channelList = channel->next;
1286 if(channel->next) channel->next->prev = channel->prev;
1288 if(channel->suspended)
1290 struct chanNode *cNode = channel->channel;
1291 struct suspended *suspended, *next_suspended;
1293 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1295 next_suspended = suspended->previous;
1296 free(suspended->suspender);
1297 free(suspended->reason);
1298 if(suspended->expires)
1299 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1304 cNode->channel_info = NULL;
1306 channel->channel->channel_info = NULL;
1309 dict_delete(channel->notes);
1310 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1311 if(!IsSuspended(channel))
1312 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1313 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1314 UnlockChannel(channel->channel);
1316 registered_channels--;
1320 expire_channels(UNUSED_ARG(void *data))
1322 struct chanData *channel, *next;
1323 struct userData *user;
1324 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1326 intervalString(delay, chanserv_conf.channel_expire_delay);
1327 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1329 for(channel = channelList; channel; channel = next)
1331 next = channel->next;
1333 /* See if the channel can be expired. */
1334 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1335 || IsProtected(channel))
1338 /* Make sure there are no high-ranking users still in the channel. */
1339 for(user=channel->users; user; user=user->next)
1340 if(user->present && (user->access >= UL_PRESENT))
1345 /* Unregister the channel */
1346 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1347 unregister_channel(channel, "registration expired.");
1350 if(chanserv_conf.channel_expire_frequency)
1351 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1355 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1357 char protect = channel->chOpts[chProtect];
1358 struct userData *cs_victim, *cs_aggressor;
1360 /* Don't protect if no one is to be protected, someone is attacking
1361 himself, or if the aggressor is an IRC Operator. */
1362 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1365 /* Don't protect if the victim isn't authenticated (because they
1366 can't be a channel user), unless we are to protect non-users
1368 cs_victim = GetChannelAccess(channel, victim->handle_info);
1369 if(protect != 'a' && !cs_victim)
1372 /* Protect if the aggressor isn't a user because at this point,
1373 the aggressor can only be less than or equal to the victim. */
1374 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1378 /* If the aggressor was a user, then the victim can't be helped. */
1385 if(cs_victim->access > cs_aggressor->access)
1390 if(cs_victim->access >= cs_aggressor->access)
1399 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1401 struct chanData *cData = channel->channel_info;
1402 struct userData *cs_victim;
1404 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1405 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1406 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1408 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1416 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1418 if(IsService(victim))
1420 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1424 if(protect_user(victim, user, channel->channel_info))
1426 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1433 static struct do_not_register *
1434 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1436 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1437 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1438 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1439 strcpy(dnr->reason, reason);
1441 if(dnr->chan_name[0] == '*')
1442 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1443 else if(strpbrk(dnr->chan_name, "*?"))
1444 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1446 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1450 static struct dnrList
1451 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1453 struct dnrList list;
1455 struct do_not_register *dnr;
1457 dnrList_init(&list);
1458 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1459 dnrList_append(&list, dnr);
1460 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1461 dnrList_append(&list, dnr);
1463 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1464 if(match_ircglob(chan_name, iter_key(it)))
1465 dnrList_append(&list, iter_data(it));
1470 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1472 struct dnrList list;
1473 struct do_not_register *dnr;
1475 char buf[INTERVALLEN];
1477 list = chanserv_find_dnrs(chan_name, handle);
1478 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1480 dnr = list.list[ii];
1483 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1484 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1487 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1490 reply("CSMSG_MORE_DNRS", list.used - ii);
1495 struct do_not_register *
1496 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1498 struct do_not_register *dnr;
1501 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1505 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1507 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1508 if(match_ircglob(chan_name, iter_key(it)))
1509 return iter_data(it);
1514 static CHANSERV_FUNC(cmd_noregister)
1517 struct do_not_register *dnr;
1518 char buf[INTERVALLEN];
1519 unsigned int matches;
1525 reply("CSMSG_DNR_SEARCH_RESULTS");
1527 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1529 dnr = iter_data(it);
1531 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1533 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1536 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1538 dnr = iter_data(it);
1540 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1542 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1545 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1547 dnr = iter_data(it);
1549 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1551 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1556 reply("MSG_MATCH_COUNT", matches);
1558 reply("MSG_NO_MATCHES");
1564 if(!IsChannelName(target) && (*target != '*'))
1566 reply("CSMSG_NOT_DNR", target);
1572 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1573 if((*target == '*') && !get_handle_info(target + 1))
1575 reply("MSG_HANDLE_UNKNOWN", target + 1);
1578 chanserv_add_dnr(target, user->handle_info->handle, reason);
1579 reply("CSMSG_NOREGISTER_CHANNEL", target);
1583 reply("CSMSG_DNR_SEARCH_RESULTS");
1585 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1587 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1589 reply("MSG_NO_MATCHES");
1593 static CHANSERV_FUNC(cmd_allowregister)
1595 const char *chan_name = argv[1];
1597 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1599 dict_remove(handle_dnrs, chan_name+1);
1600 reply("CSMSG_DNR_REMOVED", chan_name);
1602 else if(dict_find(plain_dnrs, chan_name, NULL))
1604 dict_remove(plain_dnrs, chan_name);
1605 reply("CSMSG_DNR_REMOVED", chan_name);
1607 else if(dict_find(mask_dnrs, chan_name, NULL))
1609 dict_remove(mask_dnrs, chan_name);
1610 reply("CSMSG_DNR_REMOVED", chan_name);
1614 reply("CSMSG_NO_SUCH_DNR", chan_name);
1621 chanserv_get_owned_count(struct handle_info *hi)
1623 struct userData *cList;
1626 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1627 if(cList->access == UL_OWNER)
1632 static CHANSERV_FUNC(cmd_register)
1634 struct mod_chanmode *change;
1635 struct handle_info *handle;
1636 struct chanData *cData;
1637 struct modeNode *mn;
1638 char reason[MAXLEN];
1640 unsigned int new_channel, force=0;
1641 struct do_not_register *dnr;
1645 if(channel->channel_info)
1647 reply("CSMSG_ALREADY_REGGED", channel->name);
1651 if(channel->bad_channel)
1653 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1657 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1659 reply("CSMSG_MUST_BE_OPPED", channel->name);
1664 chan_name = channel->name;
1668 if((argc < 2) || !IsChannelName(argv[1]))
1670 reply("MSG_NOT_CHANNEL_NAME");
1674 if(opserv_bad_channel(argv[1]))
1676 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1681 chan_name = argv[1];
1684 if(argc >= (new_channel+2))
1686 if(!IsHelping(user))
1688 reply("CSMSG_PROXY_FORBIDDEN");
1692 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1694 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1695 dnr = chanserv_is_dnr(chan_name, handle);
1699 handle = user->handle_info;
1700 dnr = chanserv_is_dnr(chan_name, handle);
1704 if(!IsHelping(user))
1705 reply("CSMSG_DNR_CHANNEL", chan_name);
1707 chanserv_show_dnrs(user, cmd, chan_name, handle);
1711 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1713 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1718 channel = AddChannel(argv[1], now, NULL, NULL);
1720 cData = register_channel(channel, user->handle_info->handle);
1721 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1722 cData->modes = chanserv_conf.default_modes;
1723 change = mod_chanmode_dup(&cData->modes, 1);
1724 change->args[change->argc].mode = MODE_CHANOP;
1725 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1727 mod_chanmode_announce(chanserv, channel, change);
1728 mod_chanmode_free(change);
1730 /* Initialize the channel's max user record. */
1731 cData->max = channel->members.used;
1733 if(handle != user->handle_info)
1734 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1736 reply("CSMSG_REG_SUCCESS", channel->name);
1738 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1739 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1744 make_confirmation_string(struct userData *uData)
1746 static char strbuf[16];
1751 for(src = uData->handle->handle; *src; )
1752 accum = accum * 31 + toupper(*src++);
1754 for(src = uData->channel->channel->name; *src; )
1755 accum = accum * 31 + toupper(*src++);
1756 sprintf(strbuf, "%08x", accum);
1760 static CHANSERV_FUNC(cmd_unregister)
1763 char reason[MAXLEN];
1764 struct chanData *cData;
1765 struct userData *uData;
1767 cData = channel->channel_info;
1770 reply("CSMSG_NOT_REGISTERED", channel->name);
1774 uData = GetChannelUser(cData, user->handle_info);
1775 if(!uData || (uData->access < UL_OWNER))
1777 reply("CSMSG_NO_ACCESS");
1781 if(IsProtected(cData))
1783 reply("CSMSG_UNREG_NODELETE", channel->name);
1787 if(!IsHelping(user))
1789 const char *confirm_string;
1790 if(IsSuspended(cData))
1792 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1795 confirm_string = make_confirmation_string(uData);
1796 if((argc < 2) || strcmp(argv[1], confirm_string))
1798 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1803 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1804 name = strdup(channel->name);
1805 unregister_channel(cData, reason);
1806 reply("CSMSG_UNREG_SUCCESS", name);
1811 static CHANSERV_FUNC(cmd_move)
1813 struct chanNode *target;
1814 struct modeNode *mn;
1815 struct userData *uData;
1816 char reason[MAXLEN];
1817 struct do_not_register *dnr;
1821 if(IsProtected(channel->channel_info))
1823 reply("CSMSG_MOVE_NODELETE", channel->name);
1827 if(!IsChannelName(argv[1]))
1829 reply("MSG_NOT_CHANNEL_NAME");
1833 if(opserv_bad_channel(argv[1]))
1835 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1839 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1841 for(uData = channel->channel_info->users; uData; uData = uData->next)
1843 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1845 if(!IsHelping(user))
1846 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1848 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1854 if(!(target = GetChannel(argv[1])))
1856 target = AddChannel(argv[1], now, NULL, NULL);
1857 LockChannel(target);
1858 if(!IsSuspended(channel->channel_info))
1859 AddChannelUser(chanserv, target);
1861 else if(target->channel_info)
1863 reply("CSMSG_ALREADY_REGGED", target->name);
1866 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1867 && !IsHelping(user))
1869 reply("CSMSG_MUST_BE_OPPED", target->name);
1872 else if(!IsSuspended(channel->channel_info))
1874 struct mod_chanmode change;
1875 change.modes_set = change.modes_clear = 0;
1877 change.args[0].mode = MODE_CHANOP;
1878 change.args[0].member = AddChannelUser(chanserv, target);
1879 mod_chanmode_announce(chanserv, target, &change);
1882 /* Move the channel_info to the target channel; it
1883 shouldn't be necessary to clear timeq callbacks
1884 for the old channel. */
1885 target->channel_info = channel->channel_info;
1886 target->channel_info->channel = target;
1887 channel->channel_info = NULL;
1889 reply("CSMSG_MOVE_SUCCESS", target->name);
1891 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1892 if(!IsSuspended(target->channel_info))
1894 char reason2[MAXLEN];
1895 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1896 DelChannelUser(chanserv, channel, reason2, 0);
1898 UnlockChannel(channel);
1899 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1904 merge_users(struct chanData *source, struct chanData *target)
1906 struct userData *suData, *tuData, *next;
1912 /* Insert the source's users into the scratch area. */
1913 for(suData = source->users; suData; suData = suData->next)
1914 dict_insert(merge, suData->handle->handle, suData);
1916 /* Iterate through the target's users, looking for
1917 users common to both channels. The lower access is
1918 removed from either the scratch area or target user
1920 for(tuData = target->users; tuData; tuData = next)
1922 struct userData *choice;
1924 next = tuData->next;
1926 /* If a source user exists with the same handle as a target
1927 channel's user, resolve the conflict by removing one. */
1928 suData = dict_find(merge, tuData->handle->handle, NULL);
1932 /* Pick the data we want to keep. */
1933 /* If the access is the same, use the later seen time. */
1934 if(suData->access == tuData->access)
1935 choice = (suData->seen > tuData->seen) ? suData : tuData;
1936 else /* Otherwise, keep the higher access level. */
1937 choice = (suData->access > tuData->access) ? suData : tuData;
1939 /* Remove the user that wasn't picked. */
1940 if(choice == tuData)
1942 dict_remove(merge, suData->handle->handle);
1943 del_channel_user(suData, 0);
1946 del_channel_user(tuData, 0);
1949 /* Move the remaining users to the target channel. */
1950 for(it = dict_first(merge); it; it = iter_next(it))
1952 suData = iter_data(it);
1954 /* Insert the user into the target channel's linked list. */
1955 suData->prev = NULL;
1956 suData->next = target->users;
1957 suData->channel = target;
1960 target->users->prev = suData;
1961 target->users = suData;
1963 /* Update the user counts for the target channel; the
1964 source counts are left alone. */
1965 target->userCount++;
1968 /* Possible to assert (source->users == NULL) here. */
1969 source->users = NULL;
1974 merge_bans(struct chanData *source, struct chanData *target)
1976 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1978 /* Hold on to the original head of the target ban list
1979 to avoid comparing source bans with source bans. */
1980 tFront = target->bans;
1982 /* Perform a totally expensive O(n*m) merge, ick. */
1983 for(sbData = source->bans; sbData; sbData = sNext)
1985 /* Flag to track whether the ban's been moved
1986 to the destination yet. */
1989 /* Possible to assert (sbData->prev == NULL) here. */
1990 sNext = sbData->next;
1992 for(tbData = tFront; tbData; tbData = tNext)
1994 tNext = tbData->next;
1996 /* Perform two comparisons between each source
1997 and target ban, conflicts are resolved by
1998 keeping the broader ban and copying the later
1999 expiration and triggered time. */
2000 if(match_ircglobs(tbData->mask, sbData->mask))
2002 /* There is a broader ban in the target channel that
2003 overrides one in the source channel; remove the
2004 source ban and break. */
2005 if(sbData->expires > tbData->expires)
2006 tbData->expires = sbData->expires;
2007 if(sbData->triggered > tbData->triggered)
2008 tbData->triggered = sbData->triggered;
2009 del_channel_ban(sbData);
2012 else if(match_ircglobs(sbData->mask, tbData->mask))
2014 /* There is a broader ban in the source channel that
2015 overrides one in the target channel; remove the
2016 target ban, fall through and move the source over. */
2017 if(tbData->expires > sbData->expires)
2018 sbData->expires = tbData->expires;
2019 if(tbData->triggered > sbData->triggered)
2020 sbData->triggered = tbData->triggered;
2021 if(tbData == tFront)
2023 del_channel_ban(tbData);
2026 /* Source bans can override multiple target bans, so
2027 we allow a source to run through this loop multiple
2028 times, but we can only move it once. */
2033 /* Remove the source ban from the source ban list. */
2035 sbData->next->prev = sbData->prev;
2037 /* Modify the source ban's associated channel. */
2038 sbData->channel = target;
2040 /* Insert the ban into the target channel's linked list. */
2041 sbData->prev = NULL;
2042 sbData->next = target->bans;
2045 target->bans->prev = sbData;
2046 target->bans = sbData;
2048 /* Update the user counts for the target channel. */
2053 /* Possible to assert (source->bans == NULL) here. */
2054 source->bans = NULL;
2058 merge_data(struct chanData *source, struct chanData *target)
2060 if(source->visited > target->visited)
2061 target->visited = source->visited;
2065 merge_channel(struct chanData *source, struct chanData *target)
2067 merge_users(source, target);
2068 merge_bans(source, target);
2069 merge_data(source, target);
2072 static CHANSERV_FUNC(cmd_merge)
2074 struct userData *target_user;
2075 struct chanNode *target;
2076 char reason[MAXLEN];
2080 /* Make sure the target channel exists and is registered to the user
2081 performing the command. */
2082 if(!(target = GetChannel(argv[1])))
2084 reply("MSG_INVALID_CHANNEL");
2088 if(!target->channel_info)
2090 reply("CSMSG_NOT_REGISTERED", target->name);
2094 if(IsProtected(channel->channel_info))
2096 reply("CSMSG_MERGE_NODELETE");
2100 if(IsSuspended(target->channel_info))
2102 reply("CSMSG_MERGE_SUSPENDED");
2106 if(channel == target)
2108 reply("CSMSG_MERGE_SELF");
2112 target_user = GetChannelUser(target->channel_info, user->handle_info);
2113 if(!target_user || (target_user->access < UL_OWNER))
2115 reply("CSMSG_MERGE_NOT_OWNER");
2119 /* Merge the channel structures and associated data. */
2120 merge_channel(channel->channel_info, target->channel_info);
2121 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2122 unregister_channel(channel->channel_info, reason);
2123 reply("CSMSG_MERGE_SUCCESS", target->name);
2127 static CHANSERV_FUNC(cmd_opchan)
2129 struct mod_chanmode change;
2130 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2132 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2135 channel->channel_info->may_opchan = 0;
2136 change.modes_set = change.modes_clear = 0;
2138 change.args[0].mode = MODE_CHANOP;
2139 change.args[0].member = GetUserMode(channel, chanserv);
2140 mod_chanmode_announce(chanserv, channel, &change);
2141 reply("CSMSG_OPCHAN_DONE", channel->name);
2145 static CHANSERV_FUNC(cmd_adduser)
2147 struct userData *actee;
2148 struct userData *actor;
2149 struct handle_info *handle;
2150 unsigned short access;
2154 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2156 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2160 access = user_level_from_name(argv[2], UL_OWNER);
2163 reply("CSMSG_INVALID_ACCESS", argv[2]);
2167 actor = GetChannelUser(channel->channel_info, user->handle_info);
2168 if(actor->access <= access)
2170 reply("CSMSG_NO_BUMP_ACCESS");
2174 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2177 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2179 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2183 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2184 scan_user_presence(actee, NULL);
2185 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2189 static CHANSERV_FUNC(cmd_clvl)
2191 struct handle_info *handle;
2192 struct userData *victim;
2193 struct userData *actor;
2194 unsigned short new_access;
2195 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2199 actor = GetChannelUser(channel->channel_info, user->handle_info);
2201 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2204 if(handle == user->handle_info && !privileged)
2206 reply("CSMSG_NO_SELF_CLVL");
2210 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2212 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2216 if(actor->access <= victim->access && !privileged)
2218 reply("MSG_USER_OUTRANKED", handle->handle);
2222 new_access = user_level_from_name(argv[2], UL_OWNER);
2226 reply("CSMSG_INVALID_ACCESS", argv[2]);
2230 if(new_access >= actor->access && !privileged)
2232 reply("CSMSG_NO_BUMP_ACCESS");
2236 victim->access = new_access;
2237 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2241 static CHANSERV_FUNC(cmd_deluser)
2243 struct handle_info *handle;
2244 struct userData *victim;
2245 struct userData *actor;
2246 unsigned short access;
2251 actor = GetChannelUser(channel->channel_info, user->handle_info);
2253 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2256 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2258 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2264 access = user_level_from_name(argv[1], UL_OWNER);
2267 reply("CSMSG_INVALID_ACCESS", argv[1]);
2270 if(access != victim->access)
2272 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2278 access = victim->access;
2281 if((actor->access <= victim->access) && !IsHelping(user))
2283 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2287 chan_name = strdup(channel->name);
2288 del_channel_user(victim, 1);
2289 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2295 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2297 struct userData *actor, *uData, *next;
2299 actor = GetChannelUser(channel->channel_info, user->handle_info);
2301 if(min_access > max_access)
2303 reply("CSMSG_BAD_RANGE", min_access, max_access);
2307 if((actor->access <= max_access) && !IsHelping(user))
2309 reply("CSMSG_NO_ACCESS");
2313 for(uData = channel->channel_info->users; uData; uData = next)
2317 if((uData->access >= min_access)
2318 && (uData->access <= max_access)
2319 && match_ircglob(uData->handle->handle, mask))
2320 del_channel_user(uData, 1);
2323 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2327 static CHANSERV_FUNC(cmd_mdelowner)
2329 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2332 static CHANSERV_FUNC(cmd_mdelcoowner)
2334 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2337 static CHANSERV_FUNC(cmd_mdelmaster)
2339 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2342 static CHANSERV_FUNC(cmd_mdelop)
2344 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2347 static CHANSERV_FUNC(cmd_mdelpeon)
2349 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2353 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2355 struct banData *bData, *next;
2356 char interval[INTERVALLEN];
2361 limit = now - duration;
2362 for(bData = channel->channel_info->bans; bData; bData = next)
2366 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2369 del_channel_ban(bData);
2373 intervalString(interval, duration);
2374 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2379 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2381 struct userData *actor, *uData, *next;
2382 char interval[INTERVALLEN];
2386 actor = GetChannelUser(channel->channel_info, user->handle_info);
2387 if(min_access > max_access)
2389 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2393 if((actor->access <= max_access) && !IsHelping(user))
2395 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2400 limit = now - duration;
2401 for(uData = channel->channel_info->users; uData; uData = next)
2405 if((uData->seen > limit) || uData->present)
2408 if(((uData->access >= min_access) && (uData->access <= max_access))
2409 || (!max_access && (uData->access < actor->access)))
2411 del_channel_user(uData, 1);
2416 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2420 static CHANSERV_FUNC(cmd_trim)
2422 unsigned long duration;
2423 unsigned short min_level, max_level;
2427 duration = ParseInterval(argv[2]);
2430 reply("CSMSG_CANNOT_TRIM");
2434 if(!irccasecmp(argv[1], "bans"))
2436 cmd_trim_bans(user, channel, duration);
2439 else if(!irccasecmp(argv[1], "users"))
2441 cmd_trim_users(user, channel, 0, 0, duration);
2444 else if(parse_level_range(&min_level, &max_level, argv[1]))
2446 cmd_trim_users(user, channel, min_level, max_level, duration);
2449 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2451 cmd_trim_users(user, channel, min_level, min_level, duration);
2456 reply("CSMSG_INVALID_TRIM", argv[1]);
2461 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2462 to the user. cmd_all takes advantage of this. */
2463 static CHANSERV_FUNC(cmd_up)
2465 struct mod_chanmode change;
2466 struct userData *uData;
2469 change.modes_set = change.modes_clear = 0;
2471 change.args[0].member = GetUserMode(channel, user);
2472 if(!change.args[0].member)
2475 reply("MSG_CHANNEL_ABSENT", channel->name);
2479 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2483 reply("CSMSG_GODMODE_UP", argv[0]);
2486 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2488 change.args[0].mode = MODE_CHANOP;
2489 errmsg = "CSMSG_ALREADY_OPPED";
2493 change.args[0].mode = MODE_VOICE;
2494 errmsg = "CSMSG_ALREADY_VOICED";
2496 change.args[0].mode &= ~change.args[0].member->modes;
2497 if(!change.args[0].mode)
2500 reply(errmsg, channel->name);
2503 modcmd_chanmode_announce(&change);
2507 static CHANSERV_FUNC(cmd_down)
2509 struct mod_chanmode change;
2511 change.modes_set = change.modes_clear = 0;
2513 change.args[0].member = GetUserMode(channel, user);
2514 if(!change.args[0].member)
2517 reply("MSG_CHANNEL_ABSENT", channel->name);
2521 if(!change.args[0].member->modes)
2524 reply("CSMSG_ALREADY_DOWN", channel->name);
2528 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2529 modcmd_chanmode_announce(&change);
2533 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)
2535 struct userData *cList;
2537 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2539 if(IsSuspended(cList->channel)
2540 || IsUserSuspended(cList)
2541 || !GetUserMode(cList->channel->channel, user))
2544 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2550 static CHANSERV_FUNC(cmd_upall)
2552 return cmd_all(CSFUNC_ARGS, cmd_up);
2555 static CHANSERV_FUNC(cmd_downall)
2557 return cmd_all(CSFUNC_ARGS, cmd_down);
2560 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2561 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2564 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)
2566 unsigned int ii, valid;
2567 struct userNode *victim;
2568 struct mod_chanmode *change;
2570 change = mod_chanmode_alloc(argc - 1);
2572 for(ii=valid=0; ++ii < argc; )
2574 if(!(victim = GetUserH(argv[ii])))
2576 change->args[valid].mode = mode;
2577 change->args[valid].member = GetUserMode(channel, victim);
2578 if(!change->args[valid].member)
2580 if(validate && !validate(user, channel, victim))
2585 change->argc = valid;
2586 if(valid < (argc-1))
2587 reply("CSMSG_PROCESS_FAILED");
2590 modcmd_chanmode_announce(change);
2591 reply(action, channel->name);
2593 mod_chanmode_free(change);
2597 static CHANSERV_FUNC(cmd_op)
2599 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2602 static CHANSERV_FUNC(cmd_deop)
2604 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2607 static CHANSERV_FUNC(cmd_voice)
2609 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2612 static CHANSERV_FUNC(cmd_devoice)
2614 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2618 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2624 for(ii=0; ii<channel->members.used; ii++)
2626 struct modeNode *mn = channel->members.list[ii];
2628 if(IsService(mn->user))
2631 if(!user_matches_glob(mn->user, ban, 1))
2634 if(protect_user(mn->user, user, channel->channel_info))
2638 victims[(*victimCount)++] = mn;
2644 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2646 struct userNode *victim;
2647 struct modeNode **victims;
2648 unsigned int offset, n, victimCount, duration = 0;
2649 char *reason = "Bye.", *ban, *name;
2650 char interval[INTERVALLEN];
2652 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2653 REQUIRE_PARAMS(offset);
2656 reason = unsplit_string(argv + offset, argc - offset, NULL);
2657 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2659 /* Truncate the reason to a length of TOPICLEN, as
2660 the ircd does; however, leave room for an ellipsis
2661 and the kicker's nick. */
2662 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2666 if((victim = GetUserH(argv[1])))
2668 victims = alloca(sizeof(victims[0]));
2669 victims[0] = GetUserMode(channel, victim);
2670 /* XXX: The comparison with ACTION_KICK is just because all
2671 * other actions can work on users outside the channel, and we
2672 * want to allow those (e.g. unbans) in that case. If we add
2673 * some other ejection action for in-channel users, change
2675 victimCount = victims[0] ? 1 : 0;
2677 if(IsService(victim))
2679 reply("MSG_SERVICE_IMMUNE", victim->nick);
2683 if((action == ACTION_KICK) && !victimCount)
2685 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2689 if(protect_user(victim, user, channel->channel_info))
2691 reply("CSMSG_USER_PROTECTED", victim->nick);
2695 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2696 name = victim->nick;
2700 if(!is_ircmask(argv[1]))
2702 reply("MSG_NICK_UNKNOWN", argv[1]);
2706 victims = alloca(sizeof(victims[0]) * channel->members.used);
2708 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2710 reply("CSMSG_MASK_PROTECTED", argv[1]);
2714 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2716 reply("CSMSG_LAME_MASK", argv[1]);
2720 if((action == ACTION_KICK) && (victimCount == 0))
2722 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2726 name = ban = strdup(argv[1]);
2729 /* Truncate the ban in place if necessary; we must ensure
2730 that 'ban' is a valid ban mask before sanitizing it. */
2731 sanitize_ircmask(ban);
2733 if(action & ACTION_ADD_BAN)
2735 struct banData *bData, *next;
2737 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2739 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2744 if(action & ACTION_ADD_TIMED_BAN)
2746 duration = ParseInterval(argv[2]);
2750 reply("CSMSG_DURATION_TOO_LOW");
2754 else if(duration > (86400 * 365 * 2))
2756 reply("CSMSG_DURATION_TOO_HIGH");
2762 for(bData = channel->channel_info->bans; bData; bData = next)
2764 if(match_ircglobs(bData->mask, ban))
2766 int exact = !irccasecmp(bData->mask, ban);
2768 /* The ban is redundant; there is already a ban
2769 with the same effect in place. */
2773 free(bData->reason);
2774 bData->reason = strdup(reason);
2775 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2776 reply("CSMSG_REASON_CHANGE", ban);
2780 if(exact && bData->expires)
2784 /* If the ban matches an existing one exactly,
2785 extend the expiration time if the provided
2786 duration is longer. */
2787 if(duration && ((time_t)(now + duration) > bData->expires))
2789 bData->expires = now + duration;
2800 /* Delete the expiration timeq entry and
2801 requeue if necessary. */
2802 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2805 timeq_add(bData->expires, expire_ban, bData);
2808 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2810 reply("CSMSG_BAN_ADDED", name, channel->name);
2815 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2822 if(match_ircglobs(ban, bData->mask))
2824 /* The ban we are adding makes previously existing
2825 bans redundant; silently remove them. */
2826 del_channel_ban(bData);
2830 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);
2832 name = ban = strdup(bData->mask);
2836 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2838 extern const char *hidden_host_suffix;
2839 const char *old_name = chanserv_conf.old_ban_names->list[n];
2841 unsigned int l1, l2;
2844 l2 = strlen(old_name);
2847 if(irccasecmp(ban + l1 - l2, old_name))
2849 new_mask = malloc(MAXLEN);
2850 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2852 name = ban = new_mask;
2857 if(action & ACTION_BAN)
2859 unsigned int exists;
2860 struct mod_chanmode *change;
2862 if(channel->banlist.used >= MAXBANS)
2864 reply("CSMSG_BANLIST_FULL", channel->name);
2869 exists = ChannelBanExists(channel, ban);
2870 change = mod_chanmode_alloc(victimCount + 1);
2871 for(n = 0; n < victimCount; ++n)
2873 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2874 change->args[n].member = victims[n];
2878 change->args[n].mode = MODE_BAN;
2879 change->args[n++].hostmask = ban;
2883 modcmd_chanmode_announce(change);
2885 mod_chanmode_announce(chanserv, channel, change);
2886 mod_chanmode_free(change);
2888 if(exists && (action == ACTION_BAN))
2890 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2896 if(action & ACTION_KICK)
2898 char kick_reason[MAXLEN];
2899 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2901 for(n = 0; n < victimCount; n++)
2902 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2907 /* No response, since it was automated. */
2909 else if(action & ACTION_ADD_BAN)
2912 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2914 reply("CSMSG_BAN_ADDED", name, channel->name);
2916 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2917 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2918 else if(action & ACTION_BAN)
2919 reply("CSMSG_BAN_DONE", name, channel->name);
2920 else if(action & ACTION_KICK && victimCount)
2921 reply("CSMSG_KICK_DONE", name, channel->name);
2927 static CHANSERV_FUNC(cmd_kickban)
2929 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2932 static CHANSERV_FUNC(cmd_kick)
2934 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2937 static CHANSERV_FUNC(cmd_ban)
2939 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2942 static CHANSERV_FUNC(cmd_addban)
2944 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2947 static CHANSERV_FUNC(cmd_addtimedban)
2949 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2952 static struct mod_chanmode *
2953 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2955 struct mod_chanmode *change;
2956 unsigned char *match;
2957 unsigned int ii, count;
2959 match = alloca(bans->used);
2962 for(ii = count = 0; ii < bans->used; ++ii)
2964 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2971 for(ii = count = 0; ii < bans->used; ++ii)
2973 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
2980 change = mod_chanmode_alloc(count);
2981 for(ii = count = 0; ii < bans->used; ++ii)
2985 change->args[count].mode = MODE_REMOVE | MODE_BAN;
2986 change->args[count++].hostmask = bans->list[ii]->ban;
2992 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2994 struct userNode *actee;
3000 /* may want to allow a comma delimited list of users... */
3001 if(!(actee = GetUserH(argv[1])))
3003 if(!is_ircmask(argv[1]))
3005 reply("MSG_NICK_UNKNOWN", argv[1]);
3009 mask = strdup(argv[1]);
3012 /* We don't sanitize the mask here because ircu
3014 if(action & ACTION_UNBAN)
3016 struct mod_chanmode *change;
3017 change = find_matching_bans(&channel->banlist, actee, mask);
3020 modcmd_chanmode_announce(change);
3021 mod_chanmode_free(change);
3026 if(action & ACTION_DEL_BAN)
3028 struct banData *ban, *next;
3030 ban = channel->channel_info->bans;
3034 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3037 for( ; ban && !match_ircglobs(mask, ban->mask);
3042 del_channel_ban(ban);
3049 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3051 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3057 static CHANSERV_FUNC(cmd_unban)
3059 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3062 static CHANSERV_FUNC(cmd_delban)
3064 /* it doesn't necessarily have to remove the channel ban - may want
3065 to make that an option. */
3066 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3069 static CHANSERV_FUNC(cmd_unbanme)
3071 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3072 long flags = ACTION_UNBAN;
3074 /* remove permanent bans if the user has the proper access. */
3075 if(uData->access >= UL_MASTER)
3076 flags |= ACTION_DEL_BAN;
3078 argv[1] = user->nick;
3079 return unban_user(user, channel, 2, argv, cmd, flags);
3082 static CHANSERV_FUNC(cmd_unbanall)
3084 struct mod_chanmode *change;
3087 if(!channel->banlist.used)
3089 reply("CSMSG_NO_BANS", channel->name);
3093 change = mod_chanmode_alloc(channel->banlist.used);
3094 for(ii=0; ii<channel->banlist.used; ii++)
3096 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3097 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3099 modcmd_chanmode_announce(change);
3100 mod_chanmode_free(change);
3101 reply("CSMSG_BANS_REMOVED", channel->name);
3105 static CHANSERV_FUNC(cmd_open)
3107 struct mod_chanmode *change;
3109 change = find_matching_bans(&channel->banlist, user, NULL);
3111 change = mod_chanmode_alloc(0);
3112 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3113 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3114 && channel->channel_info->modes.modes_set)
3115 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3116 modcmd_chanmode_announce(change);
3117 reply("CSMSG_CHANNEL_OPENED", channel->name);
3118 mod_chanmode_free(change);
3122 static CHANSERV_FUNC(cmd_access)
3124 struct userNode *target;
3125 struct handle_info *target_handle;
3126 struct userData *uData;
3128 char prefix[MAXLEN];
3132 struct userData *uData;
3133 const char *chanName;
3136 target_handle = user->handle_info;
3139 reply("MSG_AUTHENTICATE");
3144 if(!IsHelping(user))
3146 reply("CSMSG_ACCESS_SELF_ONLY", argv[0]);
3150 if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3154 if(!target_handle->channels)
3156 reply("CSMSG_SQUAT_ACCESS");
3159 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3160 for(uData = target_handle->channels; uData; uData = uData->u_next)
3162 struct chanData *cData = uData->channel;
3164 if(uData->access > UL_OWNER)
3166 if(IsProtected(cData) && hide && !GetTrueChannelAccess(cData, user->handle_info))
3168 chanName = cData->channel->name;
3170 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3172 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3180 target_handle = target->handle_info;
3182 else if((target = GetUserH(argv[1])))
3184 target_handle = target->handle_info;
3186 else if(argv[1][0] == '*')
3188 if(!(target_handle = get_handle_info(argv[1]+1)))
3190 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3196 reply("MSG_NICK_UNKNOWN", argv[1]);
3200 assert(target || target_handle);
3202 if(target == chanserv)
3204 reply("CSMSG_IS_CHANSERV");
3212 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3217 reply("MSG_USER_AUTHENTICATE", target->nick);
3220 reply("MSG_AUTHENTICATE");
3226 const char *epithet = NULL, *type = NULL;
3229 epithet = chanserv_conf.irc_operator_epithet;
3232 else if(IsNetworkHelper(target))
3234 epithet = chanserv_conf.network_helper_epithet;
3235 type = "network helper";
3237 else if(IsSupportHelper(target))
3239 epithet = chanserv_conf.support_helper_epithet;
3240 type = "support helper";
3244 if(target_handle->epithet)
3245 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3247 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3249 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3253 sprintf(prefix, "%s", target_handle->handle);
3256 if(!channel->channel_info)
3258 reply("CSMSG_NOT_REGISTERED", channel->name);
3262 helping = HANDLE_FLAGGED(target_handle, HELPING)
3263 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3264 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3266 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3267 /* To prevent possible information leaks, only show infolines
3268 * if the requestor is in the channel or it's their own
3270 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3272 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3274 /* Likewise, only say it's suspended if the user has active
3275 * access in that channel or it's their own entry. */
3276 if(IsUserSuspended(uData)
3277 && (GetChannelUser(channel->channel_info, user->handle_info)
3278 || (user->handle_info == uData->handle)))
3280 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3285 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3292 zoot_list(struct listData *list)
3294 struct userData *uData;
3295 unsigned int start, curr, highest, lowest;
3296 struct helpfile_table tmp_table;
3297 const char **temp, *msg;
3299 if(list->table.length == 1)
3302 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3304 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3305 msg = user_find_message(list->user, "MSG_NONE");
3306 send_message_type(4, list->user, list->bot, " %s", msg);
3308 tmp_table.width = list->table.width;
3309 tmp_table.flags = list->table.flags;
3310 list->table.contents[0][0] = " ";
3311 highest = list->highest;
3312 if (list->lowest != 0)
3313 lowest = list->lowest;
3314 else if (highest < 100)
3317 lowest = highest - 100;
3318 for(start = curr = 1; curr < list->table.length; )
3320 uData = list->users[curr-1];
3321 list->table.contents[curr++][0] = " ";
3322 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3325 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3327 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3328 temp = list->table.contents[--start];
3329 list->table.contents[start] = list->table.contents[0];
3330 tmp_table.contents = list->table.contents + start;
3331 tmp_table.length = curr - start;
3332 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3333 list->table.contents[start] = temp;
3335 highest = lowest - 1;
3336 lowest = (highest < 100) ? 0 : (highest - 99);
3342 def_list(struct listData *list)
3346 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3348 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3349 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3350 if(list->table.length == 1)
3352 msg = user_find_message(list->user, "MSG_NONE");
3353 send_message_type(4, list->user, list->bot, " %s", msg);
3358 userData_access_comp(const void *arg_a, const void *arg_b)
3360 const struct userData *a = *(struct userData**)arg_a;
3361 const struct userData *b = *(struct userData**)arg_b;
3363 if(a->access != b->access)
3364 res = b->access - a->access;
3366 res = irccasecmp(a->handle->handle, b->handle->handle);
3371 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3373 void (*send_list)(struct listData *);
3374 struct userData *uData;
3375 struct listData lData;
3376 unsigned int matches;
3380 lData.bot = cmd->parent->bot;
3381 lData.channel = channel;
3382 lData.lowest = lowest;
3383 lData.highest = highest;
3384 lData.search = (argc > 1) ? argv[1] : NULL;
3385 send_list = zoot_list;
3387 if(user->handle_info)
3389 switch(user->handle_info->userlist_style)
3391 case HI_STYLE_DEF: send_list = def_list; break;
3392 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3396 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3398 for(uData = channel->channel_info->users; uData; uData = uData->next)
3400 if((uData->access < lowest)
3401 || (uData->access > highest)
3402 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3404 lData.users[matches++] = uData;
3406 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3408 lData.table.length = matches+1;
3409 lData.table.width = 4;
3410 lData.table.flags = TABLE_NO_FREE;
3411 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3412 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3413 lData.table.contents[0] = ary;
3416 ary[2] = "Last Seen";
3418 for(matches = 1; matches < lData.table.length; ++matches)
3420 struct userData *uData = lData.users[matches-1];
3421 char seen[INTERVALLEN];
3423 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3424 lData.table.contents[matches] = ary;
3425 ary[0] = strtab(uData->access);
3426 ary[1] = uData->handle->handle;
3429 else if(!uData->seen)
3432 ary[2] = intervalString(seen, now - uData->seen);
3433 ary[2] = strdup(ary[2]);
3434 if(IsUserSuspended(uData))
3435 ary[3] = "Suspended";
3436 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3437 ary[3] = "Vacation";
3442 for(matches = 1; matches < lData.table.length; ++matches)
3444 free((char*)lData.table.contents[matches][2]);
3445 free(lData.table.contents[matches]);
3447 free(lData.table.contents[0]);
3448 free(lData.table.contents);
3452 static CHANSERV_FUNC(cmd_users)
3454 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3457 static CHANSERV_FUNC(cmd_wlist)
3459 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3462 static CHANSERV_FUNC(cmd_clist)
3464 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3467 static CHANSERV_FUNC(cmd_mlist)
3469 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3472 static CHANSERV_FUNC(cmd_olist)
3474 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3477 static CHANSERV_FUNC(cmd_plist)
3479 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3482 static CHANSERV_FUNC(cmd_bans)
3484 struct helpfile_table tbl;
3485 unsigned int matches = 0, timed = 0, ii;
3486 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3487 const char *msg_never, *triggered, *expires;
3488 struct banData *ban, **bans;
3495 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3497 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3499 if(search && !match_ircglobs(search, ban->mask))
3501 bans[matches++] = ban;
3506 tbl.length = matches + 1;
3507 tbl.width = 4 + timed;
3509 tbl.flags = TABLE_NO_FREE;
3510 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3511 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3512 tbl.contents[0][0] = "Mask";
3513 tbl.contents[0][1] = "Set By";
3514 tbl.contents[0][2] = "Triggered";
3517 tbl.contents[0][3] = "Expires";
3518 tbl.contents[0][4] = "Reason";
3521 tbl.contents[0][3] = "Reason";
3524 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3529 msg_never = user_find_message(user, "MSG_NEVER");
3530 for(ii = 0; ii < matches; )
3536 else if(ban->expires)
3537 expires = intervalString(e_buffer, ban->expires - now);
3539 expires = msg_never;
3542 triggered = intervalString(t_buffer, now - ban->triggered);
3544 triggered = msg_never;
3546 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3547 tbl.contents[ii][0] = ban->mask;
3548 tbl.contents[ii][1] = ban->owner;
3549 tbl.contents[ii][2] = strdup(triggered);
3552 tbl.contents[ii][3] = strdup(expires);
3553 tbl.contents[ii][4] = ban->reason;
3556 tbl.contents[ii][3] = ban->reason;
3558 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3559 reply("MSG_MATCH_COUNT", matches);
3560 for(ii = 1; ii < tbl.length; ++ii)
3562 free((char*)tbl.contents[ii][2]);
3564 free((char*)tbl.contents[ii][3]);
3565 free(tbl.contents[ii]);
3567 free(tbl.contents[0]);
3573 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3575 struct chanData *cData = channel->channel_info;
3576 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3578 if(cData->topic_mask)
3579 return !match_ircglob(new_topic, cData->topic_mask);
3580 else if(cData->topic)
3581 return irccasecmp(new_topic, cData->topic);
3586 static CHANSERV_FUNC(cmd_topic)
3588 struct chanData *cData;
3591 cData = channel->channel_info;
3596 SetChannelTopic(channel, chanserv, cData->topic, 1);
3597 reply("CSMSG_TOPIC_SET", cData->topic);
3601 reply("CSMSG_NO_TOPIC", channel->name);
3605 topic = unsplit_string(argv + 1, argc - 1, NULL);
3606 /* If they say "!topic *", use an empty topic. */
3607 if((topic[0] == '*') && (topic[1] == 0))
3609 if(bad_topic(channel, user, topic))
3611 char *topic_mask = cData->topic_mask;
3614 char new_topic[TOPICLEN+1], tchar;
3615 int pos=0, starpos=-1, dpos=0, len;
3617 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3624 len = strlen(topic);
3625 if((dpos + len) > TOPICLEN)
3626 len = TOPICLEN + 1 - dpos;
3627 memcpy(new_topic+dpos, topic, len);
3631 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3632 default: new_topic[dpos++] = tchar; break;
3635 if((dpos > TOPICLEN) || tchar)
3638 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3639 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3642 new_topic[dpos] = 0;
3643 SetChannelTopic(channel, chanserv, new_topic, 1);
3645 reply("CSMSG_TOPIC_LOCKED", channel->name);
3650 SetChannelTopic(channel, chanserv, topic, 1);
3652 if(cData->flags & CHANNEL_TOPIC_SNARF)
3654 /* Grab the topic and save it as the default topic. */
3656 cData->topic = strdup(channel->topic);
3662 static CHANSERV_FUNC(cmd_mode)
3664 struct mod_chanmode *change;
3668 change = &channel->channel_info->modes;
3669 if(change->modes_set || change->modes_clear) {
3670 modcmd_chanmode_announce(change);
3671 reply("CSMSG_DEFAULTED_MODES", channel->name);
3673 reply("CSMSG_NO_MODES", channel->name);
3677 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3680 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3684 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3685 && mode_lock_violated(&channel->channel_info->modes, change))
3688 mod_chanmode_format(&channel->channel_info->modes, modes);
3689 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3693 modcmd_chanmode_announce(change);
3694 mod_chanmode_free(change);
3695 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3699 static CHANSERV_FUNC(cmd_invite)
3701 struct userData *uData;
3702 struct userNode *invite;
3704 uData = GetChannelUser(channel->channel_info, user->handle_info);
3708 if(!(invite = GetUserH(argv[1])))
3710 reply("MSG_NICK_UNKNOWN", argv[1]);
3717 if(GetUserMode(channel, invite))
3719 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3725 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3726 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3728 irc_invite(chanserv, invite, channel);
3730 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3735 static CHANSERV_FUNC(cmd_inviteme)
3737 struct userData *uData;
3739 if(GetUserMode(channel, user))
3741 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3744 if(channel->channel_info
3745 && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3746 && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3747 && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3749 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3752 irc_invite(cmd->parent->bot, user, channel);
3757 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3760 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3762 /* We display things based on two dimensions:
3763 * - Issue time: present or absent
3764 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3765 * (in order of precedence, so something both expired and revoked
3766 * only counts as revoked)
3768 combo = (suspended->issued ? 4 : 0)
3769 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3771 case 0: /* no issue time, indefinite expiration */
3772 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3774 case 1: /* no issue time, expires in future */
3775 intervalString(buf1, suspended->expires-now);
3776 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3778 case 2: /* no issue time, expired */
3779 intervalString(buf1, now-suspended->expires);
3780 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3782 case 3: /* no issue time, revoked */
3783 intervalString(buf1, now-suspended->revoked);
3784 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3786 case 4: /* issue time set, indefinite expiration */
3787 intervalString(buf1, now-suspended->issued);
3788 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3790 case 5: /* issue time set, expires in future */
3791 intervalString(buf1, now-suspended->issued);
3792 intervalString(buf2, suspended->expires-now);
3793 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3795 case 6: /* issue time set, expired */
3796 intervalString(buf1, now-suspended->issued);
3797 intervalString(buf2, now-suspended->expires);
3798 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3800 case 7: /* issue time set, revoked */
3801 intervalString(buf1, now-suspended->issued);
3802 intervalString(buf2, now-suspended->revoked);
3803 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3806 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3811 static CHANSERV_FUNC(cmd_info)
3813 char modes[MAXLEN], buffer[INTERVALLEN];
3814 struct userData *uData, *owner;
3815 struct chanData *cData;
3816 struct do_not_register *dnr;
3821 cData = channel->channel_info;
3822 reply("CSMSG_CHANNEL_INFO", channel->name);
3824 uData = GetChannelUser(cData, user->handle_info);
3825 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3827 mod_chanmode_format(&cData->modes, modes);
3828 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3829 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3832 for(it = dict_first(cData->notes); it; it = iter_next(it))
3836 note = iter_data(it);
3837 if(!note_type_visible_to_user(cData, note->type, user))
3840 padding = PADLEN - 1 - strlen(iter_key(it));
3841 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3844 reply("CSMSG_CHANNEL_MAX", cData->max);
3845 for(owner = cData->users; owner; owner = owner->next)
3846 if(owner->access == UL_OWNER)
3847 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3848 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3849 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3850 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3851 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3853 privileged = IsStaff(user);
3854 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3855 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3857 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3858 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3860 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3862 struct suspended *suspended;
3863 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3864 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3865 show_suspension_info(cmd, user, suspended);
3867 else if(IsSuspended(cData))
3869 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3870 show_suspension_info(cmd, user, cData->suspended);
3875 static CHANSERV_FUNC(cmd_netinfo)
3877 extern time_t boot_time;
3878 extern unsigned long burst_length;
3879 char interval[INTERVALLEN];
3881 reply("CSMSG_NETWORK_INFO");
3882 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3883 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3884 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3885 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3886 reply("CSMSG_NETWORK_BANS", banCount);
3887 reply("CSMSG_CHANNEL_USERS", userCount);
3888 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3889 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3894 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3896 struct helpfile_table table;
3898 struct userNode *user;
3903 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3904 table.contents = alloca(list->used*sizeof(*table.contents));
3905 for(nn=0; nn<list->used; nn++)
3907 user = list->list[nn];
3908 if(user->modes & skip_flags)
3912 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3915 nick = alloca(strlen(user->nick)+3);
3916 sprintf(nick, "(%s)", user->nick);
3920 table.contents[table.length][0] = nick;
3923 table_send(chanserv, to->nick, 0, NULL, table);
3926 static CHANSERV_FUNC(cmd_ircops)
3928 reply("CSMSG_STAFF_OPERS");
3929 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3933 static CHANSERV_FUNC(cmd_helpers)
3935 reply("CSMSG_STAFF_HELPERS");
3936 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3940 static CHANSERV_FUNC(cmd_staff)
3942 reply("CSMSG_NETWORK_STAFF");
3943 cmd_ircops(CSFUNC_ARGS);
3944 cmd_helpers(CSFUNC_ARGS);
3948 static CHANSERV_FUNC(cmd_peek)
3950 struct modeNode *mn;
3951 char modes[MODELEN];
3953 struct helpfile_table table;
3955 irc_make_chanmode(channel, modes);
3957 reply("CSMSG_PEEK_INFO", channel->name);
3958 reply("CSMSG_PEEK_TOPIC", channel->topic);
3959 reply("CSMSG_PEEK_MODES", modes);
3960 reply("CSMSG_PEEK_USERS", channel->members.used);
3964 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3965 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3966 for(n = 0; n < channel->members.used; n++)
3968 mn = channel->members.list[n];
3969 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3971 table.contents[table.length] = alloca(sizeof(**table.contents));
3972 table.contents[table.length][0] = mn->user->nick;
3977 reply("CSMSG_PEEK_OPS");
3978 table_send(chanserv, user->nick, 0, NULL, table);
3981 reply("CSMSG_PEEK_NO_OPS");
3985 static MODCMD_FUNC(cmd_wipeinfo)
3987 struct handle_info *victim;
3988 struct userData *ud, *actor;
3991 actor = GetChannelUser(channel->channel_info, user->handle_info);
3992 if(!(victim = modcmd_get_handle_info(user, argv[1])))
3994 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
3996 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
3999 if((ud->access >= actor->access) && (ud != actor))
4001 reply("MSG_USER_OUTRANKED", victim->handle);
4007 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4011 static CHANSERV_FUNC(cmd_resync)
4013 struct mod_chanmode *changes;
4014 struct chanData *cData = channel->channel_info;
4015 unsigned int ii, used;
4017 changes = mod_chanmode_alloc(channel->members.used * 2);
4018 for(ii = used = 0; ii < channel->members.used; ++ii)
4020 struct modeNode *mn = channel->members.list[ii];
4021 struct userData *uData;
4022 if(IsService(mn->user))
4024 /* must not change modes for this user */
4026 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4030 changes->args[used].mode = MODE_REMOVE | mn->modes;
4031 changes->args[used++].member = mn;
4034 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4036 if(mn->modes & MODE_CHANOP)
4038 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4039 changes->args[used++].member = mn;
4041 if(!(mn->modes & MODE_VOICE))
4043 changes->args[used].mode = MODE_VOICE;
4044 changes->args[used++].member = mn;
4049 if(!(mn->modes & MODE_CHANOP))
4051 changes->args[used].mode = MODE_CHANOP;
4052 changes->args[used++].member = mn;
4056 changes->argc = used;
4057 modcmd_chanmode_announce(changes);
4058 mod_chanmode_free(changes);
4059 reply("CSMSG_RESYNCED_USERS", channel->name);
4063 static CHANSERV_FUNC(cmd_seen)
4065 struct userData *uData;
4066 struct handle_info *handle;
4067 char seen[INTERVALLEN];
4071 if(!irccasecmp(argv[1], chanserv->nick))
4073 reply("CSMSG_IS_CHANSERV");
4077 if(!(handle = get_handle_info(argv[1])))
4079 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4083 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4085 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4090 reply("CSMSG_USER_PRESENT", handle->handle);
4091 else if(uData->seen)
4092 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4094 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4096 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4097 reply("CSMSG_USER_VACATION", handle->handle);
4102 static MODCMD_FUNC(cmd_names)
4104 struct userNode *targ;
4105 struct userData *targData;
4106 unsigned int ii, pos;
4109 for(ii=pos=0; ii<channel->members.used; ++ii)
4111 targ = channel->members.list[ii]->user;
4112 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4115 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4118 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4122 if(IsUserSuspended(targData))
4124 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4127 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4128 reply("CSMSG_END_NAMES", channel->name);
4133 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4135 switch(ntype->visible_type)
4137 case NOTE_VIS_ALL: return 1;
4138 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4139 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4144 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4146 struct userData *uData;
4148 switch(ntype->set_access_type)
4150 case NOTE_SET_CHANNEL_ACCESS:
4151 if(!user->handle_info)
4153 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4155 return uData->access >= ntype->set_access.min_ulevel;
4156 case NOTE_SET_CHANNEL_SETTER:
4157 return check_user_level(channel, user, lvlSetters, 1, 0);
4158 case NOTE_SET_PRIVILEGED: default:
4159 return IsHelping(user);
4163 static CHANSERV_FUNC(cmd_note)
4165 struct chanData *cData;
4167 struct note_type *ntype;
4169 cData = channel->channel_info;
4172 reply("CSMSG_NOT_REGISTERED", channel->name);
4176 /* If no arguments, show all visible notes for the channel. */
4182 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4184 note = iter_data(it);
4185 if(!note_type_visible_to_user(cData, note->type, user))
4188 reply("CSMSG_NOTELIST_HEADER", channel->name);
4189 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4192 reply("CSMSG_NOTELIST_END", channel->name);
4194 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4196 /* If one argument, show the named note. */
4199 if((note = dict_find(cData->notes, argv[1], NULL))
4200 && note_type_visible_to_user(cData, note->type, user))
4202 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4204 else if((ntype = dict_find(note_types, argv[1], NULL))
4205 && note_type_visible_to_user(NULL, ntype, user))
4207 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4212 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4216 /* Assume they're trying to set a note. */
4220 ntype = dict_find(note_types, argv[1], NULL);
4223 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4226 else if(note_type_settable_by_user(channel, ntype, user))
4228 note_text = unsplit_string(argv+2, argc-2, NULL);
4229 if((note = dict_find(cData->notes, argv[1], NULL)))
4230 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4231 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4232 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4234 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4236 /* The note is viewable to staff only, so return 0
4237 to keep the invocation from getting logged (or
4238 regular users can see it in !events). */
4244 reply("CSMSG_NO_ACCESS");
4251 static CHANSERV_FUNC(cmd_delnote)
4256 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4257 || !note_type_settable_by_user(channel, note->type, user))
4259 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4262 dict_remove(channel->channel_info->notes, note->type->name);
4263 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4267 static CHANSERV_FUNC(cmd_events)
4269 struct logSearch discrim;
4270 struct logReport report;
4271 unsigned int matches, limit;
4273 limit = (argc > 1) ? atoi(argv[1]) : 10;
4274 if(limit < 1 || limit > 200) limit = 10;
4276 memset(&discrim, 0, sizeof(discrim));
4277 discrim.masks.bot = chanserv;
4278 discrim.masks.channel_name = channel->name;
4279 if(argc > 2) discrim.masks.command = argv[2];
4280 discrim.limit = limit;
4281 discrim.max_time = INT_MAX;
4282 discrim.severities = 1 << LOG_COMMAND;
4283 report.reporter = chanserv;
4285 reply("CSMSG_EVENT_SEARCH_RESULTS");
4286 matches = log_entry_search(&discrim, log_report_entry, &report);
4288 reply("MSG_MATCH_COUNT", matches);
4290 reply("MSG_NO_MATCHES");
4294 static CHANSERV_FUNC(cmd_say)
4300 msg = unsplit_string(argv + 1, argc - 1, NULL);
4301 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4303 else if(GetUserH(argv[1]))
4306 msg = unsplit_string(argv + 2, argc - 2, NULL);
4307 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4311 reply("You must specify the name of a channel or user.");
4317 static CHANSERV_FUNC(cmd_emote)
4323 /* CTCP is so annoying. */
4324 msg = unsplit_string(argv + 1, argc - 1, NULL);
4325 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4327 else if(GetUserH(argv[1]))
4329 msg = unsplit_string(argv + 2, argc - 2, NULL);
4330 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4334 reply("You must specify the name of a channel or user.");
4340 struct channelList *
4341 chanserv_support_channels(void)
4343 return &chanserv_conf.support_channels;
4346 static CHANSERV_FUNC(cmd_expire)
4348 int channel_count = registered_channels;
4349 expire_channels(NULL);
4350 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4355 chanserv_expire_suspension(void *data)
4357 struct suspended *suspended = data;
4358 struct chanNode *channel;
4359 struct mod_chanmode change;
4361 if(!suspended->expires || (now < suspended->expires))
4362 suspended->revoked = now;
4363 channel = suspended->cData->channel;
4364 suspended->cData->channel = channel;
4365 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4366 change.modes_set = change.modes_clear = 0;
4368 change.args[0].mode = MODE_CHANOP;
4369 change.args[0].member = AddChannelUser(chanserv, channel);
4370 mod_chanmode_announce(chanserv, channel, &change);
4373 static CHANSERV_FUNC(cmd_csuspend)
4375 struct suspended *suspended;
4376 char reason[MAXLEN];
4377 time_t expiry, duration;
4378 struct userData *uData;
4382 if(IsProtected(channel->channel_info))
4384 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4388 if(argv[1][0] == '!')
4390 else if(IsSuspended(channel->channel_info))
4392 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4393 show_suspension_info(cmd, user, channel->channel_info->suspended);
4397 if(!strcmp(argv[1], "0"))
4399 else if((duration = ParseInterval(argv[1])))
4400 expiry = now + duration;
4403 reply("MSG_INVALID_DURATION", argv[1]);
4407 unsplit_string(argv + 2, argc - 2, reason);
4409 suspended = calloc(1, sizeof(*suspended));
4410 suspended->revoked = 0;
4411 suspended->issued = now;
4412 suspended->suspender = strdup(user->handle_info->handle);
4413 suspended->expires = expiry;
4414 suspended->reason = strdup(reason);
4415 suspended->cData = channel->channel_info;
4416 suspended->previous = suspended->cData->suspended;
4417 suspended->cData->suspended = suspended;
4419 if(suspended->expires)
4420 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4422 if(IsSuspended(channel->channel_info))
4424 suspended->previous->revoked = now;
4425 if(suspended->previous->expires)
4426 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4427 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4428 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4432 /* Mark all users in channel as absent. */
4433 for(uData = channel->channel_info->users; uData; uData = uData->next)
4442 /* Mark the channel as suspended, then part. */
4443 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4444 DelChannelUser(chanserv, channel, suspended->reason, 0);
4445 reply("CSMSG_SUSPENDED", channel->name);
4446 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4447 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4452 static CHANSERV_FUNC(cmd_cunsuspend)
4454 struct suspended *suspended;
4455 char message[MAXLEN];
4457 if(!IsSuspended(channel->channel_info))
4459 reply("CSMSG_NOT_SUSPENDED", channel->name);
4463 suspended = channel->channel_info->suspended;
4465 /* Expire the suspension and join ChanServ to the channel. */
4466 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4467 chanserv_expire_suspension(suspended);
4468 reply("CSMSG_UNSUSPENDED", channel->name);
4469 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4470 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4474 typedef struct chanservSearch
4482 unsigned long flags;
4486 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4489 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4494 search = malloc(sizeof(struct chanservSearch));
4495 memset(search, 0, sizeof(*search));
4498 for(i = 0; i < argc; i++)
4500 /* Assume all criteria require arguments. */
4503 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4507 if(!irccasecmp(argv[i], "name"))
4508 search->name = argv[++i];
4509 else if(!irccasecmp(argv[i], "registrar"))
4510 search->registrar = argv[++i];
4511 else if(!irccasecmp(argv[i], "unvisited"))
4512 search->unvisited = ParseInterval(argv[++i]);
4513 else if(!irccasecmp(argv[i], "registered"))
4514 search->registered = ParseInterval(argv[++i]);
4515 else if(!irccasecmp(argv[i], "flags"))
4518 if(!irccasecmp(argv[i], "nodelete"))
4519 search->flags |= CHANNEL_NODELETE;
4520 else if(!irccasecmp(argv[i], "suspended"))
4521 search->flags |= CHANNEL_SUSPENDED;
4524 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4528 else if(!irccasecmp(argv[i], "limit"))
4529 search->limit = strtoul(argv[++i], NULL, 10);
4532 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4537 if(search->name && !strcmp(search->name, "*"))
4539 if(search->registrar && !strcmp(search->registrar, "*"))
4540 search->registrar = 0;
4549 chanserv_channel_match(struct chanData *channel, search_t search)
4551 const char *name = channel->channel->name;
4552 if((search->name && !match_ircglob(name, search->name)) ||
4553 (search->registrar && !channel->registrar) ||
4554 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4555 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4556 (search->registered && (now - channel->registered) > search->registered) ||
4557 (search->flags && ((search->flags & channel->flags) != search->flags)))
4564 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4566 struct chanData *channel;
4567 unsigned int matches = 0;
4569 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4571 if(!chanserv_channel_match(channel, search))
4581 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4586 search_print(struct chanData *channel, void *data)
4588 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4591 static CHANSERV_FUNC(cmd_search)
4594 unsigned int matches;
4595 channel_search_func action;
4599 if(!irccasecmp(argv[1], "count"))
4600 action = search_count;
4601 else if(!irccasecmp(argv[1], "print"))
4602 action = search_print;
4605 reply("CSMSG_ACTION_INVALID", argv[1]);
4609 search = chanserv_search_create(user, argc - 2, argv + 2);
4613 if(action == search_count)
4614 search->limit = INT_MAX;
4616 if(action == search_print)
4617 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4619 matches = chanserv_channel_search(search, action, user);
4622 reply("MSG_MATCH_COUNT", matches);
4624 reply("MSG_NO_MATCHES");
4630 static CHANSERV_FUNC(cmd_unvisited)
4632 struct chanData *cData;
4633 time_t interval = chanserv_conf.channel_expire_delay;
4634 char buffer[INTERVALLEN];
4635 unsigned int limit = 25, matches = 0;
4639 interval = ParseInterval(argv[1]);
4641 limit = atoi(argv[2]);
4644 intervalString(buffer, interval);
4645 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4647 for(cData = channelList; cData && matches < limit; cData = cData->next)
4649 if((now - cData->visited) < interval)
4652 intervalString(buffer, now - cData->visited);
4653 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4660 static MODCMD_FUNC(chan_opt_defaulttopic)
4666 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4668 reply("CSMSG_TOPIC_LOCKED", channel->name);
4672 topic = unsplit_string(argv+1, argc-1, NULL);
4674 free(channel->channel_info->topic);
4675 if(topic[0] == '*' && topic[1] == 0)
4677 topic = channel->channel_info->topic = NULL;
4681 topic = channel->channel_info->topic = strdup(topic);
4682 if(channel->channel_info->topic_mask
4683 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4684 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4686 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4689 if(channel->channel_info->topic)
4690 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4692 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4696 static MODCMD_FUNC(chan_opt_topicmask)
4700 struct chanData *cData = channel->channel_info;
4703 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4705 reply("CSMSG_TOPIC_LOCKED", channel->name);
4709 mask = unsplit_string(argv+1, argc-1, NULL);
4711 if(cData->topic_mask)
4712 free(cData->topic_mask);
4713 if(mask[0] == '*' && mask[1] == 0)
4715 cData->topic_mask = 0;
4719 cData->topic_mask = strdup(mask);
4721 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4722 else if(!match_ircglob(cData->topic, cData->topic_mask))
4723 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4727 if(channel->channel_info->topic_mask)
4728 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4730 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4734 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4738 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4742 if(greeting[0] == '*' && greeting[1] == 0)
4746 unsigned int length = strlen(greeting);
4747 if(length > chanserv_conf.greeting_length)
4749 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4752 *data = strdup(greeting);
4761 reply(name, user_find_message(user, "MSG_NONE"));
4765 static MODCMD_FUNC(chan_opt_greeting)
4767 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4770 static MODCMD_FUNC(chan_opt_usergreeting)
4772 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4775 static MODCMD_FUNC(chan_opt_modes)
4777 struct mod_chanmode *new_modes;
4778 char modes[MODELEN];
4782 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4784 reply("CSMSG_NO_ACCESS");
4787 if(argv[1][0] == '*' && argv[1][1] == 0)
4789 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4791 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4793 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4796 else if(new_modes->argc > 1)
4798 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4799 mod_chanmode_free(new_modes);
4804 channel->channel_info->modes = *new_modes;
4805 modcmd_chanmode_announce(new_modes);
4806 mod_chanmode_free(new_modes);
4810 mod_chanmode_format(&channel->channel_info->modes, modes);
4812 reply("CSMSG_SET_MODES", modes);
4814 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4818 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4820 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4822 struct chanData *cData = channel->channel_info;
4827 /* Set flag according to value. */
4828 if(enabled_string(argv[1]))
4830 cData->flags |= mask;
4833 else if(disabled_string(argv[1]))
4835 cData->flags &= ~mask;
4840 reply("MSG_INVALID_BINARY", argv[1]);
4846 /* Find current option value. */
4847 value = (cData->flags & mask) ? 1 : 0;
4851 reply(name, user_find_message(user, "MSG_ON"));
4853 reply(name, user_find_message(user, "MSG_OFF"));
4857 static MODCMD_FUNC(chan_opt_nodelete)
4859 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4861 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4865 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4868 static MODCMD_FUNC(chan_opt_userinfo)
4870 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4873 static MODCMD_FUNC(chan_opt_voice)
4875 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4878 static MODCMD_FUNC(chan_opt_dynlimit)
4880 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4883 static MODCMD_FUNC(chan_opt_topicsnarf)
4885 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4887 reply("CSMSG_TOPIC_LOCKED", channel->name);
4890 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4893 static MODCMD_FUNC(chan_opt_peoninvite)
4895 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4898 static MODCMD_FUNC(chan_opt_defaults)
4900 struct userData *uData;
4901 struct chanData *cData;
4902 const char *confirm;
4903 enum levelOption lvlOpt;
4904 enum charOption chOpt;
4906 cData = channel->channel_info;
4907 uData = GetChannelUser(cData, user->handle_info);
4908 if(!uData || (uData->access < UL_OWNER))
4910 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4913 confirm = make_confirmation_string(uData);
4914 if((argc < 2) || strcmp(argv[1], confirm))
4916 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4919 cData->flags = CHANNEL_DEFAULT_FLAGS;
4920 cData->modes = chanserv_conf.default_modes;
4921 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4922 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4923 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4924 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4925 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4930 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4932 struct chanData *cData = channel->channel_info;
4933 struct userData *uData;
4934 unsigned short value;
4938 if(!check_user_level(channel, user, option, 1, 1))
4940 reply("CSMSG_CANNOT_SET");
4943 value = user_level_from_name(argv[1], UL_OWNER+1);
4944 if(!value && !isdigit(argv[1][0]))
4946 reply("CSMSG_INVALID_ACCESS", argv[1]);
4949 uData = GetChannelUser(cData, user->handle_info);
4952 reply("CSMSG_BAD_SETLEVEL");
4955 cData->lvlOpts[option] = value;
4957 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4961 static MODCMD_FUNC(chan_opt_enfops)
4963 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4966 static MODCMD_FUNC(chan_opt_giveops)
4968 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4971 static MODCMD_FUNC(chan_opt_enfmodes)
4973 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4976 static MODCMD_FUNC(chan_opt_enftopic)
4978 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4981 static MODCMD_FUNC(chan_opt_pubcmd)
4983 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4986 static MODCMD_FUNC(chan_opt_setters)
4988 return channel_level_option(lvlSetters, CSFUNC_ARGS);
4991 static MODCMD_FUNC(chan_opt_ctcpusers)
4993 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
4997 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4999 struct chanData *cData = channel->channel_info;
5000 int count = charOptions[option].count, index;
5004 index = atoi(argv[1]);
5006 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5008 reply("CSMSG_INVALID_NUMERIC", index);
5009 /* Show possible values. */
5010 for(index = 0; index < count; index++)
5011 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5015 cData->chOpts[option] = charOptions[option].values[index].value;
5019 /* Find current option value. */
5022 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5026 /* Somehow, the option value is corrupt; reset it to the default. */
5027 cData->chOpts[option] = charOptions[option].default_value;
5032 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5036 static MODCMD_FUNC(chan_opt_protect)
5038 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5041 static MODCMD_FUNC(chan_opt_toys)
5043 return channel_multiple_option(chToys, CSFUNC_ARGS);
5046 static MODCMD_FUNC(chan_opt_ctcpreaction)
5048 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5051 static MODCMD_FUNC(chan_opt_topicrefresh)
5053 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5056 static struct svccmd_list set_shows_list;
5059 handle_svccmd_unbind(struct svccmd *target) {
5061 for(ii=0; ii<set_shows_list.used; ++ii)
5062 if(target == set_shows_list.list[ii])
5063 set_shows_list.used = 0;
5066 static CHANSERV_FUNC(cmd_set)
5068 struct svccmd *subcmd;
5072 /* Check if we need to (re-)initialize set_shows_list. */
5073 if(!set_shows_list.used)
5075 if(!set_shows_list.size)
5077 set_shows_list.size = chanserv_conf.set_shows->used;
5078 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5080 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5082 const char *name = chanserv_conf.set_shows->list[ii];
5083 sprintf(buf, "%s %s", argv[0], name);
5084 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5087 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5090 svccmd_list_append(&set_shows_list, subcmd);
5096 reply("CSMSG_CHANNEL_OPTIONS");
5097 for(ii = 0; ii < set_shows_list.used; ii++)
5099 subcmd = set_shows_list.list[ii];
5100 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5105 sprintf(buf, "%s %s", argv[0], argv[1]);
5106 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5109 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5112 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5114 reply("CSMSG_NO_ACCESS");
5118 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5122 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5124 struct userData *uData;
5126 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5129 reply("CSMSG_NOT_USER", channel->name);
5135 /* Just show current option value. */
5137 else if(enabled_string(argv[1]))
5139 uData->flags |= mask;
5141 else if(disabled_string(argv[1]))
5143 uData->flags &= ~mask;
5147 reply("MSG_INVALID_BINARY", argv[1]);
5151 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5155 static MODCMD_FUNC(user_opt_noautoop)
5157 struct userData *uData;
5159 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5162 reply("CSMSG_NOT_USER", channel->name);
5165 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5166 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5168 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5171 static MODCMD_FUNC(user_opt_autoinvite)
5173 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5176 static MODCMD_FUNC(user_opt_info)
5178 struct userData *uData;
5181 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5185 /* If they got past the command restrictions (which require access)
5186 * but fail this test, we have some fool with security override on.
5188 reply("CSMSG_NOT_USER", channel->name);
5194 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5197 if(infoline[0] == '*' && infoline[1] == 0)
5200 uData->info = strdup(infoline);
5203 reply("CSMSG_USET_INFO", uData->info);
5205 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5209 struct svccmd_list uset_shows_list;
5211 static CHANSERV_FUNC(cmd_uset)
5213 struct svccmd *subcmd;
5217 /* Check if we need to (re-)initialize uset_shows_list. */
5218 if(!uset_shows_list.used)
5222 "NoAutoOp", "AutoInvite", "Info"
5225 if(!uset_shows_list.size)
5227 uset_shows_list.size = ArrayLength(options);
5228 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5230 for(ii = 0; ii < ArrayLength(options); ii++)
5232 const char *name = options[ii];
5233 sprintf(buf, "%s %s", argv[0], name);
5234 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5237 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5240 svccmd_list_append(&uset_shows_list, subcmd);
5246 /* Do this so options are presented in a consistent order. */
5247 reply("CSMSG_USER_OPTIONS");
5248 for(ii = 0; ii < uset_shows_list.used; ii++)
5249 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5253 sprintf(buf, "%s %s", argv[0], argv[1]);
5254 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5257 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5261 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5264 static CHANSERV_FUNC(cmd_giveownership)
5266 struct handle_info *new_owner_hi;
5267 struct userData *new_owner, *curr_user;
5268 struct chanData *cData = channel->channel_info;
5269 struct do_not_register *dnr;
5271 unsigned short co_access;
5272 char reason[MAXLEN];
5275 curr_user = GetChannelAccess(cData, user->handle_info);
5276 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5277 if(!curr_user || (curr_user->access != UL_OWNER))
5279 struct userData *owner = NULL;
5280 for(curr_user = channel->channel_info->users;
5282 curr_user = curr_user->next)
5284 if(curr_user->access != UL_OWNER)
5288 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5294 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5296 if(new_owner_hi == user->handle_info)
5298 reply("CSMSG_NO_TRANSFER_SELF");
5301 new_owner = GetChannelAccess(cData, new_owner_hi);
5304 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5307 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5309 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5312 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5313 if(!IsHelping(user))
5314 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5316 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5319 if(new_owner->access >= UL_COOWNER)
5320 co_access = new_owner->access;
5322 co_access = UL_COOWNER;
5323 new_owner->access = UL_OWNER;
5325 curr_user->access = co_access;
5326 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5327 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5328 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5332 static CHANSERV_FUNC(cmd_suspend)
5334 struct handle_info *hi;
5335 struct userData *self, *target;
5338 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5339 self = GetChannelUser(channel->channel_info, user->handle_info);
5340 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5342 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5345 if(target->access >= self->access)
5347 reply("MSG_USER_OUTRANKED", hi->handle);
5350 if(target->flags & USER_SUSPENDED)
5352 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5357 target->present = 0;
5360 target->flags |= USER_SUSPENDED;
5361 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5365 static CHANSERV_FUNC(cmd_unsuspend)
5367 struct handle_info *hi;
5368 struct userData *self, *target;
5371 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5372 self = GetChannelUser(channel->channel_info, user->handle_info);
5373 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5375 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5378 if(target->access >= self->access)
5380 reply("MSG_USER_OUTRANKED", hi->handle);
5383 if(!(target->flags & USER_SUSPENDED))
5385 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5388 target->flags &= ~USER_SUSPENDED;
5389 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5393 static MODCMD_FUNC(cmd_deleteme)
5395 struct handle_info *hi;
5396 struct userData *target;
5397 const char *confirm_string;
5398 unsigned short access;
5401 hi = user->handle_info;
5402 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5404 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5407 if(target->access == UL_OWNER)
5409 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5412 confirm_string = make_confirmation_string(target);
5413 if((argc < 2) || strcmp(argv[1], confirm_string))
5415 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5418 access = target->access;
5419 channel_name = strdup(channel->name);
5420 del_channel_user(target, 1);
5421 reply("CSMSG_DELETED_YOU", access, channel_name);
5427 chanserv_refresh_topics(UNUSED_ARG(void *data))
5429 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5430 struct chanData *cData;
5433 for(cData = channelList; cData; cData = cData->next)
5435 if(IsSuspended(cData))
5437 opt = cData->chOpts[chTopicRefresh];
5440 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5443 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5444 cData->last_refresh = refresh_num;
5446 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5449 static CHANSERV_FUNC(cmd_unf)
5453 char response[MAXLEN];
5454 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5455 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5456 irc_privmsg(cmd->parent->bot, channel->name, response);
5459 reply("CSMSG_UNF_RESPONSE");
5463 static CHANSERV_FUNC(cmd_ping)
5467 char response[MAXLEN];
5468 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5469 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5470 irc_privmsg(cmd->parent->bot, channel->name, response);
5473 reply("CSMSG_PING_RESPONSE");
5477 static CHANSERV_FUNC(cmd_wut)
5481 char response[MAXLEN];
5482 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5483 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5484 irc_privmsg(cmd->parent->bot, channel->name, response);
5487 reply("CSMSG_WUT_RESPONSE");
5491 static CHANSERV_FUNC(cmd_8ball)
5493 unsigned int i, j, accum;
5498 for(i=1; i<argc; i++)
5499 for(j=0; argv[i][j]; j++)
5500 accum = (accum << 5) - accum + toupper(argv[i][j]);
5501 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5504 char response[MAXLEN];
5505 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5506 irc_privmsg(cmd->parent->bot, channel->name, response);
5509 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5513 static CHANSERV_FUNC(cmd_d)
5515 unsigned long sides, count, modifier, ii, total;
5516 char response[MAXLEN], *sep;
5520 if((count = strtoul(argv[1], &sep, 10)) <= 1)
5528 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5529 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5533 else if((sep[0] == '-') && isdigit(sep[1]))
5534 modifier = strtoul(sep, NULL, 10);
5535 else if((sep[0] == '+') && isdigit(sep[1]))
5536 modifier = strtoul(sep+1, NULL, 10);
5543 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5548 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5551 for(total = ii = 0; ii < count; ++ii)
5552 total += (rand() % sides) + 1;
5555 if((count > 1) || modifier)
5557 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5558 sprintf(response, fmt, total, count, sides, modifier);
5562 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5563 sprintf(response, fmt, total, sides);
5566 send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5568 send_message_type(4, user, cmd->parent->bot, "%s", response);
5572 static CHANSERV_FUNC(cmd_huggle)
5574 char response[MAXLEN];
5576 /* CTCP must be via PRIVMSG, never notice */
5579 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5580 sprintf(response, fmt, user->nick);
5581 irc_privmsg(cmd->parent->bot, channel->name, response);
5585 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5586 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5592 chanserv_adjust_limit(void *data)
5594 struct mod_chanmode change;
5595 struct chanData *cData = data;
5596 struct chanNode *channel = cData->channel;
5599 if(IsSuspended(cData))
5602 cData->limitAdjusted = now;
5603 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5604 if(cData->modes.modes_set & MODE_LIMIT)
5606 if(limit > cData->modes.new_limit)
5607 limit = cData->modes.new_limit;
5608 else if(limit == cData->modes.new_limit)
5612 change.modes_set = MODE_LIMIT;
5613 change.modes_clear = 0;
5614 change.new_limit = limit;
5616 mod_chanmode_announce(chanserv, channel, &change);
5620 handle_new_channel(struct chanNode *channel)
5622 struct chanData *cData;
5624 if(!(cData = channel->channel_info))
5627 if(cData->modes.modes_set || cData->modes.modes_clear)
5628 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5630 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5631 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5634 /* Welcome to my worst nightmare. Warning: Read (or modify)
5635 the code below at your own risk. */
5637 handle_join(struct modeNode *mNode)
5639 struct mod_chanmode change;
5640 struct userNode *user = mNode->user;
5641 struct chanNode *channel = mNode->channel;
5642 struct chanData *cData;
5643 struct userData *uData = NULL;
5644 struct banData *bData;
5645 struct handle_info *handle;
5646 unsigned int modes = 0, info = 0;
5649 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5652 cData = channel->channel_info;
5653 if(channel->members.used > cData->max)
5654 cData->max = channel->members.used;
5656 /* Check for bans. If they're joining through a ban, one of two
5658 * 1: Join during a netburst, by riding the break. Kick them
5659 * unless they have ops or voice in the channel.
5660 * 2: They're allowed to join through the ban (an invite in
5661 * ircu2.10, or a +e on Hybrid, or something).
5662 * If they're not joining through a ban, and the banlist is not
5663 * full, see if they're on the banlist for the channel. If so,
5666 if(user->uplink->burst && !mNode->modes)
5669 for(ii = 0; ii < channel->banlist.used; ii++)
5671 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5673 /* Riding a netburst. Naughty. */
5674 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5680 change.modes_set = change.modes_clear = 0;
5682 if(channel->banlist.used < MAXBANS)
5684 /* Not joining through a ban. */
5685 for(bData = cData->bans;
5686 bData && !user_matches_glob(user, bData->mask, 1);
5687 bData = bData->next);
5691 char kick_reason[MAXLEN];
5692 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5694 bData->triggered = now;
5695 if(bData != cData->bans)
5697 /* Shuffle the ban to the head of the list. */
5698 if(bData->next) bData->next->prev = bData->prev;
5699 if(bData->prev) bData->prev->next = bData->next;
5702 bData->next = cData->bans;
5705 cData->bans->prev = bData;
5706 cData->bans = bData;
5709 change.args[0].mode = MODE_BAN;
5710 change.args[0].hostmask = bData->mask;
5711 mod_chanmode_announce(chanserv, channel, &change);
5712 KickChannelUser(user, channel, chanserv, kick_reason);
5717 /* ChanServ will not modify the limits in join-flooded channels.
5718 It will also skip DynLimit processing when the user (or srvx)
5719 is bursting in, because there are likely more incoming. */
5720 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5721 && !user->uplink->burst
5722 && !channel->join_flooded
5723 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5725 /* The user count has begun "bumping" into the channel limit,
5726 so set a timer to raise the limit a bit. Any previous
5727 timers are removed so three incoming users within the delay
5728 results in one limit change, not three. */
5730 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5731 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5734 if(cData->lvlOpts[lvlGiveOps] == 0)
5735 modes |= MODE_CHANOP;
5736 else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5737 modes |= MODE_VOICE;
5739 greeting = cData->greeting;
5740 if(user->handle_info)
5742 handle = user->handle_info;
5744 if(IsHelper(user) && !IsHelping(user))
5747 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5749 if(channel == chanserv_conf.support_channels.list[ii])
5751 HANDLE_SET_FLAG(user->handle_info, HELPING);
5757 uData = GetTrueChannelAccess(cData, handle);
5758 if(uData && !IsUserSuspended(uData))
5760 /* Ops and above were handled by the above case. */
5761 if(IsUserAutoOp(uData))
5763 if(uData->access < cData->lvlOpts[lvlGiveOps])
5764 modes |= MODE_VOICE;
5766 modes |= MODE_CHANOP;
5768 if(uData->access >= UL_PRESENT)
5769 cData->visited = now;
5774 if(cData->user_greeting)
5775 greeting = cData->user_greeting;
5777 && (cData->flags & CHANNEL_INFO_LINES)
5778 && ((now - uData->seen) >= chanserv_conf.info_delay)
5783 if(!user->uplink->burst)
5787 change.args[0].mode = modes;
5788 change.args[0].member = mNode;
5789 mod_chanmode_announce(chanserv, channel, &change);
5791 if(greeting && !user->uplink->burst)
5792 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5794 send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5800 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5802 struct mod_chanmode change;
5803 struct userData *channel;
5804 unsigned int ii, jj;
5806 if(!user->handle_info)
5809 change.modes_set = change.modes_clear = 0;
5811 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5813 struct chanNode *cn;
5814 struct modeNode *mn;
5815 if(IsUserSuspended(channel)
5816 || IsSuspended(channel->channel)
5817 || !(cn = channel->channel->channel))
5820 mn = GetUserMode(cn, user);
5823 if(!IsUserSuspended(channel)
5824 && IsUserAutoInvite(channel)
5825 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5827 irc_invite(chanserv, user, cn);
5831 if(channel->access >= UL_PRESENT)
5832 channel->channel->visited = now;
5834 if(IsUserAutoOp(channel))
5836 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5837 change.args[0].mode = MODE_CHANOP;
5839 change.args[0].mode = MODE_VOICE;
5840 change.args[0].member = mn;
5841 mod_chanmode_announce(chanserv, cn, &change);
5844 channel->seen = now;
5845 channel->present = 1;
5848 for(ii = 0; ii < user->channels.used; ++ii)
5850 struct chanNode *channel = user->channels.list[ii]->channel;
5851 struct banData *ban;
5853 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5854 || !channel->channel_info)
5856 for(jj = 0; jj < channel->banlist.used; ++jj)
5857 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5859 if(jj < channel->banlist.used)
5861 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5863 char kick_reason[MAXLEN];
5864 if(!user_matches_glob(user, ban->mask, 1))
5866 change.args[0].mode = MODE_BAN;
5867 change.args[0].hostmask = ban->mask;
5868 mod_chanmode_announce(chanserv, channel, &change);
5869 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5870 KickChannelUser(user, channel, chanserv, kick_reason);
5871 ban->triggered = now;
5876 if(IsSupportHelper(user))
5878 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5880 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5882 HANDLE_SET_FLAG(user->handle_info, HELPING);
5890 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5892 struct chanData *cData;
5893 struct userData *uData;
5895 cData = channel->channel_info;
5896 if(!cData || IsSuspended(cData) || IsLocal(user))
5899 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5901 /* Allow for a bit of padding so that the limit doesn't
5902 track the user count exactly, which could get annoying. */
5903 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5905 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5906 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5910 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5911 scan_user_presence(uData, user);
5913 if(IsHelping(user) && IsSupportHelper(user))
5915 unsigned int ii, jj;
5916 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5918 for(jj = 0; jj < user->channels.used; ++jj)
5919 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5921 if(jj < user->channels.used)
5924 if(ii == chanserv_conf.support_channels.used)
5925 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5930 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5932 char *reason = "CSMSG_USER_PROTECTED";
5934 if(!channel->channel_info || !kicker || IsService(kicker)
5935 || (kicker == victim) || IsSuspended(channel->channel_info)
5936 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5939 if(protect_user(victim, kicker, channel->channel_info))
5940 KickChannelUser(kicker, channel, chanserv, reason);
5944 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5946 struct chanData *cData;
5948 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5950 cData = channel->channel_info;
5951 if(bad_topic(channel, user, channel->topic))
5953 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5954 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5955 SetChannelTopic(channel, chanserv, old_topic, 1);
5956 else if(cData->topic)
5957 SetChannelTopic(channel, chanserv, cData->topic, 1);
5960 /* With topicsnarf, grab the topic and save it as the default topic. */
5961 if(cData->flags & CHANNEL_TOPIC_SNARF)
5964 cData->topic = strdup(channel->topic);
5970 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5972 struct mod_chanmode *bounce = NULL;
5973 unsigned int bnc, ii;
5976 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5979 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5980 && mode_lock_violated(&channel->channel_info->modes, change))
5982 char correct[MAXLEN];
5983 bounce = mod_chanmode_alloc(change->argc + 1);
5984 *bounce = channel->channel_info->modes;
5985 mod_chanmode_format(&channel->channel_info->modes, correct);
5986 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5988 for(ii = bnc = 0; ii < change->argc; ++ii)
5990 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5992 const struct userNode *victim = change->args[ii].member->user;
5993 if(!protect_user(victim, user, channel->channel_info))
5996 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
5999 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6000 bounce->args[bnc].member = GetUserMode(channel, user);
6001 if(bounce->args[bnc].member)
6005 bounce->args[bnc].mode = MODE_CHANOP;
6006 bounce->args[bnc].member = change->args[ii].member;
6008 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6010 else if(change->args[ii].mode & MODE_CHANOP)
6012 const struct userNode *victim = change->args[ii].member->user;
6013 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6016 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6017 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6018 bounce->args[bnc].member = change->args[ii].member;
6021 else if(change->args[ii].mode & MODE_BAN)
6023 const char *ban = change->args[ii].hostmask;
6024 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6027 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6028 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6029 bounce->args[bnc].hostmask = ban;
6031 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6036 if((bounce->argc = bnc))
6037 mod_chanmode_announce(chanserv, channel, bounce);
6038 mod_chanmode_free(bounce);
6043 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6045 struct chanNode *channel;
6046 struct banData *bData;
6047 struct mod_chanmode change;
6048 unsigned int ii, jj;
6049 char kick_reason[MAXLEN];
6051 change.modes_set = change.modes_clear = 0;
6053 change.args[0].mode = MODE_BAN;
6054 for(ii = 0; ii < user->channels.used; ++ii)
6056 channel = user->channels.list[ii]->channel;
6057 /* Need not check for bans if they're opped or voiced. */
6058 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6060 /* Need not check for bans unless channel registration is active. */
6061 if(!channel->channel_info || IsSuspended(channel->channel_info))
6063 /* Look for a matching ban already on the channel. */
6064 for(jj = 0; jj < channel->banlist.used; ++jj)
6065 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6067 /* Need not act if we found one. */
6068 if(jj < channel->banlist.used)
6070 /* Look for a matching ban in this channel. */
6071 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6073 if(!user_matches_glob(user, bData->mask, 1))
6075 change.args[0].hostmask = bData->mask;
6076 mod_chanmode_announce(chanserv, channel, &change);
6077 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6078 KickChannelUser(user, channel, chanserv, kick_reason);
6079 bData->triggered = now;
6080 break; /* we don't need to check any more bans in the channel */
6085 static void handle_rename(struct handle_info *handle, const char *old_handle)
6087 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6091 dict_remove2(handle_dnrs, old_handle, 1);
6092 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6093 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6098 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6100 struct userNode *h_user;
6102 if(handle->channels)
6104 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6105 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6107 while(handle->channels)
6108 del_channel_user(handle->channels, 1);
6113 handle_server_link(UNUSED_ARG(struct server *server))
6115 struct chanData *cData;
6117 for(cData = channelList; cData; cData = cData->next)
6119 if(!IsSuspended(cData))
6120 cData->may_opchan = 1;
6121 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6122 && !cData->channel->join_flooded
6123 && ((cData->channel->limit - cData->channel->members.used)
6124 < chanserv_conf.adjust_threshold))
6126 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6127 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6133 chanserv_conf_read(void)
6137 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6138 struct mod_chanmode *change;
6139 struct string_list *strlist;
6140 struct chanNode *chan;
6143 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6145 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6148 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6149 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6150 chanserv_conf.support_channels.used = 0;
6151 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6153 for(ii = 0; ii < strlist->used; ++ii)
6155 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6158 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6160 channelList_append(&chanserv_conf.support_channels, chan);
6163 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6166 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6169 chan = AddChannel(str, now, str2, NULL);
6171 channelList_append(&chanserv_conf.support_channels, chan);
6173 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6174 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6175 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6176 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6177 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6178 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6179 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6180 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6181 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6182 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6183 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6184 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6185 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6186 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6187 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6188 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6189 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6190 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6191 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6192 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6193 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6195 NickChange(chanserv, str, 0);
6196 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6197 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6198 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6199 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6200 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6201 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6202 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6203 chanserv_conf.max_owned = str ? atoi(str) : 5;
6204 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6205 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6206 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6207 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6208 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6209 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6210 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6213 safestrncpy(mode_line, str, sizeof(mode_line));
6214 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6215 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6217 chanserv_conf.default_modes = *change;
6218 mod_chanmode_free(change);
6220 free_string_list(chanserv_conf.set_shows);
6221 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6223 strlist = string_list_copy(strlist);
6226 static const char *list[] = {
6227 /* multiple choice options */
6228 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6229 "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6230 "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6231 /* binary options */
6232 "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6236 strlist = alloc_string_list(ArrayLength(list)-1);
6237 for(ii=0; list[ii]; ii++)
6238 string_list_append(strlist, strdup(list[ii]));
6240 chanserv_conf.set_shows = strlist;
6241 /* We don't look things up now, in case the list refers to options
6242 * defined by modules initialized after this point. Just mark the
6243 * function list as invalid, so it will be initialized.
6245 set_shows_list.used = 0;
6246 free_string_list(chanserv_conf.eightball);
6247 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6250 strlist = string_list_copy(strlist);
6254 strlist = alloc_string_list(4);
6255 string_list_append(strlist, strdup("Yes."));
6256 string_list_append(strlist, strdup("No."));
6257 string_list_append(strlist, strdup("Maybe so."));
6259 chanserv_conf.eightball = strlist;
6260 free_string_list(chanserv_conf.old_ban_names);
6261 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6263 strlist = string_list_copy(strlist);
6265 strlist = alloc_string_list(2);
6266 chanserv_conf.old_ban_names = strlist;
6270 chanserv_note_type_read(const char *key, struct record_data *rd)
6273 struct note_type *ntype;
6276 if(!(obj = GET_RECORD_OBJECT(rd)))
6278 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6281 if(!(ntype = chanserv_create_note_type(key)))
6283 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6287 /* Figure out set access */
6288 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6290 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6291 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6293 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6295 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6296 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6298 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6300 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6304 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6305 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6306 ntype->set_access.min_opserv = 0;
6309 /* Figure out visibility */
6310 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6311 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6312 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6313 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6314 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6315 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6316 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6317 ntype->visible_type = NOTE_VIS_ALL;
6319 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6321 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6322 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6326 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6328 struct handle_info *handle;
6329 struct userData *uData;
6330 char *seen, *inf, *flags;
6332 unsigned short access;
6334 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6336 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6340 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6341 if(access > UL_OWNER)
6343 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6347 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6348 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6349 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6350 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6351 handle = get_handle_info(key);
6354 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6358 uData = add_channel_user(chan, handle, access, last_seen, inf);
6359 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6363 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6365 struct banData *bData;
6366 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6367 time_t set_time, triggered_time, expires_time;
6369 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6371 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6375 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6376 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6377 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6378 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6379 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6380 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6382 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6383 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6385 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6387 expires_time = set_time + atoi(s_duration);
6391 if(expires_time && (expires_time < now))
6394 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6397 static struct suspended *
6398 chanserv_read_suspended(dict_t obj)
6400 struct suspended *suspended = calloc(1, sizeof(*suspended));
6404 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6405 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6406 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6407 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6408 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6409 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6410 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6411 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6412 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6413 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6418 chanserv_channel_read(const char *key, struct record_data *hir)
6420 struct suspended *suspended;
6421 struct mod_chanmode *modes;
6422 struct chanNode *cNode;
6423 struct chanData *cData;
6424 struct dict *channel, *obj;
6425 char *str, *argv[10];
6429 channel = hir->d.object;
6431 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6434 cNode = AddChannel(key, now, NULL, NULL);
6437 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6440 cData = register_channel(cNode, str);
6443 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6447 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6449 enum levelOption lvlOpt;
6450 enum charOption chOpt;
6451 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6453 if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6455 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6457 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6459 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6461 cData->chOpts[chOpt] = str[0];
6463 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6464 cData->flags = atoi(str);
6466 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6468 enum levelOption lvlOpt;
6469 enum charOption chOpt;
6472 cData->flags = base64toint(str, 5);
6473 count = strlen(str += 5);
6474 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6477 switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6479 case 'c': lvl = UL_COOWNER; break;
6480 case 'm': lvl = UL_MASTER; break;
6481 case 'n': lvl = UL_OWNER+1; break;
6482 case 'o': lvl = UL_OP; break;
6483 case 'p': lvl = UL_PEON; break;
6484 case 'w': lvl = UL_OWNER; break;
6485 default: lvl = 0; break;
6487 cData->lvlOpts[lvlOpt] = lvl;
6489 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6490 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6493 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6495 suspended = chanserv_read_suspended(obj);
6496 cData->suspended = suspended;
6497 suspended->cData = cData;
6498 /* We could use suspended->expires and suspended->revoked to
6499 * set the CHANNEL_SUSPENDED flag, but we don't. */
6501 else if(cData->flags & CHANNEL_SUSPENDED)
6503 suspended = calloc(1, sizeof(*suspended));
6504 suspended->issued = 0;
6505 suspended->revoked = 0;
6506 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6507 suspended->expires = str ? atoi(str) : 0;
6508 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6509 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6510 suspended->reason = strdup(str ? str : "No reason");
6511 suspended->previous = NULL;
6512 cData->suspended = suspended;
6513 suspended->cData = cData;
6518 if((cData->flags & CHANNEL_SUSPENDED)
6519 && suspended->expires
6520 && (suspended->expires <= now))
6522 cData->flags &= ~CHANNEL_SUSPENDED;
6525 if(!(cData->flags & CHANNEL_SUSPENDED))
6527 struct mod_chanmode change;
6528 change.modes_set = change.modes_clear = 0;
6530 change.args[0].mode = MODE_CHANOP;
6531 change.args[0].member = AddChannelUser(chanserv, cNode);
6532 mod_chanmode_announce(chanserv, cNode, &change);
6534 else if(suspended->expires > now)
6536 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6539 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6540 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6541 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6542 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6543 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6544 cData->max = str ? atoi(str) : 0;
6545 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6546 cData->greeting = str ? strdup(str) : NULL;
6547 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6548 cData->user_greeting = str ? strdup(str) : NULL;
6549 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6550 cData->topic_mask = str ? strdup(str) : NULL;
6551 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6552 cData->topic = str ? strdup(str) : NULL;
6554 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6555 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6556 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6557 cData->modes = *modes;
6558 if(cData->modes.argc > 1)
6559 cData->modes.argc = 1;
6560 if(!IsSuspended(cData))
6561 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6562 mod_chanmode_free(modes);
6565 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6566 for(it = dict_first(obj); it; it = iter_next(it))
6567 user_read_helper(iter_key(it), iter_data(it), cData);
6569 if(!cData->users && !IsProtected(cData))
6571 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6572 unregister_channel(cData, "has empty user list.");
6576 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6577 for(it = dict_first(obj); it; it = iter_next(it))
6578 ban_read_helper(iter_key(it), iter_data(it), cData);
6580 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6581 for(it = dict_first(obj); it; it = iter_next(it))
6583 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6584 struct record_data *rd = iter_data(it);
6585 const char *note, *setter;
6587 if(rd->type != RECDB_OBJECT)
6589 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6593 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6595 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6597 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6601 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6602 if(!setter) setter = "<unknown>";
6603 chanserv_add_channel_note(cData, ntype, setter, note);
6611 chanserv_dnr_read(const char *key, struct record_data *hir)
6613 const char *setter, *reason, *str;
6614 struct do_not_register *dnr;
6616 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6619 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6622 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6625 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6628 dnr = chanserv_add_dnr(key, setter, reason);
6631 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6633 dnr->set = atoi(str);
6639 chanserv_saxdb_read(struct dict *database)
6641 struct dict *section;
6644 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6645 for(it = dict_first(section); it; it = iter_next(it))
6646 chanserv_note_type_read(iter_key(it), iter_data(it));
6648 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6649 for(it = dict_first(section); it; it = iter_next(it))
6650 chanserv_channel_read(iter_key(it), iter_data(it));
6652 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6653 for(it = dict_first(section); it; it = iter_next(it))
6654 chanserv_dnr_read(iter_key(it), iter_data(it));
6660 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6662 int high_present = 0;
6663 saxdb_start_record(ctx, KEY_USERS, 1);
6664 for(; uData; uData = uData->next)
6666 if((uData->access >= UL_PRESENT) && uData->present)
6668 saxdb_start_record(ctx, uData->handle->handle, 0);
6669 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6670 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6672 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6674 saxdb_write_string(ctx, KEY_INFO, uData->info);
6675 saxdb_end_record(ctx);
6677 saxdb_end_record(ctx);
6678 return high_present;
6682 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6686 saxdb_start_record(ctx, KEY_BANS, 1);
6687 for(; bData; bData = bData->next)
6689 saxdb_start_record(ctx, bData->mask, 0);
6690 saxdb_write_int(ctx, KEY_SET, bData->set);
6691 if(bData->triggered)
6692 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6694 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6696 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6698 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6699 saxdb_end_record(ctx);
6701 saxdb_end_record(ctx);
6705 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6707 saxdb_start_record(ctx, name, 0);
6708 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6709 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6711 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6713 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6715 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6717 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6718 saxdb_end_record(ctx);
6722 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6726 enum levelOption lvlOpt;
6727 enum charOption chOpt;
6729 saxdb_start_record(ctx, channel->channel->name, 1);
6731 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6732 saxdb_write_int(ctx, KEY_MAX, channel->max);
6734 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6735 if(channel->registrar)
6736 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6737 if(channel->greeting)
6738 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6739 if(channel->user_greeting)
6740 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6741 if(channel->topic_mask)
6742 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6743 if(channel->suspended)
6744 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6746 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6747 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6748 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6749 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6750 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6752 buf[0] = channel->chOpts[chOpt];
6754 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6756 saxdb_end_record(ctx);
6758 if(channel->modes.modes_set || channel->modes.modes_clear)
6760 mod_chanmode_format(&channel->modes, buf);
6761 saxdb_write_string(ctx, KEY_MODES, buf);
6764 high_present = chanserv_write_users(ctx, channel->users);
6765 chanserv_write_bans(ctx, channel->bans);
6767 if(dict_size(channel->notes))
6771 saxdb_start_record(ctx, KEY_NOTES, 1);
6772 for(it = dict_first(channel->notes); it; it = iter_next(it))
6774 struct note *note = iter_data(it);
6775 saxdb_start_record(ctx, iter_key(it), 0);
6776 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6777 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6778 saxdb_end_record(ctx);
6780 saxdb_end_record(ctx);
6783 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6784 saxdb_end_record(ctx);
6788 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6792 saxdb_start_record(ctx, ntype->name, 0);
6793 switch(ntype->set_access_type)
6795 case NOTE_SET_CHANNEL_ACCESS:
6796 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6798 case NOTE_SET_CHANNEL_SETTER:
6799 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6801 case NOTE_SET_PRIVILEGED: default:
6802 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6805 switch(ntype->visible_type)
6807 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6808 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6809 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6811 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6812 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6813 saxdb_end_record(ctx);
6817 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6819 struct do_not_register *dnr;
6822 for(it = dict_first(dnrs); it; it = iter_next(it))
6824 dnr = iter_data(it);
6825 saxdb_start_record(ctx, dnr->chan_name, 0);
6827 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6828 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6829 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6830 saxdb_end_record(ctx);
6835 chanserv_saxdb_write(struct saxdb_context *ctx)
6838 struct chanData *channel;
6841 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6842 for(it = dict_first(note_types); it; it = iter_next(it))
6843 chanserv_write_note_type(ctx, iter_data(it));
6844 saxdb_end_record(ctx);
6847 saxdb_start_record(ctx, KEY_DNR, 1);
6848 write_dnrs_helper(ctx, handle_dnrs);
6849 write_dnrs_helper(ctx, plain_dnrs);
6850 write_dnrs_helper(ctx, mask_dnrs);
6851 saxdb_end_record(ctx);
6854 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6855 for(channel = channelList; channel; channel = channel->next)
6856 chanserv_write_channel(ctx, channel);
6857 saxdb_end_record(ctx);
6863 chanserv_db_cleanup(void) {
6865 unreg_part_func(handle_part);
6867 unregister_channel(channelList, "terminating.");
6868 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6869 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6870 free(chanserv_conf.support_channels.list);
6871 dict_delete(handle_dnrs);
6872 dict_delete(plain_dnrs);
6873 dict_delete(mask_dnrs);
6874 dict_delete(note_types);
6875 free_string_list(chanserv_conf.eightball);
6876 free_string_list(chanserv_conf.old_ban_names);
6877 free_string_list(chanserv_conf.set_shows);
6878 free(set_shows_list.list);
6879 free(uset_shows_list.list);
6882 struct userData *helper = helperList;
6883 helperList = helperList->next;
6888 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6889 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6890 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6893 init_chanserv(const char *nick)
6895 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6896 conf_register_reload(chanserv_conf_read);
6898 reg_server_link_func(handle_server_link);
6900 reg_new_channel_func(handle_new_channel);
6901 reg_join_func(handle_join);
6902 reg_part_func(handle_part);
6903 reg_kick_func(handle_kick);
6904 reg_topic_func(handle_topic);
6905 reg_mode_change_func(handle_mode);
6906 reg_nick_change_func(handle_nick_change);
6908 reg_auth_func(handle_auth);
6909 reg_handle_rename_func(handle_rename);
6910 reg_unreg_func(handle_unreg);
6912 handle_dnrs = dict_new();
6913 dict_set_free_data(handle_dnrs, free);
6914 plain_dnrs = dict_new();
6915 dict_set_free_data(plain_dnrs, free);
6916 mask_dnrs = dict_new();
6917 dict_set_free_data(mask_dnrs, free);
6919 reg_svccmd_unbind_func(handle_svccmd_unbind);
6920 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6921 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6922 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6923 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6924 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6925 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6926 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6927 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6928 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6930 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6931 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6933 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6934 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6935 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6936 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6937 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6939 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6940 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6941 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6942 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6943 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6945 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6946 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6947 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6948 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6950 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6951 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6952 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6953 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6954 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6955 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6956 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6957 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6959 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6960 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6961 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6962 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6963 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6964 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6965 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6966 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6967 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6968 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6969 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6970 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6971 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6972 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6974 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
6975 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6976 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6977 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6978 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6980 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6981 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6983 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6984 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6985 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6986 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6987 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6988 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6989 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6990 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6991 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6992 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6994 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
6995 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
6997 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
6998 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
6999 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7000 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7002 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7003 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7004 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7005 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7006 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7008 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7009 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7010 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7011 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7012 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7013 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7015 /* Channel options */
7016 DEFINE_CHANNEL_OPTION(defaulttopic);
7017 DEFINE_CHANNEL_OPTION(topicmask);
7018 DEFINE_CHANNEL_OPTION(greeting);
7019 DEFINE_CHANNEL_OPTION(usergreeting);
7020 DEFINE_CHANNEL_OPTION(modes);
7021 DEFINE_CHANNEL_OPTION(enfops);
7022 DEFINE_CHANNEL_OPTION(giveops);
7023 DEFINE_CHANNEL_OPTION(protect);
7024 DEFINE_CHANNEL_OPTION(enfmodes);
7025 DEFINE_CHANNEL_OPTION(enftopic);
7026 DEFINE_CHANNEL_OPTION(pubcmd);
7027 DEFINE_CHANNEL_OPTION(voice);
7028 DEFINE_CHANNEL_OPTION(userinfo);
7029 DEFINE_CHANNEL_OPTION(dynlimit);
7030 DEFINE_CHANNEL_OPTION(topicsnarf);
7031 DEFINE_CHANNEL_OPTION(nodelete);
7032 DEFINE_CHANNEL_OPTION(toys);
7033 DEFINE_CHANNEL_OPTION(setters);
7034 DEFINE_CHANNEL_OPTION(topicrefresh);
7035 DEFINE_CHANNEL_OPTION(ctcpusers);
7036 DEFINE_CHANNEL_OPTION(ctcpreaction);
7037 DEFINE_CHANNEL_OPTION(peoninvite);
7038 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7040 /* Alias set topic to set defaulttopic for compatibility. */
7041 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7044 DEFINE_USER_OPTION(noautoop);
7045 DEFINE_USER_OPTION(autoinvite);
7046 DEFINE_USER_OPTION(info);
7048 /* Alias uset autovoice to uset autoop. */
7049 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7051 note_types = dict_new();
7052 dict_set_free_data(note_types, chanserv_deref_note_type);
7055 chanserv = AddService(nick, "Channel Services");
7056 service_register(chanserv, '!');
7057 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7059 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7061 if(chanserv_conf.channel_expire_frequency)
7062 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7064 if(chanserv_conf.refresh_period)
7066 time_t next_refresh;
7067 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7068 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7071 reg_exit_func(chanserv_db_cleanup);
7072 message_register_table(msgtab);