1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for opserv_bad_channel() */
27 #define CHANSERV_CONF_NAME "services/chanserv"
29 /* ChanServ options */
30 #define KEY_SUPPORT_CHANNEL "support_channel"
31 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
32 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
33 #define KEY_INFO_DELAY "info_delay"
34 #define KEY_MAX_GREETLEN "max_greetlen"
35 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
36 #define KEY_ADJUST_DELAY "adjust_delay"
37 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
38 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
39 #define KEY_MAX_CHAN_USERS "max_chan_users"
40 #define KEY_MAX_CHAN_BANS "max_chan_bans"
41 #define KEY_NICK "nick"
42 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
43 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
44 #define KEY_SWITCH_TIMEOUT "switch_timeout"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
56 /* ChanServ database */
57 #define KEY_CHANNELS "channels"
58 #define KEY_NOTE_TYPES "note_types"
60 /* Note type parameters */
61 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
62 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
63 #define KEY_NOTE_SETTER_ACCESS "setter_access"
64 #define KEY_NOTE_VISIBILITY "visibility"
65 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
66 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
67 #define KEY_NOTE_VIS_ALL "all"
68 #define KEY_NOTE_MAX_LENGTH "max_length"
69 #define KEY_NOTE_SETTER "setter"
70 #define KEY_NOTE_NOTE "note"
72 /* Do-not-register channels */
74 #define KEY_DNR_SET "set"
75 #define KEY_DNR_SETTER "setter"
76 #define KEY_DNR_REASON "reason"
79 #define KEY_REGISTERED "registered"
80 #define KEY_REGISTRAR "registrar"
81 #define KEY_SUSPENDED "suspended"
82 #define KEY_PREVIOUS "previous"
83 #define KEY_SUSPENDER "suspender"
84 #define KEY_ISSUED "issued"
85 #define KEY_REVOKED "revoked"
86 #define KEY_SUSPEND_EXPIRES "suspend_expires"
87 #define KEY_SUSPEND_REASON "suspend_reason"
88 #define KEY_VISITED "visited"
89 #define KEY_TOPIC "topic"
90 #define KEY_GREETING "greeting"
91 #define KEY_USER_GREETING "user_greeting"
92 #define KEY_MODES "modes"
93 #define KEY_FLAGS "flags"
94 #define KEY_OPTIONS "options"
95 #define KEY_USERS "users"
96 #define KEY_BANS "bans"
98 #define KEY_NOTES "notes"
99 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_LEVEL "level"
103 #define KEY_INFO "info"
104 #define KEY_SEEN "seen"
107 #define KEY_OWNER "owner"
108 #define KEY_REASON "reason"
109 #define KEY_SET "set"
110 #define KEY_DURATION "duration"
111 #define KEY_EXPIRES "expires"
112 #define KEY_TRIGGERED "triggered"
114 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_INFO_LINES)
115 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
117 /* Administrative messages */
118 static const struct message_entry msgtab[] = {
119 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
121 /* Channel registration */
122 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
123 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
124 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
125 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
126 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
127 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
129 /* Do-not-register channels */
130 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
131 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
132 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
133 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
134 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
135 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
136 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
137 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
138 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
139 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
140 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
142 /* Channel unregistration */
143 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
144 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
145 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
146 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
149 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
150 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
152 /* Channel merging */
153 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
154 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
155 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
156 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
157 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
159 /* Handle unregistration */
160 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
163 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
164 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
165 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
166 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
167 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
168 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
169 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
170 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
171 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
172 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
173 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
174 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
175 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
177 /* Removing yourself from a channel. */
178 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
179 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
180 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
182 /* User management */
183 { "CSMSG_ADDED_USER", "Added new %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
241 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
242 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
243 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
244 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
245 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
246 { "CSMSG_SET_MODES", "$bModes $b %s" },
247 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
248 { "CSMSG_SET_USERINFO", "$bUserInfo $b %s" },
249 { "CSMSG_SET_VOICE", "$bVoice $b %s" },
250 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
251 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %s" },
252 { "CSMSG_SET_PEONINVITE", "$bPeonInvite $b %s" },
253 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
254 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
255 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
256 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
257 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
258 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
259 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
260 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
261 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
262 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
263 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
264 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
265 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
266 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
267 { "CSMSG_USET_INFO", "$bInfo $b %s" },
269 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
270 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
271 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
272 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
273 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
274 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
275 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
276 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
277 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
278 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
279 { "CSMSG_PROTECT_NONE", "No users will be protected." },
280 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
281 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
282 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
283 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
284 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
285 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
286 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
287 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
288 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
289 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
290 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
291 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
293 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
294 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
295 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
296 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
297 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
299 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
300 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
301 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
303 /* Channel userlist */
304 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
305 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
306 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
307 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
309 /* Channel note list */
310 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
311 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
312 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
313 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
314 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
315 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
316 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
317 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
318 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
319 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
320 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
321 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
322 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
323 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
324 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
326 /* Channel [un]suspension */
327 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
328 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
329 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
330 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
331 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
332 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
333 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
335 /* Access information */
336 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
337 { "CSMSG_ACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
338 { "CSMSG_SQUAT_ACCESS", "You do not have access to any channels." },
339 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
340 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
341 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
342 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
343 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
344 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
345 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
346 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
348 /* Seen information */
349 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
350 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
351 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
352 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
354 /* Names information */
355 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
356 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
358 /* Channel information */
359 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
360 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
361 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
362 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
363 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
364 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
365 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
366 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
367 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
368 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
369 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
370 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
371 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
372 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
373 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
374 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
375 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
378 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
379 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
381 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
382 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
383 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
384 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
385 { "CSMSG_PEEK_OPS", "$bOps:$b" },
386 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
388 /* Network information */
389 { "CSMSG_NETWORK_INFO", "Network Information:" },
390 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
391 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
392 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
393 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
394 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
395 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
396 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
399 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
400 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
401 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
403 /* Channel searches */
404 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
405 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
406 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
407 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
409 /* Channel configuration */
410 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
411 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
412 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
415 { "CSMSG_USER_OPTIONS", "User Options:" },
416 { "CSMSG_USER_PROTECTED", "That user is protected." },
419 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
420 { "CSMSG_PING_RESPONSE", "Pong!" },
421 { "CSMSG_WUT_RESPONSE", "wut" },
422 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
423 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
424 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
425 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
426 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
427 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
428 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
431 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
435 /* eject_user and unban_user flags */
436 #define ACTION_KICK 0x0001
437 #define ACTION_BAN 0x0002
438 #define ACTION_ADD_BAN 0x0004
439 #define ACTION_ADD_TIMED_BAN 0x0008
440 #define ACTION_UNBAN 0x0010
441 #define ACTION_DEL_BAN 0x0020
443 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
444 #define MODELEN 40 + KEYLEN
448 #define CSFUNC_ARGS user, channel, argc, argv, cmd
450 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
451 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
452 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
453 reply("MSG_MISSING_PARAMS", argv[0]); \
457 DECLARE_LIST(dnrList, struct do_not_register *);
458 DEFINE_LIST(dnrList, struct do_not_register *);
460 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
462 struct userNode *chanserv;
464 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
465 static struct log_type *CS_LOG;
469 struct channelList support_channels;
470 struct mod_chanmode default_modes;
472 unsigned long db_backup_frequency;
473 unsigned long channel_expire_frequency;
476 unsigned int adjust_delay;
477 long channel_expire_delay;
478 unsigned int nodelete_level;
480 unsigned int adjust_threshold;
481 int join_flood_threshold;
483 unsigned int greeting_length;
484 unsigned int refresh_period;
486 unsigned int max_owned;
487 unsigned int max_chan_users;
488 unsigned int max_chan_bans;
490 struct string_list *set_shows;
491 struct string_list *eightball;
492 struct string_list *old_ban_names;
494 const char *ctcp_short_ban_duration;
495 const char *ctcp_long_ban_duration;
497 const char *irc_operator_epithet;
498 const char *network_helper_epithet;
499 const char *support_helper_epithet;
504 struct userNode *user;
505 struct userNode *bot;
506 struct chanNode *channel;
508 unsigned short lowest;
509 unsigned short highest;
510 struct userData **users;
511 struct helpfile_table table;
514 enum note_access_type
516 NOTE_SET_CHANNEL_ACCESS,
517 NOTE_SET_CHANNEL_SETTER,
521 enum note_visible_type
524 NOTE_VIS_CHANNEL_USERS,
530 enum note_access_type set_access_type;
532 unsigned int min_opserv;
533 unsigned short min_ulevel;
535 enum note_visible_type visible_type;
536 unsigned int max_length;
543 struct note_type *type;
544 char setter[NICKSERV_HANDLE_LEN+1];
548 static unsigned int registered_channels;
549 static unsigned int banCount;
551 static const struct {
554 unsigned short level;
557 { "peon", "Peon", UL_PEON, '+' },
558 { "op", "Op", UL_OP, '@' },
559 { "master", "Master", UL_MASTER, '%' },
560 { "coowner", "Coowner", UL_COOWNER, '*' },
561 { "owner", "Owner", UL_OWNER, '!' },
562 { "helper", "BUG:", UL_HELPER, 'X' }
565 static const struct {
568 unsigned short default_value;
569 unsigned int old_idx;
571 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2 },
572 { "CSMSG_SET_ENFOPS", "enfops", 300, 1 },
573 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3 },
574 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4 },
575 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5 },
576 { "CSMSG_SET_SETTERS", "setters", 400, 7 },
577 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9 }
580 struct charOptionValues {
583 } protectValues[] = {
584 { 'a', "CSMSG_PROTECT_ALL" },
585 { 'e', "CSMSG_PROTECT_EQUAL" },
586 { 'l', "CSMSG_PROTECT_LOWER" },
587 { 'n', "CSMSG_PROTECT_NONE" }
589 { 'd', "CSMSG_TOYS_DISABLED" },
590 { 'n', "CSMSG_TOYS_PRIVATE" },
591 { 'p', "CSMSG_TOYS_PUBLIC" }
592 }, topicRefreshValues[] = {
593 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
594 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
595 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
596 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
597 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
598 }, ctcpReactionValues[] = {
599 { 'k', "CSMSG_CTCPREACTION_KICK" },
600 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
601 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
602 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
605 static const struct {
609 unsigned int old_idx;
611 struct charOptionValues *values;
613 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
614 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
615 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
616 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
619 struct userData *helperList;
620 struct chanData *channelList;
621 static struct module *chanserv_module;
622 static unsigned int userCount;
624 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
625 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
626 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
629 user_level_from_name(const char *name, unsigned short clamp_level)
631 unsigned int level = 0, ii;
634 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
635 if(!irccasecmp(name, accessLevels[ii].name))
636 level = accessLevels[ii].level;
637 if(level > clamp_level)
643 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
646 *minl = strtoul(arg, &sep, 10);
654 *maxl = strtoul(sep+1, &sep, 10);
662 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
664 struct userData *uData, **head;
666 if(!channel || !handle)
669 if(override && HANDLE_FLAGGED(handle, HELPING)
670 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
672 for(uData = helperList;
673 uData && uData->handle != handle;
674 uData = uData->next);
678 uData = calloc(1, sizeof(struct userData));
679 uData->handle = handle;
681 uData->access = UL_HELPER;
687 uData->next = helperList;
689 helperList->prev = uData;
697 for(uData = channel->users; uData; uData = uData->next)
698 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
701 head = &(channel->users);
704 if(uData && (uData != *head))
706 /* Shuffle the user to the head of whatever list he was in. */
708 uData->next->prev = uData->prev;
710 uData->prev->next = uData->next;
716 (**head).prev = uData;
723 /* Returns non-zero if user has at least the minimum access.
724 * exempt_owner is set when handling !set, so the owner can set things
727 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
729 struct userData *uData;
730 struct chanData *cData = channel->channel_info;
731 unsigned short minimum = cData->lvlOpts[opt];
734 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
737 if(minimum <= uData->access)
739 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
744 /* Scan for other users authenticated to the same handle
745 still in the channel. If so, keep them listed as present.
747 user is optional, if not null, it skips checking that userNode
748 (for the handle_part function) */
750 scan_handle_presence(struct chanNode *channel, struct handle_info *handle, struct userNode *user)
752 struct userData *uData;
754 if(!channel->channel_info || IsSuspended(channel->channel_info))
757 uData = GetTrueChannelAccess(channel->channel_info, handle);
760 struct modeNode *mn = find_handle_in_channel(channel, handle, user);
773 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
775 unsigned int eflags, argc;
777 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
779 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
780 if(!channel->channel_info
781 || IsSuspended(channel->channel_info)
783 || !ircncasecmp(text, "ACTION ", 7))
785 /* Figure out the minimum level needed to CTCP the channel */
786 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
788 /* We need to enforce against them; do so. */
791 argv[1] = user->nick;
793 if(GetUserMode(channel, user))
794 eflags |= ACTION_KICK;
795 switch(channel->channel_info->chOpts[chCTCPReaction]) {
796 default: case 'k': /* just do the kick */ break;
798 eflags |= ACTION_BAN;
801 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
802 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
805 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
806 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
809 argv[argc++] = bad_ctcp_reason;
810 eject_user(chanserv, channel, argc, argv, NULL, eflags);
814 chanserv_create_note_type(const char *name)
816 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
817 strcpy(ntype->name, name);
819 dict_insert(note_types, ntype->name, ntype);
824 chanserv_deref_note_type(void *data)
826 struct note_type *ntype = data;
828 if(--ntype->refs > 0)
834 chanserv_flush_note_type(struct note_type *ntype)
836 struct chanData *cData;
837 for(cData = channelList; cData; cData = cData->next)
838 dict_remove(cData->notes, ntype->name);
842 chanserv_truncate_notes(struct note_type *ntype)
844 struct chanData *cData;
846 unsigned int size = sizeof(*note) + ntype->max_length;
848 for(cData = channelList; cData; cData = cData->next) {
849 note = dict_find(cData->notes, ntype->name, NULL);
852 if(strlen(note->note) <= ntype->max_length)
854 dict_remove2(cData->notes, ntype->name, 1);
855 note = realloc(note, size);
856 note->note[ntype->max_length] = 0;
857 dict_insert(cData->notes, ntype->name, note);
861 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
864 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
867 unsigned int len = strlen(text);
869 if(len > type->max_length) len = type->max_length;
870 note = calloc(1, sizeof(*note) + len);
872 strncpy(note->setter, setter, sizeof(note->setter)-1);
873 memcpy(note->note, text, len);
875 dict_insert(channel->notes, type->name, note);
881 chanserv_free_note(void *data)
883 struct note *note = data;
885 chanserv_deref_note_type(note->type);
886 assert(note->type->refs > 0); /* must use delnote to remove the type */
890 static MODCMD_FUNC(cmd_createnote) {
891 struct note_type *ntype;
892 unsigned int arg = 1, existed = 0, max_length;
894 if((ntype = dict_find(note_types, argv[1], NULL)))
897 ntype = chanserv_create_note_type(argv[arg]);
898 if(!irccasecmp(argv[++arg], "privileged"))
901 ntype->set_access_type = NOTE_SET_PRIVILEGED;
902 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
904 else if(!irccasecmp(argv[arg], "channel"))
906 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
909 reply("CSMSG_INVALID_ACCESS", argv[arg]);
912 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
913 ntype->set_access.min_ulevel = ulvl;
915 else if(!irccasecmp(argv[arg], "setter"))
917 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
921 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
925 if(!irccasecmp(argv[++arg], "privileged"))
926 ntype->visible_type = NOTE_VIS_PRIVILEGED;
927 else if(!irccasecmp(argv[arg], "channel_users"))
928 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
929 else if(!irccasecmp(argv[arg], "all"))
930 ntype->visible_type = NOTE_VIS_ALL;
932 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
936 if((arg+1) >= argc) {
937 reply("MSG_MISSING_PARAMS", argv[0]);
940 max_length = strtoul(argv[++arg], NULL, 0);
941 if(max_length < 20 || max_length > 450)
943 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
946 if(existed && (max_length < ntype->max_length))
948 ntype->max_length = max_length;
949 chanserv_truncate_notes(ntype);
951 ntype->max_length = max_length;
954 reply("CSMSG_NOTE_MODIFIED", ntype->name);
956 reply("CSMSG_NOTE_CREATED", ntype->name);
961 dict_remove(note_types, ntype->name);
965 static MODCMD_FUNC(cmd_removenote) {
966 struct note_type *ntype;
969 ntype = dict_find(note_types, argv[1], NULL);
970 force = (argc > 2) && !irccasecmp(argv[2], "force");
973 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
980 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
983 chanserv_flush_note_type(ntype);
985 dict_remove(note_types, argv[1]);
986 reply("CSMSG_NOTE_DELETED", argv[1]);
991 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
995 if(orig->modes_set & change->modes_clear)
997 if(orig->modes_clear & change->modes_set)
999 if((orig->modes_set & MODE_KEY)
1000 && strcmp(orig->new_key, change->new_key))
1002 if((orig->modes_set & MODE_LIMIT)
1003 && (orig->new_limit != change->new_limit))
1008 static char max_length_text[MAXLEN+1][16];
1010 static struct helpfile_expansion
1011 chanserv_expand_variable(const char *variable)
1013 struct helpfile_expansion exp;
1015 if(!irccasecmp(variable, "notes"))
1018 exp.type = HF_TABLE;
1019 exp.value.table.length = 1;
1020 exp.value.table.width = 3;
1021 exp.value.table.flags = 0;
1022 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1023 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1024 exp.value.table.contents[0][0] = "Note Type";
1025 exp.value.table.contents[0][1] = "Visibility";
1026 exp.value.table.contents[0][2] = "Max Length";
1027 for(it=dict_first(note_types); it; it=iter_next(it))
1029 struct note_type *ntype = iter_data(it);
1032 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1033 row = exp.value.table.length++;
1034 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1035 exp.value.table.contents[row][0] = ntype->name;
1036 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1037 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1039 if(!max_length_text[ntype->max_length][0])
1040 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1041 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1046 exp.type = HF_STRING;
1047 exp.value.str = NULL;
1051 static struct chanData*
1052 register_channel(struct chanNode *cNode, char *registrar)
1054 struct chanData *channel;
1055 enum levelOption lvlOpt;
1056 enum charOption chOpt;
1058 channel = calloc(1, sizeof(struct chanData));
1060 channel->notes = dict_new();
1061 dict_set_free_data(channel->notes, chanserv_free_note);
1063 channel->registrar = strdup(registrar);
1064 channel->registered = now;
1065 channel->visited = now;
1066 channel->limitAdjusted = now;
1067 channel->flags = CHANNEL_DEFAULT_FLAGS;
1068 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1069 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1070 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1071 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1073 channel->prev = NULL;
1074 channel->next = channelList;
1077 channelList->prev = channel;
1078 channelList = channel;
1079 registered_channels++;
1081 channel->channel = cNode;
1083 cNode->channel_info = channel;
1088 static struct userData*
1089 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1091 struct userData *ud;
1093 if(access > UL_OWNER)
1096 ud = calloc(1, sizeof(*ud));
1097 ud->channel = channel;
1098 ud->handle = handle;
1100 ud->access = access;
1101 ud->info = info ? strdup(info) : NULL;
1104 ud->next = channel->users;
1106 channel->users->prev = ud;
1107 channel->users = ud;
1109 channel->userCount++;
1113 ud->u_next = ud->handle->channels;
1115 ud->u_next->u_prev = ud;
1116 ud->handle->channels = ud;
1121 static void unregister_channel(struct chanData *channel, const char *reason);
1124 del_channel_user(struct userData *user, int do_gc)
1126 struct chanData *channel = user->channel;
1128 channel->userCount--;
1132 user->prev->next = user->next;
1134 channel->users = user->next;
1136 user->next->prev = user->prev;
1139 user->u_prev->u_next = user->u_next;
1141 user->handle->channels = user->u_next;
1143 user->u_next->u_prev = user->u_prev;
1147 if(do_gc && !channel->users && !IsProtected(channel))
1148 unregister_channel(channel, "lost all users.");
1151 static void expire_ban(void *data);
1153 static struct banData*
1154 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1157 unsigned int ii, l1, l2;
1162 bd = malloc(sizeof(struct banData));
1164 bd->channel = channel;
1166 bd->triggered = triggered;
1167 bd->expires = expires;
1169 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1171 extern const char *hidden_host_suffix;
1172 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1176 l2 = strlen(old_name);
1179 if(irccasecmp(mask + l1 - l2, old_name))
1181 new_mask = alloca(MAXLEN);
1182 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1185 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1187 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1188 bd->reason = reason ? strdup(reason) : NULL;
1191 timeq_add(expires, expire_ban, bd);
1194 bd->next = channel->bans;
1196 channel->bans->prev = bd;
1198 channel->banCount++;
1205 del_channel_ban(struct banData *ban)
1207 ban->channel->banCount--;
1211 ban->prev->next = ban->next;
1213 ban->channel->bans = ban->next;
1216 ban->next->prev = ban->prev;
1219 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1228 expire_ban(void *data)
1230 struct banData *bd = data;
1231 if(!IsSuspended(bd->channel))
1233 struct banList bans;
1234 struct mod_chanmode change;
1236 bans = bd->channel->channel->banlist;
1237 change.modes_set = change.modes_clear = 0;
1239 for(ii=0; ii<bans.used; ii++)
1241 if(!strcmp(bans.list[ii]->ban, bd->mask))
1244 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1245 change.args[0].hostmask = bd->mask;
1246 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1252 del_channel_ban(bd);
1255 static void chanserv_expire_suspension(void *data);
1258 unregister_channel(struct chanData *channel, const char *reason)
1260 char msgbuf[MAXLEN];
1262 /* After channel unregistration, the following must be cleaned
1264 - Channel information.
1267 - Channel suspension data.
1268 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1274 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1276 while(channel->users)
1277 del_channel_user(channel->users, 0);
1279 while(channel->bans)
1280 del_channel_ban(channel->bans);
1282 if(channel->topic) free(channel->topic);
1283 if(channel->registrar) free(channel->registrar);
1284 if(channel->greeting) free(channel->greeting);
1285 if(channel->user_greeting) free(channel->user_greeting);
1286 if(channel->topic_mask) free(channel->topic_mask);
1288 if(channel->prev) channel->prev->next = channel->next;
1289 else channelList = channel->next;
1291 if(channel->next) channel->next->prev = channel->prev;
1293 if(channel->suspended)
1295 struct chanNode *cNode = channel->channel;
1296 struct suspended *suspended, *next_suspended;
1298 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1300 next_suspended = suspended->previous;
1301 free(suspended->suspender);
1302 free(suspended->reason);
1303 if(suspended->expires)
1304 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1309 cNode->channel_info = NULL;
1311 channel->channel->channel_info = NULL;
1314 dict_delete(channel->notes);
1315 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1316 if(!IsSuspended(channel))
1317 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1318 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1319 UnlockChannel(channel->channel);
1321 registered_channels--;
1325 expire_channels(UNUSED_ARG(void *data))
1327 struct chanData *channel, *next;
1328 struct userData *user;
1329 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1331 intervalString(delay, chanserv_conf.channel_expire_delay);
1332 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1334 for(channel = channelList; channel; channel = next)
1336 next = channel->next;
1338 /* See if the channel can be expired. */
1339 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1340 || IsProtected(channel))
1343 /* Make sure there are no high-ranking users still in the channel. */
1344 for(user=channel->users; user; user=user->next)
1345 if(user->present && (user->access >= UL_PRESENT))
1350 /* Unregister the channel */
1351 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1352 unregister_channel(channel, "registration expired.");
1355 if(chanserv_conf.channel_expire_frequency)
1356 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1360 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1362 char protect = channel->chOpts[chProtect];
1363 struct userData *cs_victim, *cs_aggressor;
1365 /* Don't protect if no one is to be protected, someone is attacking
1366 himself, or if the aggressor is an IRC Operator. */
1367 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1370 /* Don't protect if the victim isn't authenticated (because they
1371 can't be a channel user), unless we are to protect non-users
1373 cs_victim = GetChannelAccess(channel, victim->handle_info);
1374 if(protect != 'a' && !cs_victim)
1377 /* Protect if the aggressor isn't a user because at this point,
1378 the aggressor can only be less than or equal to the victim. */
1379 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1383 /* If the aggressor was a user, then the victim can't be helped. */
1390 if(cs_victim->access > cs_aggressor->access)
1395 if(cs_victim->access >= cs_aggressor->access)
1404 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1406 struct chanData *cData = channel->channel_info;
1407 struct userData *cs_victim;
1409 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1410 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1411 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1413 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1421 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1423 if(IsService(victim))
1425 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1429 if(protect_user(victim, user, channel->channel_info))
1431 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1438 static struct do_not_register *
1439 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1441 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1442 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1443 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1444 strcpy(dnr->reason, reason);
1446 if(dnr->chan_name[0] == '*')
1447 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1448 else if(strpbrk(dnr->chan_name, "*?"))
1449 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1451 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1455 static struct dnrList
1456 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1458 struct dnrList list;
1460 struct do_not_register *dnr;
1462 dnrList_init(&list);
1463 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1464 dnrList_append(&list, dnr);
1465 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1466 dnrList_append(&list, dnr);
1468 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1469 if(match_ircglob(chan_name, iter_key(it)))
1470 dnrList_append(&list, iter_data(it));
1475 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1477 struct dnrList list;
1478 struct do_not_register *dnr;
1480 char buf[INTERVALLEN];
1482 list = chanserv_find_dnrs(chan_name, handle);
1483 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1485 dnr = list.list[ii];
1488 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1489 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1492 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1495 reply("CSMSG_MORE_DNRS", list.used - ii);
1500 struct do_not_register *
1501 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1503 struct do_not_register *dnr;
1506 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1510 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1512 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1513 if(match_ircglob(chan_name, iter_key(it)))
1514 return iter_data(it);
1519 static CHANSERV_FUNC(cmd_noregister)
1522 struct do_not_register *dnr;
1523 char buf[INTERVALLEN];
1524 unsigned int matches;
1530 reply("CSMSG_DNR_SEARCH_RESULTS");
1532 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1534 dnr = iter_data(it);
1536 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1538 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1541 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1543 dnr = iter_data(it);
1545 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1547 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1550 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1552 dnr = iter_data(it);
1554 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1556 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1561 reply("MSG_MATCH_COUNT", matches);
1563 reply("MSG_NO_MATCHES");
1569 if(!IsChannelName(target) && (*target != '*'))
1571 reply("CSMSG_NOT_DNR", target);
1577 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1578 if((*target == '*') && !get_handle_info(target + 1))
1580 reply("MSG_HANDLE_UNKNOWN", target + 1);
1583 chanserv_add_dnr(target, user->handle_info->handle, reason);
1584 reply("CSMSG_NOREGISTER_CHANNEL", target);
1588 reply("CSMSG_DNR_SEARCH_RESULTS");
1590 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1592 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1594 reply("MSG_NO_MATCHES");
1598 static CHANSERV_FUNC(cmd_allowregister)
1600 const char *chan_name = argv[1];
1602 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1604 dict_remove(handle_dnrs, chan_name+1);
1605 reply("CSMSG_DNR_REMOVED", chan_name);
1607 else if(dict_find(plain_dnrs, chan_name, NULL))
1609 dict_remove(plain_dnrs, chan_name);
1610 reply("CSMSG_DNR_REMOVED", chan_name);
1612 else if(dict_find(mask_dnrs, chan_name, NULL))
1614 dict_remove(mask_dnrs, chan_name);
1615 reply("CSMSG_DNR_REMOVED", chan_name);
1619 reply("CSMSG_NO_SUCH_DNR", chan_name);
1626 chanserv_get_owned_count(struct handle_info *hi)
1628 struct userData *cList;
1631 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1632 if(cList->access == UL_OWNER)
1637 static CHANSERV_FUNC(cmd_register)
1639 struct mod_chanmode *change;
1640 struct handle_info *handle;
1641 struct chanData *cData;
1642 struct modeNode *mn;
1643 char reason[MAXLEN];
1645 unsigned int new_channel, force=0;
1646 struct do_not_register *dnr;
1650 if(channel->channel_info)
1652 reply("CSMSG_ALREADY_REGGED", channel->name);
1656 if(channel->bad_channel)
1658 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1662 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1664 reply("CSMSG_MUST_BE_OPPED", channel->name);
1669 chan_name = channel->name;
1673 if((argc < 2) || !IsChannelName(argv[1]))
1675 reply("MSG_NOT_CHANNEL_NAME");
1679 if(opserv_bad_channel(argv[1]))
1681 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1686 chan_name = argv[1];
1689 if(argc >= (new_channel+2))
1691 if(!IsHelping(user))
1693 reply("CSMSG_PROXY_FORBIDDEN");
1697 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1699 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1700 dnr = chanserv_is_dnr(chan_name, handle);
1704 handle = user->handle_info;
1705 dnr = chanserv_is_dnr(chan_name, handle);
1709 if(!IsHelping(user))
1710 reply("CSMSG_DNR_CHANNEL", chan_name);
1712 chanserv_show_dnrs(user, cmd, chan_name, handle);
1716 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1718 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1723 channel = AddChannel(argv[1], now, NULL, NULL);
1725 cData = register_channel(channel, user->handle_info->handle);
1726 add_channel_user(cData, handle, UL_OWNER, 0, NULL);
1727 scan_handle_presence(channel, handle, NULL);
1728 cData->modes = chanserv_conf.default_modes;
1729 change = mod_chanmode_dup(&cData->modes, 1);
1730 change->args[change->argc].mode = MODE_CHANOP;
1731 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1733 mod_chanmode_announce(chanserv, channel, change);
1734 mod_chanmode_free(change);
1736 /* Initialize the channel's max user record. */
1737 cData->max = channel->members.used;
1739 if(handle != user->handle_info)
1740 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1742 reply("CSMSG_REG_SUCCESS", channel->name);
1744 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1745 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1750 make_confirmation_string(struct userData *uData)
1752 static char strbuf[16];
1757 for(src = uData->handle->handle; *src; )
1758 accum = accum * 31 + toupper(*src++);
1760 for(src = uData->channel->channel->name; *src; )
1761 accum = accum * 31 + toupper(*src++);
1762 sprintf(strbuf, "%08x", accum);
1766 static CHANSERV_FUNC(cmd_unregister)
1769 char reason[MAXLEN];
1770 struct chanData *cData;
1771 struct userData *uData;
1773 cData = channel->channel_info;
1776 reply("CSMSG_NOT_REGISTERED", channel->name);
1780 uData = GetChannelUser(cData, user->handle_info);
1781 if(!uData || (uData->access < UL_OWNER))
1783 reply("CSMSG_NO_ACCESS");
1787 if(IsProtected(cData))
1789 reply("CSMSG_UNREG_NODELETE", channel->name);
1793 if(!IsHelping(user))
1795 const char *confirm_string;
1796 if(IsSuspended(cData))
1798 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1801 confirm_string = make_confirmation_string(uData);
1802 if((argc < 2) || strcmp(argv[1], confirm_string))
1804 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1809 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1810 name = strdup(channel->name);
1811 unregister_channel(cData, reason);
1812 reply("CSMSG_UNREG_SUCCESS", name);
1817 static CHANSERV_FUNC(cmd_move)
1819 struct chanNode *target;
1820 struct modeNode *mn;
1821 struct userData *uData;
1822 char reason[MAXLEN];
1823 struct do_not_register *dnr;
1827 if(IsProtected(channel->channel_info))
1829 reply("CSMSG_MOVE_NODELETE", channel->name);
1833 if(!IsChannelName(argv[1]))
1835 reply("MSG_NOT_CHANNEL_NAME");
1839 if(opserv_bad_channel(argv[1]))
1841 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1845 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1847 for(uData = channel->channel_info->users; uData; uData = uData->next)
1849 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1851 if(!IsHelping(user))
1852 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1854 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1860 if(!(target = GetChannel(argv[1])))
1862 target = AddChannel(argv[1], now, NULL, NULL);
1863 LockChannel(target);
1864 if(!IsSuspended(channel->channel_info))
1865 AddChannelUser(chanserv, target);
1867 else if(target->channel_info)
1869 reply("CSMSG_ALREADY_REGGED", target->name);
1872 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1873 && !IsHelping(user))
1875 reply("CSMSG_MUST_BE_OPPED", target->name);
1878 else if(!IsSuspended(channel->channel_info))
1880 struct mod_chanmode change;
1881 change.modes_set = change.modes_clear = 0;
1883 change.args[0].mode = MODE_CHANOP;
1884 change.args[0].member = AddChannelUser(chanserv, target);
1885 mod_chanmode_announce(chanserv, target, &change);
1888 /* Move the channel_info to the target channel; it
1889 shouldn't be necessary to clear timeq callbacks
1890 for the old channel. */
1891 target->channel_info = channel->channel_info;
1892 target->channel_info->channel = target;
1893 channel->channel_info = NULL;
1895 reply("CSMSG_MOVE_SUCCESS", target->name);
1897 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1898 if(!IsSuspended(target->channel_info))
1900 char reason2[MAXLEN];
1901 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1902 DelChannelUser(chanserv, channel, reason2, 0);
1904 UnlockChannel(channel);
1905 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1910 merge_users(struct chanData *source, struct chanData *target)
1912 struct userData *suData, *tuData, *next;
1918 /* Insert the source's users into the scratch area. */
1919 for(suData = source->users; suData; suData = suData->next)
1920 dict_insert(merge, suData->handle->handle, suData);
1922 /* Iterate through the target's users, looking for
1923 users common to both channels. The lower access is
1924 removed from either the scratch area or target user
1926 for(tuData = target->users; tuData; tuData = next)
1928 struct userData *choice;
1930 next = tuData->next;
1932 /* If a source user exists with the same handle as a target
1933 channel's user, resolve the conflict by removing one. */
1934 suData = dict_find(merge, tuData->handle->handle, NULL);
1938 /* Pick the data we want to keep. */
1939 /* If the access is the same, use the later seen time. */
1940 if(suData->access == tuData->access)
1941 choice = (suData->seen > tuData->seen) ? suData : tuData;
1942 else /* Otherwise, keep the higher access level. */
1943 choice = (suData->access > tuData->access) ? suData : tuData;
1945 /* Remove the user that wasn't picked. */
1946 if(choice == tuData)
1948 dict_remove(merge, suData->handle->handle);
1949 del_channel_user(suData, 0);
1952 del_channel_user(tuData, 0);
1955 /* Move the remaining users to the target channel. */
1956 for(it = dict_first(merge); it; it = iter_next(it))
1958 suData = iter_data(it);
1960 /* Insert the user into the target channel's linked list. */
1961 suData->prev = NULL;
1962 suData->next = target->users;
1963 suData->channel = target;
1966 target->users->prev = suData;
1967 target->users = suData;
1969 /* Update the user counts for the target channel; the
1970 source counts are left alone. */
1971 target->userCount++;
1974 /* Possible to assert (source->users == NULL) here. */
1975 source->users = NULL;
1980 merge_bans(struct chanData *source, struct chanData *target)
1982 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1984 /* Hold on to the original head of the target ban list
1985 to avoid comparing source bans with source bans. */
1986 tFront = target->bans;
1988 /* Perform a totally expensive O(n*m) merge, ick. */
1989 for(sbData = source->bans; sbData; sbData = sNext)
1991 /* Flag to track whether the ban's been moved
1992 to the destination yet. */
1995 /* Possible to assert (sbData->prev == NULL) here. */
1996 sNext = sbData->next;
1998 for(tbData = tFront; tbData; tbData = tNext)
2000 tNext = tbData->next;
2002 /* Perform two comparisons between each source
2003 and target ban, conflicts are resolved by
2004 keeping the broader ban and copying the later
2005 expiration and triggered time. */
2006 if(match_ircglobs(tbData->mask, sbData->mask))
2008 /* There is a broader ban in the target channel that
2009 overrides one in the source channel; remove the
2010 source ban and break. */
2011 if(sbData->expires > tbData->expires)
2012 tbData->expires = sbData->expires;
2013 if(sbData->triggered > tbData->triggered)
2014 tbData->triggered = sbData->triggered;
2015 del_channel_ban(sbData);
2018 else if(match_ircglobs(sbData->mask, tbData->mask))
2020 /* There is a broader ban in the source channel that
2021 overrides one in the target channel; remove the
2022 target ban, fall through and move the source over. */
2023 if(tbData->expires > sbData->expires)
2024 sbData->expires = tbData->expires;
2025 if(tbData->triggered > sbData->triggered)
2026 sbData->triggered = tbData->triggered;
2027 if(tbData == tFront)
2029 del_channel_ban(tbData);
2032 /* Source bans can override multiple target bans, so
2033 we allow a source to run through this loop multiple
2034 times, but we can only move it once. */
2039 /* Remove the source ban from the source ban list. */
2041 sbData->next->prev = sbData->prev;
2043 /* Modify the source ban's associated channel. */
2044 sbData->channel = target;
2046 /* Insert the ban into the target channel's linked list. */
2047 sbData->prev = NULL;
2048 sbData->next = target->bans;
2051 target->bans->prev = sbData;
2052 target->bans = sbData;
2054 /* Update the user counts for the target channel. */
2059 /* Possible to assert (source->bans == NULL) here. */
2060 source->bans = NULL;
2064 merge_data(struct chanData *source, struct chanData *target)
2066 if(source->visited > target->visited)
2067 target->visited = source->visited;
2071 merge_channel(struct chanData *source, struct chanData *target)
2073 merge_users(source, target);
2074 merge_bans(source, target);
2075 merge_data(source, target);
2078 static CHANSERV_FUNC(cmd_merge)
2080 struct userData *target_user;
2081 struct chanNode *target;
2082 char reason[MAXLEN];
2086 /* Make sure the target channel exists and is registered to the user
2087 performing the command. */
2088 if(!(target = GetChannel(argv[1])))
2090 reply("MSG_INVALID_CHANNEL");
2094 if(!target->channel_info)
2096 reply("CSMSG_NOT_REGISTERED", target->name);
2100 if(IsProtected(channel->channel_info))
2102 reply("CSMSG_MERGE_NODELETE");
2106 if(IsSuspended(target->channel_info))
2108 reply("CSMSG_MERGE_SUSPENDED");
2112 if(channel == target)
2114 reply("CSMSG_MERGE_SELF");
2118 target_user = GetChannelUser(target->channel_info, user->handle_info);
2119 if(!target_user || (target_user->access < UL_OWNER))
2121 reply("CSMSG_MERGE_NOT_OWNER");
2125 /* Merge the channel structures and associated data. */
2126 merge_channel(channel->channel_info, target->channel_info);
2127 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2128 unregister_channel(channel->channel_info, reason);
2129 reply("CSMSG_MERGE_SUCCESS", target->name);
2133 static CHANSERV_FUNC(cmd_opchan)
2135 struct mod_chanmode change;
2136 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2138 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2141 channel->channel_info->may_opchan = 0;
2142 change.modes_set = change.modes_clear = 0;
2144 change.args[0].mode = MODE_CHANOP;
2145 change.args[0].member = GetUserMode(channel, chanserv);
2146 mod_chanmode_announce(chanserv, channel, &change);
2147 reply("CSMSG_OPCHAN_DONE", channel->name);
2151 static CHANSERV_FUNC(cmd_adduser)
2153 struct userData *actee;
2154 struct userData *actor;
2155 struct handle_info *handle;
2156 unsigned short access;
2160 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2162 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2166 access = user_level_from_name(argv[1], UL_OWNER);
2169 reply("CSMSG_INVALID_ACCESS", argv[1]);
2173 actor = GetChannelUser(channel->channel_info, user->handle_info);
2174 if(actor->access <= access)
2176 reply("CSMSG_NO_BUMP_ACCESS");
2180 if(!(handle = modcmd_get_handle_info(user, argv[2])))
2183 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2185 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2189 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2190 scan_handle_presence(channel, handle, NULL);
2191 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2195 static CHANSERV_FUNC(cmd_clvl)
2197 struct handle_info *handle;
2198 struct userData *victim;
2199 struct userData *actor;
2200 unsigned short new_access;
2201 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2205 actor = GetChannelUser(channel->channel_info, user->handle_info);
2207 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2210 if(handle == user->handle_info && !privileged)
2212 reply("CSMSG_NO_SELF_CLVL");
2216 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2218 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2222 if(actor->access <= victim->access && !privileged)
2224 reply("MSG_USER_OUTRANKED", handle->handle);
2228 new_access = user_level_from_name(argv[2], UL_OWNER);
2232 reply("CSMSG_INVALID_ACCESS", argv[2]);
2236 if(new_access >= actor->access && !privileged)
2238 reply("CSMSG_NO_BUMP_ACCESS");
2242 victim->access = new_access;
2243 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2247 static CHANSERV_FUNC(cmd_deluser)
2249 struct handle_info *handle;
2250 struct userData *victim;
2251 struct userData *actor;
2252 unsigned short access;
2257 actor = GetChannelUser(channel->channel_info, user->handle_info);
2259 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2262 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2264 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2270 access = user_level_from_name(argv[1], UL_OWNER);
2273 reply("CSMSG_INVALID_ACCESS", argv[1]);
2276 if(access != victim->access)
2278 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2284 access = victim->access;
2287 if((actor->access <= victim->access) && !IsHelping(user))
2289 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2293 chan_name = strdup(channel->name);
2294 del_channel_user(victim, 1);
2295 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2301 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2303 struct userData *actor, *uData, *next;
2305 actor = GetChannelUser(channel->channel_info, user->handle_info);
2307 if(min_access > max_access)
2309 reply("CSMSG_BAD_RANGE", min_access, max_access);
2313 if((actor->access <= max_access) && !IsHelping(user))
2315 reply("CSMSG_NO_ACCESS");
2319 for(uData = channel->channel_info->users; uData; uData = next)
2323 if((uData->access >= min_access)
2324 && (uData->access <= max_access)
2325 && match_ircglob(uData->handle->handle, mask))
2326 del_channel_user(uData, 1);
2329 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2333 static CHANSERV_FUNC(cmd_mdelowner)
2335 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2338 static CHANSERV_FUNC(cmd_mdelcoowner)
2340 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2343 static CHANSERV_FUNC(cmd_mdelmaster)
2345 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2348 static CHANSERV_FUNC(cmd_mdelop)
2350 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2353 static CHANSERV_FUNC(cmd_mdelpeon)
2355 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2359 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2361 struct banData *bData, *next;
2362 char interval[INTERVALLEN];
2367 limit = now - duration;
2368 for(bData = channel->channel_info->bans; bData; bData = next)
2372 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2375 del_channel_ban(bData);
2379 intervalString(interval, duration);
2380 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2385 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2387 struct userData *actor, *uData, *next;
2388 char interval[INTERVALLEN];
2392 actor = GetChannelUser(channel->channel_info, user->handle_info);
2393 if(min_access > max_access)
2395 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2399 if((actor->access <= max_access) && !IsHelping(user))
2401 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2406 limit = now - duration;
2407 for(uData = channel->channel_info->users; uData; uData = next)
2411 if((uData->seen > limit) || uData->present)
2414 if(((uData->access >= min_access) && (uData->access <= max_access))
2415 || (max_access && (uData->access < actor->access)))
2417 del_channel_user(uData, 1);
2422 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2426 static CHANSERV_FUNC(cmd_trim)
2428 unsigned long duration;
2429 unsigned short min_level, max_level;
2433 duration = ParseInterval(argv[2]);
2436 reply("CSMSG_CANNOT_TRIM");
2440 if(!irccasecmp(argv[1], "bans"))
2442 cmd_trim_bans(user, channel, duration);
2445 else if(!irccasecmp(argv[1], "users"))
2447 cmd_trim_users(user, channel, 0, 0, duration);
2450 else if(parse_level_range(&min_level, &max_level, argv[1]))
2452 cmd_trim_users(user, channel, min_level, max_level, duration);
2455 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2457 cmd_trim_users(user, channel, min_level, min_level, duration);
2462 reply("CSMSG_INVALID_TRIM", argv[1]);
2467 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2468 to the user. cmd_all takes advantage of this. */
2469 static CHANSERV_FUNC(cmd_up)
2471 struct mod_chanmode change;
2472 struct userData *uData;
2475 change.modes_set = change.modes_clear = 0;
2477 change.args[0].member = GetUserMode(channel, user);
2478 if(!change.args[0].member)
2481 reply("MSG_CHANNEL_ABSENT", channel->name);
2485 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2489 reply("CSMSG_GODMODE_UP", argv[0]);
2492 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2494 change.args[0].mode = MODE_CHANOP;
2495 errmsg = "CSMSG_ALREADY_OPPED";
2499 change.args[0].mode = MODE_VOICE;
2500 errmsg = "CSMSG_ALREADY_VOICED";
2502 change.args[0].mode &= ~change.args[0].member->modes;
2503 if(!change.args[0].mode)
2506 reply(errmsg, channel->name);
2509 modcmd_chanmode_announce(&change);
2513 static CHANSERV_FUNC(cmd_down)
2515 struct mod_chanmode change;
2517 change.modes_set = change.modes_clear = 0;
2519 change.args[0].member = GetUserMode(channel, user);
2520 if(!change.args[0].member)
2523 reply("MSG_CHANNEL_ABSENT", channel->name);
2527 if(!change.args[0].member->modes)
2530 reply("CSMSG_ALREADY_DOWN", channel->name);
2534 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2535 modcmd_chanmode_announce(&change);
2539 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2541 struct userData *cList;
2543 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2545 if(IsSuspended(cList->channel)
2546 || IsUserSuspended(cList)
2547 || !GetUserMode(cList->channel->channel, user))
2550 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2556 static CHANSERV_FUNC(cmd_upall)
2558 return cmd_all(CSFUNC_ARGS, cmd_up);
2561 static CHANSERV_FUNC(cmd_downall)
2563 return cmd_all(CSFUNC_ARGS, cmd_down);
2566 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2567 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2570 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
2572 unsigned int ii, valid;
2573 struct userNode *victim;
2574 struct mod_chanmode *change;
2576 change = mod_chanmode_alloc(argc - 1);
2578 for(ii=valid=0; ++ii < argc; )
2580 if(!(victim = GetUserH(argv[ii])))
2582 change->args[valid].mode = mode;
2583 change->args[valid].member = GetUserMode(channel, victim);
2584 if(!change->args[valid].member)
2586 if(validate && !validate(user, channel, victim))
2591 change->argc = valid;
2592 if(valid < (argc-1))
2593 reply("CSMSG_PROCESS_FAILED");
2596 modcmd_chanmode_announce(change);
2597 reply(action, channel->name);
2599 mod_chanmode_free(change);
2603 static CHANSERV_FUNC(cmd_op)
2605 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2608 static CHANSERV_FUNC(cmd_deop)
2610 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2613 static CHANSERV_FUNC(cmd_voice)
2615 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2618 static CHANSERV_FUNC(cmd_devoice)
2620 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2624 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2630 for(ii=0; ii<channel->members.used; ii++)
2632 struct modeNode *mn = channel->members.list[ii];
2634 if(IsService(mn->user))
2637 if(!user_matches_glob(mn->user, ban, 1))
2640 if(protect_user(mn->user, user, channel->channel_info))
2644 victims[(*victimCount)++] = mn;
2650 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2652 struct userNode *victim;
2653 struct modeNode **victims;
2654 unsigned int offset, n, victimCount, duration = 0;
2655 char *reason = "Bye.", *ban, *name;
2656 char interval[INTERVALLEN];
2658 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2659 REQUIRE_PARAMS(offset);
2662 reason = unsplit_string(argv + offset, argc - offset, NULL);
2663 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2665 /* Truncate the reason to a length of TOPICLEN, as
2666 the ircd does; however, leave room for an ellipsis
2667 and the kicker's nick. */
2668 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2672 if((victim = GetUserH(argv[1])))
2674 victims = alloca(sizeof(victims[0]));
2675 victims[0] = GetUserMode(channel, victim);
2676 /* XXX: The comparison with ACTION_KICK is just because all
2677 * other actions can work on users outside the channel, and we
2678 * want to allow those (e.g. unbans) in that case. If we add
2679 * some other ejection action for in-channel users, change
2681 victimCount = victims[0] ? 1 : 0;
2683 if(IsService(victim))
2685 reply("MSG_SERVICE_IMMUNE", victim->nick);
2689 if((action == ACTION_KICK) && !victimCount)
2691 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2695 if(protect_user(victim, user, channel->channel_info))
2697 reply("CSMSG_USER_PROTECTED", victim->nick);
2701 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2702 name = victim->nick;
2706 if(!is_ircmask(argv[1]))
2708 reply("MSG_NICK_UNKNOWN", argv[1]);
2712 victims = alloca(sizeof(victims[0]) * channel->members.used);
2714 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2716 reply("CSMSG_MASK_PROTECTED", argv[1]);
2720 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2722 reply("CSMSG_LAME_MASK", argv[1]);
2726 if((action == ACTION_KICK) && (victimCount == 0))
2728 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2732 name = ban = strdup(argv[1]);
2735 /* Truncate the ban in place if necessary; we must ensure
2736 that 'ban' is a valid ban mask before sanitizing it. */
2737 sanitize_ircmask(ban);
2739 if(action & ACTION_ADD_BAN)
2741 struct banData *bData, *next;
2743 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2745 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2750 if(action & ACTION_ADD_TIMED_BAN)
2752 duration = ParseInterval(argv[2]);
2756 reply("CSMSG_DURATION_TOO_LOW");
2760 else if(duration > (86400 * 365 * 2))
2762 reply("CSMSG_DURATION_TOO_HIGH");
2768 for(bData = channel->channel_info->bans; bData; bData = next)
2770 if(match_ircglobs(bData->mask, ban))
2772 int exact = !irccasecmp(bData->mask, ban);
2774 /* The ban is redundant; there is already a ban
2775 with the same effect in place. */
2779 free(bData->reason);
2780 bData->reason = strdup(reason);
2781 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2782 reply("CSMSG_REASON_CHANGE", ban);
2786 if(exact && bData->expires)
2790 /* If the ban matches an existing one exactly,
2791 extend the expiration time if the provided
2792 duration is longer. */
2793 if(duration && ((time_t)(now + duration) > bData->expires))
2795 bData->expires = now + duration;
2806 /* Delete the expiration timeq entry and
2807 requeue if necessary. */
2808 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2811 timeq_add(bData->expires, expire_ban, bData);
2814 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2816 reply("CSMSG_BAN_ADDED", name, channel->name);
2821 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2828 if(match_ircglobs(ban, bData->mask))
2830 /* The ban we are adding makes previously existing
2831 bans redundant; silently remove them. */
2832 del_channel_ban(bData);
2836 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
2838 name = ban = strdup(bData->mask);
2842 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2844 extern const char *hidden_host_suffix;
2845 const char *old_name = chanserv_conf.old_ban_names->list[n];
2847 unsigned int l1, l2;
2850 l2 = strlen(old_name);
2853 if(irccasecmp(ban + l1 - l2, old_name))
2855 new_mask = malloc(MAXLEN);
2856 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2858 name = ban = new_mask;
2863 if(action & ACTION_BAN)
2865 unsigned int exists;
2866 struct mod_chanmode *change;
2868 if(channel->banlist.used >= MAXBANS)
2870 reply("CSMSG_BANLIST_FULL", channel->name);
2875 exists = ChannelBanExists(channel, ban);
2876 change = mod_chanmode_alloc(victimCount + 1);
2877 for(n = 0; n < victimCount; ++n)
2879 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2880 change->args[n].member = victims[n];
2884 change->args[n].mode = MODE_BAN;
2885 change->args[n++].hostmask = ban;
2889 modcmd_chanmode_announce(change);
2891 mod_chanmode_announce(chanserv, channel, change);
2892 mod_chanmode_free(change);
2894 if(exists && (action == ACTION_BAN))
2896 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2902 if(action & ACTION_KICK)
2904 char kick_reason[MAXLEN];
2905 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2907 for(n = 0; n < victimCount; n++)
2908 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2913 /* No response, since it was automated. */
2915 else if(action & ACTION_ADD_BAN)
2918 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2920 reply("CSMSG_BAN_ADDED", name, channel->name);
2922 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2923 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2924 else if(action & ACTION_BAN)
2925 reply("CSMSG_BAN_DONE", name, channel->name);
2926 else if(action & ACTION_KICK && victimCount)
2927 reply("CSMSG_KICK_DONE", name, channel->name);
2933 static CHANSERV_FUNC(cmd_kickban)
2935 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2938 static CHANSERV_FUNC(cmd_kick)
2940 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2943 static CHANSERV_FUNC(cmd_ban)
2945 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2948 static CHANSERV_FUNC(cmd_addban)
2950 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2953 static CHANSERV_FUNC(cmd_addtimedban)
2955 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2958 static struct mod_chanmode *
2959 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2961 struct mod_chanmode *change;
2962 unsigned char *match;
2963 unsigned int ii, count;
2965 match = alloca(bans->used);
2968 for(ii = count = 0; ii < bans->used; ++ii)
2970 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2977 for(ii = count = 0; ii < bans->used; ++ii)
2979 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
2986 change = mod_chanmode_alloc(count);
2987 for(ii = count = 0; ii < bans->used; ++ii)
2991 change->args[count].mode = MODE_REMOVE | MODE_BAN;
2992 change->args[count++].hostmask = bans->list[ii]->ban;
2998 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3000 struct userNode *actee;
3006 /* may want to allow a comma delimited list of users... */
3007 if(!(actee = GetUserH(argv[1])))
3009 if(!is_ircmask(argv[1]))
3011 reply("MSG_NICK_UNKNOWN", argv[1]);
3015 mask = strdup(argv[1]);
3018 /* We don't sanitize the mask here because ircu
3020 if(action & ACTION_UNBAN)
3022 struct mod_chanmode *change;
3023 change = find_matching_bans(&channel->banlist, actee, mask);
3026 modcmd_chanmode_announce(change);
3027 mod_chanmode_free(change);
3032 if(action & ACTION_DEL_BAN)
3034 struct banData *ban, *next;
3036 ban = channel->channel_info->bans;
3040 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3043 for( ; ban && !match_ircglobs(mask, ban->mask);
3048 del_channel_ban(ban);
3055 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3057 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3063 static CHANSERV_FUNC(cmd_unban)
3065 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3068 static CHANSERV_FUNC(cmd_delban)
3070 /* it doesn't necessarily have to remove the channel ban - may want
3071 to make that an option. */
3072 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3075 static CHANSERV_FUNC(cmd_unbanme)
3077 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3078 long flags = ACTION_UNBAN;
3080 /* remove permanent bans if the user has the proper access. */
3081 if(uData->access >= UL_MASTER)
3082 flags |= ACTION_DEL_BAN;
3084 argv[1] = user->nick;
3085 return unban_user(user, channel, 2, argv, cmd, flags);
3088 static CHANSERV_FUNC(cmd_unbanall)
3090 struct mod_chanmode *change;
3093 if(!channel->banlist.used)
3095 reply("CSMSG_NO_BANS", channel->name);
3099 change = mod_chanmode_alloc(channel->banlist.used);
3100 for(ii=0; ii<channel->banlist.used; ii++)
3102 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3103 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3105 modcmd_chanmode_announce(change);
3106 mod_chanmode_free(change);
3107 reply("CSMSG_BANS_REMOVED", channel->name);
3111 static CHANSERV_FUNC(cmd_open)
3113 struct mod_chanmode *change;
3115 change = find_matching_bans(&channel->banlist, user, NULL);
3117 change = mod_chanmode_alloc(0);
3118 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3119 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3120 && channel->channel_info->modes.modes_set)
3121 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3122 modcmd_chanmode_announce(change);
3123 reply("CSMSG_CHANNEL_OPENED", channel->name);
3124 mod_chanmode_free(change);
3128 static CHANSERV_FUNC(cmd_access)
3130 struct userNode *target;
3131 struct handle_info *target_handle;
3132 struct userData *uData;
3134 char prefix[MAXLEN];
3138 struct userData *uData;
3139 const char *chanName;
3142 target_handle = user->handle_info;
3145 reply("MSG_AUTHENTICATE");
3150 if(!IsHelping(user))
3152 reply("CSMSG_ACCESS_SELF_ONLY", argv[0]);
3156 if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3160 if(!target_handle->channels)
3162 reply("CSMSG_SQUAT_ACCESS");
3165 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3166 for(uData = target_handle->channels; uData; uData = uData->u_next)
3168 struct chanData *cData = uData->channel;
3170 if(uData->access > UL_OWNER)
3172 if(IsProtected(cData) && hide && !GetTrueChannelAccess(cData, user->handle_info))
3174 chanName = cData->channel->name;
3176 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3178 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3186 target_handle = target->handle_info;
3188 else if((target = GetUserH(argv[1])))
3190 target_handle = target->handle_info;
3192 else if(argv[1][0] == '*')
3194 if(!(target_handle = get_handle_info(argv[1]+1)))
3196 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3202 reply("MSG_NICK_UNKNOWN", argv[1]);
3206 assert(target || target_handle);
3208 if(target == chanserv)
3210 reply("CSMSG_IS_CHANSERV");
3218 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3223 reply("MSG_USER_AUTHENTICATE", target->nick);
3226 reply("MSG_AUTHENTICATE");
3232 const char *epithet = NULL, *type = NULL;
3235 epithet = chanserv_conf.irc_operator_epithet;
3238 else if(IsNetworkHelper(target))
3240 epithet = chanserv_conf.network_helper_epithet;
3241 type = "network helper";
3243 else if(IsSupportHelper(target))
3245 epithet = chanserv_conf.support_helper_epithet;
3246 type = "support helper";
3250 if(target_handle->epithet)
3251 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3253 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3255 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3259 sprintf(prefix, "%s", target_handle->handle);
3262 if(!channel->channel_info)
3264 reply("CSMSG_NOT_REGISTERED", channel->name);
3268 helping = HANDLE_FLAGGED(target_handle, HELPING)
3269 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3270 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3272 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3273 /* To prevent possible information leaks, only show infolines
3274 * if the requestor is in the channel or it's their own
3276 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3278 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3280 /* Likewise, only say it's suspended if the user has active
3281 * access in that channel or it's their own entry. */
3282 if(IsUserSuspended(uData)
3283 && (GetChannelUser(channel->channel_info, user->handle_info)
3284 || (user->handle_info == uData->handle)))
3286 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3291 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3298 zoot_list(struct listData *list)
3300 struct userData *uData;
3301 unsigned int start, curr;
3302 struct helpfile_table tmp_table;
3303 const char **temp, *msg;
3305 if(list->table.length == 1)
3308 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3310 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3311 msg = user_find_message(list->user, "MSG_NONE");
3312 send_message_type(4, list->user, list->bot, " %s", msg);
3314 tmp_table.width = list->table.width;
3315 tmp_table.flags = list->table.flags;
3316 list->table.contents[0][0] = " ";
3317 for(start = curr = 1; curr < list->table.length; )
3319 uData = list->users[curr-1];
3320 list->table.contents[curr++][0] = " ";
3321 if((curr == list->table.length) || (list->users[curr-1]->access != uData->access))
3324 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3326 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3327 temp = list->table.contents[--start];
3328 list->table.contents[start] = list->table.contents[0];
3329 tmp_table.contents = list->table.contents + start;
3330 tmp_table.length = curr - start;
3331 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3332 list->table.contents[start] = temp;
3339 def_list(struct listData *list)
3343 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3345 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3346 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3347 if(list->table.length == 1)
3349 msg = user_find_message(list->user, "MSG_NONE");
3350 send_message_type(4, list->user, list->bot, " %s", msg);
3355 userData_access_comp(const void *arg_a, const void *arg_b)
3357 const struct userData *a = *(struct userData**)arg_a;
3358 const struct userData *b = *(struct userData**)arg_b;
3360 if(a->access != b->access)
3361 res = b->access - a->access;
3363 res = irccasecmp(a->handle->handle, b->handle->handle);
3368 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3370 void (*send_list)(struct listData *);
3371 struct userData *uData;
3372 struct listData lData;
3373 unsigned int matches;
3377 lData.bot = cmd->parent->bot;
3378 lData.channel = channel;
3379 lData.lowest = lowest;
3380 lData.highest = highest;
3381 lData.search = (argc > 1) ? argv[1] : NULL;
3382 send_list = zoot_list;
3384 if(user->handle_info)
3386 switch(user->handle_info->userlist_style)
3388 case HI_STYLE_DEF: send_list = def_list; break;
3389 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3393 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3395 for(uData = channel->channel_info->users; uData; uData = uData->next)
3397 if((uData->access < lowest)
3398 || (uData->access > highest)
3399 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3401 lData.users[matches++] = uData;
3403 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3405 lData.table.length = matches+1;
3406 lData.table.width = 4;
3407 lData.table.flags = TABLE_NO_FREE;
3408 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3409 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3410 lData.table.contents[0] = ary;
3413 ary[2] = "Last Seen";
3415 for(matches = 1; matches < lData.table.length; ++matches)
3417 struct userData *uData = lData.users[matches-1];
3418 char seen[INTERVALLEN];
3420 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3421 lData.table.contents[matches] = ary;
3422 ary[0] = strtab(uData->access);
3423 ary[1] = uData->handle->handle;
3426 else if(!uData->seen)
3429 ary[2] = intervalString(seen, now - uData->seen);
3430 ary[2] = strdup(ary[2]);
3431 if(IsUserSuspended(uData))
3432 ary[3] = "Suspended";
3433 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3434 ary[3] = "Vacation";
3439 for(matches = 1; matches < lData.table.length; ++matches)
3441 free((char*)lData.table.contents[matches][2]);
3442 free(lData.table.contents[matches]);
3444 free(lData.table.contents[0]);
3445 free(lData.table.contents);
3449 static CHANSERV_FUNC(cmd_users)
3451 return cmd_list_users(CSFUNC_ARGS, 0, UL_OWNER);
3454 static CHANSERV_FUNC(cmd_wlist)
3456 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3459 static CHANSERV_FUNC(cmd_clist)
3461 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3463 static CHANSERV_FUNC(cmd_mlist)
3465 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3467 static CHANSERV_FUNC(cmd_olist)
3469 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3471 static CHANSERV_FUNC(cmd_plist)
3473 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3476 static CHANSERV_FUNC(cmd_bans)
3478 struct helpfile_table tbl;
3479 unsigned int matches = 0, timed = 0, ii;
3480 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3481 const char *msg_never, *triggered, *expires;
3482 struct banData *ban, **bans;
3489 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3491 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3493 if(search && !match_ircglobs(search, ban->mask))
3495 bans[matches++] = ban;
3500 tbl.length = matches + 1;
3501 tbl.width = 4 + timed;
3503 tbl.flags = TABLE_NO_FREE;
3504 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3505 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3506 tbl.contents[0][0] = "Mask";
3507 tbl.contents[0][1] = "Set By";
3508 tbl.contents[0][2] = "Triggered";
3511 tbl.contents[0][3] = "Expires";
3512 tbl.contents[0][4] = "Reason";
3515 tbl.contents[0][3] = "Reason";
3518 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3523 msg_never = user_find_message(user, "MSG_NEVER");
3524 for(ii = 0; ii < matches; )
3530 else if(ban->expires)
3531 expires = intervalString(e_buffer, ban->expires - now);
3533 expires = msg_never;
3536 triggered = intervalString(t_buffer, now - ban->triggered);
3538 triggered = msg_never;
3540 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3541 tbl.contents[ii][0] = ban->mask;
3542 tbl.contents[ii][1] = ban->owner;
3543 tbl.contents[ii][2] = strdup(triggered);
3546 tbl.contents[ii][3] = strdup(expires);
3547 tbl.contents[ii][4] = ban->reason;
3550 tbl.contents[ii][3] = ban->reason;
3552 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3553 reply("MSG_MATCH_COUNT", matches);
3554 for(ii = 1; ii < tbl.length; ++ii)
3556 free((char*)tbl.contents[ii][2]);
3558 free((char*)tbl.contents[ii][3]);
3559 free(tbl.contents[ii]);
3561 free(tbl.contents[0]);
3567 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3569 struct chanData *cData = channel->channel_info;
3570 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3572 if(cData->topic_mask)
3573 return !match_ircglob(new_topic, cData->topic_mask);
3574 else if(cData->topic)
3575 return irccasecmp(new_topic, cData->topic);
3580 static CHANSERV_FUNC(cmd_topic)
3582 struct chanData *cData;
3585 cData = channel->channel_info;
3590 SetChannelTopic(channel, chanserv, cData->topic, 1);
3591 reply("CSMSG_TOPIC_SET", cData->topic);
3595 reply("CSMSG_NO_TOPIC", channel->name);
3599 topic = unsplit_string(argv + 1, argc - 1, NULL);
3600 /* If they say "!topic *", use an empty topic. */
3601 if((topic[0] == '*') && (topic[1] == 0))
3603 if(bad_topic(channel, user, topic))
3605 char *topic_mask = cData->topic_mask;
3608 char new_topic[TOPICLEN+1], tchar;
3609 int pos=0, starpos=-1, dpos=0, len;
3611 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3618 len = strlen(topic);
3619 if((dpos + len) > TOPICLEN)
3620 len = TOPICLEN + 1 - dpos;
3621 memcpy(new_topic+dpos, topic, len);
3625 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3626 default: new_topic[dpos++] = tchar; break;
3629 if((dpos > TOPICLEN) || tchar)
3632 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3633 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3636 new_topic[dpos] = 0;
3637 SetChannelTopic(channel, chanserv, new_topic, 1);
3639 reply("CSMSG_TOPIC_LOCKED", channel->name);
3644 SetChannelTopic(channel, chanserv, topic, 1);
3646 if(cData->flags & CHANNEL_TOPIC_SNARF)
3648 /* Grab the topic and save it as the default topic. */
3650 cData->topic = strdup(channel->topic);
3656 static CHANSERV_FUNC(cmd_mode)
3658 struct mod_chanmode *change;
3662 change = &channel->channel_info->modes;
3663 if(change->modes_set || change->modes_clear) {
3664 modcmd_chanmode_announce(change);
3665 reply("CSMSG_DEFAULTED_MODES", channel->name);
3667 reply("CSMSG_NO_MODES", channel->name);
3671 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3674 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3678 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3679 && mode_lock_violated(&channel->channel_info->modes, change))
3682 mod_chanmode_format(&channel->channel_info->modes, modes);
3683 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3687 modcmd_chanmode_announce(change);
3688 mod_chanmode_free(change);
3689 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3693 static CHANSERV_FUNC(cmd_invite)
3695 struct userData *uData;
3696 struct userNode *invite;
3698 uData = GetChannelUser(channel->channel_info, user->handle_info);
3702 if(!(invite = GetUserH(argv[1])))
3704 reply("MSG_NICK_UNKNOWN", argv[1]);
3711 if(GetUserMode(channel, invite))
3713 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3719 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3720 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3722 irc_invite(chanserv, invite, channel);
3724 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3729 static CHANSERV_FUNC(cmd_inviteme)
3731 struct userData *uData;
3733 if(GetUserMode(channel, user))
3735 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3738 if(channel->channel_info
3739 && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3740 && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3741 && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3743 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3746 irc_invite(cmd->parent->bot, user, channel);
3751 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3754 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3756 /* We display things based on two dimensions:
3757 * - Issue time: present or absent
3758 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3759 * (in order of precedence, so something both expired and revoked
3760 * only counts as revoked)
3762 combo = (suspended->issued ? 4 : 0)
3763 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3765 case 0: /* no issue time, indefinite expiration */
3766 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3768 case 1: /* no issue time, expires in future */
3769 intervalString(buf1, suspended->expires-now);
3770 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3772 case 2: /* no issue time, expired */
3773 intervalString(buf1, now-suspended->expires);
3774 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3776 case 3: /* no issue time, revoked */
3777 intervalString(buf1, now-suspended->revoked);
3778 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3780 case 4: /* issue time set, indefinite expiration */
3781 intervalString(buf1, now-suspended->issued);
3782 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3784 case 5: /* issue time set, expires in future */
3785 intervalString(buf1, now-suspended->issued);
3786 intervalString(buf2, suspended->expires-now);
3787 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3789 case 6: /* issue time set, expired */
3790 intervalString(buf1, now-suspended->issued);
3791 intervalString(buf2, now-suspended->expires);
3792 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3794 case 7: /* issue time set, revoked */
3795 intervalString(buf1, now-suspended->issued);
3796 intervalString(buf2, now-suspended->revoked);
3797 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3800 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3805 static CHANSERV_FUNC(cmd_info)
3807 char modes[MAXLEN], buffer[INTERVALLEN];
3808 struct userData *uData, *owner;
3809 struct chanData *cData;
3810 struct do_not_register *dnr;
3815 cData = channel->channel_info;
3816 reply("CSMSG_CHANNEL_INFO", channel->name);
3818 uData = GetChannelUser(cData, user->handle_info);
3819 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3821 mod_chanmode_format(&cData->modes, modes);
3822 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3823 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3826 for(it = dict_first(cData->notes); it; it = iter_next(it))
3830 note = iter_data(it);
3831 if(!note_type_visible_to_user(cData, note->type, user))
3834 padding = PADLEN - 1 - strlen(iter_key(it));
3835 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3838 reply("CSMSG_CHANNEL_MAX", cData->max);
3839 for(owner = cData->users; owner; owner = owner->next)
3840 if(owner->access == UL_OWNER)
3841 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3842 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3843 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3844 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3845 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3847 privileged = IsStaff(user);
3848 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3849 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3851 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3852 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3854 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3856 struct suspended *suspended;
3857 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3858 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3859 show_suspension_info(cmd, user, suspended);
3861 else if(IsSuspended(cData))
3863 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3864 show_suspension_info(cmd, user, cData->suspended);
3869 static CHANSERV_FUNC(cmd_netinfo)
3871 extern time_t boot_time;
3872 extern unsigned long burst_length;
3873 char interval[INTERVALLEN];
3875 reply("CSMSG_NETWORK_INFO");
3876 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3877 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3878 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3879 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3880 reply("CSMSG_NETWORK_BANS", banCount);
3881 reply("CSMSG_CHANNEL_USERS", userCount);
3882 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3883 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3888 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3890 struct helpfile_table table;
3892 struct userNode *user;
3897 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3898 table.contents = alloca(list->used*sizeof(*table.contents));
3899 for(nn=0; nn<list->used; nn++)
3901 user = list->list[nn];
3902 if(user->modes & skip_flags)
3906 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3909 nick = alloca(strlen(user->nick)+3);
3910 sprintf(nick, "(%s)", user->nick);
3914 table.contents[table.length][0] = nick;
3917 table_send(chanserv, to->nick, 0, NULL, table);
3920 static CHANSERV_FUNC(cmd_ircops)
3922 reply("CSMSG_STAFF_OPERS");
3923 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3927 static CHANSERV_FUNC(cmd_helpers)
3929 reply("CSMSG_STAFF_HELPERS");
3930 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3934 static CHANSERV_FUNC(cmd_staff)
3936 reply("CSMSG_NETWORK_STAFF");
3937 cmd_ircops(CSFUNC_ARGS);
3938 cmd_helpers(CSFUNC_ARGS);
3942 static CHANSERV_FUNC(cmd_peek)
3944 struct modeNode *mn;
3945 char modes[MODELEN];
3947 struct helpfile_table table;
3949 irc_make_chanmode(channel, modes);
3951 reply("CSMSG_PEEK_INFO", channel->name);
3952 reply("CSMSG_PEEK_TOPIC", channel->topic);
3953 reply("CSMSG_PEEK_MODES", modes);
3954 reply("CSMSG_PEEK_USERS", channel->members.used);
3958 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3959 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3960 for(n = 0; n < channel->members.used; n++)
3962 mn = channel->members.list[n];
3963 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3965 table.contents[table.length] = alloca(sizeof(**table.contents));
3966 table.contents[table.length][0] = mn->user->nick;
3971 reply("CSMSG_PEEK_OPS");
3972 table_send(chanserv, user->nick, 0, NULL, table);
3975 reply("CSMSG_PEEK_NO_OPS");
3979 static MODCMD_FUNC(cmd_wipeinfo)
3981 struct handle_info *victim;
3982 struct userData *ud, *actor;
3985 actor = GetChannelUser(channel->channel_info, user->handle_info);
3986 if(!(victim = modcmd_get_handle_info(user, argv[1])))
3988 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
3990 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
3993 if((ud->access >= actor->access) && (ud != actor))
3995 reply("MSG_USER_OUTRANKED", victim->handle);
4001 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4005 static CHANSERV_FUNC(cmd_resync)
4007 struct mod_chanmode *changes;
4008 struct chanData *cData = channel->channel_info;
4009 unsigned int ii, used;
4011 changes = mod_chanmode_alloc(channel->members.used * 2);
4012 for(ii = used = 0; ii < channel->members.used; ++ii)
4014 struct modeNode *mn = channel->members.list[ii];
4015 struct userData *uData;
4016 if(IsService(mn->user))
4018 /* must not change modes for this user */
4020 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4024 changes->args[used].mode = MODE_REMOVE | mn->modes;
4025 changes->args[used++].member = mn;
4028 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4030 if(mn->modes & MODE_CHANOP)
4032 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4033 changes->args[used++].member = mn;
4035 if(!(mn->modes & MODE_VOICE))
4037 changes->args[used].mode = MODE_VOICE;
4038 changes->args[used++].member = mn;
4043 if(!(mn->modes & MODE_CHANOP))
4045 changes->args[used].mode = MODE_CHANOP;
4046 changes->args[used++].member = mn;
4050 changes->argc = used;
4051 modcmd_chanmode_announce(changes);
4052 mod_chanmode_free(changes);
4053 reply("CSMSG_RESYNCED_USERS", channel->name);
4057 static CHANSERV_FUNC(cmd_seen)
4059 struct userData *uData;
4060 struct handle_info *handle;
4061 char seen[INTERVALLEN];
4065 if(!irccasecmp(argv[1], chanserv->nick))
4067 reply("CSMSG_IS_CHANSERV");
4071 if(!(handle = get_handle_info(argv[1])))
4073 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4077 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4079 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4084 reply("CSMSG_USER_PRESENT", handle->handle);
4085 else if(uData->seen)
4086 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4088 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4090 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4091 reply("CSMSG_USER_VACATION", handle->handle);
4096 static MODCMD_FUNC(cmd_names)
4098 struct userNode *targ;
4099 struct userData *targData;
4100 unsigned int ii, pos;
4103 for(ii=pos=0; ii<channel->members.used; ++ii)
4105 targ = channel->members.list[ii]->user;
4106 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4109 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4112 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4116 if(IsUserSuspended(targData))
4118 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4121 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4122 reply("CSMSG_END_NAMES", channel->name);
4127 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4129 switch(ntype->visible_type)
4131 case NOTE_VIS_ALL: return 1;
4132 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4133 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4138 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4140 struct userData *uData;
4142 switch(ntype->set_access_type)
4144 case NOTE_SET_CHANNEL_ACCESS:
4145 if(!user->handle_info)
4147 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4149 return uData->access >= ntype->set_access.min_ulevel;
4150 case NOTE_SET_CHANNEL_SETTER:
4151 return check_user_level(channel, user, lvlSetters, 1, 0);
4152 case NOTE_SET_PRIVILEGED: default:
4153 return IsHelping(user);
4157 static CHANSERV_FUNC(cmd_note)
4159 struct chanData *cData;
4161 struct note_type *ntype;
4163 cData = channel->channel_info;
4166 reply("CSMSG_NOT_REGISTERED", channel->name);
4170 /* If no arguments, show all visible notes for the channel. */
4176 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4178 note = iter_data(it);
4179 if(!note_type_visible_to_user(cData, note->type, user))
4182 reply("CSMSG_NOTELIST_HEADER", channel->name);
4183 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4186 reply("CSMSG_NOTELIST_END", channel->name);
4188 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4190 /* If one argument, show the named note. */
4193 if((note = dict_find(cData->notes, argv[1], NULL))
4194 && note_type_visible_to_user(cData, note->type, user))
4196 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4198 else if((ntype = dict_find(note_types, argv[1], NULL))
4199 && note_type_visible_to_user(NULL, ntype, user))
4201 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4206 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4210 /* Assume they're trying to set a note. */
4214 ntype = dict_find(note_types, argv[1], NULL);
4217 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4220 else if(note_type_settable_by_user(channel, ntype, user))
4222 note_text = unsplit_string(argv+2, argc-2, NULL);
4223 if((note = dict_find(cData->notes, argv[1], NULL)))
4224 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4225 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4226 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4228 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4230 /* The note is viewable to staff only, so return 0
4231 to keep the invocation from getting logged (or
4232 regular users can see it in !events). */
4238 reply("CSMSG_NO_ACCESS");
4245 static CHANSERV_FUNC(cmd_delnote)
4250 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4251 || !note_type_settable_by_user(channel, note->type, user))
4253 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4256 dict_remove(channel->channel_info->notes, note->type->name);
4257 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4261 static CHANSERV_FUNC(cmd_events)
4263 struct logSearch discrim;
4264 struct logReport report;
4265 unsigned int matches, limit;
4267 limit = (argc > 1) ? atoi(argv[1]) : 10;
4268 if(limit < 1 || limit > 200) limit = 10;
4270 memset(&discrim, 0, sizeof(discrim));
4271 discrim.masks.bot = chanserv;
4272 discrim.masks.channel_name = channel->name;
4273 if(argc > 2) discrim.masks.command = argv[2];
4274 discrim.limit = limit;
4275 discrim.max_time = INT_MAX;
4276 discrim.severities = 1 << LOG_COMMAND;
4277 report.reporter = chanserv;
4279 reply("CSMSG_EVENT_SEARCH_RESULTS");
4280 matches = log_entry_search(&discrim, log_report_entry, &report);
4282 reply("MSG_MATCH_COUNT", matches);
4284 reply("MSG_NO_MATCHES");
4288 static CHANSERV_FUNC(cmd_say)
4294 msg = unsplit_string(argv + 1, argc - 1, NULL);
4295 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4297 else if(GetUserH(argv[1]))
4300 msg = unsplit_string(argv + 2, argc - 2, NULL);
4301 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4305 reply("You must specify the name of a channel or user.");
4311 static CHANSERV_FUNC(cmd_emote)
4317 /* CTCP is so annoying. */
4318 msg = unsplit_string(argv + 1, argc - 1, NULL);
4319 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4321 else if(GetUserH(argv[1]))
4323 msg = unsplit_string(argv + 2, argc - 2, NULL);
4324 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4328 reply("You must specify the name of a channel or user.");
4334 struct channelList *
4335 chanserv_support_channels(void)
4337 return &chanserv_conf.support_channels;
4340 static CHANSERV_FUNC(cmd_expire)
4342 int channel_count = registered_channels;
4343 expire_channels(NULL);
4344 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4349 chanserv_expire_suspension(void *data)
4351 struct suspended *suspended = data;
4352 struct chanNode *channel;
4353 struct mod_chanmode change;
4355 if(!suspended->expires || (now < suspended->expires))
4356 suspended->revoked = now;
4357 channel = suspended->cData->channel;
4358 suspended->cData->channel = channel;
4359 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4360 change.modes_set = change.modes_clear = 0;
4362 change.args[0].mode = MODE_CHANOP;
4363 change.args[0].member = AddChannelUser(chanserv, channel);
4364 mod_chanmode_announce(chanserv, channel, &change);
4367 static CHANSERV_FUNC(cmd_csuspend)
4369 struct suspended *suspended;
4370 char reason[MAXLEN];
4371 time_t expiry, duration;
4372 struct userData *uData;
4376 if(IsProtected(channel->channel_info))
4378 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4382 if(argv[1][0] == '!')
4384 else if(IsSuspended(channel->channel_info))
4386 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4387 show_suspension_info(cmd, user, channel->channel_info->suspended);
4391 if(!strcmp(argv[1], "0"))
4393 else if((duration = ParseInterval(argv[1])))
4394 expiry = now + duration;
4397 reply("MSG_INVALID_DURATION", argv[1]);
4401 unsplit_string(argv + 2, argc - 2, reason);
4403 suspended = calloc(1, sizeof(*suspended));
4404 suspended->revoked = 0;
4405 suspended->issued = now;
4406 suspended->suspender = strdup(user->handle_info->handle);
4407 suspended->expires = expiry;
4408 suspended->reason = strdup(reason);
4409 suspended->cData = channel->channel_info;
4410 suspended->previous = suspended->cData->suspended;
4411 suspended->cData->suspended = suspended;
4413 if(suspended->expires)
4414 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4416 if(IsSuspended(channel->channel_info))
4418 suspended->previous->revoked = now;
4419 if(suspended->previous->expires)
4420 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4421 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4422 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4426 /* Mark all users in channel as absent. */
4427 for(uData = channel->channel_info->users; uData; uData = uData->next)
4436 /* Mark the channel as suspended, then part. */
4437 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4438 DelChannelUser(chanserv, channel, suspended->reason, 0);
4439 reply("CSMSG_SUSPENDED", channel->name);
4440 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4441 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4446 static CHANSERV_FUNC(cmd_cunsuspend)
4448 struct suspended *suspended;
4449 char message[MAXLEN];
4451 if(!IsSuspended(channel->channel_info))
4453 reply("CSMSG_NOT_SUSPENDED", channel->name);
4457 suspended = channel->channel_info->suspended;
4459 /* Expire the suspension and join ChanServ to the channel. */
4460 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4461 chanserv_expire_suspension(suspended);
4462 reply("CSMSG_UNSUSPENDED", channel->name);
4463 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4464 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4468 typedef struct chanservSearch
4476 unsigned long flags;
4480 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4483 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4488 search = malloc(sizeof(struct chanservSearch));
4489 memset(search, 0, sizeof(*search));
4492 for(i = 0; i < argc; i++)
4494 /* Assume all criteria require arguments. */
4497 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4501 if(!irccasecmp(argv[i], "name"))
4502 search->name = argv[++i];
4503 else if(!irccasecmp(argv[i], "registrar"))
4504 search->registrar = argv[++i];
4505 else if(!irccasecmp(argv[i], "unvisited"))
4506 search->unvisited = ParseInterval(argv[++i]);
4507 else if(!irccasecmp(argv[i], "registered"))
4508 search->registered = ParseInterval(argv[++i]);
4509 else if(!irccasecmp(argv[i], "flags"))
4512 if(!irccasecmp(argv[i], "nodelete"))
4513 search->flags |= CHANNEL_NODELETE;
4514 else if(!irccasecmp(argv[i], "suspended"))
4515 search->flags |= CHANNEL_SUSPENDED;
4518 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4522 else if(!irccasecmp(argv[i], "limit"))
4523 search->limit = strtoul(argv[++i], NULL, 10);
4526 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4531 if(search->name && !strcmp(search->name, "*"))
4533 if(search->registrar && !strcmp(search->registrar, "*"))
4534 search->registrar = 0;
4543 chanserv_channel_match(struct chanData *channel, search_t search)
4545 const char *name = channel->channel->name;
4546 if((search->name && !match_ircglob(name, search->name)) ||
4547 (search->registrar && !channel->registrar) ||
4548 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4549 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4550 (search->registered && (now - channel->registered) > search->registered) ||
4551 (search->flags && ((search->flags & channel->flags) != search->flags)))
4558 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4560 struct chanData *channel;
4561 unsigned int matches = 0;
4563 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4565 if(!chanserv_channel_match(channel, search))
4575 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4580 search_print(struct chanData *channel, void *data)
4582 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4585 static CHANSERV_FUNC(cmd_search)
4588 unsigned int matches;
4589 channel_search_func action;
4593 if(!irccasecmp(argv[1], "count"))
4594 action = search_count;
4595 else if(!irccasecmp(argv[1], "print"))
4596 action = search_print;
4599 reply("CSMSG_ACTION_INVALID", argv[1]);
4603 search = chanserv_search_create(user, argc - 2, argv + 2);
4607 if(action == search_count)
4608 search->limit = INT_MAX;
4610 if(action == search_print)
4611 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4613 matches = chanserv_channel_search(search, action, user);
4616 reply("MSG_MATCH_COUNT", matches);
4618 reply("MSG_NO_MATCHES");
4624 static CHANSERV_FUNC(cmd_unvisited)
4626 struct chanData *cData;
4627 time_t interval = chanserv_conf.channel_expire_delay;
4628 char buffer[INTERVALLEN];
4629 unsigned int limit = 25, matches = 0;
4633 interval = ParseInterval(argv[1]);
4635 limit = atoi(argv[2]);
4638 intervalString(buffer, interval);
4639 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4641 for(cData = channelList; cData && matches < limit; cData = cData->next)
4643 if((now - cData->visited) < interval)
4646 intervalString(buffer, now - cData->visited);
4647 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4654 static MODCMD_FUNC(chan_opt_defaulttopic)
4660 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4662 reply("CSMSG_TOPIC_LOCKED", channel->name);
4666 topic = unsplit_string(argv+1, argc-1, NULL);
4668 free(channel->channel_info->topic);
4669 if(topic[0] == '*' && topic[1] == 0)
4671 topic = channel->channel_info->topic = NULL;
4675 topic = channel->channel_info->topic = strdup(topic);
4676 if(channel->channel_info->topic_mask
4677 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4678 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4680 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4683 if(channel->channel_info->topic)
4684 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4686 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4690 static MODCMD_FUNC(chan_opt_topicmask)
4694 struct chanData *cData = channel->channel_info;
4697 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4699 reply("CSMSG_TOPIC_LOCKED", channel->name);
4703 mask = unsplit_string(argv+1, argc-1, NULL);
4705 if(cData->topic_mask)
4706 free(cData->topic_mask);
4707 if(mask[0] == '*' && mask[1] == 0)
4709 cData->topic_mask = 0;
4713 cData->topic_mask = strdup(mask);
4715 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4716 else if(!match_ircglob(cData->topic, cData->topic_mask))
4717 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4721 if(channel->channel_info->topic_mask)
4722 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4724 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4728 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4732 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4736 if(greeting[0] == '*' && greeting[1] == 0)
4740 unsigned int length = strlen(greeting);
4741 if(length > chanserv_conf.greeting_length)
4743 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4746 *data = strdup(greeting);
4755 reply(name, user_find_message(user, "MSG_NONE"));
4759 static MODCMD_FUNC(chan_opt_greeting)
4761 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4764 static MODCMD_FUNC(chan_opt_usergreeting)
4766 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4769 static MODCMD_FUNC(chan_opt_modes)
4771 struct mod_chanmode *new_modes;
4772 char modes[MODELEN];
4776 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4778 reply("CSMSG_NO_ACCESS");
4781 if(argv[1][0] == '*' && argv[1][1] == 0)
4783 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4785 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4787 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4790 else if(new_modes->argc > 1)
4792 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4793 mod_chanmode_free(new_modes);
4798 channel->channel_info->modes = *new_modes;
4799 modcmd_chanmode_announce(new_modes);
4800 mod_chanmode_free(new_modes);
4804 mod_chanmode_format(&channel->channel_info->modes, modes);
4806 reply("CSMSG_SET_MODES", modes);
4808 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4812 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4814 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4816 struct chanData *cData = channel->channel_info;
4821 /* Set flag according to value. */
4822 if(enabled_string(argv[1]))
4824 cData->flags |= mask;
4827 else if(disabled_string(argv[1]))
4829 cData->flags &= ~mask;
4834 reply("MSG_INVALID_BINARY", argv[1]);
4840 /* Find current option value. */
4841 value = (cData->flags & mask) ? 1 : 0;
4845 reply(name, user_find_message(user, "MSG_ON"));
4847 reply(name, user_find_message(user, "MSG_OFF"));
4851 static MODCMD_FUNC(chan_opt_nodelete)
4853 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4855 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4859 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4862 static MODCMD_FUNC(chan_opt_userinfo)
4864 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4867 static MODCMD_FUNC(chan_opt_voice)
4869 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4872 static MODCMD_FUNC(chan_opt_dynlimit)
4874 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4877 static MODCMD_FUNC(chan_opt_topicsnarf)
4879 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4881 reply("CSMSG_TOPIC_LOCKED", channel->name);
4884 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4887 static MODCMD_FUNC(chan_opt_peoninvite)
4889 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4892 static MODCMD_FUNC(chan_opt_defaults)
4894 struct userData *uData;
4895 struct chanData *cData;
4896 const char *confirm;
4897 enum levelOption lvlOpt;
4898 enum charOption chOpt;
4900 cData = channel->channel_info;
4901 uData = GetChannelUser(cData, user->handle_info);
4902 if(!uData || (uData->access < UL_OWNER))
4904 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4907 confirm = make_confirmation_string(uData);
4908 if((argc < 2) || strcmp(argv[1], confirm))
4910 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4913 cData->flags = CHANNEL_DEFAULT_FLAGS;
4914 cData->modes = chanserv_conf.default_modes;
4915 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4916 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4917 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4918 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4919 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4924 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4926 struct chanData *cData = channel->channel_info;
4927 struct userData *uData;
4928 unsigned short value;
4932 if(!check_user_level(channel, user, option, 1, 1))
4934 reply("CSMSG_CANNOT_SET");
4937 value = user_level_from_name(argv[1], UL_OWNER+1);
4938 if(!value && !isdigit(argv[1][0]))
4940 reply("CSMSG_INVALID_ACCESS", index);
4943 uData = GetChannelUser(cData, user->handle_info);
4944 if(!uData || (uData->access < value))
4946 reply("CSMSG_BAD_SETLEVEL");
4949 cData->lvlOpts[option] = value;
4951 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4955 static MODCMD_FUNC(chan_opt_enfops)
4957 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4960 static MODCMD_FUNC(chan_opt_giveops)
4962 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4965 static MODCMD_FUNC(chan_opt_enfmodes)
4967 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4970 static MODCMD_FUNC(chan_opt_enftopic)
4972 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4975 static MODCMD_FUNC(chan_opt_pubcmd)
4977 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4980 static MODCMD_FUNC(chan_opt_setters)
4982 return channel_level_option(lvlSetters, CSFUNC_ARGS);
4985 static MODCMD_FUNC(chan_opt_ctcpusers)
4987 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
4991 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4993 struct chanData *cData = channel->channel_info;
4994 int count = charOptions[option].count, index;
4998 index = atoi(argv[1]);
5000 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5002 reply("CSMSG_INVALID_NUMERIC", index);
5003 /* Show possible values. */
5004 for(index = 0; index < count; index++)
5005 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5009 cData->chOpts[option] = charOptions[option].values[index].value;
5013 /* Find current option value. */
5016 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5020 /* Somehow, the option value is corrupt; reset it to the default. */
5021 cData->chOpts[option] = charOptions[option].default_value;
5026 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5030 static MODCMD_FUNC(chan_opt_protect)
5032 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5035 static MODCMD_FUNC(chan_opt_toys)
5037 return channel_multiple_option(chToys, CSFUNC_ARGS);
5040 static MODCMD_FUNC(chan_opt_ctcpreaction)
5042 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5045 static MODCMD_FUNC(chan_opt_topicrefresh)
5047 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5050 static struct svccmd_list set_shows_list;
5053 handle_svccmd_unbind(struct svccmd *target) {
5055 for(ii=0; ii<set_shows_list.used; ++ii)
5056 if(target == set_shows_list.list[ii])
5057 set_shows_list.used = 0;
5060 static CHANSERV_FUNC(cmd_set)
5062 struct svccmd *subcmd;
5066 /* Check if we need to (re-)initialize set_shows_list. */
5067 if(!set_shows_list.used)
5069 if(!set_shows_list.size)
5071 set_shows_list.size = chanserv_conf.set_shows->used;
5072 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5074 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5076 const char *name = chanserv_conf.set_shows->list[ii];
5077 sprintf(buf, "%s %s", argv[0], name);
5078 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5081 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5084 svccmd_list_append(&set_shows_list, subcmd);
5090 reply("CSMSG_CHANNEL_OPTIONS");
5091 for(ii = 0; ii < set_shows_list.used; ii++)
5093 subcmd = set_shows_list.list[ii];
5094 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5099 sprintf(buf, "%s %s", argv[0], argv[1]);
5100 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5103 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5106 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5108 reply("CSMSG_NO_ACCESS");
5112 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5116 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5118 struct userData *uData;
5120 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5123 reply("CSMSG_NOT_USER", channel->name);
5129 /* Just show current option value. */
5131 else if(enabled_string(argv[1]))
5133 uData->flags |= mask;
5135 else if(disabled_string(argv[1]))
5137 uData->flags &= ~mask;
5141 reply("MSG_INVALID_BINARY", argv[1]);
5145 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5149 static MODCMD_FUNC(user_opt_noautoop)
5151 struct userData *uData;
5153 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5156 reply("CSMSG_NOT_USER", channel->name);
5159 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5160 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5162 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5165 static MODCMD_FUNC(user_opt_autoinvite)
5167 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5170 static MODCMD_FUNC(user_opt_info)
5172 struct userData *uData;
5175 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5179 /* If they got past the command restrictions (which require access)
5180 * but fail this test, we have some fool with security override on.
5182 reply("CSMSG_NOT_USER", channel->name);
5188 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5191 if(infoline[0] == '*' && infoline[1] == 0)
5194 uData->info = strdup(infoline);
5197 reply("CSMSG_USET_INFO", uData->info);
5199 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5203 struct svccmd_list uset_shows_list;
5205 static CHANSERV_FUNC(cmd_uset)
5207 struct svccmd *subcmd;
5211 /* Check if we need to (re-)initialize uset_shows_list. */
5212 if(!uset_shows_list.used)
5216 "NoAutoOp", "AutoInvite", "Info"
5219 if(!uset_shows_list.size)
5221 uset_shows_list.size = ArrayLength(options);
5222 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5224 for(ii = 0; ii < ArrayLength(options); ii++)
5226 const char *name = options[ii];
5227 sprintf(buf, "%s %s", argv[0], name);
5228 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5231 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5234 svccmd_list_append(&uset_shows_list, subcmd);
5240 /* Do this so options are presented in a consistent order. */
5241 reply("CSMSG_USER_OPTIONS");
5242 for(ii = 0; ii < uset_shows_list.used; ii++)
5243 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5247 sprintf(buf, "%s %s", argv[0], argv[1]);
5248 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5251 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5255 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5258 static CHANSERV_FUNC(cmd_giveownership)
5260 struct handle_info *new_owner_hi;
5261 struct userData *new_owner, *curr_user;
5262 struct chanData *cData = channel->channel_info;
5263 struct do_not_register *dnr;
5265 unsigned short co_access;
5266 char reason[MAXLEN];
5269 curr_user = GetChannelAccess(cData, user->handle_info);
5270 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5271 if(!curr_user || (curr_user->access != UL_OWNER))
5273 struct userData *owner = NULL;
5274 for(curr_user = channel->channel_info->users;
5276 curr_user = curr_user->next)
5278 if(curr_user->access != UL_OWNER)
5282 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5288 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5290 if(new_owner_hi == user->handle_info)
5292 reply("CSMSG_NO_TRANSFER_SELF");
5295 new_owner = GetChannelAccess(cData, new_owner_hi);
5298 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5301 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5303 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5306 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5307 if(!IsHelping(user))
5308 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5310 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5313 if(new_owner->access >= UL_COOWNER)
5314 co_access = new_owner->access;
5316 co_access = UL_COOWNER;
5317 new_owner->access = UL_OWNER;
5319 curr_user->access = co_access;
5320 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5321 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5322 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5326 static CHANSERV_FUNC(cmd_suspend)
5328 struct handle_info *hi;
5329 struct userData *self, *target;
5332 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5333 self = GetChannelUser(channel->channel_info, user->handle_info);
5334 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5336 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5339 if(target->access >= self->access)
5341 reply("MSG_USER_OUTRANKED", hi->handle);
5344 if(target->flags & USER_SUSPENDED)
5346 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5351 target->present = 0;
5354 target->flags |= USER_SUSPENDED;
5355 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5359 static CHANSERV_FUNC(cmd_unsuspend)
5361 struct handle_info *hi;
5362 struct userData *self, *target;
5365 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5366 self = GetChannelUser(channel->channel_info, user->handle_info);
5367 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5369 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5372 if(target->access >= self->access)
5374 reply("MSG_USER_OUTRANKED", hi->handle);
5377 if(!(target->flags & USER_SUSPENDED))
5379 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5382 target->flags &= ~USER_SUSPENDED;
5383 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5387 static MODCMD_FUNC(cmd_deleteme)
5389 struct handle_info *hi;
5390 struct userData *target;
5391 const char *confirm_string;
5392 unsigned short access;
5395 hi = user->handle_info;
5396 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5398 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5401 if(target->access == UL_OWNER)
5403 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5406 confirm_string = make_confirmation_string(target);
5407 if((argc < 2) || strcmp(argv[1], confirm_string))
5409 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5412 access = target->access;
5413 channel_name = strdup(channel->name);
5414 del_channel_user(target, 1);
5415 reply("CSMSG_DELETED_YOU", access, channel_name);
5421 chanserv_refresh_topics(UNUSED_ARG(void *data))
5423 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5424 struct chanData *cData;
5427 for(cData = channelList; cData; cData = cData->next)
5429 if(IsSuspended(cData))
5431 opt = cData->chOpts[chTopicRefresh];
5434 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5437 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5438 cData->last_refresh = refresh_num;
5440 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5443 static CHANSERV_FUNC(cmd_unf)
5447 char response[MAXLEN];
5448 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5449 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5450 irc_privmsg(cmd->parent->bot, channel->name, response);
5453 reply("CSMSG_UNF_RESPONSE");
5457 static CHANSERV_FUNC(cmd_ping)
5461 char response[MAXLEN];
5462 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5463 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5464 irc_privmsg(cmd->parent->bot, channel->name, response);
5467 reply("CSMSG_PING_RESPONSE");
5471 static CHANSERV_FUNC(cmd_wut)
5475 char response[MAXLEN];
5476 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5477 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5478 irc_privmsg(cmd->parent->bot, channel->name, response);
5481 reply("CSMSG_WUT_RESPONSE");
5485 static CHANSERV_FUNC(cmd_8ball)
5487 unsigned int i, j, accum;
5492 for(i=1; i<argc; i++)
5493 for(j=0; argv[i][j]; j++)
5494 accum = (accum << 5) - accum + toupper(argv[i][j]);
5495 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5498 char response[MAXLEN];
5499 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5500 irc_privmsg(cmd->parent->bot, channel->name, response);
5503 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5507 static CHANSERV_FUNC(cmd_d)
5509 unsigned long sides, count, modifier, ii, total;
5510 char response[MAXLEN], *sep;
5514 if((count = strtoul(argv[1], &sep, 10)) <= 1)
5522 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5523 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5527 else if((sep[0] == '-') && isdigit(sep[1]))
5528 modifier = strtoul(sep, NULL, 10);
5529 else if((sep[0] == '+') && isdigit(sep[1]))
5530 modifier = strtoul(sep+1, NULL, 10);
5537 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5542 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5545 for(total = ii = 0; ii < count; ++ii)
5546 total += (rand() % sides) + 1;
5549 if((count > 1) || modifier)
5551 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5552 sprintf(response, fmt, total, count, sides, modifier);
5556 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5557 sprintf(response, fmt, total, sides);
5560 send_target_message(5, channel->name, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5562 send_message_type(4, user, cmd->parent->bot, "%s", response);
5566 static CHANSERV_FUNC(cmd_huggle)
5568 char response[MAXLEN];
5570 /* CTCP must be via PRIVMSG, never notice */
5573 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5574 sprintf(response, fmt, user->nick);
5575 irc_privmsg(cmd->parent->bot, channel->name, response);
5579 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5580 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5586 chanserv_adjust_limit(void *data)
5588 struct mod_chanmode change;
5589 struct chanData *cData = data;
5590 struct chanNode *channel = cData->channel;
5593 if(IsSuspended(cData))
5596 cData->limitAdjusted = now;
5597 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5598 if(cData->modes.modes_set & MODE_LIMIT)
5600 if(limit > cData->modes.new_limit)
5601 limit = cData->modes.new_limit;
5602 else if(limit == cData->modes.new_limit)
5606 change.modes_set = MODE_LIMIT;
5607 change.modes_clear = 0;
5608 change.new_limit = limit;
5610 mod_chanmode_announce(chanserv, channel, &change);
5614 handle_new_channel(struct chanNode *channel)
5616 struct chanData *cData;
5618 if(!(cData = channel->channel_info))
5621 if(cData->modes.modes_set || cData->modes.modes_clear)
5622 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5624 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5625 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5628 /* Welcome to my worst nightmare. Warning: Read (or modify)
5629 the code below at your own risk. */
5631 handle_join(struct modeNode *mNode)
5633 struct mod_chanmode change;
5634 struct userNode *user = mNode->user;
5635 struct chanNode *channel = mNode->channel;
5636 struct chanData *cData;
5637 struct userData *uData = NULL;
5638 struct banData *bData;
5639 struct handle_info *handle;
5640 unsigned int modes = 0, info = 0;
5643 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5646 cData = channel->channel_info;
5647 if(channel->members.used > cData->max)
5648 cData->max = channel->members.used;
5650 /* Check for bans. If they're joining through a ban, one of two
5652 * 1: Join during a netburst, by riding the break. Kick them
5653 * unless they have ops or voice in the channel.
5654 * 2: They're allowed to join through the ban (an invite in
5655 * ircu2.10, or a +e on Hybrid, or something).
5656 * If they're not joining through a ban, and the banlist is not
5657 * full, see if they're on the banlist for the channel. If so,
5660 if(user->uplink->burst && !mNode->modes)
5663 for(ii = 0; ii < channel->banlist.used; ii++)
5665 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5667 /* Riding a netburst. Naughty. */
5668 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5674 change.modes_set = change.modes_clear = 0;
5676 if(channel->banlist.used < MAXBANS)
5678 /* Not joining through a ban. */
5679 for(bData = cData->bans;
5680 bData && !user_matches_glob(user, bData->mask, 1);
5681 bData = bData->next);
5685 char kick_reason[MAXLEN];
5686 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5688 bData->triggered = now;
5689 if(bData != cData->bans)
5691 /* Shuffle the ban to the head of the list. */
5692 if(bData->next) bData->next->prev = bData->prev;
5693 if(bData->prev) bData->prev->next = bData->next;
5696 bData->next = cData->bans;
5699 cData->bans->prev = bData;
5700 cData->bans = bData;
5703 change.args[0].mode = MODE_BAN;
5704 change.args[0].hostmask = bData->mask;
5705 mod_chanmode_announce(chanserv, channel, &change);
5706 KickChannelUser(user, channel, chanserv, kick_reason);
5711 /* ChanServ will not modify the limits in join-flooded channels.
5712 It will also skip DynLimit processing when the user (or srvx)
5713 is bursting in, because there are likely more incoming. */
5714 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5715 && !user->uplink->burst
5716 && !channel->join_flooded
5717 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5719 /* The user count has begun "bumping" into the channel limit,
5720 so set a timer to raise the limit a bit. Any previous
5721 timers are removed so three incoming users within the delay
5722 results in one limit change, not three. */
5724 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5725 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5728 if(cData->lvlOpts[lvlGiveOps] == 0)
5729 modes |= MODE_CHANOP;
5730 else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5731 modes |= MODE_VOICE;
5733 greeting = cData->greeting;
5734 if(user->handle_info)
5736 handle = user->handle_info;
5738 if(IsHelper(user) && !IsHelping(user))
5741 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5743 if(channel == chanserv_conf.support_channels.list[ii])
5745 HANDLE_SET_FLAG(user->handle_info, HELPING);
5751 uData = GetTrueChannelAccess(cData, handle);
5752 if(uData && !IsUserSuspended(uData))
5754 /* Ops and above were handled by the above case. */
5755 if(IsUserAutoOp(uData))
5757 if(uData->access < cData->lvlOpts[lvlGiveOps])
5758 modes |= MODE_VOICE;
5760 modes |= MODE_CHANOP;
5762 if(uData->access >= UL_PRESENT)
5763 cData->visited = now;
5768 if(cData->user_greeting)
5769 greeting = cData->user_greeting;
5771 && (cData->flags & CHANNEL_INFO_LINES)
5772 && ((now - uData->seen) >= chanserv_conf.info_delay)
5777 if(!user->uplink->burst)
5781 change.args[0].mode = modes;
5782 change.args[0].member = mNode;
5783 mod_chanmode_announce(chanserv, channel, &change);
5785 if(greeting && !user->uplink->burst)
5786 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5788 send_target_message(4, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5794 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5796 struct mod_chanmode change;
5797 struct userData *channel;
5798 unsigned int ii, jj;
5800 if(!user->handle_info)
5803 change.modes_set = change.modes_clear = 0;
5805 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5807 struct chanNode *cn;
5808 struct modeNode *mn;
5809 if(IsSuspended(channel->channel) || !(cn = channel->channel->channel))
5812 mn = GetUserMode(cn, user);
5815 if(!IsUserSuspended(channel)
5816 && IsUserAutoInvite(channel)
5817 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5819 irc_invite(chanserv, user, cn);
5823 if(channel->access >= UL_PRESENT)
5824 channel->channel->visited = now;
5826 if(IsUserAutoOp(channel))
5828 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5829 change.args[0].mode = MODE_CHANOP;
5831 change.args[0].mode = MODE_VOICE;
5832 change.args[0].member = mn;
5833 mod_chanmode_announce(chanserv, cn, &change);
5836 channel->seen = now;
5837 channel->present = 1;
5840 for(ii = 0; ii < user->channels.used; ++ii)
5842 struct chanNode *channel = user->channels.list[ii]->channel;
5843 struct banData *ban;
5845 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5846 || !channel->channel_info)
5848 for(jj = 0; jj < channel->banlist.used; ++jj)
5849 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5851 if(jj < channel->banlist.used)
5853 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5855 char kick_reason[MAXLEN];
5856 if(!user_matches_glob(user, ban->mask, 1))
5858 change.args[0].mode = MODE_BAN;
5859 change.args[0].hostmask = ban->mask;
5860 mod_chanmode_announce(chanserv, channel, &change);
5861 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5862 KickChannelUser(user, channel, chanserv, kick_reason);
5863 ban->triggered = now;
5868 if(IsSupportHelper(user))
5870 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5872 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5874 HANDLE_SET_FLAG(user->handle_info, HELPING);
5882 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5884 struct chanData *cData;
5885 struct userData *uData;
5886 struct handle_info *handle;
5888 cData = channel->channel_info;
5889 if(!cData || IsSuspended(cData) || IsLocal(user)) return;
5891 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5893 /* Allow for a bit of padding so that the limit doesn't
5894 track the user count exactly, which could get annoying. */
5895 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5897 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5898 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5902 if((handle = user->handle_info) && (uData = GetTrueChannelAccess(cData, handle)))
5905 scan_handle_presence(channel, handle, user);
5908 if(IsHelping(user) && IsSupportHelper(user))
5910 unsigned int ii, jj;
5911 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5913 for(jj = 0; jj < user->channels.used; ++jj)
5914 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5916 if(jj < user->channels.used)
5919 if(ii == chanserv_conf.support_channels.used)
5920 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5925 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5927 char *reason = "CSMSG_USER_PROTECTED";
5929 if(!channel->channel_info || !kicker || IsService(kicker)
5930 || (kicker == victim) || IsSuspended(channel->channel_info)
5931 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5934 if(protect_user(victim, kicker, channel->channel_info))
5935 KickChannelUser(kicker, channel, chanserv, reason);
5939 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5941 struct chanData *cData;
5943 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5945 cData = channel->channel_info;
5946 if(bad_topic(channel, user, channel->topic))
5948 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5949 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5950 SetChannelTopic(channel, chanserv, old_topic, 1);
5951 else if(cData->topic)
5952 SetChannelTopic(channel, chanserv, cData->topic, 1);
5955 /* With topicsnarf, grab the topic and save it as the default topic. */
5956 if(cData->flags & CHANNEL_TOPIC_SNARF)
5959 cData->topic = strdup(channel->topic);
5965 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5967 struct mod_chanmode *bounce = NULL;
5968 unsigned int bnc, ii;
5971 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5974 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5975 && mode_lock_violated(&channel->channel_info->modes, change))
5977 char correct[MAXLEN];
5978 bounce = mod_chanmode_alloc(change->argc + 1);
5979 *bounce = channel->channel_info->modes;
5980 mod_chanmode_format(&channel->channel_info->modes, correct);
5981 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
5983 for(ii = bnc = 0; ii < change->argc; ++ii)
5985 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
5987 const struct userNode *victim = change->args[ii].member->user;
5988 if(!protect_user(victim, user, channel->channel_info))
5991 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
5994 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
5995 bounce->args[bnc].member = GetUserMode(channel, user);
5996 if(bounce->args[bnc].member)
5999 bounce->args[bnc].mode = MODE_CHANOP;
6000 bounce->args[bnc].member = change->args[ii].member;
6002 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6004 else if(change->args[ii].mode & MODE_CHANOP)
6006 const struct userNode *victim = change->args[ii].member->user;
6007 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6010 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6011 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6012 bounce->args[bnc].member = change->args[ii].member;
6015 else if(change->args[ii].mode & MODE_BAN)
6017 const char *ban = change->args[ii].hostmask;
6018 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6021 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6022 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6023 bounce->args[bnc].hostmask = ban;
6025 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", remove);
6030 if((bounce->argc = bnc))
6031 mod_chanmode_announce(chanserv, channel, bounce);
6032 mod_chanmode_free(bounce);
6037 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6039 struct chanNode *channel;
6040 struct banData *bData;
6041 struct mod_chanmode change;
6042 unsigned int ii, jj;
6043 char kick_reason[MAXLEN];
6045 change.modes_set = change.modes_clear = 0;
6047 change.args[0].mode = MODE_BAN;
6048 for(ii = 0; ii < user->channels.used; ++ii)
6050 channel = user->channels.list[ii]->channel;
6051 /* Need not check for bans if they're opped or voiced. */
6052 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6054 /* Need not check for bans unless channel registration is active. */
6055 if(!channel->channel_info || IsSuspended(channel->channel_info))
6057 /* Look for a matching ban already on the channel. */
6058 for(jj = 0; jj < channel->banlist.used; ++jj)
6059 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6061 /* Need not act if we found one. */
6062 if(jj < channel->banlist.used)
6064 /* Look for a matching ban in this channel. */
6065 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6067 if(!user_matches_glob(user, bData->mask, 1))
6069 change.args[0].hostmask = bData->mask;
6070 mod_chanmode_announce(chanserv, channel, &change);
6071 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6072 KickChannelUser(user, channel, chanserv, kick_reason);
6073 bData->triggered = now;
6074 break; /* we don't need to check any more bans in the channel */
6079 static void handle_rename(struct handle_info *handle, const char *old_handle)
6081 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6085 dict_remove2(handle_dnrs, old_handle, 1);
6086 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6087 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6092 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6094 struct userNode *h_user;
6096 if(handle->channels)
6098 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6099 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6101 while(handle->channels)
6102 del_channel_user(handle->channels, 1);
6107 handle_server_link(UNUSED_ARG(struct server *server))
6109 struct chanData *cData;
6111 for(cData = channelList; cData; cData = cData->next)
6113 if(!IsSuspended(cData))
6114 cData->may_opchan = 1;
6115 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6116 && !cData->channel->join_flooded
6117 && ((cData->channel->limit - cData->channel->members.used)
6118 < chanserv_conf.adjust_threshold))
6120 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6121 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6127 chanserv_conf_read(void)
6131 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6132 struct mod_chanmode *change;
6133 struct string_list *strlist;
6134 struct chanNode *chan;
6137 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6139 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6142 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6143 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6144 chanserv_conf.support_channels.used = 0;
6145 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6147 for(ii = 0; ii < strlist->used; ++ii)
6149 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6152 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6154 channelList_append(&chanserv_conf.support_channels, chan);
6157 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6160 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6163 chan = AddChannel(str, now, str2, NULL);
6165 channelList_append(&chanserv_conf.support_channels, chan);
6167 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6168 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6169 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6170 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6171 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6172 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6173 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6174 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6175 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6176 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6177 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6178 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6179 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6180 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6181 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6182 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6183 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6184 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6185 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6186 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6187 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6188 if(str) NickChange(chanserv, str, 0);
6189 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6190 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6191 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6192 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6193 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6194 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6195 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6196 chanserv_conf.max_owned = str ? atoi(str) : 5;
6197 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6198 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6199 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6200 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6201 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6202 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6203 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6206 safestrncpy(mode_line, str, sizeof(mode_line));
6207 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6208 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6210 chanserv_conf.default_modes = *change;
6211 mod_chanmode_free(change);
6213 free_string_list(chanserv_conf.set_shows);
6214 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6216 strlist = string_list_copy(strlist);
6219 static const char *list[] = {
6220 /* multiple choice options */
6221 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6222 "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6223 "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6224 /* binary options */
6225 "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6229 strlist = alloc_string_list(ArrayLength(list)-1);
6230 for(ii=0; list[ii]; ii++)
6231 string_list_append(strlist, strdup(list[ii]));
6233 chanserv_conf.set_shows = strlist;
6234 /* We don't look things up now, in case the list refers to options
6235 * defined by modules initialized after this point. Just mark the
6236 * function list as invalid, so it will be initialized.
6238 set_shows_list.used = 0;
6239 free_string_list(chanserv_conf.eightball);
6240 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6243 strlist = string_list_copy(strlist);
6247 strlist = alloc_string_list(4);
6248 string_list_append(strlist, strdup("Yes."));
6249 string_list_append(strlist, strdup("No."));
6250 string_list_append(strlist, strdup("Maybe so."));
6252 chanserv_conf.eightball = strlist;
6253 free_string_list(chanserv_conf.old_ban_names);
6254 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6256 strlist = string_list_copy(strlist);
6258 strlist = alloc_string_list(2);
6259 chanserv_conf.old_ban_names = strlist;
6263 chanserv_note_type_read(const char *key, struct record_data *rd)
6266 struct note_type *ntype;
6269 if(!(obj = GET_RECORD_OBJECT(rd)))
6271 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6274 if(!(ntype = chanserv_create_note_type(key)))
6276 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6280 /* Figure out set access */
6281 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6283 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6284 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6286 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6288 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6289 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6291 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6293 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6297 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6298 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6299 ntype->set_access.min_opserv = 0;
6302 /* Figure out visibility */
6303 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6304 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6305 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6306 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6307 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6308 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6309 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6310 ntype->visible_type = NOTE_VIS_ALL;
6312 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6314 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6315 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6319 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6321 struct handle_info *handle;
6322 struct userData *uData;
6323 char *seen, *inf, *flags;
6325 unsigned short access;
6327 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6329 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6333 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6334 if(access > UL_OWNER)
6336 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6340 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6341 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6342 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6343 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6344 handle = get_handle_info(key);
6347 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6351 uData = add_channel_user(chan, handle, access, last_seen, inf);
6352 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6356 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6358 struct banData *bData;
6359 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6360 time_t set_time, triggered_time, expires_time;
6362 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6364 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6368 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6369 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6370 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6371 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6372 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6373 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6375 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6376 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6378 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6380 expires_time = set_time + atoi(s_duration);
6384 if(expires_time && (expires_time < now))
6387 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6390 static struct suspended *
6391 chanserv_read_suspended(dict_t obj)
6393 struct suspended *suspended = calloc(1, sizeof(*suspended));
6397 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6398 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6399 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6400 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6401 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6402 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6403 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6404 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6405 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6406 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6411 chanserv_channel_read(const char *key, struct record_data *hir)
6413 struct suspended *suspended;
6414 struct mod_chanmode *modes;
6415 struct chanNode *cNode;
6416 struct chanData *cData;
6417 struct dict *channel, *obj;
6418 char *str, *argv[10];
6422 channel = hir->d.object;
6424 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6427 cNode = AddChannel(key, now, NULL, NULL);
6430 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6433 cData = register_channel(cNode, str);
6436 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6440 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6442 enum levelOption lvlOpt;
6443 enum charOption chOpt;
6444 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6446 if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6448 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6450 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6452 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6454 cData->chOpts[chOpt] = str[0];
6456 if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6457 cData->flags = atoi(str);
6459 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6461 enum levelOption lvlOpt;
6462 enum charOption chOpt;
6465 cData->flags = base64toint(str, 5);
6466 count = strlen(str += 5);
6467 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6470 switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6472 case 'c': lvl = UL_COOWNER; break;
6473 case 'm': lvl = UL_MASTER; break;
6474 case 'n': lvl = UL_OWNER+1; break;
6475 case 'o': lvl = UL_OP; break;
6476 case 'p': lvl = UL_PEON; break;
6477 case 'w': lvl = UL_OWNER; break;
6478 default: lvl = 0; break;
6480 cData->lvlOpts[lvlOpt] = lvl;
6482 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6483 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6486 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6488 suspended = chanserv_read_suspended(obj);
6489 cData->suspended = suspended;
6490 suspended->cData = cData;
6491 /* We could use suspended->expires and suspended->revoked to
6492 * set the CHANNEL_SUSPENDED flag, but we don't. */
6494 else if(cData->flags & CHANNEL_SUSPENDED)
6496 suspended = calloc(1, sizeof(*suspended));
6497 suspended->issued = 0;
6498 suspended->revoked = 0;
6499 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6500 suspended->expires = str ? atoi(str) : 0;
6501 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6502 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6503 suspended->reason = strdup(str ? str : "No reason");
6504 suspended->previous = NULL;
6505 cData->suspended = suspended;
6506 suspended->cData = cData;
6511 if((cData->flags & CHANNEL_SUSPENDED)
6512 && suspended->expires
6513 && (suspended->expires <= now))
6515 cData->flags &= ~CHANNEL_SUSPENDED;
6518 if((cData->flags & CHANNEL_SUSPENDED) && (suspended->expires > now))
6520 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6524 struct mod_chanmode change;
6525 change.modes_set = change.modes_clear = 0;
6527 change.args[0].mode = MODE_CHANOP;
6528 change.args[0].member = AddChannelUser(chanserv, cNode);
6529 mod_chanmode_announce(chanserv, cNode, &change);
6532 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6533 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6534 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6535 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6536 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6537 cData->max = str ? atoi(str) : 0;
6538 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6539 cData->greeting = str ? strdup(str) : NULL;
6540 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6541 cData->user_greeting = str ? strdup(str) : NULL;
6542 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6543 cData->topic_mask = str ? strdup(str) : NULL;
6544 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6545 cData->topic = str ? strdup(str) : NULL;
6547 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6548 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6549 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6550 cData->modes = *modes;
6551 if(cData->modes.argc > 1)
6552 cData->modes.argc = 1;
6553 if(!IsSuspended(cData))
6554 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6555 mod_chanmode_free(modes);
6558 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6559 for(it = dict_first(obj); it; it = iter_next(it))
6560 user_read_helper(iter_key(it), iter_data(it), cData);
6562 if(!cData->users && !IsProtected(cData))
6564 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6565 unregister_channel(cData, "has empty user list.");
6569 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6570 for(it = dict_first(obj); it; it = iter_next(it))
6571 ban_read_helper(iter_key(it), iter_data(it), cData);
6573 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6574 for(it = dict_first(obj); it; it = iter_next(it))
6576 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6577 struct record_data *rd = iter_data(it);
6578 const char *note, *setter;
6580 if(rd->type != RECDB_OBJECT)
6582 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6586 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6588 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6590 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6594 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6595 if(!setter) setter = "<unknown>";
6596 chanserv_add_channel_note(cData, ntype, setter, note);
6604 chanserv_dnr_read(const char *key, struct record_data *hir)
6606 const char *setter, *reason, *str;
6607 struct do_not_register *dnr;
6609 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6612 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6615 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6618 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6621 dnr = chanserv_add_dnr(key, setter, reason);
6624 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6626 dnr->set = atoi(str);
6632 chanserv_saxdb_read(struct dict *database)
6634 struct dict *section;
6637 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6638 for(it = dict_first(section); it; it = iter_next(it))
6639 chanserv_note_type_read(iter_key(it), iter_data(it));
6641 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6642 for(it = dict_first(section); it; it = iter_next(it))
6643 chanserv_channel_read(iter_key(it), iter_data(it));
6645 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6646 for(it = dict_first(section); it; it = iter_next(it))
6647 chanserv_dnr_read(iter_key(it), iter_data(it));
6653 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6655 int high_present = 0;
6656 saxdb_start_record(ctx, KEY_USERS, 1);
6657 for(; uData; uData = uData->next)
6659 if((uData->access >= UL_PRESENT) && uData->present)
6661 saxdb_start_record(ctx, uData->handle->handle, 0);
6662 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6663 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6665 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6667 saxdb_write_string(ctx, KEY_INFO, uData->info);
6668 saxdb_end_record(ctx);
6670 saxdb_end_record(ctx);
6671 return high_present;
6675 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6679 saxdb_start_record(ctx, KEY_BANS, 1);
6680 for(; bData; bData = bData->next)
6682 saxdb_start_record(ctx, bData->mask, 0);
6683 saxdb_write_int(ctx, KEY_SET, bData->set);
6684 if(bData->triggered)
6685 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6687 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6689 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6691 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6692 saxdb_end_record(ctx);
6694 saxdb_end_record(ctx);
6698 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6700 saxdb_start_record(ctx, name, 0);
6701 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6702 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6704 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6706 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6708 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6710 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6711 saxdb_end_record(ctx);
6715 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6719 enum levelOption lvlOpt;
6720 enum charOption chOpt;
6722 saxdb_start_record(ctx, channel->channel->name, 1);
6724 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6725 saxdb_write_int(ctx, KEY_MAX, channel->max);
6727 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6728 if(channel->registrar)
6729 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6730 if(channel->greeting)
6731 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6732 if(channel->user_greeting)
6733 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6734 if(channel->topic_mask)
6735 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6736 if(channel->suspended)
6737 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6739 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6740 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6741 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6742 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6743 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6745 buf[0] = channel->chOpts[chOpt];
6747 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6749 saxdb_end_record(ctx);
6751 if(channel->modes.modes_set || channel->modes.modes_clear)
6753 mod_chanmode_format(&channel->modes, buf);
6754 saxdb_write_string(ctx, KEY_MODES, buf);
6757 high_present = chanserv_write_users(ctx, channel->users);
6758 chanserv_write_bans(ctx, channel->bans);
6760 if(dict_size(channel->notes))
6764 saxdb_start_record(ctx, KEY_NOTES, 1);
6765 for(it = dict_first(channel->notes); it; it = iter_next(it))
6767 struct note *note = iter_data(it);
6768 saxdb_start_record(ctx, iter_key(it), 0);
6769 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6770 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6771 saxdb_end_record(ctx);
6773 saxdb_end_record(ctx);
6776 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6777 saxdb_end_record(ctx);
6781 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6785 saxdb_start_record(ctx, ntype->name, 0);
6786 switch(ntype->set_access_type)
6788 case NOTE_SET_CHANNEL_ACCESS:
6789 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6791 case NOTE_SET_CHANNEL_SETTER:
6792 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6794 case NOTE_SET_PRIVILEGED: default:
6795 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6798 switch(ntype->visible_type)
6800 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6801 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6802 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6804 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6805 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6806 saxdb_end_record(ctx);
6810 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6812 struct do_not_register *dnr;
6815 for(it = dict_first(dnrs); it; it = iter_next(it))
6817 dnr = iter_data(it);
6818 saxdb_start_record(ctx, dnr->chan_name, 0);
6820 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6821 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6822 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6823 saxdb_end_record(ctx);
6828 chanserv_saxdb_write(struct saxdb_context *ctx)
6831 struct chanData *channel;
6834 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6835 for(it = dict_first(note_types); it; it = iter_next(it))
6836 chanserv_write_note_type(ctx, iter_data(it));
6837 saxdb_end_record(ctx);
6840 saxdb_start_record(ctx, KEY_DNR, 1);
6841 write_dnrs_helper(ctx, handle_dnrs);
6842 write_dnrs_helper(ctx, plain_dnrs);
6843 write_dnrs_helper(ctx, mask_dnrs);
6844 saxdb_end_record(ctx);
6847 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6848 for(channel = channelList; channel; channel = channel->next)
6849 chanserv_write_channel(ctx, channel);
6850 saxdb_end_record(ctx);
6856 chanserv_db_cleanup(void) {
6858 unreg_part_func(handle_part);
6860 unregister_channel(channelList, "terminating.");
6861 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6862 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6863 free(chanserv_conf.support_channels.list);
6864 dict_delete(handle_dnrs);
6865 dict_delete(plain_dnrs);
6866 dict_delete(mask_dnrs);
6867 dict_delete(note_types);
6868 free_string_list(chanserv_conf.eightball);
6869 free_string_list(chanserv_conf.old_ban_names);
6870 free_string_list(chanserv_conf.set_shows);
6871 free(set_shows_list.list);
6872 free(uset_shows_list.list);
6875 struct userData *helper = helperList;
6876 helperList = helperList->next;
6881 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6882 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6883 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6886 init_chanserv(const char *nick)
6888 chanserv = AddService(nick, "Channel Services");
6889 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6890 conf_register_reload(chanserv_conf_read);
6892 reg_server_link_func(handle_server_link);
6894 reg_new_channel_func(handle_new_channel);
6895 reg_join_func(handle_join);
6896 reg_part_func(handle_part);
6897 reg_kick_func(handle_kick);
6898 reg_topic_func(handle_topic);
6899 reg_mode_change_func(handle_mode);
6900 reg_nick_change_func(handle_nick_change);
6902 reg_auth_func(handle_auth);
6903 reg_handle_rename_func(handle_rename);
6904 reg_unreg_func(handle_unreg);
6906 handle_dnrs = dict_new();
6907 dict_set_free_data(handle_dnrs, free);
6908 plain_dnrs = dict_new();
6909 dict_set_free_data(plain_dnrs, free);
6910 mask_dnrs = dict_new();
6911 dict_set_free_data(mask_dnrs, free);
6913 reg_svccmd_unbind_func(handle_svccmd_unbind);
6914 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6915 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6916 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6917 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6918 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6919 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6920 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6921 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6922 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6924 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6925 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6927 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6928 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6929 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6930 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6931 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6933 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6934 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6935 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6936 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6937 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6939 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6940 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6941 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6942 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6944 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6945 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6946 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6947 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6948 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6949 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6950 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6951 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6953 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6954 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6955 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6956 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6957 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6958 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6959 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6960 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6961 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6962 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6963 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6964 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6965 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6966 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6968 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "coowner", NULL);
6969 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6970 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6971 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "master", NULL);
6972 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6974 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6975 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6977 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6978 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6979 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6980 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6981 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6982 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6983 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6984 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6985 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6986 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6988 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
6989 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
6991 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
6992 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
6993 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
6994 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
6996 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
6997 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
6998 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
6999 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7000 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7002 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7003 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7004 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7005 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7006 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7007 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7009 /* Channel options */
7010 DEFINE_CHANNEL_OPTION(defaulttopic);
7011 DEFINE_CHANNEL_OPTION(topicmask);
7012 DEFINE_CHANNEL_OPTION(greeting);
7013 DEFINE_CHANNEL_OPTION(usergreeting);
7014 DEFINE_CHANNEL_OPTION(modes);
7015 DEFINE_CHANNEL_OPTION(enfops);
7016 DEFINE_CHANNEL_OPTION(giveops);
7017 DEFINE_CHANNEL_OPTION(protect);
7018 DEFINE_CHANNEL_OPTION(enfmodes);
7019 DEFINE_CHANNEL_OPTION(enftopic);
7020 DEFINE_CHANNEL_OPTION(pubcmd);
7021 DEFINE_CHANNEL_OPTION(voice);
7022 DEFINE_CHANNEL_OPTION(userinfo);
7023 DEFINE_CHANNEL_OPTION(dynlimit);
7024 DEFINE_CHANNEL_OPTION(topicsnarf);
7025 DEFINE_CHANNEL_OPTION(nodelete);
7026 DEFINE_CHANNEL_OPTION(toys);
7027 DEFINE_CHANNEL_OPTION(setters);
7028 DEFINE_CHANNEL_OPTION(topicrefresh);
7029 DEFINE_CHANNEL_OPTION(ctcpusers);
7030 DEFINE_CHANNEL_OPTION(ctcpreaction);
7031 DEFINE_CHANNEL_OPTION(peoninvite);
7032 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7034 /* Alias set topic to set defaulttopic for compatibility. */
7035 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7038 DEFINE_USER_OPTION(noautoop);
7039 DEFINE_USER_OPTION(autoinvite);
7040 DEFINE_USER_OPTION(info);
7042 /* Alias uset autovoice to uset autoop. */
7043 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7045 note_types = dict_new();
7046 dict_set_free_data(note_types, chanserv_deref_note_type);
7047 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7048 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7050 if(chanserv_conf.channel_expire_frequency)
7051 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7053 if(chanserv_conf.refresh_period)
7055 time_t next_refresh;
7056 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7057 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7060 reg_exit_func(chanserv_db_cleanup);
7061 message_register_table(msgtab);