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_BAD_SETTERS", "You cannot change Setters to above your level." },
241 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
242 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
243 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
244 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
245 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
246 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
247 { "CSMSG_SET_MODES", "$bModes $b %s" },
248 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
249 { "CSMSG_SET_USERINFO", "$bUserInfo $b %s" },
250 { "CSMSG_SET_VOICE", "$bVoice $b %s" },
251 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
252 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %s" },
253 { "CSMSG_SET_PEONINVITE", "$bPeonInvite $b %s" },
254 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
255 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
256 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
257 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
258 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
259 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
260 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
261 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
262 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
263 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
264 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
265 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
266 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
267 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
268 { "CSMSG_USET_INFO", "$bInfo $b %s" },
270 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
271 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
272 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
273 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
274 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
275 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
276 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
277 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
278 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
279 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
280 { "CSMSG_PROTECT_NONE", "No users will be protected." },
281 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
282 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
283 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
284 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
285 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
286 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
287 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
288 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
289 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
290 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
291 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
292 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
294 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
295 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
296 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
297 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
298 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
300 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
301 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
302 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
304 /* Channel userlist */
305 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
306 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
307 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
308 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
310 /* Channel note list */
311 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
312 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
313 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
314 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
315 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
316 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
317 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
318 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
319 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
320 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
321 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
322 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
323 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
324 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
325 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
327 /* Channel [un]suspension */
328 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
329 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
330 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
331 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
332 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
333 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
334 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
336 /* Access information */
337 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
338 { "CSMSG_ACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
339 { "CSMSG_SQUAT_ACCESS", "You do not have access to any channels." },
340 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
341 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
342 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
343 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
344 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
345 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
346 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
347 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
349 /* Seen information */
350 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
351 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
352 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
353 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
355 /* Names information */
356 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
357 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
359 /* Channel information */
360 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
361 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
362 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
363 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
364 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
365 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
366 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
367 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
368 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
369 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
370 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
371 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
372 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
373 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
374 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
375 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
378 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
379 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
380 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
382 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
383 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
384 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
385 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
386 { "CSMSG_PEEK_OPS", "$bOps:$b" },
387 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
389 /* Network information */
390 { "CSMSG_NETWORK_INFO", "Network Information:" },
391 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
392 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
393 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
394 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
395 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
396 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
397 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
400 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
401 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
402 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
404 /* Channel searches */
405 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
406 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
407 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
408 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
410 /* Channel configuration */
411 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
412 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
413 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
416 { "CSMSG_USER_OPTIONS", "User Options:" },
417 { "CSMSG_USER_PROTECTED", "That user is protected." },
420 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
421 { "CSMSG_PING_RESPONSE", "Pong!" },
422 { "CSMSG_WUT_RESPONSE", "wut" },
423 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
424 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
425 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
426 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
427 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
428 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
429 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
432 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
436 /* eject_user and unban_user flags */
437 #define ACTION_KICK 0x0001
438 #define ACTION_BAN 0x0002
439 #define ACTION_ADD_BAN 0x0004
440 #define ACTION_ADD_TIMED_BAN 0x0008
441 #define ACTION_UNBAN 0x0010
442 #define ACTION_DEL_BAN 0x0020
444 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
445 #define MODELEN 40 + KEYLEN
449 #define CSFUNC_ARGS user, channel, argc, argv, cmd
451 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
452 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
453 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
454 reply("MSG_MISSING_PARAMS", argv[0]); \
458 DECLARE_LIST(dnrList, struct do_not_register *);
459 DEFINE_LIST(dnrList, struct do_not_register *);
461 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
463 struct userNode *chanserv;
465 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
466 static struct log_type *CS_LOG;
470 struct channelList support_channels;
471 struct mod_chanmode default_modes;
473 unsigned long db_backup_frequency;
474 unsigned long channel_expire_frequency;
477 unsigned int adjust_delay;
478 long channel_expire_delay;
479 unsigned int nodelete_level;
481 unsigned int adjust_threshold;
482 int join_flood_threshold;
484 unsigned int greeting_length;
485 unsigned int refresh_period;
487 unsigned int max_owned;
488 unsigned int max_chan_users;
489 unsigned int max_chan_bans;
491 struct string_list *set_shows;
492 struct string_list *eightball;
493 struct string_list *old_ban_names;
495 const char *ctcp_short_ban_duration;
496 const char *ctcp_long_ban_duration;
498 const char *irc_operator_epithet;
499 const char *network_helper_epithet;
500 const char *support_helper_epithet;
505 struct userNode *user;
506 struct userNode *bot;
507 struct chanNode *channel;
509 unsigned short lowest;
510 unsigned short highest;
511 struct userData **users;
512 struct helpfile_table table;
515 enum note_access_type
517 NOTE_SET_CHANNEL_ACCESS,
518 NOTE_SET_CHANNEL_SETTER,
522 enum note_visible_type
525 NOTE_VIS_CHANNEL_USERS,
531 enum note_access_type set_access_type;
533 unsigned int min_opserv;
534 unsigned short min_ulevel;
536 enum note_visible_type visible_type;
537 unsigned int max_length;
544 struct note_type *type;
545 char setter[NICKSERV_HANDLE_LEN+1];
549 static unsigned int registered_channels;
550 static unsigned int banCount;
552 static const struct {
555 unsigned short level;
558 { "peon", "Peon", UL_PEON, '+' },
559 { "op", "Op", UL_OP, '@' },
560 { "master", "Master", UL_MASTER, '%' },
561 { "coowner", "Coowner", UL_COOWNER, '*' },
562 { "owner", "Owner", UL_OWNER, '!' },
563 { "helper", "BUG:", UL_HELPER, 'X' }
566 static const struct {
569 unsigned short default_value;
570 unsigned int old_idx;
572 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2 },
573 { "CSMSG_SET_ENFOPS", "enfops", 300, 1 },
574 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3 },
575 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4 },
576 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5 },
577 { "CSMSG_SET_SETTERS", "setters", 400, 7 },
578 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9 }
581 struct charOptionValues {
584 } protectValues[] = {
585 { 'a', "CSMSG_PROTECT_ALL" },
586 { 'e', "CSMSG_PROTECT_EQUAL" },
587 { 'l', "CSMSG_PROTECT_LOWER" },
588 { 'n', "CSMSG_PROTECT_NONE" }
590 { 'd', "CSMSG_TOYS_DISABLED" },
591 { 'n', "CSMSG_TOYS_PRIVATE" },
592 { 'p', "CSMSG_TOYS_PUBLIC" }
593 }, topicRefreshValues[] = {
594 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
595 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
596 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
597 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
598 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
599 }, ctcpReactionValues[] = {
600 { 'k', "CSMSG_CTCPREACTION_KICK" },
601 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
602 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
603 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
606 static const struct {
610 unsigned int old_idx;
612 struct charOptionValues *values;
614 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
615 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
616 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
617 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
620 struct userData *helperList;
621 struct chanData *channelList;
622 static struct module *chanserv_module;
623 static unsigned int userCount;
625 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
626 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
627 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
630 user_level_from_name(const char *name, unsigned short clamp_level)
632 unsigned int level = 0, ii;
635 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
636 if(!irccasecmp(name, accessLevels[ii].name))
637 level = accessLevels[ii].level;
638 if(level > clamp_level)
644 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
647 *minl = strtoul(arg, &sep, 10);
655 *maxl = strtoul(sep+1, &sep, 10);
663 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
665 struct userData *uData, **head;
667 if(!channel || !handle)
670 if(override && HANDLE_FLAGGED(handle, HELPING)
671 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
673 for(uData = helperList;
674 uData && uData->handle != handle;
675 uData = uData->next);
679 uData = calloc(1, sizeof(struct userData));
680 uData->handle = handle;
682 uData->access = UL_HELPER;
688 uData->next = helperList;
690 helperList->prev = uData;
698 for(uData = channel->users; uData; uData = uData->next)
699 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
702 head = &(channel->users);
705 if(uData && (uData != *head))
707 /* Shuffle the user to the head of whatever list he was in. */
709 uData->next->prev = uData->prev;
711 uData->prev->next = uData->next;
717 (**head).prev = uData;
724 /* Returns non-zero if user has at least the minimum access.
725 * exempt_owner is set when handling !set, so the owner can set things
728 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
730 struct userData *uData;
731 struct chanData *cData = channel->channel_info;
732 unsigned short minimum = cData->lvlOpts[opt];
735 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
738 if(minimum <= uData->access)
740 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
745 /* Scan for other users authenticated to the same handle
746 still in the channel. If so, keep them listed as present.
748 user is optional, if not null, it skips checking that userNode
749 (for the handle_part function) */
751 scan_user_presence(struct userData *uData, struct userNode *user)
755 if(IsSuspended(uData->channel)
756 || IsUserSuspended(uData)
757 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
769 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
771 unsigned int eflags, argc;
773 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
775 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
776 if(!channel->channel_info
777 || IsSuspended(channel->channel_info)
779 || !ircncasecmp(text, "ACTION ", 7))
781 /* Figure out the minimum level needed to CTCP the channel */
782 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
784 /* We need to enforce against them; do so. */
787 argv[1] = user->nick;
789 if(GetUserMode(channel, user))
790 eflags |= ACTION_KICK;
791 switch(channel->channel_info->chOpts[chCTCPReaction]) {
792 default: case 'k': /* just do the kick */ break;
794 eflags |= ACTION_BAN;
797 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
798 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
801 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
802 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
805 argv[argc++] = bad_ctcp_reason;
806 eject_user(chanserv, channel, argc, argv, NULL, eflags);
810 chanserv_create_note_type(const char *name)
812 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
813 strcpy(ntype->name, name);
815 dict_insert(note_types, ntype->name, ntype);
820 chanserv_deref_note_type(void *data)
822 struct note_type *ntype = data;
824 if(--ntype->refs > 0)
830 chanserv_flush_note_type(struct note_type *ntype)
832 struct chanData *cData;
833 for(cData = channelList; cData; cData = cData->next)
834 dict_remove(cData->notes, ntype->name);
838 chanserv_truncate_notes(struct note_type *ntype)
840 struct chanData *cData;
842 unsigned int size = sizeof(*note) + ntype->max_length;
844 for(cData = channelList; cData; cData = cData->next) {
845 note = dict_find(cData->notes, ntype->name, NULL);
848 if(strlen(note->note) <= ntype->max_length)
850 dict_remove2(cData->notes, ntype->name, 1);
851 note = realloc(note, size);
852 note->note[ntype->max_length] = 0;
853 dict_insert(cData->notes, ntype->name, note);
857 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
860 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
863 unsigned int len = strlen(text);
865 if(len > type->max_length) len = type->max_length;
866 note = calloc(1, sizeof(*note) + len);
868 strncpy(note->setter, setter, sizeof(note->setter)-1);
869 memcpy(note->note, text, len);
871 dict_insert(channel->notes, type->name, note);
877 chanserv_free_note(void *data)
879 struct note *note = data;
881 chanserv_deref_note_type(note->type);
882 assert(note->type->refs > 0); /* must use delnote to remove the type */
886 static MODCMD_FUNC(cmd_createnote) {
887 struct note_type *ntype;
888 unsigned int arg = 1, existed = 0, max_length;
890 if((ntype = dict_find(note_types, argv[1], NULL)))
893 ntype = chanserv_create_note_type(argv[arg]);
894 if(!irccasecmp(argv[++arg], "privileged"))
897 ntype->set_access_type = NOTE_SET_PRIVILEGED;
898 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
900 else if(!irccasecmp(argv[arg], "channel"))
902 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
905 reply("CSMSG_INVALID_ACCESS", argv[arg]);
908 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
909 ntype->set_access.min_ulevel = ulvl;
911 else if(!irccasecmp(argv[arg], "setter"))
913 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
917 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
921 if(!irccasecmp(argv[++arg], "privileged"))
922 ntype->visible_type = NOTE_VIS_PRIVILEGED;
923 else if(!irccasecmp(argv[arg], "channel_users"))
924 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
925 else if(!irccasecmp(argv[arg], "all"))
926 ntype->visible_type = NOTE_VIS_ALL;
928 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
932 if((arg+1) >= argc) {
933 reply("MSG_MISSING_PARAMS", argv[0]);
936 max_length = strtoul(argv[++arg], NULL, 0);
937 if(max_length < 20 || max_length > 450)
939 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
942 if(existed && (max_length < ntype->max_length))
944 ntype->max_length = max_length;
945 chanserv_truncate_notes(ntype);
947 ntype->max_length = max_length;
950 reply("CSMSG_NOTE_MODIFIED", ntype->name);
952 reply("CSMSG_NOTE_CREATED", ntype->name);
957 dict_remove(note_types, ntype->name);
961 static MODCMD_FUNC(cmd_removenote) {
962 struct note_type *ntype;
965 ntype = dict_find(note_types, argv[1], NULL);
966 force = (argc > 2) && !irccasecmp(argv[2], "force");
969 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
976 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
979 chanserv_flush_note_type(ntype);
981 dict_remove(note_types, argv[1]);
982 reply("CSMSG_NOTE_DELETED", argv[1]);
987 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
991 if(orig->modes_set & change->modes_clear)
993 if(orig->modes_clear & change->modes_set)
995 if((orig->modes_set & MODE_KEY)
996 && strcmp(orig->new_key, change->new_key))
998 if((orig->modes_set & MODE_LIMIT)
999 && (orig->new_limit != change->new_limit))
1004 static char max_length_text[MAXLEN+1][16];
1006 static struct helpfile_expansion
1007 chanserv_expand_variable(const char *variable)
1009 struct helpfile_expansion exp;
1011 if(!irccasecmp(variable, "notes"))
1014 exp.type = HF_TABLE;
1015 exp.value.table.length = 1;
1016 exp.value.table.width = 3;
1017 exp.value.table.flags = 0;
1018 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1019 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1020 exp.value.table.contents[0][0] = "Note Type";
1021 exp.value.table.contents[0][1] = "Visibility";
1022 exp.value.table.contents[0][2] = "Max Length";
1023 for(it=dict_first(note_types); it; it=iter_next(it))
1025 struct note_type *ntype = iter_data(it);
1028 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1029 row = exp.value.table.length++;
1030 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1031 exp.value.table.contents[row][0] = ntype->name;
1032 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1033 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1035 if(!max_length_text[ntype->max_length][0])
1036 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1037 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1042 exp.type = HF_STRING;
1043 exp.value.str = NULL;
1047 static struct chanData*
1048 register_channel(struct chanNode *cNode, char *registrar)
1050 struct chanData *channel;
1051 enum levelOption lvlOpt;
1052 enum charOption chOpt;
1054 channel = calloc(1, sizeof(struct chanData));
1056 channel->notes = dict_new();
1057 dict_set_free_data(channel->notes, chanserv_free_note);
1059 channel->registrar = strdup(registrar);
1060 channel->registered = now;
1061 channel->visited = now;
1062 channel->limitAdjusted = now;
1063 channel->flags = CHANNEL_DEFAULT_FLAGS;
1064 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1065 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1066 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1067 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1069 channel->prev = NULL;
1070 channel->next = channelList;
1073 channelList->prev = channel;
1074 channelList = channel;
1075 registered_channels++;
1077 channel->channel = cNode;
1079 cNode->channel_info = channel;
1084 static struct userData*
1085 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1087 struct userData *ud;
1089 if(access > UL_OWNER)
1092 ud = calloc(1, sizeof(*ud));
1093 ud->channel = channel;
1094 ud->handle = handle;
1096 ud->access = access;
1097 ud->info = info ? strdup(info) : NULL;
1100 ud->next = channel->users;
1102 channel->users->prev = ud;
1103 channel->users = ud;
1105 channel->userCount++;
1109 ud->u_next = ud->handle->channels;
1111 ud->u_next->u_prev = ud;
1112 ud->handle->channels = ud;
1117 static void unregister_channel(struct chanData *channel, const char *reason);
1120 del_channel_user(struct userData *user, int do_gc)
1122 struct chanData *channel = user->channel;
1124 channel->userCount--;
1128 user->prev->next = user->next;
1130 channel->users = user->next;
1132 user->next->prev = user->prev;
1135 user->u_prev->u_next = user->u_next;
1137 user->handle->channels = user->u_next;
1139 user->u_next->u_prev = user->u_prev;
1143 if(do_gc && !channel->users && !IsProtected(channel))
1144 unregister_channel(channel, "lost all users.");
1147 static void expire_ban(void *data);
1149 static struct banData*
1150 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1153 unsigned int ii, l1, l2;
1158 bd = malloc(sizeof(struct banData));
1160 bd->channel = channel;
1162 bd->triggered = triggered;
1163 bd->expires = expires;
1165 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1167 extern const char *hidden_host_suffix;
1168 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1172 l2 = strlen(old_name);
1175 if(irccasecmp(mask + l1 - l2, old_name))
1177 new_mask = alloca(MAXLEN);
1178 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1181 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1183 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1184 bd->reason = reason ? strdup(reason) : NULL;
1187 timeq_add(expires, expire_ban, bd);
1190 bd->next = channel->bans;
1192 channel->bans->prev = bd;
1194 channel->banCount++;
1201 del_channel_ban(struct banData *ban)
1203 ban->channel->banCount--;
1207 ban->prev->next = ban->next;
1209 ban->channel->bans = ban->next;
1212 ban->next->prev = ban->prev;
1215 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1224 expire_ban(void *data)
1226 struct banData *bd = data;
1227 if(!IsSuspended(bd->channel))
1229 struct banList bans;
1230 struct mod_chanmode change;
1232 bans = bd->channel->channel->banlist;
1233 change.modes_set = change.modes_clear = 0;
1235 for(ii=0; ii<bans.used; ii++)
1237 if(!strcmp(bans.list[ii]->ban, bd->mask))
1240 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1241 change.args[0].hostmask = bd->mask;
1242 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1248 del_channel_ban(bd);
1251 static void chanserv_expire_suspension(void *data);
1254 unregister_channel(struct chanData *channel, const char *reason)
1256 char msgbuf[MAXLEN];
1258 /* After channel unregistration, the following must be cleaned
1260 - Channel information.
1263 - Channel suspension data.
1264 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1270 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1272 while(channel->users)
1273 del_channel_user(channel->users, 0);
1275 while(channel->bans)
1276 del_channel_ban(channel->bans);
1278 if(channel->topic) free(channel->topic);
1279 if(channel->registrar) free(channel->registrar);
1280 if(channel->greeting) free(channel->greeting);
1281 if(channel->user_greeting) free(channel->user_greeting);
1282 if(channel->topic_mask) free(channel->topic_mask);
1284 if(channel->prev) channel->prev->next = channel->next;
1285 else channelList = channel->next;
1287 if(channel->next) channel->next->prev = channel->prev;
1289 if(channel->suspended)
1291 struct chanNode *cNode = channel->channel;
1292 struct suspended *suspended, *next_suspended;
1294 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1296 next_suspended = suspended->previous;
1297 free(suspended->suspender);
1298 free(suspended->reason);
1299 if(suspended->expires)
1300 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1305 cNode->channel_info = NULL;
1307 channel->channel->channel_info = NULL;
1310 dict_delete(channel->notes);
1311 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1312 if(!IsSuspended(channel))
1313 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1314 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1315 UnlockChannel(channel->channel);
1317 registered_channels--;
1321 expire_channels(UNUSED_ARG(void *data))
1323 struct chanData *channel, *next;
1324 struct userData *user;
1325 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1327 intervalString(delay, chanserv_conf.channel_expire_delay);
1328 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1330 for(channel = channelList; channel; channel = next)
1332 next = channel->next;
1334 /* See if the channel can be expired. */
1335 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1336 || IsProtected(channel))
1339 /* Make sure there are no high-ranking users still in the channel. */
1340 for(user=channel->users; user; user=user->next)
1341 if(user->present && (user->access >= UL_PRESENT))
1346 /* Unregister the channel */
1347 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1348 unregister_channel(channel, "registration expired.");
1351 if(chanserv_conf.channel_expire_frequency)
1352 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1356 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1358 char protect = channel->chOpts[chProtect];
1359 struct userData *cs_victim, *cs_aggressor;
1361 /* Don't protect if no one is to be protected, someone is attacking
1362 himself, or if the aggressor is an IRC Operator. */
1363 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1366 /* Don't protect if the victim isn't authenticated (because they
1367 can't be a channel user), unless we are to protect non-users
1369 cs_victim = GetChannelAccess(channel, victim->handle_info);
1370 if(protect != 'a' && !cs_victim)
1373 /* Protect if the aggressor isn't a user because at this point,
1374 the aggressor can only be less than or equal to the victim. */
1375 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1379 /* If the aggressor was a user, then the victim can't be helped. */
1386 if(cs_victim->access > cs_aggressor->access)
1391 if(cs_victim->access >= cs_aggressor->access)
1400 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1402 struct chanData *cData = channel->channel_info;
1403 struct userData *cs_victim;
1405 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1406 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1407 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1409 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1417 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1419 if(IsService(victim))
1421 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1425 if(protect_user(victim, user, channel->channel_info))
1427 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1434 static struct do_not_register *
1435 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1437 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1438 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1439 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1440 strcpy(dnr->reason, reason);
1442 if(dnr->chan_name[0] == '*')
1443 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1444 else if(strpbrk(dnr->chan_name, "*?"))
1445 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1447 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1451 static struct dnrList
1452 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1454 struct dnrList list;
1456 struct do_not_register *dnr;
1458 dnrList_init(&list);
1459 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1460 dnrList_append(&list, dnr);
1461 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1462 dnrList_append(&list, dnr);
1464 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1465 if(match_ircglob(chan_name, iter_key(it)))
1466 dnrList_append(&list, iter_data(it));
1471 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1473 struct dnrList list;
1474 struct do_not_register *dnr;
1476 char buf[INTERVALLEN];
1478 list = chanserv_find_dnrs(chan_name, handle);
1479 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1481 dnr = list.list[ii];
1484 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1485 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1488 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1491 reply("CSMSG_MORE_DNRS", list.used - ii);
1496 struct do_not_register *
1497 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1499 struct do_not_register *dnr;
1502 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1506 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1508 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1509 if(match_ircglob(chan_name, iter_key(it)))
1510 return iter_data(it);
1515 static CHANSERV_FUNC(cmd_noregister)
1518 struct do_not_register *dnr;
1519 char buf[INTERVALLEN];
1520 unsigned int matches;
1526 reply("CSMSG_DNR_SEARCH_RESULTS");
1528 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1530 dnr = iter_data(it);
1532 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1534 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1537 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1539 dnr = iter_data(it);
1541 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1543 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1546 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1548 dnr = iter_data(it);
1550 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1552 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1557 reply("MSG_MATCH_COUNT", matches);
1559 reply("MSG_NO_MATCHES");
1565 if(!IsChannelName(target) && (*target != '*'))
1567 reply("CSMSG_NOT_DNR", target);
1573 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1574 if((*target == '*') && !get_handle_info(target + 1))
1576 reply("MSG_HANDLE_UNKNOWN", target + 1);
1579 chanserv_add_dnr(target, user->handle_info->handle, reason);
1580 reply("CSMSG_NOREGISTER_CHANNEL", target);
1584 reply("CSMSG_DNR_SEARCH_RESULTS");
1586 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1588 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1590 reply("MSG_NO_MATCHES");
1594 static CHANSERV_FUNC(cmd_allowregister)
1596 const char *chan_name = argv[1];
1598 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1600 dict_remove(handle_dnrs, chan_name+1);
1601 reply("CSMSG_DNR_REMOVED", chan_name);
1603 else if(dict_find(plain_dnrs, chan_name, NULL))
1605 dict_remove(plain_dnrs, chan_name);
1606 reply("CSMSG_DNR_REMOVED", chan_name);
1608 else if(dict_find(mask_dnrs, chan_name, NULL))
1610 dict_remove(mask_dnrs, chan_name);
1611 reply("CSMSG_DNR_REMOVED", chan_name);
1615 reply("CSMSG_NO_SUCH_DNR", chan_name);
1622 chanserv_get_owned_count(struct handle_info *hi)
1624 struct userData *cList;
1627 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1628 if(cList->access == UL_OWNER)
1633 static CHANSERV_FUNC(cmd_register)
1635 struct mod_chanmode *change;
1636 struct handle_info *handle;
1637 struct chanData *cData;
1638 struct modeNode *mn;
1639 char reason[MAXLEN];
1641 unsigned int new_channel, force=0;
1642 struct do_not_register *dnr;
1646 if(channel->channel_info)
1648 reply("CSMSG_ALREADY_REGGED", channel->name);
1652 if(channel->bad_channel)
1654 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1658 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1660 reply("CSMSG_MUST_BE_OPPED", channel->name);
1665 chan_name = channel->name;
1669 if((argc < 2) || !IsChannelName(argv[1]))
1671 reply("MSG_NOT_CHANNEL_NAME");
1675 if(opserv_bad_channel(argv[1]))
1677 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1682 chan_name = argv[1];
1685 if(argc >= (new_channel+2))
1687 if(!IsHelping(user))
1689 reply("CSMSG_PROXY_FORBIDDEN");
1693 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1695 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1696 dnr = chanserv_is_dnr(chan_name, handle);
1700 handle = user->handle_info;
1701 dnr = chanserv_is_dnr(chan_name, handle);
1705 if(!IsHelping(user))
1706 reply("CSMSG_DNR_CHANNEL", chan_name);
1708 chanserv_show_dnrs(user, cmd, chan_name, handle);
1712 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1714 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1719 channel = AddChannel(argv[1], now, NULL, NULL);
1721 cData = register_channel(channel, user->handle_info->handle);
1722 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1723 cData->modes = chanserv_conf.default_modes;
1724 change = mod_chanmode_dup(&cData->modes, 1);
1725 change->args[change->argc].mode = MODE_CHANOP;
1726 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1728 mod_chanmode_announce(chanserv, channel, change);
1729 mod_chanmode_free(change);
1731 /* Initialize the channel's max user record. */
1732 cData->max = channel->members.used;
1734 if(handle != user->handle_info)
1735 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1737 reply("CSMSG_REG_SUCCESS", channel->name);
1739 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1740 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1745 make_confirmation_string(struct userData *uData)
1747 static char strbuf[16];
1752 for(src = uData->handle->handle; *src; )
1753 accum = accum * 31 + toupper(*src++);
1755 for(src = uData->channel->channel->name; *src; )
1756 accum = accum * 31 + toupper(*src++);
1757 sprintf(strbuf, "%08x", accum);
1761 static CHANSERV_FUNC(cmd_unregister)
1764 char reason[MAXLEN];
1765 struct chanData *cData;
1766 struct userData *uData;
1768 cData = channel->channel_info;
1771 reply("CSMSG_NOT_REGISTERED", channel->name);
1775 uData = GetChannelUser(cData, user->handle_info);
1776 if(!uData || (uData->access < UL_OWNER))
1778 reply("CSMSG_NO_ACCESS");
1782 if(IsProtected(cData))
1784 reply("CSMSG_UNREG_NODELETE", channel->name);
1788 if(!IsHelping(user))
1790 const char *confirm_string;
1791 if(IsSuspended(cData))
1793 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1796 confirm_string = make_confirmation_string(uData);
1797 if((argc < 2) || strcmp(argv[1], confirm_string))
1799 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1804 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1805 name = strdup(channel->name);
1806 unregister_channel(cData, reason);
1807 reply("CSMSG_UNREG_SUCCESS", name);
1812 static CHANSERV_FUNC(cmd_move)
1814 struct chanNode *target;
1815 struct modeNode *mn;
1816 struct userData *uData;
1817 char reason[MAXLEN];
1818 struct do_not_register *dnr;
1822 if(IsProtected(channel->channel_info))
1824 reply("CSMSG_MOVE_NODELETE", channel->name);
1828 if(!IsChannelName(argv[1]))
1830 reply("MSG_NOT_CHANNEL_NAME");
1834 if(opserv_bad_channel(argv[1]))
1836 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1840 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1842 for(uData = channel->channel_info->users; uData; uData = uData->next)
1844 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1846 if(!IsHelping(user))
1847 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1849 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1855 if(!(target = GetChannel(argv[1])))
1857 target = AddChannel(argv[1], now, NULL, NULL);
1858 LockChannel(target);
1859 if(!IsSuspended(channel->channel_info))
1860 AddChannelUser(chanserv, target);
1862 else if(target->channel_info)
1864 reply("CSMSG_ALREADY_REGGED", target->name);
1867 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1868 && !IsHelping(user))
1870 reply("CSMSG_MUST_BE_OPPED", target->name);
1873 else if(!IsSuspended(channel->channel_info))
1875 struct mod_chanmode change;
1876 change.modes_set = change.modes_clear = 0;
1878 change.args[0].mode = MODE_CHANOP;
1879 change.args[0].member = AddChannelUser(chanserv, target);
1880 mod_chanmode_announce(chanserv, target, &change);
1883 /* Move the channel_info to the target channel; it
1884 shouldn't be necessary to clear timeq callbacks
1885 for the old channel. */
1886 target->channel_info = channel->channel_info;
1887 target->channel_info->channel = target;
1888 channel->channel_info = NULL;
1890 reply("CSMSG_MOVE_SUCCESS", target->name);
1892 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1893 if(!IsSuspended(target->channel_info))
1895 char reason2[MAXLEN];
1896 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1897 DelChannelUser(chanserv, channel, reason2, 0);
1899 UnlockChannel(channel);
1900 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1905 merge_users(struct chanData *source, struct chanData *target)
1907 struct userData *suData, *tuData, *next;
1913 /* Insert the source's users into the scratch area. */
1914 for(suData = source->users; suData; suData = suData->next)
1915 dict_insert(merge, suData->handle->handle, suData);
1917 /* Iterate through the target's users, looking for
1918 users common to both channels. The lower access is
1919 removed from either the scratch area or target user
1921 for(tuData = target->users; tuData; tuData = next)
1923 struct userData *choice;
1925 next = tuData->next;
1927 /* If a source user exists with the same handle as a target
1928 channel's user, resolve the conflict by removing one. */
1929 suData = dict_find(merge, tuData->handle->handle, NULL);
1933 /* Pick the data we want to keep. */
1934 /* If the access is the same, use the later seen time. */
1935 if(suData->access == tuData->access)
1936 choice = (suData->seen > tuData->seen) ? suData : tuData;
1937 else /* Otherwise, keep the higher access level. */
1938 choice = (suData->access > tuData->access) ? suData : tuData;
1940 /* Remove the user that wasn't picked. */
1941 if(choice == tuData)
1943 dict_remove(merge, suData->handle->handle);
1944 del_channel_user(suData, 0);
1947 del_channel_user(tuData, 0);
1950 /* Move the remaining users to the target channel. */
1951 for(it = dict_first(merge); it; it = iter_next(it))
1953 suData = iter_data(it);
1955 /* Insert the user into the target channel's linked list. */
1956 suData->prev = NULL;
1957 suData->next = target->users;
1958 suData->channel = target;
1961 target->users->prev = suData;
1962 target->users = suData;
1964 /* Update the user counts for the target channel; the
1965 source counts are left alone. */
1966 target->userCount++;
1969 /* Possible to assert (source->users == NULL) here. */
1970 source->users = NULL;
1975 merge_bans(struct chanData *source, struct chanData *target)
1977 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1979 /* Hold on to the original head of the target ban list
1980 to avoid comparing source bans with source bans. */
1981 tFront = target->bans;
1983 /* Perform a totally expensive O(n*m) merge, ick. */
1984 for(sbData = source->bans; sbData; sbData = sNext)
1986 /* Flag to track whether the ban's been moved
1987 to the destination yet. */
1990 /* Possible to assert (sbData->prev == NULL) here. */
1991 sNext = sbData->next;
1993 for(tbData = tFront; tbData; tbData = tNext)
1995 tNext = tbData->next;
1997 /* Perform two comparisons between each source
1998 and target ban, conflicts are resolved by
1999 keeping the broader ban and copying the later
2000 expiration and triggered time. */
2001 if(match_ircglobs(tbData->mask, sbData->mask))
2003 /* There is a broader ban in the target channel that
2004 overrides one in the source channel; remove the
2005 source ban and break. */
2006 if(sbData->expires > tbData->expires)
2007 tbData->expires = sbData->expires;
2008 if(sbData->triggered > tbData->triggered)
2009 tbData->triggered = sbData->triggered;
2010 del_channel_ban(sbData);
2013 else if(match_ircglobs(sbData->mask, tbData->mask))
2015 /* There is a broader ban in the source channel that
2016 overrides one in the target channel; remove the
2017 target ban, fall through and move the source over. */
2018 if(tbData->expires > sbData->expires)
2019 sbData->expires = tbData->expires;
2020 if(tbData->triggered > sbData->triggered)
2021 sbData->triggered = tbData->triggered;
2022 if(tbData == tFront)
2024 del_channel_ban(tbData);
2027 /* Source bans can override multiple target bans, so
2028 we allow a source to run through this loop multiple
2029 times, but we can only move it once. */
2034 /* Remove the source ban from the source ban list. */
2036 sbData->next->prev = sbData->prev;
2038 /* Modify the source ban's associated channel. */
2039 sbData->channel = target;
2041 /* Insert the ban into the target channel's linked list. */
2042 sbData->prev = NULL;
2043 sbData->next = target->bans;
2046 target->bans->prev = sbData;
2047 target->bans = sbData;
2049 /* Update the user counts for the target channel. */
2054 /* Possible to assert (source->bans == NULL) here. */
2055 source->bans = NULL;
2059 merge_data(struct chanData *source, struct chanData *target)
2061 if(source->visited > target->visited)
2062 target->visited = source->visited;
2066 merge_channel(struct chanData *source, struct chanData *target)
2068 merge_users(source, target);
2069 merge_bans(source, target);
2070 merge_data(source, target);
2073 static CHANSERV_FUNC(cmd_merge)
2075 struct userData *target_user;
2076 struct chanNode *target;
2077 char reason[MAXLEN];
2081 /* Make sure the target channel exists and is registered to the user
2082 performing the command. */
2083 if(!(target = GetChannel(argv[1])))
2085 reply("MSG_INVALID_CHANNEL");
2089 if(!target->channel_info)
2091 reply("CSMSG_NOT_REGISTERED", target->name);
2095 if(IsProtected(channel->channel_info))
2097 reply("CSMSG_MERGE_NODELETE");
2101 if(IsSuspended(target->channel_info))
2103 reply("CSMSG_MERGE_SUSPENDED");
2107 if(channel == target)
2109 reply("CSMSG_MERGE_SELF");
2113 target_user = GetChannelUser(target->channel_info, user->handle_info);
2114 if(!target_user || (target_user->access < UL_OWNER))
2116 reply("CSMSG_MERGE_NOT_OWNER");
2120 /* Merge the channel structures and associated data. */
2121 merge_channel(channel->channel_info, target->channel_info);
2122 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2123 unregister_channel(channel->channel_info, reason);
2124 reply("CSMSG_MERGE_SUCCESS", target->name);
2128 static CHANSERV_FUNC(cmd_opchan)
2130 struct mod_chanmode change;
2131 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2133 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2136 channel->channel_info->may_opchan = 0;
2137 change.modes_set = change.modes_clear = 0;
2139 change.args[0].mode = MODE_CHANOP;
2140 change.args[0].member = GetUserMode(channel, chanserv);
2141 mod_chanmode_announce(chanserv, channel, &change);
2142 reply("CSMSG_OPCHAN_DONE", channel->name);
2146 static CHANSERV_FUNC(cmd_adduser)
2148 struct userData *actee;
2149 struct userData *actor;
2150 struct handle_info *handle;
2151 unsigned short access;
2155 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2157 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2161 access = user_level_from_name(argv[2], UL_OWNER);
2164 reply("CSMSG_INVALID_ACCESS", argv[2]);
2168 actor = GetChannelUser(channel->channel_info, user->handle_info);
2169 if(actor->access <= access)
2171 reply("CSMSG_NO_BUMP_ACCESS");
2175 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2178 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2180 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2184 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2185 scan_user_presence(actee, NULL);
2186 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2190 static CHANSERV_FUNC(cmd_clvl)
2192 struct handle_info *handle;
2193 struct userData *victim;
2194 struct userData *actor;
2195 unsigned short new_access;
2196 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2200 actor = GetChannelUser(channel->channel_info, user->handle_info);
2202 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2205 if(handle == user->handle_info && !privileged)
2207 reply("CSMSG_NO_SELF_CLVL");
2211 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2213 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2217 if(actor->access <= victim->access && !privileged)
2219 reply("MSG_USER_OUTRANKED", handle->handle);
2223 new_access = user_level_from_name(argv[2], UL_OWNER);
2227 reply("CSMSG_INVALID_ACCESS", argv[2]);
2231 if(new_access >= actor->access && !privileged)
2233 reply("CSMSG_NO_BUMP_ACCESS");
2237 victim->access = new_access;
2238 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2242 static CHANSERV_FUNC(cmd_deluser)
2244 struct handle_info *handle;
2245 struct userData *victim;
2246 struct userData *actor;
2247 unsigned short access;
2252 actor = GetChannelUser(channel->channel_info, user->handle_info);
2254 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2257 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2259 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2265 access = user_level_from_name(argv[1], UL_OWNER);
2268 reply("CSMSG_INVALID_ACCESS", argv[1]);
2271 if(access != victim->access)
2273 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2279 access = victim->access;
2282 if((actor->access <= victim->access) && !IsHelping(user))
2284 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2288 chan_name = strdup(channel->name);
2289 del_channel_user(victim, 1);
2290 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2296 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2298 struct userData *actor, *uData, *next;
2300 actor = GetChannelUser(channel->channel_info, user->handle_info);
2302 if(min_access > max_access)
2304 reply("CSMSG_BAD_RANGE", min_access, max_access);
2308 if((actor->access <= max_access) && !IsHelping(user))
2310 reply("CSMSG_NO_ACCESS");
2314 for(uData = channel->channel_info->users; uData; uData = next)
2318 if((uData->access >= min_access)
2319 && (uData->access <= max_access)
2320 && match_ircglob(uData->handle->handle, mask))
2321 del_channel_user(uData, 1);
2324 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2328 static CHANSERV_FUNC(cmd_mdelowner)
2330 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2333 static CHANSERV_FUNC(cmd_mdelcoowner)
2335 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2338 static CHANSERV_FUNC(cmd_mdelmaster)
2340 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2343 static CHANSERV_FUNC(cmd_mdelop)
2345 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2348 static CHANSERV_FUNC(cmd_mdelpeon)
2350 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2354 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2356 struct banData *bData, *next;
2357 char interval[INTERVALLEN];
2362 limit = now - duration;
2363 for(bData = channel->channel_info->bans; bData; bData = next)
2367 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2370 del_channel_ban(bData);
2374 intervalString(interval, duration);
2375 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2380 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2382 struct userData *actor, *uData, *next;
2383 char interval[INTERVALLEN];
2387 actor = GetChannelUser(channel->channel_info, user->handle_info);
2388 if(min_access > max_access)
2390 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2394 if((actor->access <= max_access) && !IsHelping(user))
2396 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2401 limit = now - duration;
2402 for(uData = channel->channel_info->users; uData; uData = next)
2406 if((uData->seen > limit) || uData->present)
2409 if(((uData->access >= min_access) && (uData->access <= max_access))
2410 || (!max_access && (uData->access < actor->access)))
2412 del_channel_user(uData, 1);
2420 max_access = UL_OWNER;
2422 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2426 static CHANSERV_FUNC(cmd_trim)
2428 unsigned long duration;
2429 unsigned short min_level, max_level;
2433 duration = ParseInterval(argv[2]);
2436 reply("CSMSG_CANNOT_TRIM");
2440 if(!irccasecmp(argv[1], "bans"))
2442 cmd_trim_bans(user, channel, duration);
2445 else if(!irccasecmp(argv[1], "users"))
2447 cmd_trim_users(user, channel, 0, 0, duration);
2450 else if(parse_level_range(&min_level, &max_level, argv[1]))
2452 cmd_trim_users(user, channel, min_level, max_level, duration);
2455 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2457 cmd_trim_users(user, channel, min_level, min_level, duration);
2462 reply("CSMSG_INVALID_TRIM", argv[1]);
2467 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2468 to the user. cmd_all takes advantage of this. */
2469 static CHANSERV_FUNC(cmd_up)
2471 struct mod_chanmode change;
2472 struct userData *uData;
2475 change.modes_set = change.modes_clear = 0;
2477 change.args[0].member = GetUserMode(channel, user);
2478 if(!change.args[0].member)
2481 reply("MSG_CHANNEL_ABSENT", channel->name);
2485 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2489 reply("CSMSG_GODMODE_UP", argv[0]);
2492 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2494 change.args[0].mode = MODE_CHANOP;
2495 errmsg = "CSMSG_ALREADY_OPPED";
2499 change.args[0].mode = MODE_VOICE;
2500 errmsg = "CSMSG_ALREADY_VOICED";
2502 change.args[0].mode &= ~change.args[0].member->modes;
2503 if(!change.args[0].mode)
2506 reply(errmsg, channel->name);
2509 modcmd_chanmode_announce(&change);
2513 static CHANSERV_FUNC(cmd_down)
2515 struct mod_chanmode change;
2517 change.modes_set = change.modes_clear = 0;
2519 change.args[0].member = GetUserMode(channel, user);
2520 if(!change.args[0].member)
2523 reply("MSG_CHANNEL_ABSENT", channel->name);
2527 if(!change.args[0].member->modes)
2530 reply("CSMSG_ALREADY_DOWN", channel->name);
2534 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2535 modcmd_chanmode_announce(&change);
2539 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2541 struct userData *cList;
2543 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2545 if(IsSuspended(cList->channel)
2546 || IsUserSuspended(cList)
2547 || !GetUserMode(cList->channel->channel, user))
2550 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2556 static CHANSERV_FUNC(cmd_upall)
2558 return cmd_all(CSFUNC_ARGS, cmd_up);
2561 static CHANSERV_FUNC(cmd_downall)
2563 return cmd_all(CSFUNC_ARGS, cmd_down);
2566 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2567 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2570 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
2572 unsigned int ii, valid;
2573 struct userNode *victim;
2574 struct mod_chanmode *change;
2576 change = mod_chanmode_alloc(argc - 1);
2578 for(ii=valid=0; ++ii < argc; )
2580 if(!(victim = GetUserH(argv[ii])))
2582 change->args[valid].mode = mode;
2583 change->args[valid].member = GetUserMode(channel, victim);
2584 if(!change->args[valid].member)
2586 if(validate && !validate(user, channel, victim))
2591 change->argc = valid;
2592 if(valid < (argc-1))
2593 reply("CSMSG_PROCESS_FAILED");
2596 modcmd_chanmode_announce(change);
2597 reply(action, channel->name);
2599 mod_chanmode_free(change);
2603 static CHANSERV_FUNC(cmd_op)
2605 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2608 static CHANSERV_FUNC(cmd_deop)
2610 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2613 static CHANSERV_FUNC(cmd_voice)
2615 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2618 static CHANSERV_FUNC(cmd_devoice)
2620 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2624 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2630 for(ii=0; ii<channel->members.used; ii++)
2632 struct modeNode *mn = channel->members.list[ii];
2634 if(IsService(mn->user))
2637 if(!user_matches_glob(mn->user, ban, 1))
2640 if(protect_user(mn->user, user, channel->channel_info))
2644 victims[(*victimCount)++] = mn;
2650 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2652 struct userNode *victim;
2653 struct modeNode **victims;
2654 unsigned int offset, n, victimCount, duration = 0;
2655 char *reason = "Bye.", *ban, *name;
2656 char interval[INTERVALLEN];
2658 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2659 REQUIRE_PARAMS(offset);
2662 reason = unsplit_string(argv + offset, argc - offset, NULL);
2663 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2665 /* Truncate the reason to a length of TOPICLEN, as
2666 the ircd does; however, leave room for an ellipsis
2667 and the kicker's nick. */
2668 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2672 if((victim = GetUserH(argv[1])))
2674 victims = alloca(sizeof(victims[0]));
2675 victims[0] = GetUserMode(channel, victim);
2676 /* XXX: The comparison with ACTION_KICK is just because all
2677 * other actions can work on users outside the channel, and we
2678 * want to allow those (e.g. unbans) in that case. If we add
2679 * some other ejection action for in-channel users, change
2681 victimCount = victims[0] ? 1 : 0;
2683 if(IsService(victim))
2685 reply("MSG_SERVICE_IMMUNE", victim->nick);
2689 if((action == ACTION_KICK) && !victimCount)
2691 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2695 if(protect_user(victim, user, channel->channel_info))
2697 reply("CSMSG_USER_PROTECTED", victim->nick);
2701 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2702 name = victim->nick;
2706 if(!is_ircmask(argv[1]))
2708 reply("MSG_NICK_UNKNOWN", argv[1]);
2712 victims = alloca(sizeof(victims[0]) * channel->members.used);
2714 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2716 reply("CSMSG_MASK_PROTECTED", argv[1]);
2720 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2722 reply("CSMSG_LAME_MASK", argv[1]);
2726 if((action == ACTION_KICK) && (victimCount == 0))
2728 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2732 name = ban = strdup(argv[1]);
2735 /* Truncate the ban in place if necessary; we must ensure
2736 that 'ban' is a valid ban mask before sanitizing it. */
2737 sanitize_ircmask(ban);
2739 if(action & ACTION_ADD_BAN)
2741 struct banData *bData, *next;
2743 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2745 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2750 if(action & ACTION_ADD_TIMED_BAN)
2752 duration = ParseInterval(argv[2]);
2756 reply("CSMSG_DURATION_TOO_LOW");
2760 else if(duration > (86400 * 365 * 2))
2762 reply("CSMSG_DURATION_TOO_HIGH");
2768 for(bData = channel->channel_info->bans; bData; bData = next)
2770 if(match_ircglobs(bData->mask, ban))
2772 int exact = !irccasecmp(bData->mask, ban);
2774 /* The ban is redundant; there is already a ban
2775 with the same effect in place. */
2779 free(bData->reason);
2780 bData->reason = strdup(reason);
2781 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2782 reply("CSMSG_REASON_CHANGE", ban);
2786 if(exact && bData->expires)
2790 /* If the ban matches an existing one exactly,
2791 extend the expiration time if the provided
2792 duration is longer. */
2793 if(duration && ((time_t)(now + duration) > bData->expires))
2795 bData->expires = now + duration;
2806 /* Delete the expiration timeq entry and
2807 requeue if necessary. */
2808 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2811 timeq_add(bData->expires, expire_ban, bData);
2814 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2816 reply("CSMSG_BAN_ADDED", name, channel->name);
2821 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2828 if(match_ircglobs(ban, bData->mask))
2830 /* The ban we are adding makes previously existing
2831 bans redundant; silently remove them. */
2832 del_channel_ban(bData);
2836 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
2838 name = ban = strdup(bData->mask);
2842 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2844 extern const char *hidden_host_suffix;
2845 const char *old_name = chanserv_conf.old_ban_names->list[n];
2847 unsigned int l1, l2;
2850 l2 = strlen(old_name);
2853 if(irccasecmp(ban + l1 - l2, old_name))
2855 new_mask = malloc(MAXLEN);
2856 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2858 name = ban = new_mask;
2863 if(action & ACTION_BAN)
2865 unsigned int exists;
2866 struct mod_chanmode *change;
2868 if(channel->banlist.used >= MAXBANS)
2870 reply("CSMSG_BANLIST_FULL", channel->name);
2875 exists = ChannelBanExists(channel, ban);
2876 change = mod_chanmode_alloc(victimCount + 1);
2877 for(n = 0; n < victimCount; ++n)
2879 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2880 change->args[n].member = victims[n];
2884 change->args[n].mode = MODE_BAN;
2885 change->args[n++].hostmask = ban;
2889 modcmd_chanmode_announce(change);
2891 mod_chanmode_announce(chanserv, channel, change);
2892 mod_chanmode_free(change);
2894 if(exists && (action == ACTION_BAN))
2896 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2902 if(action & ACTION_KICK)
2904 char kick_reason[MAXLEN];
2905 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2907 for(n = 0; n < victimCount; n++)
2908 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2913 /* No response, since it was automated. */
2915 else if(action & ACTION_ADD_BAN)
2918 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2920 reply("CSMSG_BAN_ADDED", name, channel->name);
2922 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2923 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2924 else if(action & ACTION_BAN)
2925 reply("CSMSG_BAN_DONE", name, channel->name);
2926 else if(action & ACTION_KICK && victimCount)
2927 reply("CSMSG_KICK_DONE", name, channel->name);
2933 static CHANSERV_FUNC(cmd_kickban)
2935 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2938 static CHANSERV_FUNC(cmd_kick)
2940 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2943 static CHANSERV_FUNC(cmd_ban)
2945 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2948 static CHANSERV_FUNC(cmd_addban)
2950 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2953 static CHANSERV_FUNC(cmd_addtimedban)
2955 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2958 static struct mod_chanmode *
2959 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2961 struct mod_chanmode *change;
2962 unsigned char *match;
2963 unsigned int ii, count;
2965 match = alloca(bans->used);
2968 for(ii = count = 0; ii < bans->used; ++ii)
2970 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2977 for(ii = count = 0; ii < bans->used; ++ii)
2979 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
2986 change = mod_chanmode_alloc(count);
2987 for(ii = count = 0; ii < bans->used; ++ii)
2991 change->args[count].mode = MODE_REMOVE | MODE_BAN;
2992 change->args[count++].hostmask = bans->list[ii]->ban;
2998 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3000 struct userNode *actee;
3006 /* may want to allow a comma delimited list of users... */
3007 if(!(actee = GetUserH(argv[1])))
3009 if(!is_ircmask(argv[1]))
3011 reply("MSG_NICK_UNKNOWN", argv[1]);
3015 mask = strdup(argv[1]);
3018 /* We don't sanitize the mask here because ircu
3020 if(action & ACTION_UNBAN)
3022 struct mod_chanmode *change;
3023 change = find_matching_bans(&channel->banlist, actee, mask);
3026 modcmd_chanmode_announce(change);
3027 mod_chanmode_free(change);
3032 if(action & ACTION_DEL_BAN)
3034 struct banData *ban, *next;
3036 ban = channel->channel_info->bans;
3040 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3043 for( ; ban && !match_ircglobs(mask, ban->mask);
3048 del_channel_ban(ban);
3055 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3057 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3063 static CHANSERV_FUNC(cmd_unban)
3065 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3068 static CHANSERV_FUNC(cmd_delban)
3070 /* it doesn't necessarily have to remove the channel ban - may want
3071 to make that an option. */
3072 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3075 static CHANSERV_FUNC(cmd_unbanme)
3077 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3078 long flags = ACTION_UNBAN;
3080 /* remove permanent bans if the user has the proper access. */
3081 if(uData->access >= UL_MASTER)
3082 flags |= ACTION_DEL_BAN;
3084 argv[1] = user->nick;
3085 return unban_user(user, channel, 2, argv, cmd, flags);
3088 static CHANSERV_FUNC(cmd_unbanall)
3090 struct mod_chanmode *change;
3093 if(!channel->banlist.used)
3095 reply("CSMSG_NO_BANS", channel->name);
3099 change = mod_chanmode_alloc(channel->banlist.used);
3100 for(ii=0; ii<channel->banlist.used; ii++)
3102 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3103 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3105 modcmd_chanmode_announce(change);
3106 mod_chanmode_free(change);
3107 reply("CSMSG_BANS_REMOVED", channel->name);
3111 static CHANSERV_FUNC(cmd_open)
3113 struct mod_chanmode *change;
3115 change = find_matching_bans(&channel->banlist, user, NULL);
3117 change = mod_chanmode_alloc(0);
3118 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3119 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3120 && channel->channel_info->modes.modes_set)
3121 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3122 modcmd_chanmode_announce(change);
3123 reply("CSMSG_CHANNEL_OPENED", channel->name);
3124 mod_chanmode_free(change);
3128 static CHANSERV_FUNC(cmd_access)
3130 struct userNode *target;
3131 struct handle_info *target_handle;
3132 struct userData *uData;
3134 char prefix[MAXLEN];
3138 struct userData *uData;
3139 const char *chanName;
3142 target_handle = user->handle_info;
3145 reply("MSG_AUTHENTICATE");
3150 if(!IsHelping(user))
3152 reply("CSMSG_ACCESS_SELF_ONLY", argv[0]);
3156 if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3160 if(!target_handle->channels)
3162 reply("CSMSG_SQUAT_ACCESS");
3165 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3166 for(uData = target_handle->channels; uData; uData = uData->u_next)
3168 struct chanData *cData = uData->channel;
3170 if(uData->access > UL_OWNER)
3172 if(IsProtected(cData) && hide && !GetTrueChannelAccess(cData, user->handle_info))
3174 chanName = cData->channel->name;
3176 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3178 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3186 target_handle = target->handle_info;
3188 else if((target = GetUserH(argv[1])))
3190 target_handle = target->handle_info;
3192 else if(argv[1][0] == '*')
3194 if(!(target_handle = get_handle_info(argv[1]+1)))
3196 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3202 reply("MSG_NICK_UNKNOWN", argv[1]);
3206 assert(target || target_handle);
3208 if(target == chanserv)
3210 reply("CSMSG_IS_CHANSERV");
3218 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3223 reply("MSG_USER_AUTHENTICATE", target->nick);
3226 reply("MSG_AUTHENTICATE");
3232 const char *epithet = NULL, *type = NULL;
3235 epithet = chanserv_conf.irc_operator_epithet;
3238 else if(IsNetworkHelper(target))
3240 epithet = chanserv_conf.network_helper_epithet;
3241 type = "network helper";
3243 else if(IsSupportHelper(target))
3245 epithet = chanserv_conf.support_helper_epithet;
3246 type = "support helper";
3250 if(target_handle->epithet)
3251 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3253 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3255 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3259 sprintf(prefix, "%s", target_handle->handle);
3262 if(!channel->channel_info)
3264 reply("CSMSG_NOT_REGISTERED", channel->name);
3268 helping = HANDLE_FLAGGED(target_handle, HELPING)
3269 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3270 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3272 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3273 /* To prevent possible information leaks, only show infolines
3274 * if the requestor is in the channel or it's their own
3276 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3278 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3280 /* Likewise, only say it's suspended if the user has active
3281 * access in that channel or it's their own entry. */
3282 if(IsUserSuspended(uData)
3283 && (GetChannelUser(channel->channel_info, user->handle_info)
3284 || (user->handle_info == uData->handle)))
3286 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3291 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3298 zoot_list(struct listData *list)
3300 struct userData *uData;
3301 unsigned int start, curr, highest, lowest;
3302 struct helpfile_table tmp_table;
3303 const char **temp, *msg;
3305 if(list->table.length == 1)
3308 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3310 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3311 msg = user_find_message(list->user, "MSG_NONE");
3312 send_message_type(4, list->user, list->bot, " %s", msg);
3314 tmp_table.width = list->table.width;
3315 tmp_table.flags = list->table.flags;
3316 list->table.contents[0][0] = " ";
3317 highest = list->highest;
3318 if (list->lowest != 0)
3319 lowest = list->lowest;
3320 else if (highest < 100)
3323 lowest = highest - 100;
3324 for(start = curr = 1; curr < list->table.length; )
3326 uData = list->users[curr-1];
3327 list->table.contents[curr++][0] = " ";
3328 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3331 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3333 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3334 temp = list->table.contents[--start];
3335 list->table.contents[start] = list->table.contents[0];
3336 tmp_table.contents = list->table.contents + start;
3337 tmp_table.length = curr - start;
3338 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3339 list->table.contents[start] = temp;
3341 highest = lowest - 1;
3342 lowest = (highest < 100) ? 0 : (highest - 99);
3348 def_list(struct listData *list)
3352 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3354 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3355 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3356 if(list->table.length == 1)
3358 msg = user_find_message(list->user, "MSG_NONE");
3359 send_message_type(4, list->user, list->bot, " %s", msg);
3364 userData_access_comp(const void *arg_a, const void *arg_b)
3366 const struct userData *a = *(struct userData**)arg_a;
3367 const struct userData *b = *(struct userData**)arg_b;
3369 if(a->access != b->access)
3370 res = b->access - a->access;
3372 res = irccasecmp(a->handle->handle, b->handle->handle);
3377 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3379 void (*send_list)(struct listData *);
3380 struct userData *uData;
3381 struct listData lData;
3382 unsigned int matches;
3386 lData.bot = cmd->parent->bot;
3387 lData.channel = channel;
3388 lData.lowest = lowest;
3389 lData.highest = highest;
3390 lData.search = (argc > 1) ? argv[1] : NULL;
3391 send_list = zoot_list;
3393 if(user->handle_info)
3395 switch(user->handle_info->userlist_style)
3397 case HI_STYLE_DEF: send_list = def_list; break;
3398 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3402 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3404 for(uData = channel->channel_info->users; uData; uData = uData->next)
3406 if((uData->access < lowest)
3407 || (uData->access > highest)
3408 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3410 lData.users[matches++] = uData;
3412 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3414 lData.table.length = matches+1;
3415 lData.table.width = 4;
3416 lData.table.flags = TABLE_NO_FREE;
3417 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3418 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3419 lData.table.contents[0] = ary;
3422 ary[2] = "Last Seen";
3424 for(matches = 1; matches < lData.table.length; ++matches)
3426 struct userData *uData = lData.users[matches-1];
3427 char seen[INTERVALLEN];
3429 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3430 lData.table.contents[matches] = ary;
3431 ary[0] = strtab(uData->access);
3432 ary[1] = uData->handle->handle;
3435 else if(!uData->seen)
3438 ary[2] = intervalString(seen, now - uData->seen);
3439 ary[2] = strdup(ary[2]);
3440 if(IsUserSuspended(uData))
3441 ary[3] = "Suspended";
3442 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3443 ary[3] = "Vacation";
3448 for(matches = 1; matches < lData.table.length; ++matches)
3450 free((char*)lData.table.contents[matches][2]);
3451 free(lData.table.contents[matches]);
3453 free(lData.table.contents[0]);
3454 free(lData.table.contents);
3458 static CHANSERV_FUNC(cmd_users)
3460 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3463 static CHANSERV_FUNC(cmd_wlist)
3465 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3468 static CHANSERV_FUNC(cmd_clist)
3470 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3473 static CHANSERV_FUNC(cmd_mlist)
3475 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3478 static CHANSERV_FUNC(cmd_olist)
3480 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3483 static CHANSERV_FUNC(cmd_plist)
3485 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3488 static CHANSERV_FUNC(cmd_bans)
3490 struct helpfile_table tbl;
3491 unsigned int matches = 0, timed = 0, ii;
3492 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3493 const char *msg_never, *triggered, *expires;
3494 struct banData *ban, **bans;
3501 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3503 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3505 if(search && !match_ircglobs(search, ban->mask))
3507 bans[matches++] = ban;
3512 tbl.length = matches + 1;
3513 tbl.width = 4 + timed;
3515 tbl.flags = TABLE_NO_FREE;
3516 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3517 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3518 tbl.contents[0][0] = "Mask";
3519 tbl.contents[0][1] = "Set By";
3520 tbl.contents[0][2] = "Triggered";
3523 tbl.contents[0][3] = "Expires";
3524 tbl.contents[0][4] = "Reason";
3527 tbl.contents[0][3] = "Reason";
3530 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3535 msg_never = user_find_message(user, "MSG_NEVER");
3536 for(ii = 0; ii < matches; )
3542 else if(ban->expires)
3543 expires = intervalString(e_buffer, ban->expires - now);
3545 expires = msg_never;
3548 triggered = intervalString(t_buffer, now - ban->triggered);
3550 triggered = msg_never;
3552 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3553 tbl.contents[ii][0] = ban->mask;
3554 tbl.contents[ii][1] = ban->owner;
3555 tbl.contents[ii][2] = strdup(triggered);
3558 tbl.contents[ii][3] = strdup(expires);
3559 tbl.contents[ii][4] = ban->reason;
3562 tbl.contents[ii][3] = ban->reason;
3564 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3565 reply("MSG_MATCH_COUNT", matches);
3566 for(ii = 1; ii < tbl.length; ++ii)
3568 free((char*)tbl.contents[ii][2]);
3570 free((char*)tbl.contents[ii][3]);
3571 free(tbl.contents[ii]);
3573 free(tbl.contents[0]);
3579 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3581 struct chanData *cData = channel->channel_info;
3582 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3584 if(cData->topic_mask)
3585 return !match_ircglob(new_topic, cData->topic_mask);
3586 else if(cData->topic)
3587 return irccasecmp(new_topic, cData->topic);
3592 static CHANSERV_FUNC(cmd_topic)
3594 struct chanData *cData;
3597 cData = channel->channel_info;
3602 SetChannelTopic(channel, chanserv, cData->topic, 1);
3603 reply("CSMSG_TOPIC_SET", cData->topic);
3607 reply("CSMSG_NO_TOPIC", channel->name);
3611 topic = unsplit_string(argv + 1, argc - 1, NULL);
3612 /* If they say "!topic *", use an empty topic. */
3613 if((topic[0] == '*') && (topic[1] == 0))
3615 if(bad_topic(channel, user, topic))
3617 char *topic_mask = cData->topic_mask;
3620 char new_topic[TOPICLEN+1], tchar;
3621 int pos=0, starpos=-1, dpos=0, len;
3623 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3630 len = strlen(topic);
3631 if((dpos + len) > TOPICLEN)
3632 len = TOPICLEN + 1 - dpos;
3633 memcpy(new_topic+dpos, topic, len);
3637 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3638 default: new_topic[dpos++] = tchar; break;
3641 if((dpos > TOPICLEN) || tchar)
3644 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3645 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3648 new_topic[dpos] = 0;
3649 SetChannelTopic(channel, chanserv, new_topic, 1);
3651 reply("CSMSG_TOPIC_LOCKED", channel->name);
3656 SetChannelTopic(channel, chanserv, topic, 1);
3658 if(cData->flags & CHANNEL_TOPIC_SNARF)
3660 /* Grab the topic and save it as the default topic. */
3662 cData->topic = strdup(channel->topic);
3668 static CHANSERV_FUNC(cmd_mode)
3670 struct mod_chanmode *change;
3674 change = &channel->channel_info->modes;
3675 if(change->modes_set || change->modes_clear) {
3676 modcmd_chanmode_announce(change);
3677 reply("CSMSG_DEFAULTED_MODES", channel->name);
3679 reply("CSMSG_NO_MODES", channel->name);
3683 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3686 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3690 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3691 && mode_lock_violated(&channel->channel_info->modes, change))
3694 mod_chanmode_format(&channel->channel_info->modes, modes);
3695 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3699 modcmd_chanmode_announce(change);
3700 mod_chanmode_free(change);
3701 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3705 static CHANSERV_FUNC(cmd_invite)
3707 struct userData *uData;
3708 struct userNode *invite;
3710 uData = GetChannelUser(channel->channel_info, user->handle_info);
3714 if(!(invite = GetUserH(argv[1])))
3716 reply("MSG_NICK_UNKNOWN", argv[1]);
3723 if(GetUserMode(channel, invite))
3725 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3731 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3732 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3734 irc_invite(chanserv, invite, channel);
3736 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3741 static CHANSERV_FUNC(cmd_inviteme)
3743 struct userData *uData;
3745 if(GetUserMode(channel, user))
3747 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3750 if(channel->channel_info
3751 && !(channel->channel_info->flags & CHANNEL_PEON_INVITE)
3752 && (uData = GetChannelUser(channel->channel_info, user->handle_info))
3753 && (uData->access < channel->channel_info->lvlOpts[lvlGiveOps]))
3755 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3758 irc_invite(cmd->parent->bot, user, channel);
3763 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3766 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3768 /* We display things based on two dimensions:
3769 * - Issue time: present or absent
3770 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3771 * (in order of precedence, so something both expired and revoked
3772 * only counts as revoked)
3774 combo = (suspended->issued ? 4 : 0)
3775 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3777 case 0: /* no issue time, indefinite expiration */
3778 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3780 case 1: /* no issue time, expires in future */
3781 intervalString(buf1, suspended->expires-now);
3782 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3784 case 2: /* no issue time, expired */
3785 intervalString(buf1, now-suspended->expires);
3786 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3788 case 3: /* no issue time, revoked */
3789 intervalString(buf1, now-suspended->revoked);
3790 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3792 case 4: /* issue time set, indefinite expiration */
3793 intervalString(buf1, now-suspended->issued);
3794 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3796 case 5: /* issue time set, expires in future */
3797 intervalString(buf1, now-suspended->issued);
3798 intervalString(buf2, suspended->expires-now);
3799 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3801 case 6: /* issue time set, expired */
3802 intervalString(buf1, now-suspended->issued);
3803 intervalString(buf2, now-suspended->expires);
3804 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3806 case 7: /* issue time set, revoked */
3807 intervalString(buf1, now-suspended->issued);
3808 intervalString(buf2, now-suspended->revoked);
3809 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3812 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3817 static CHANSERV_FUNC(cmd_info)
3819 char modes[MAXLEN], buffer[INTERVALLEN];
3820 struct userData *uData, *owner;
3821 struct chanData *cData;
3822 struct do_not_register *dnr;
3827 cData = channel->channel_info;
3828 reply("CSMSG_CHANNEL_INFO", channel->name);
3830 uData = GetChannelUser(cData, user->handle_info);
3831 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3833 mod_chanmode_format(&cData->modes, modes);
3834 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3835 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3838 for(it = dict_first(cData->notes); it; it = iter_next(it))
3842 note = iter_data(it);
3843 if(!note_type_visible_to_user(cData, note->type, user))
3846 padding = PADLEN - 1 - strlen(iter_key(it));
3847 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3850 reply("CSMSG_CHANNEL_MAX", cData->max);
3851 for(owner = cData->users; owner; owner = owner->next)
3852 if(owner->access == UL_OWNER)
3853 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3854 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3855 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3856 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3857 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3859 privileged = IsStaff(user);
3860 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3861 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3863 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3864 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3866 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3868 struct suspended *suspended;
3869 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3870 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3871 show_suspension_info(cmd, user, suspended);
3873 else if(IsSuspended(cData))
3875 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3876 show_suspension_info(cmd, user, cData->suspended);
3881 static CHANSERV_FUNC(cmd_netinfo)
3883 extern time_t boot_time;
3884 extern unsigned long burst_length;
3885 char interval[INTERVALLEN];
3887 reply("CSMSG_NETWORK_INFO");
3888 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3889 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3890 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3891 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3892 reply("CSMSG_NETWORK_BANS", banCount);
3893 reply("CSMSG_CHANNEL_USERS", userCount);
3894 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3895 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3900 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3902 struct helpfile_table table;
3904 struct userNode *user;
3909 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3910 table.contents = alloca(list->used*sizeof(*table.contents));
3911 for(nn=0; nn<list->used; nn++)
3913 user = list->list[nn];
3914 if(user->modes & skip_flags)
3918 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3921 nick = alloca(strlen(user->nick)+3);
3922 sprintf(nick, "(%s)", user->nick);
3926 table.contents[table.length][0] = nick;
3929 table_send(chanserv, to->nick, 0, NULL, table);
3932 static CHANSERV_FUNC(cmd_ircops)
3934 reply("CSMSG_STAFF_OPERS");
3935 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3939 static CHANSERV_FUNC(cmd_helpers)
3941 reply("CSMSG_STAFF_HELPERS");
3942 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3946 static CHANSERV_FUNC(cmd_staff)
3948 reply("CSMSG_NETWORK_STAFF");
3949 cmd_ircops(CSFUNC_ARGS);
3950 cmd_helpers(CSFUNC_ARGS);
3954 static CHANSERV_FUNC(cmd_peek)
3956 struct modeNode *mn;
3957 char modes[MODELEN];
3959 struct helpfile_table table;
3961 irc_make_chanmode(channel, modes);
3963 reply("CSMSG_PEEK_INFO", channel->name);
3964 reply("CSMSG_PEEK_TOPIC", channel->topic);
3965 reply("CSMSG_PEEK_MODES", modes);
3966 reply("CSMSG_PEEK_USERS", channel->members.used);
3970 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3971 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3972 for(n = 0; n < channel->members.used; n++)
3974 mn = channel->members.list[n];
3975 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3977 table.contents[table.length] = alloca(sizeof(**table.contents));
3978 table.contents[table.length][0] = mn->user->nick;
3983 reply("CSMSG_PEEK_OPS");
3984 table_send(chanserv, user->nick, 0, NULL, table);
3987 reply("CSMSG_PEEK_NO_OPS");
3991 static MODCMD_FUNC(cmd_wipeinfo)
3993 struct handle_info *victim;
3994 struct userData *ud, *actor;
3997 actor = GetChannelUser(channel->channel_info, user->handle_info);
3998 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4000 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4002 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4005 if((ud->access >= actor->access) && (ud != actor))
4007 reply("MSG_USER_OUTRANKED", victim->handle);
4013 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4017 static CHANSERV_FUNC(cmd_resync)
4019 struct mod_chanmode *changes;
4020 struct chanData *cData = channel->channel_info;
4021 unsigned int ii, used;
4023 changes = mod_chanmode_alloc(channel->members.used * 2);
4024 for(ii = used = 0; ii < channel->members.used; ++ii)
4026 struct modeNode *mn = channel->members.list[ii];
4027 struct userData *uData;
4028 if(IsService(mn->user))
4030 /* must not change modes for this user */
4032 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4036 changes->args[used].mode = MODE_REMOVE | mn->modes;
4037 changes->args[used++].member = mn;
4040 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4042 if(mn->modes & MODE_CHANOP)
4044 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4045 changes->args[used++].member = mn;
4047 if(!(mn->modes & MODE_VOICE))
4049 changes->args[used].mode = MODE_VOICE;
4050 changes->args[used++].member = mn;
4055 if(!(mn->modes & MODE_CHANOP))
4057 changes->args[used].mode = MODE_CHANOP;
4058 changes->args[used++].member = mn;
4062 changes->argc = used;
4063 modcmd_chanmode_announce(changes);
4064 mod_chanmode_free(changes);
4065 reply("CSMSG_RESYNCED_USERS", channel->name);
4069 static CHANSERV_FUNC(cmd_seen)
4071 struct userData *uData;
4072 struct handle_info *handle;
4073 char seen[INTERVALLEN];
4077 if(!irccasecmp(argv[1], chanserv->nick))
4079 reply("CSMSG_IS_CHANSERV");
4083 if(!(handle = get_handle_info(argv[1])))
4085 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4089 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4091 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4096 reply("CSMSG_USER_PRESENT", handle->handle);
4097 else if(uData->seen)
4098 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4100 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4102 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4103 reply("CSMSG_USER_VACATION", handle->handle);
4108 static MODCMD_FUNC(cmd_names)
4110 struct userNode *targ;
4111 struct userData *targData;
4112 unsigned int ii, pos;
4115 for(ii=pos=0; ii<channel->members.used; ++ii)
4117 targ = channel->members.list[ii]->user;
4118 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4121 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4124 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4128 if(IsUserSuspended(targData))
4130 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4133 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4134 reply("CSMSG_END_NAMES", channel->name);
4139 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4141 switch(ntype->visible_type)
4143 case NOTE_VIS_ALL: return 1;
4144 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4145 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4150 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4152 struct userData *uData;
4154 switch(ntype->set_access_type)
4156 case NOTE_SET_CHANNEL_ACCESS:
4157 if(!user->handle_info)
4159 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4161 return uData->access >= ntype->set_access.min_ulevel;
4162 case NOTE_SET_CHANNEL_SETTER:
4163 return check_user_level(channel, user, lvlSetters, 1, 0);
4164 case NOTE_SET_PRIVILEGED: default:
4165 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4169 static CHANSERV_FUNC(cmd_note)
4171 struct chanData *cData;
4173 struct note_type *ntype;
4175 cData = channel->channel_info;
4178 reply("CSMSG_NOT_REGISTERED", channel->name);
4182 /* If no arguments, show all visible notes for the channel. */
4188 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4190 note = iter_data(it);
4191 if(!note_type_visible_to_user(cData, note->type, user))
4194 reply("CSMSG_NOTELIST_HEADER", channel->name);
4195 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4198 reply("CSMSG_NOTELIST_END", channel->name);
4200 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4202 /* If one argument, show the named note. */
4205 if((note = dict_find(cData->notes, argv[1], NULL))
4206 && note_type_visible_to_user(cData, note->type, user))
4208 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4210 else if((ntype = dict_find(note_types, argv[1], NULL))
4211 && note_type_visible_to_user(NULL, ntype, user))
4213 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4218 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4222 /* Assume they're trying to set a note. */
4226 ntype = dict_find(note_types, argv[1], NULL);
4229 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4232 else if(note_type_settable_by_user(channel, ntype, user))
4234 note_text = unsplit_string(argv+2, argc-2, NULL);
4235 if((note = dict_find(cData->notes, argv[1], NULL)))
4236 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4237 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4238 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4240 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4242 /* The note is viewable to staff only, so return 0
4243 to keep the invocation from getting logged (or
4244 regular users can see it in !events). */
4250 reply("CSMSG_NO_ACCESS");
4257 static CHANSERV_FUNC(cmd_delnote)
4262 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4263 || !note_type_settable_by_user(channel, note->type, user))
4265 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4268 dict_remove(channel->channel_info->notes, note->type->name);
4269 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4273 static CHANSERV_FUNC(cmd_events)
4275 struct logSearch discrim;
4276 struct logReport report;
4277 unsigned int matches, limit;
4279 limit = (argc > 1) ? atoi(argv[1]) : 10;
4280 if(limit < 1 || limit > 200) limit = 10;
4282 memset(&discrim, 0, sizeof(discrim));
4283 discrim.masks.bot = chanserv;
4284 discrim.masks.channel_name = channel->name;
4285 if(argc > 2) discrim.masks.command = argv[2];
4286 discrim.limit = limit;
4287 discrim.max_time = INT_MAX;
4288 discrim.severities = 1 << LOG_COMMAND;
4289 report.reporter = chanserv;
4291 reply("CSMSG_EVENT_SEARCH_RESULTS");
4292 matches = log_entry_search(&discrim, log_report_entry, &report);
4294 reply("MSG_MATCH_COUNT", matches);
4296 reply("MSG_NO_MATCHES");
4300 static CHANSERV_FUNC(cmd_say)
4306 msg = unsplit_string(argv + 1, argc - 1, NULL);
4307 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4309 else if(GetUserH(argv[1]))
4312 msg = unsplit_string(argv + 2, argc - 2, NULL);
4313 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4317 reply("You must specify the name of a channel or user.");
4323 static CHANSERV_FUNC(cmd_emote)
4329 /* CTCP is so annoying. */
4330 msg = unsplit_string(argv + 1, argc - 1, NULL);
4331 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4333 else if(GetUserH(argv[1]))
4335 msg = unsplit_string(argv + 2, argc - 2, NULL);
4336 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4340 reply("You must specify the name of a channel or user.");
4346 struct channelList *
4347 chanserv_support_channels(void)
4349 return &chanserv_conf.support_channels;
4352 static CHANSERV_FUNC(cmd_expire)
4354 int channel_count = registered_channels;
4355 expire_channels(NULL);
4356 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4361 chanserv_expire_suspension(void *data)
4363 struct suspended *suspended = data;
4364 struct chanNode *channel;
4365 struct mod_chanmode change;
4367 if(!suspended->expires || (now < suspended->expires))
4368 suspended->revoked = now;
4369 channel = suspended->cData->channel;
4370 suspended->cData->channel = channel;
4371 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4372 change.modes_set = change.modes_clear = 0;
4374 change.args[0].mode = MODE_CHANOP;
4375 change.args[0].member = AddChannelUser(chanserv, channel);
4376 mod_chanmode_announce(chanserv, channel, &change);
4379 static CHANSERV_FUNC(cmd_csuspend)
4381 struct suspended *suspended;
4382 char reason[MAXLEN];
4383 time_t expiry, duration;
4384 struct userData *uData;
4388 if(IsProtected(channel->channel_info))
4390 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4394 if(argv[1][0] == '!')
4396 else if(IsSuspended(channel->channel_info))
4398 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4399 show_suspension_info(cmd, user, channel->channel_info->suspended);
4403 if(!strcmp(argv[1], "0"))
4405 else if((duration = ParseInterval(argv[1])))
4406 expiry = now + duration;
4409 reply("MSG_INVALID_DURATION", argv[1]);
4413 unsplit_string(argv + 2, argc - 2, reason);
4415 suspended = calloc(1, sizeof(*suspended));
4416 suspended->revoked = 0;
4417 suspended->issued = now;
4418 suspended->suspender = strdup(user->handle_info->handle);
4419 suspended->expires = expiry;
4420 suspended->reason = strdup(reason);
4421 suspended->cData = channel->channel_info;
4422 suspended->previous = suspended->cData->suspended;
4423 suspended->cData->suspended = suspended;
4425 if(suspended->expires)
4426 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4428 if(IsSuspended(channel->channel_info))
4430 suspended->previous->revoked = now;
4431 if(suspended->previous->expires)
4432 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4433 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4434 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4438 /* Mark all users in channel as absent. */
4439 for(uData = channel->channel_info->users; uData; uData = uData->next)
4448 /* Mark the channel as suspended, then part. */
4449 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4450 DelChannelUser(chanserv, channel, suspended->reason, 0);
4451 reply("CSMSG_SUSPENDED", channel->name);
4452 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4453 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4458 static CHANSERV_FUNC(cmd_cunsuspend)
4460 struct suspended *suspended;
4461 char message[MAXLEN];
4463 if(!IsSuspended(channel->channel_info))
4465 reply("CSMSG_NOT_SUSPENDED", channel->name);
4469 suspended = channel->channel_info->suspended;
4471 /* Expire the suspension and join ChanServ to the channel. */
4472 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4473 chanserv_expire_suspension(suspended);
4474 reply("CSMSG_UNSUSPENDED", channel->name);
4475 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4476 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4480 typedef struct chanservSearch
4488 unsigned long flags;
4492 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4495 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4500 search = malloc(sizeof(struct chanservSearch));
4501 memset(search, 0, sizeof(*search));
4504 for(i = 0; i < argc; i++)
4506 /* Assume all criteria require arguments. */
4509 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4513 if(!irccasecmp(argv[i], "name"))
4514 search->name = argv[++i];
4515 else if(!irccasecmp(argv[i], "registrar"))
4516 search->registrar = argv[++i];
4517 else if(!irccasecmp(argv[i], "unvisited"))
4518 search->unvisited = ParseInterval(argv[++i]);
4519 else if(!irccasecmp(argv[i], "registered"))
4520 search->registered = ParseInterval(argv[++i]);
4521 else if(!irccasecmp(argv[i], "flags"))
4524 if(!irccasecmp(argv[i], "nodelete"))
4525 search->flags |= CHANNEL_NODELETE;
4526 else if(!irccasecmp(argv[i], "suspended"))
4527 search->flags |= CHANNEL_SUSPENDED;
4530 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4534 else if(!irccasecmp(argv[i], "limit"))
4535 search->limit = strtoul(argv[++i], NULL, 10);
4538 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4543 if(search->name && !strcmp(search->name, "*"))
4545 if(search->registrar && !strcmp(search->registrar, "*"))
4546 search->registrar = 0;
4555 chanserv_channel_match(struct chanData *channel, search_t search)
4557 const char *name = channel->channel->name;
4558 if((search->name && !match_ircglob(name, search->name)) ||
4559 (search->registrar && !channel->registrar) ||
4560 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4561 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4562 (search->registered && (now - channel->registered) > search->registered) ||
4563 (search->flags && ((search->flags & channel->flags) != search->flags)))
4570 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4572 struct chanData *channel;
4573 unsigned int matches = 0;
4575 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4577 if(!chanserv_channel_match(channel, search))
4587 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4592 search_print(struct chanData *channel, void *data)
4594 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4597 static CHANSERV_FUNC(cmd_search)
4600 unsigned int matches;
4601 channel_search_func action;
4605 if(!irccasecmp(argv[1], "count"))
4606 action = search_count;
4607 else if(!irccasecmp(argv[1], "print"))
4608 action = search_print;
4611 reply("CSMSG_ACTION_INVALID", argv[1]);
4615 search = chanserv_search_create(user, argc - 2, argv + 2);
4619 if(action == search_count)
4620 search->limit = INT_MAX;
4622 if(action == search_print)
4623 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4625 matches = chanserv_channel_search(search, action, user);
4628 reply("MSG_MATCH_COUNT", matches);
4630 reply("MSG_NO_MATCHES");
4636 static CHANSERV_FUNC(cmd_unvisited)
4638 struct chanData *cData;
4639 time_t interval = chanserv_conf.channel_expire_delay;
4640 char buffer[INTERVALLEN];
4641 unsigned int limit = 25, matches = 0;
4645 interval = ParseInterval(argv[1]);
4647 limit = atoi(argv[2]);
4650 intervalString(buffer, interval);
4651 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4653 for(cData = channelList; cData && matches < limit; cData = cData->next)
4655 if((now - cData->visited) < interval)
4658 intervalString(buffer, now - cData->visited);
4659 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4666 static MODCMD_FUNC(chan_opt_defaulttopic)
4672 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4674 reply("CSMSG_TOPIC_LOCKED", channel->name);
4678 topic = unsplit_string(argv+1, argc-1, NULL);
4680 free(channel->channel_info->topic);
4681 if(topic[0] == '*' && topic[1] == 0)
4683 topic = channel->channel_info->topic = NULL;
4687 topic = channel->channel_info->topic = strdup(topic);
4688 if(channel->channel_info->topic_mask
4689 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4690 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4692 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4695 if(channel->channel_info->topic)
4696 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4698 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4702 static MODCMD_FUNC(chan_opt_topicmask)
4706 struct chanData *cData = channel->channel_info;
4709 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4711 reply("CSMSG_TOPIC_LOCKED", channel->name);
4715 mask = unsplit_string(argv+1, argc-1, NULL);
4717 if(cData->topic_mask)
4718 free(cData->topic_mask);
4719 if(mask[0] == '*' && mask[1] == 0)
4721 cData->topic_mask = 0;
4725 cData->topic_mask = strdup(mask);
4727 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4728 else if(!match_ircglob(cData->topic, cData->topic_mask))
4729 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4733 if(channel->channel_info->topic_mask)
4734 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4736 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4740 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4744 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4748 if(greeting[0] == '*' && greeting[1] == 0)
4752 unsigned int length = strlen(greeting);
4753 if(length > chanserv_conf.greeting_length)
4755 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4758 *data = strdup(greeting);
4767 reply(name, user_find_message(user, "MSG_NONE"));
4771 static MODCMD_FUNC(chan_opt_greeting)
4773 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4776 static MODCMD_FUNC(chan_opt_usergreeting)
4778 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4781 static MODCMD_FUNC(chan_opt_modes)
4783 struct mod_chanmode *new_modes;
4784 char modes[MODELEN];
4788 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4790 reply("CSMSG_NO_ACCESS");
4793 if(argv[1][0] == '*' && argv[1][1] == 0)
4795 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4797 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4799 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4802 else if(new_modes->argc > 1)
4804 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4805 mod_chanmode_free(new_modes);
4810 channel->channel_info->modes = *new_modes;
4811 modcmd_chanmode_announce(new_modes);
4812 mod_chanmode_free(new_modes);
4816 mod_chanmode_format(&channel->channel_info->modes, modes);
4818 reply("CSMSG_SET_MODES", modes);
4820 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4824 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4826 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4828 struct chanData *cData = channel->channel_info;
4833 /* Set flag according to value. */
4834 if(enabled_string(argv[1]))
4836 cData->flags |= mask;
4839 else if(disabled_string(argv[1]))
4841 cData->flags &= ~mask;
4846 reply("MSG_INVALID_BINARY", argv[1]);
4852 /* Find current option value. */
4853 value = (cData->flags & mask) ? 1 : 0;
4857 reply(name, user_find_message(user, "MSG_ON"));
4859 reply(name, user_find_message(user, "MSG_OFF"));
4863 static MODCMD_FUNC(chan_opt_nodelete)
4865 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4867 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4871 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4874 static MODCMD_FUNC(chan_opt_userinfo)
4876 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4879 static MODCMD_FUNC(chan_opt_voice)
4881 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4884 static MODCMD_FUNC(chan_opt_dynlimit)
4886 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4889 static MODCMD_FUNC(chan_opt_topicsnarf)
4891 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4893 reply("CSMSG_TOPIC_LOCKED", channel->name);
4896 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4899 static MODCMD_FUNC(chan_opt_peoninvite)
4901 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4904 static MODCMD_FUNC(chan_opt_defaults)
4906 struct userData *uData;
4907 struct chanData *cData;
4908 const char *confirm;
4909 enum levelOption lvlOpt;
4910 enum charOption chOpt;
4912 cData = channel->channel_info;
4913 uData = GetChannelUser(cData, user->handle_info);
4914 if(!uData || (uData->access < UL_OWNER))
4916 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4919 confirm = make_confirmation_string(uData);
4920 if((argc < 2) || strcmp(argv[1], confirm))
4922 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4925 cData->flags = CHANNEL_DEFAULT_FLAGS;
4926 cData->modes = chanserv_conf.default_modes;
4927 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4928 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4929 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4930 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4931 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4936 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4938 struct chanData *cData = channel->channel_info;
4939 struct userData *uData;
4940 unsigned short value;
4944 if(!check_user_level(channel, user, option, 1, 1))
4946 reply("CSMSG_CANNOT_SET");
4949 value = user_level_from_name(argv[1], UL_OWNER+1);
4950 if(!value && strcmp(argv[1], "0"))
4952 reply("CSMSG_INVALID_ACCESS", argv[1]);
4955 uData = GetChannelUser(cData, user->handle_info);
4956 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4958 reply("CSMSG_BAD_SETLEVEL");
4961 if((option == lvlSetters) && (value > uData->access))
4963 reply("CSMSG_BAD_SETTERS");
4966 cData->lvlOpts[option] = value;
4968 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4972 static MODCMD_FUNC(chan_opt_enfops)
4974 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4977 static MODCMD_FUNC(chan_opt_giveops)
4979 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4982 static MODCMD_FUNC(chan_opt_enfmodes)
4984 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4987 static MODCMD_FUNC(chan_opt_enftopic)
4989 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
4992 static MODCMD_FUNC(chan_opt_pubcmd)
4994 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
4997 static MODCMD_FUNC(chan_opt_setters)
4999 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5002 static MODCMD_FUNC(chan_opt_ctcpusers)
5004 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5008 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5010 struct chanData *cData = channel->channel_info;
5011 int count = charOptions[option].count, index;
5015 index = atoi(argv[1]);
5017 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5019 reply("CSMSG_INVALID_NUMERIC", index);
5020 /* Show possible values. */
5021 for(index = 0; index < count; index++)
5022 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5026 cData->chOpts[option] = charOptions[option].values[index].value;
5030 /* Find current option value. */
5033 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5037 /* Somehow, the option value is corrupt; reset it to the default. */
5038 cData->chOpts[option] = charOptions[option].default_value;
5043 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5047 static MODCMD_FUNC(chan_opt_protect)
5049 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5052 static MODCMD_FUNC(chan_opt_toys)
5054 return channel_multiple_option(chToys, CSFUNC_ARGS);
5057 static MODCMD_FUNC(chan_opt_ctcpreaction)
5059 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5062 static MODCMD_FUNC(chan_opt_topicrefresh)
5064 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5067 static struct svccmd_list set_shows_list;
5070 handle_svccmd_unbind(struct svccmd *target) {
5072 for(ii=0; ii<set_shows_list.used; ++ii)
5073 if(target == set_shows_list.list[ii])
5074 set_shows_list.used = 0;
5077 static CHANSERV_FUNC(cmd_set)
5079 struct svccmd *subcmd;
5083 /* Check if we need to (re-)initialize set_shows_list. */
5084 if(!set_shows_list.used)
5086 if(!set_shows_list.size)
5088 set_shows_list.size = chanserv_conf.set_shows->used;
5089 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5091 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5093 const char *name = chanserv_conf.set_shows->list[ii];
5094 sprintf(buf, "%s %s", argv[0], name);
5095 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5098 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5101 svccmd_list_append(&set_shows_list, subcmd);
5107 reply("CSMSG_CHANNEL_OPTIONS");
5108 for(ii = 0; ii < set_shows_list.used; ii++)
5110 subcmd = set_shows_list.list[ii];
5111 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5116 sprintf(buf, "%s %s", argv[0], argv[1]);
5117 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5120 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5123 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5125 reply("CSMSG_NO_ACCESS");
5129 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5133 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5135 struct userData *uData;
5137 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5140 reply("CSMSG_NOT_USER", channel->name);
5146 /* Just show current option value. */
5148 else if(enabled_string(argv[1]))
5150 uData->flags |= mask;
5152 else if(disabled_string(argv[1]))
5154 uData->flags &= ~mask;
5158 reply("MSG_INVALID_BINARY", argv[1]);
5162 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5166 static MODCMD_FUNC(user_opt_noautoop)
5168 struct userData *uData;
5170 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5173 reply("CSMSG_NOT_USER", channel->name);
5176 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5177 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5179 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5182 static MODCMD_FUNC(user_opt_autoinvite)
5184 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5187 static MODCMD_FUNC(user_opt_info)
5189 struct userData *uData;
5192 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5196 /* If they got past the command restrictions (which require access)
5197 * but fail this test, we have some fool with security override on.
5199 reply("CSMSG_NOT_USER", channel->name);
5205 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5208 if(infoline[0] == '*' && infoline[1] == 0)
5211 uData->info = strdup(infoline);
5214 reply("CSMSG_USET_INFO", uData->info);
5216 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5220 struct svccmd_list uset_shows_list;
5222 static CHANSERV_FUNC(cmd_uset)
5224 struct svccmd *subcmd;
5228 /* Check if we need to (re-)initialize uset_shows_list. */
5229 if(!uset_shows_list.used)
5233 "NoAutoOp", "AutoInvite", "Info"
5236 if(!uset_shows_list.size)
5238 uset_shows_list.size = ArrayLength(options);
5239 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5241 for(ii = 0; ii < ArrayLength(options); ii++)
5243 const char *name = options[ii];
5244 sprintf(buf, "%s %s", argv[0], name);
5245 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5248 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5251 svccmd_list_append(&uset_shows_list, subcmd);
5257 /* Do this so options are presented in a consistent order. */
5258 reply("CSMSG_USER_OPTIONS");
5259 for(ii = 0; ii < uset_shows_list.used; ii++)
5260 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5264 sprintf(buf, "%s %s", argv[0], argv[1]);
5265 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5268 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5272 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5275 static CHANSERV_FUNC(cmd_giveownership)
5277 struct handle_info *new_owner_hi;
5278 struct userData *new_owner, *curr_user;
5279 struct chanData *cData = channel->channel_info;
5280 struct do_not_register *dnr;
5282 unsigned short co_access;
5283 char reason[MAXLEN];
5286 curr_user = GetChannelAccess(cData, user->handle_info);
5287 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5288 if(!curr_user || (curr_user->access != UL_OWNER))
5290 struct userData *owner = NULL;
5291 for(curr_user = channel->channel_info->users;
5293 curr_user = curr_user->next)
5295 if(curr_user->access != UL_OWNER)
5299 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5305 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5307 if(new_owner_hi == user->handle_info)
5309 reply("CSMSG_NO_TRANSFER_SELF");
5312 new_owner = GetChannelAccess(cData, new_owner_hi);
5315 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5318 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5320 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5323 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5324 if(!IsHelping(user))
5325 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5327 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5330 if(new_owner->access >= UL_COOWNER)
5331 co_access = new_owner->access;
5333 co_access = UL_COOWNER;
5334 new_owner->access = UL_OWNER;
5336 curr_user->access = co_access;
5337 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5338 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5339 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5343 static CHANSERV_FUNC(cmd_suspend)
5345 struct handle_info *hi;
5346 struct userData *self, *target;
5349 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5350 self = GetChannelUser(channel->channel_info, user->handle_info);
5351 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5353 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5356 if(target->access >= self->access)
5358 reply("MSG_USER_OUTRANKED", hi->handle);
5361 if(target->flags & USER_SUSPENDED)
5363 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5368 target->present = 0;
5371 target->flags |= USER_SUSPENDED;
5372 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5376 static CHANSERV_FUNC(cmd_unsuspend)
5378 struct handle_info *hi;
5379 struct userData *self, *target;
5382 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5383 self = GetChannelUser(channel->channel_info, user->handle_info);
5384 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5386 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5389 if(target->access >= self->access)
5391 reply("MSG_USER_OUTRANKED", hi->handle);
5394 if(!(target->flags & USER_SUSPENDED))
5396 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5399 target->flags &= ~USER_SUSPENDED;
5400 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5404 static MODCMD_FUNC(cmd_deleteme)
5406 struct handle_info *hi;
5407 struct userData *target;
5408 const char *confirm_string;
5409 unsigned short access;
5412 hi = user->handle_info;
5413 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5415 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5418 if(target->access == UL_OWNER)
5420 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5423 confirm_string = make_confirmation_string(target);
5424 if((argc < 2) || strcmp(argv[1], confirm_string))
5426 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5429 access = target->access;
5430 channel_name = strdup(channel->name);
5431 del_channel_user(target, 1);
5432 reply("CSMSG_DELETED_YOU", access, channel_name);
5438 chanserv_refresh_topics(UNUSED_ARG(void *data))
5440 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5441 struct chanData *cData;
5444 for(cData = channelList; cData; cData = cData->next)
5446 if(IsSuspended(cData))
5448 opt = cData->chOpts[chTopicRefresh];
5451 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5454 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5455 cData->last_refresh = refresh_num;
5457 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5460 static CHANSERV_FUNC(cmd_unf)
5464 char response[MAXLEN];
5465 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5466 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5467 irc_privmsg(cmd->parent->bot, channel->name, response);
5470 reply("CSMSG_UNF_RESPONSE");
5474 static CHANSERV_FUNC(cmd_ping)
5478 char response[MAXLEN];
5479 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5480 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5481 irc_privmsg(cmd->parent->bot, channel->name, response);
5484 reply("CSMSG_PING_RESPONSE");
5488 static CHANSERV_FUNC(cmd_wut)
5492 char response[MAXLEN];
5493 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5494 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5495 irc_privmsg(cmd->parent->bot, channel->name, response);
5498 reply("CSMSG_WUT_RESPONSE");
5502 static CHANSERV_FUNC(cmd_8ball)
5504 unsigned int i, j, accum;
5509 for(i=1; i<argc; i++)
5510 for(j=0; argv[i][j]; j++)
5511 accum = (accum << 5) - accum + toupper(argv[i][j]);
5512 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5515 char response[MAXLEN];
5516 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5517 irc_privmsg(cmd->parent->bot, channel->name, response);
5520 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5524 static CHANSERV_FUNC(cmd_d)
5526 unsigned long sides, count, modifier, ii, total;
5527 char response[MAXLEN], *sep;
5531 if((count = strtoul(argv[1], &sep, 10)) < 1)
5541 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5542 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5546 else if((sep[0] == '-') && isdigit(sep[1]))
5547 modifier = strtoul(sep, NULL, 10);
5548 else if((sep[0] == '+') && isdigit(sep[1]))
5549 modifier = strtoul(sep+1, NULL, 10);
5556 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5561 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5564 for(total = ii = 0; ii < count; ++ii)
5565 total += (rand() % sides) + 1;
5568 if((count > 1) || modifier)
5570 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5571 sprintf(response, fmt, total, count, sides, modifier);
5575 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5576 sprintf(response, fmt, total, sides);
5579 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5581 send_message_type(4, user, cmd->parent->bot, "%s", response);
5585 static CHANSERV_FUNC(cmd_huggle)
5587 char response[MAXLEN];
5589 /* CTCP must be via PRIVMSG, never notice */
5592 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5593 sprintf(response, fmt, user->nick);
5594 irc_privmsg(cmd->parent->bot, channel->name, response);
5598 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5599 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5605 chanserv_adjust_limit(void *data)
5607 struct mod_chanmode change;
5608 struct chanData *cData = data;
5609 struct chanNode *channel = cData->channel;
5612 if(IsSuspended(cData))
5615 cData->limitAdjusted = now;
5616 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5617 if(cData->modes.modes_set & MODE_LIMIT)
5619 if(limit > cData->modes.new_limit)
5620 limit = cData->modes.new_limit;
5621 else if(limit == cData->modes.new_limit)
5625 change.modes_set = MODE_LIMIT;
5626 change.modes_clear = 0;
5627 change.new_limit = limit;
5629 mod_chanmode_announce(chanserv, channel, &change);
5633 handle_new_channel(struct chanNode *channel)
5635 struct chanData *cData;
5637 if(!(cData = channel->channel_info))
5640 if(cData->modes.modes_set || cData->modes.modes_clear)
5641 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5643 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5644 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5647 /* Welcome to my worst nightmare. Warning: Read (or modify)
5648 the code below at your own risk. */
5650 handle_join(struct modeNode *mNode)
5652 struct mod_chanmode change;
5653 struct userNode *user = mNode->user;
5654 struct chanNode *channel = mNode->channel;
5655 struct chanData *cData;
5656 struct userData *uData = NULL;
5657 struct banData *bData;
5658 struct handle_info *handle;
5659 unsigned int modes = 0, info = 0;
5662 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5665 cData = channel->channel_info;
5666 if(channel->members.used > cData->max)
5667 cData->max = channel->members.used;
5669 /* Check for bans. If they're joining through a ban, one of two
5671 * 1: Join during a netburst, by riding the break. Kick them
5672 * unless they have ops or voice in the channel.
5673 * 2: They're allowed to join through the ban (an invite in
5674 * ircu2.10, or a +e on Hybrid, or something).
5675 * If they're not joining through a ban, and the banlist is not
5676 * full, see if they're on the banlist for the channel. If so,
5679 if(user->uplink->burst && !mNode->modes)
5682 for(ii = 0; ii < channel->banlist.used; ii++)
5684 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5686 /* Riding a netburst. Naughty. */
5687 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5693 change.modes_set = change.modes_clear = 0;
5695 if(channel->banlist.used < MAXBANS)
5697 /* Not joining through a ban. */
5698 for(bData = cData->bans;
5699 bData && !user_matches_glob(user, bData->mask, 1);
5700 bData = bData->next);
5704 char kick_reason[MAXLEN];
5705 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5707 bData->triggered = now;
5708 if(bData != cData->bans)
5710 /* Shuffle the ban to the head of the list. */
5711 if(bData->next) bData->next->prev = bData->prev;
5712 if(bData->prev) bData->prev->next = bData->next;
5715 bData->next = cData->bans;
5718 cData->bans->prev = bData;
5719 cData->bans = bData;
5722 change.args[0].mode = MODE_BAN;
5723 change.args[0].hostmask = bData->mask;
5724 mod_chanmode_announce(chanserv, channel, &change);
5725 KickChannelUser(user, channel, chanserv, kick_reason);
5730 /* ChanServ will not modify the limits in join-flooded channels.
5731 It will also skip DynLimit processing when the user (or srvx)
5732 is bursting in, because there are likely more incoming. */
5733 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5734 && !user->uplink->burst
5735 && !channel->join_flooded
5736 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5738 /* The user count has begun "bumping" into the channel limit,
5739 so set a timer to raise the limit a bit. Any previous
5740 timers are removed so three incoming users within the delay
5741 results in one limit change, not three. */
5743 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5744 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5747 if(cData->lvlOpts[lvlGiveOps] == 0)
5748 modes |= MODE_CHANOP;
5749 else if((cData->flags & CHANNEL_VOICE_ALL) && !channel->join_flooded)
5750 modes |= MODE_VOICE;
5752 greeting = cData->greeting;
5753 if(user->handle_info)
5755 handle = user->handle_info;
5757 if(IsHelper(user) && !IsHelping(user))
5760 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5762 if(channel == chanserv_conf.support_channels.list[ii])
5764 HANDLE_SET_FLAG(user->handle_info, HELPING);
5770 uData = GetTrueChannelAccess(cData, handle);
5771 if(uData && !IsUserSuspended(uData))
5773 /* Ops and above were handled by the above case. */
5774 if(IsUserAutoOp(uData))
5776 if(uData->access < cData->lvlOpts[lvlGiveOps])
5777 modes |= MODE_VOICE;
5779 modes |= MODE_CHANOP;
5781 if(uData->access >= UL_PRESENT)
5782 cData->visited = now;
5783 if(cData->user_greeting)
5784 greeting = cData->user_greeting;
5786 && (cData->flags & CHANNEL_INFO_LINES)
5787 && ((now - uData->seen) >= chanserv_conf.info_delay)
5794 if(!user->uplink->burst)
5798 change.args[0].mode = modes;
5799 change.args[0].member = mNode;
5800 mod_chanmode_announce(chanserv, channel, &change);
5802 if(greeting && !user->uplink->burst)
5803 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5805 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5811 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5813 struct mod_chanmode change;
5814 struct userData *channel;
5815 unsigned int ii, jj;
5817 if(!user->handle_info)
5820 change.modes_set = change.modes_clear = 0;
5822 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5824 struct chanNode *cn;
5825 struct modeNode *mn;
5826 if(IsUserSuspended(channel)
5827 || IsSuspended(channel->channel)
5828 || !(cn = channel->channel->channel))
5831 mn = GetUserMode(cn, user);
5834 if(!IsUserSuspended(channel)
5835 && IsUserAutoInvite(channel)
5836 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5838 irc_invite(chanserv, user, cn);
5842 if(channel->access >= UL_PRESENT)
5843 channel->channel->visited = now;
5845 if(IsUserAutoOp(channel))
5847 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5848 change.args[0].mode = MODE_CHANOP;
5850 change.args[0].mode = MODE_VOICE;
5851 change.args[0].member = mn;
5852 mod_chanmode_announce(chanserv, cn, &change);
5855 channel->seen = now;
5856 channel->present = 1;
5859 for(ii = 0; ii < user->channels.used; ++ii)
5861 struct chanNode *channel = user->channels.list[ii]->channel;
5862 struct banData *ban;
5864 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5865 || !channel->channel_info)
5867 for(jj = 0; jj < channel->banlist.used; ++jj)
5868 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5870 if(jj < channel->banlist.used)
5872 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5874 char kick_reason[MAXLEN];
5875 if(!user_matches_glob(user, ban->mask, 1))
5877 change.args[0].mode = MODE_BAN;
5878 change.args[0].hostmask = ban->mask;
5879 mod_chanmode_announce(chanserv, channel, &change);
5880 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5881 KickChannelUser(user, channel, chanserv, kick_reason);
5882 ban->triggered = now;
5887 if(IsSupportHelper(user))
5889 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5891 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5893 HANDLE_SET_FLAG(user->handle_info, HELPING);
5901 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5903 struct chanData *cData;
5904 struct userData *uData;
5906 cData = channel->channel_info;
5907 if(!cData || IsSuspended(cData) || IsLocal(user))
5910 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5912 /* Allow for a bit of padding so that the limit doesn't
5913 track the user count exactly, which could get annoying. */
5914 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5916 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5917 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5921 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5922 scan_user_presence(uData, user);
5924 if(IsHelping(user) && IsSupportHelper(user))
5926 unsigned int ii, jj;
5927 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5929 for(jj = 0; jj < user->channels.used; ++jj)
5930 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5932 if(jj < user->channels.used)
5935 if(ii == chanserv_conf.support_channels.used)
5936 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5941 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5943 if(!channel->channel_info || !kicker || IsService(kicker)
5944 || (kicker == victim) || IsSuspended(channel->channel_info)
5945 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5948 if(protect_user(victim, kicker, channel->channel_info))
5950 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
5951 KickChannelUser(kicker, channel, chanserv, reason);
5956 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5958 struct chanData *cData;
5960 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user)) return 0;
5962 cData = channel->channel_info;
5963 if(bad_topic(channel, user, channel->topic))
5965 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5966 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
5967 SetChannelTopic(channel, chanserv, old_topic, 1);
5968 else if(cData->topic)
5969 SetChannelTopic(channel, chanserv, cData->topic, 1);
5972 /* With topicsnarf, grab the topic and save it as the default topic. */
5973 if(cData->flags & CHANNEL_TOPIC_SNARF)
5976 cData->topic = strdup(channel->topic);
5982 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
5984 struct mod_chanmode *bounce = NULL;
5985 unsigned int bnc, ii;
5988 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
5991 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
5992 && mode_lock_violated(&channel->channel_info->modes, change))
5994 char correct[MAXLEN];
5995 bounce = mod_chanmode_alloc(change->argc + 1);
5996 *bounce = channel->channel_info->modes;
5997 mod_chanmode_format(&channel->channel_info->modes, correct);
5998 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6000 for(ii = bnc = 0; ii < change->argc; ++ii)
6002 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6004 const struct userNode *victim = change->args[ii].member->user;
6005 if(!protect_user(victim, user, channel->channel_info))
6008 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6011 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6012 bounce->args[bnc].member = GetUserMode(channel, user);
6013 if(bounce->args[bnc].member)
6017 bounce->args[bnc].mode = MODE_CHANOP;
6018 bounce->args[bnc].member = change->args[ii].member;
6020 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6022 else if(change->args[ii].mode & MODE_CHANOP)
6024 const struct userNode *victim = change->args[ii].member->user;
6025 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6028 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6029 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6030 bounce->args[bnc].member = change->args[ii].member;
6033 else if(change->args[ii].mode & MODE_BAN)
6035 const char *ban = change->args[ii].hostmask;
6036 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6039 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6040 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6041 bounce->args[bnc].hostmask = ban;
6043 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6048 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6049 mod_chanmode_announce(chanserv, channel, bounce);
6050 mod_chanmode_free(bounce);
6055 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6057 struct chanNode *channel;
6058 struct banData *bData;
6059 struct mod_chanmode change;
6060 unsigned int ii, jj;
6061 char kick_reason[MAXLEN];
6063 change.modes_set = change.modes_clear = 0;
6065 change.args[0].mode = MODE_BAN;
6066 for(ii = 0; ii < user->channels.used; ++ii)
6068 channel = user->channels.list[ii]->channel;
6069 /* Need not check for bans if they're opped or voiced. */
6070 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6072 /* Need not check for bans unless channel registration is active. */
6073 if(!channel->channel_info || IsSuspended(channel->channel_info))
6075 /* Look for a matching ban already on the channel. */
6076 for(jj = 0; jj < channel->banlist.used; ++jj)
6077 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6079 /* Need not act if we found one. */
6080 if(jj < channel->banlist.used)
6082 /* Look for a matching ban in this channel. */
6083 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6085 if(!user_matches_glob(user, bData->mask, 1))
6087 change.args[0].hostmask = bData->mask;
6088 mod_chanmode_announce(chanserv, channel, &change);
6089 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6090 KickChannelUser(user, channel, chanserv, kick_reason);
6091 bData->triggered = now;
6092 break; /* we don't need to check any more bans in the channel */
6097 static void handle_rename(struct handle_info *handle, const char *old_handle)
6099 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6103 dict_remove2(handle_dnrs, old_handle, 1);
6104 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6105 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6110 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6112 struct userNode *h_user;
6114 if(handle->channels)
6116 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6117 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6119 while(handle->channels)
6120 del_channel_user(handle->channels, 1);
6125 handle_server_link(UNUSED_ARG(struct server *server))
6127 struct chanData *cData;
6129 for(cData = channelList; cData; cData = cData->next)
6131 if(!IsSuspended(cData))
6132 cData->may_opchan = 1;
6133 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6134 && !cData->channel->join_flooded
6135 && ((cData->channel->limit - cData->channel->members.used)
6136 < chanserv_conf.adjust_threshold))
6138 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6139 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6145 chanserv_conf_read(void)
6149 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6150 struct mod_chanmode *change;
6151 struct string_list *strlist;
6152 struct chanNode *chan;
6155 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6157 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6160 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6161 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6162 chanserv_conf.support_channels.used = 0;
6163 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6165 for(ii = 0; ii < strlist->used; ++ii)
6167 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6170 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6172 channelList_append(&chanserv_conf.support_channels, chan);
6175 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6178 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6181 chan = AddChannel(str, now, str2, NULL);
6183 channelList_append(&chanserv_conf.support_channels, chan);
6185 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6186 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6187 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6188 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6189 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6190 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6191 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6192 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6193 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6194 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6195 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6196 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6197 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6198 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6199 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6200 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6201 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6202 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6203 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6204 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6205 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6207 NickChange(chanserv, str, 0);
6208 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6209 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6210 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6211 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6212 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6213 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6214 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6215 chanserv_conf.max_owned = str ? atoi(str) : 5;
6216 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6217 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6218 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6219 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6220 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6221 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6222 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6225 safestrncpy(mode_line, str, sizeof(mode_line));
6226 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6227 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6229 chanserv_conf.default_modes = *change;
6230 mod_chanmode_free(change);
6232 free_string_list(chanserv_conf.set_shows);
6233 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6235 strlist = string_list_copy(strlist);
6238 static const char *list[] = {
6239 /* multiple choice options */
6240 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6241 "PubCmd", "GiveOps", "EnfOps", "EnfModes", "EnfTopic", "Protect",
6242 "Toys", "Setters", "TopicRefresh", "CtcpUsers", "CtcpReaction",
6243 /* binary options */
6244 "Voice", "UserInfo", "DynLimit", "TopicSnarf", "PeonInvite", "NoDelete",
6248 strlist = alloc_string_list(ArrayLength(list)-1);
6249 for(ii=0; list[ii]; ii++)
6250 string_list_append(strlist, strdup(list[ii]));
6252 chanserv_conf.set_shows = strlist;
6253 /* We don't look things up now, in case the list refers to options
6254 * defined by modules initialized after this point. Just mark the
6255 * function list as invalid, so it will be initialized.
6257 set_shows_list.used = 0;
6258 free_string_list(chanserv_conf.eightball);
6259 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6262 strlist = string_list_copy(strlist);
6266 strlist = alloc_string_list(4);
6267 string_list_append(strlist, strdup("Yes."));
6268 string_list_append(strlist, strdup("No."));
6269 string_list_append(strlist, strdup("Maybe so."));
6271 chanserv_conf.eightball = strlist;
6272 free_string_list(chanserv_conf.old_ban_names);
6273 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6275 strlist = string_list_copy(strlist);
6277 strlist = alloc_string_list(2);
6278 chanserv_conf.old_ban_names = strlist;
6282 chanserv_note_type_read(const char *key, struct record_data *rd)
6285 struct note_type *ntype;
6288 if(!(obj = GET_RECORD_OBJECT(rd)))
6290 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6293 if(!(ntype = chanserv_create_note_type(key)))
6295 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6299 /* Figure out set access */
6300 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6302 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6303 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6305 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6307 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6308 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6310 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6312 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6316 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6317 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6318 ntype->set_access.min_opserv = 0;
6321 /* Figure out visibility */
6322 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6323 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6324 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6325 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6326 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6327 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6328 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6329 ntype->visible_type = NOTE_VIS_ALL;
6331 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6333 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6334 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6338 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6340 struct handle_info *handle;
6341 struct userData *uData;
6342 char *seen, *inf, *flags;
6344 unsigned short access;
6346 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6348 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6352 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6353 if(access > UL_OWNER)
6355 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6359 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6360 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6361 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6362 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6363 handle = get_handle_info(key);
6366 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6370 uData = add_channel_user(chan, handle, access, last_seen, inf);
6371 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6375 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6377 struct banData *bData;
6378 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6379 time_t set_time, triggered_time, expires_time;
6381 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6383 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6387 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6388 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6389 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6390 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6391 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6392 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6394 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6395 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6397 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6399 expires_time = set_time + atoi(s_duration);
6403 if(expires_time && (expires_time < now))
6406 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6409 static struct suspended *
6410 chanserv_read_suspended(dict_t obj)
6412 struct suspended *suspended = calloc(1, sizeof(*suspended));
6416 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6417 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6418 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6419 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6420 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6421 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6422 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6423 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6424 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6425 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6430 chanserv_channel_read(const char *key, struct record_data *hir)
6432 struct suspended *suspended;
6433 struct mod_chanmode *modes;
6434 struct chanNode *cNode;
6435 struct chanData *cData;
6436 struct dict *channel, *obj;
6437 char *str, *argv[10];
6441 channel = hir->d.object;
6443 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6446 cNode = AddChannel(key, now, NULL, NULL);
6449 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6452 cData = register_channel(cNode, str);
6455 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6459 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6461 enum levelOption lvlOpt;
6462 enum charOption chOpt;
6463 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6465 if(!(str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING)))
6467 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6469 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6471 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6473 cData->chOpts[chOpt] = str[0];
6475 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6476 cData->flags = atoi(str);
6478 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6480 enum levelOption lvlOpt;
6481 enum charOption chOpt;
6484 cData->flags = base64toint(str, 5);
6485 count = strlen(str += 5);
6486 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6489 switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6491 case 'c': lvl = UL_COOWNER; break;
6492 case 'm': lvl = UL_MASTER; break;
6493 case 'n': lvl = UL_OWNER+1; break;
6494 case 'o': lvl = UL_OP; break;
6495 case 'p': lvl = UL_PEON; break;
6496 case 'w': lvl = UL_OWNER; break;
6497 default: lvl = 0; break;
6499 cData->lvlOpts[lvlOpt] = lvl;
6501 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6502 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6505 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6507 suspended = chanserv_read_suspended(obj);
6508 cData->suspended = suspended;
6509 suspended->cData = cData;
6510 /* We could use suspended->expires and suspended->revoked to
6511 * set the CHANNEL_SUSPENDED flag, but we don't. */
6513 else if(cData->flags & CHANNEL_SUSPENDED)
6515 suspended = calloc(1, sizeof(*suspended));
6516 suspended->issued = 0;
6517 suspended->revoked = 0;
6518 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6519 suspended->expires = str ? atoi(str) : 0;
6520 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6521 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6522 suspended->reason = strdup(str ? str : "No reason");
6523 suspended->previous = NULL;
6524 cData->suspended = suspended;
6525 suspended->cData = cData;
6530 if((cData->flags & CHANNEL_SUSPENDED)
6531 && suspended->expires
6532 && (suspended->expires <= now))
6534 cData->flags &= ~CHANNEL_SUSPENDED;
6537 if(!(cData->flags & CHANNEL_SUSPENDED))
6539 struct mod_chanmode change;
6540 change.modes_set = change.modes_clear = 0;
6542 change.args[0].mode = MODE_CHANOP;
6543 change.args[0].member = AddChannelUser(chanserv, cNode);
6544 mod_chanmode_announce(chanserv, cNode, &change);
6546 else if(suspended->expires > now)
6548 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6551 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6552 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6553 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6554 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6555 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6556 cData->max = str ? atoi(str) : 0;
6557 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6558 cData->greeting = str ? strdup(str) : NULL;
6559 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6560 cData->user_greeting = str ? strdup(str) : NULL;
6561 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6562 cData->topic_mask = str ? strdup(str) : NULL;
6563 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6564 cData->topic = str ? strdup(str) : NULL;
6566 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6567 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6568 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6569 cData->modes = *modes;
6570 if(cData->modes.argc > 1)
6571 cData->modes.argc = 1;
6572 if(!IsSuspended(cData))
6573 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6574 mod_chanmode_free(modes);
6577 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6578 for(it = dict_first(obj); it; it = iter_next(it))
6579 user_read_helper(iter_key(it), iter_data(it), cData);
6581 if(!cData->users && !IsProtected(cData))
6583 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6584 unregister_channel(cData, "has empty user list.");
6588 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6589 for(it = dict_first(obj); it; it = iter_next(it))
6590 ban_read_helper(iter_key(it), iter_data(it), cData);
6592 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6593 for(it = dict_first(obj); it; it = iter_next(it))
6595 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6596 struct record_data *rd = iter_data(it);
6597 const char *note, *setter;
6599 if(rd->type != RECDB_OBJECT)
6601 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6605 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6607 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6609 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6613 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6614 if(!setter) setter = "<unknown>";
6615 chanserv_add_channel_note(cData, ntype, setter, note);
6623 chanserv_dnr_read(const char *key, struct record_data *hir)
6625 const char *setter, *reason, *str;
6626 struct do_not_register *dnr;
6628 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6631 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6634 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6637 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6640 dnr = chanserv_add_dnr(key, setter, reason);
6643 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6645 dnr->set = atoi(str);
6651 chanserv_saxdb_read(struct dict *database)
6653 struct dict *section;
6656 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6657 for(it = dict_first(section); it; it = iter_next(it))
6658 chanserv_note_type_read(iter_key(it), iter_data(it));
6660 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6661 for(it = dict_first(section); it; it = iter_next(it))
6662 chanserv_channel_read(iter_key(it), iter_data(it));
6664 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6665 for(it = dict_first(section); it; it = iter_next(it))
6666 chanserv_dnr_read(iter_key(it), iter_data(it));
6672 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6674 int high_present = 0;
6675 saxdb_start_record(ctx, KEY_USERS, 1);
6676 for(; uData; uData = uData->next)
6678 if((uData->access >= UL_PRESENT) && uData->present)
6680 saxdb_start_record(ctx, uData->handle->handle, 0);
6681 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6682 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6684 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6686 saxdb_write_string(ctx, KEY_INFO, uData->info);
6687 saxdb_end_record(ctx);
6689 saxdb_end_record(ctx);
6690 return high_present;
6694 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6698 saxdb_start_record(ctx, KEY_BANS, 1);
6699 for(; bData; bData = bData->next)
6701 saxdb_start_record(ctx, bData->mask, 0);
6702 saxdb_write_int(ctx, KEY_SET, bData->set);
6703 if(bData->triggered)
6704 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6706 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6708 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6710 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6711 saxdb_end_record(ctx);
6713 saxdb_end_record(ctx);
6717 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6719 saxdb_start_record(ctx, name, 0);
6720 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6721 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6723 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6725 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6727 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6729 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6730 saxdb_end_record(ctx);
6734 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6738 enum levelOption lvlOpt;
6739 enum charOption chOpt;
6741 saxdb_start_record(ctx, channel->channel->name, 1);
6743 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6744 saxdb_write_int(ctx, KEY_MAX, channel->max);
6746 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6747 if(channel->registrar)
6748 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6749 if(channel->greeting)
6750 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6751 if(channel->user_greeting)
6752 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6753 if(channel->topic_mask)
6754 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6755 if(channel->suspended)
6756 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6758 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6759 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6760 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6761 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6762 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6764 buf[0] = channel->chOpts[chOpt];
6766 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6768 saxdb_end_record(ctx);
6770 if(channel->modes.modes_set || channel->modes.modes_clear)
6772 mod_chanmode_format(&channel->modes, buf);
6773 saxdb_write_string(ctx, KEY_MODES, buf);
6776 high_present = chanserv_write_users(ctx, channel->users);
6777 chanserv_write_bans(ctx, channel->bans);
6779 if(dict_size(channel->notes))
6783 saxdb_start_record(ctx, KEY_NOTES, 1);
6784 for(it = dict_first(channel->notes); it; it = iter_next(it))
6786 struct note *note = iter_data(it);
6787 saxdb_start_record(ctx, iter_key(it), 0);
6788 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6789 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6790 saxdb_end_record(ctx);
6792 saxdb_end_record(ctx);
6795 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6796 saxdb_end_record(ctx);
6800 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6804 saxdb_start_record(ctx, ntype->name, 0);
6805 switch(ntype->set_access_type)
6807 case NOTE_SET_CHANNEL_ACCESS:
6808 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6810 case NOTE_SET_CHANNEL_SETTER:
6811 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6813 case NOTE_SET_PRIVILEGED: default:
6814 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6817 switch(ntype->visible_type)
6819 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6820 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6821 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6823 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6824 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6825 saxdb_end_record(ctx);
6829 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6831 struct do_not_register *dnr;
6834 for(it = dict_first(dnrs); it; it = iter_next(it))
6836 dnr = iter_data(it);
6837 saxdb_start_record(ctx, dnr->chan_name, 0);
6839 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6840 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6841 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6842 saxdb_end_record(ctx);
6847 chanserv_saxdb_write(struct saxdb_context *ctx)
6850 struct chanData *channel;
6853 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6854 for(it = dict_first(note_types); it; it = iter_next(it))
6855 chanserv_write_note_type(ctx, iter_data(it));
6856 saxdb_end_record(ctx);
6859 saxdb_start_record(ctx, KEY_DNR, 1);
6860 write_dnrs_helper(ctx, handle_dnrs);
6861 write_dnrs_helper(ctx, plain_dnrs);
6862 write_dnrs_helper(ctx, mask_dnrs);
6863 saxdb_end_record(ctx);
6866 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6867 for(channel = channelList; channel; channel = channel->next)
6868 chanserv_write_channel(ctx, channel);
6869 saxdb_end_record(ctx);
6875 chanserv_db_cleanup(void) {
6877 unreg_part_func(handle_part);
6879 unregister_channel(channelList, "terminating.");
6880 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6881 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6882 free(chanserv_conf.support_channels.list);
6883 dict_delete(handle_dnrs);
6884 dict_delete(plain_dnrs);
6885 dict_delete(mask_dnrs);
6886 dict_delete(note_types);
6887 free_string_list(chanserv_conf.eightball);
6888 free_string_list(chanserv_conf.old_ban_names);
6889 free_string_list(chanserv_conf.set_shows);
6890 free(set_shows_list.list);
6891 free(uset_shows_list.list);
6894 struct userData *helper = helperList;
6895 helperList = helperList->next;
6900 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6901 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6902 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6905 init_chanserv(const char *nick)
6907 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6908 conf_register_reload(chanserv_conf_read);
6910 reg_server_link_func(handle_server_link);
6912 reg_new_channel_func(handle_new_channel);
6913 reg_join_func(handle_join);
6914 reg_part_func(handle_part);
6915 reg_kick_func(handle_kick);
6916 reg_topic_func(handle_topic);
6917 reg_mode_change_func(handle_mode);
6918 reg_nick_change_func(handle_nick_change);
6920 reg_auth_func(handle_auth);
6921 reg_handle_rename_func(handle_rename);
6922 reg_unreg_func(handle_unreg);
6924 handle_dnrs = dict_new();
6925 dict_set_free_data(handle_dnrs, free);
6926 plain_dnrs = dict_new();
6927 dict_set_free_data(plain_dnrs, free);
6928 mask_dnrs = dict_new();
6929 dict_set_free_data(mask_dnrs, free);
6931 reg_svccmd_unbind_func(handle_svccmd_unbind);
6932 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6933 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6934 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6935 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6936 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6937 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6938 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6939 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6940 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6942 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6943 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6945 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6946 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6947 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6948 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6949 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6951 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
6952 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
6953 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
6954 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6955 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6957 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6958 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", NULL);
6959 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6960 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
6962 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
6963 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
6964 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6965 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
6966 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
6967 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6968 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6969 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
6971 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6972 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6973 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6974 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
6975 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
6976 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6977 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
6978 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
6979 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
6980 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "peon", NULL);
6981 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
6982 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
6983 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6984 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
6986 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
6987 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6988 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6989 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
6990 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "peon", NULL);
6992 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "peon", "flags", "+nolog", NULL);
6993 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
6995 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
6996 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6997 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6998 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
6999 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7000 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7001 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7002 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7003 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7004 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7006 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7007 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7009 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7010 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7011 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7012 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7014 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7015 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7016 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7017 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7018 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7020 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7021 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7022 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7023 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7024 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7025 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7027 /* Channel options */
7028 DEFINE_CHANNEL_OPTION(defaulttopic);
7029 DEFINE_CHANNEL_OPTION(topicmask);
7030 DEFINE_CHANNEL_OPTION(greeting);
7031 DEFINE_CHANNEL_OPTION(usergreeting);
7032 DEFINE_CHANNEL_OPTION(modes);
7033 DEFINE_CHANNEL_OPTION(enfops);
7034 DEFINE_CHANNEL_OPTION(giveops);
7035 DEFINE_CHANNEL_OPTION(protect);
7036 DEFINE_CHANNEL_OPTION(enfmodes);
7037 DEFINE_CHANNEL_OPTION(enftopic);
7038 DEFINE_CHANNEL_OPTION(pubcmd);
7039 DEFINE_CHANNEL_OPTION(voice);
7040 DEFINE_CHANNEL_OPTION(userinfo);
7041 DEFINE_CHANNEL_OPTION(dynlimit);
7042 DEFINE_CHANNEL_OPTION(topicsnarf);
7043 DEFINE_CHANNEL_OPTION(nodelete);
7044 DEFINE_CHANNEL_OPTION(toys);
7045 DEFINE_CHANNEL_OPTION(setters);
7046 DEFINE_CHANNEL_OPTION(topicrefresh);
7047 DEFINE_CHANNEL_OPTION(ctcpusers);
7048 DEFINE_CHANNEL_OPTION(ctcpreaction);
7049 DEFINE_CHANNEL_OPTION(peoninvite);
7050 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7052 /* Alias set topic to set defaulttopic for compatibility. */
7053 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7056 DEFINE_USER_OPTION(noautoop);
7057 DEFINE_USER_OPTION(autoinvite);
7058 DEFINE_USER_OPTION(info);
7060 /* Alias uset autovoice to uset autoop. */
7061 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7063 note_types = dict_new();
7064 dict_set_free_data(note_types, chanserv_deref_note_type);
7067 chanserv = AddService(nick, "Channel Services");
7068 service_register(chanserv, '!');
7069 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7071 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7073 if(chanserv_conf.channel_expire_frequency)
7074 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7076 if(chanserv_conf.refresh_period)
7078 time_t next_refresh;
7079 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7080 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7083 reg_exit_func(chanserv_db_cleanup);
7084 message_register_table(msgtab);