1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
46 #define KEY_SWITCH_TIMEOUT "switch_timeout"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "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." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "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." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
199 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
202 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
203 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
204 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
205 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
206 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
207 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
208 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
209 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
210 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
211 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
212 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
213 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
214 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
215 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
216 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
217 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
219 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
221 /* Channel management */
222 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
223 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
224 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
226 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
227 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
228 { "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" },
229 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
230 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
231 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
232 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
234 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
235 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
236 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
237 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
238 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
239 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
240 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
241 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
242 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
243 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
244 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
245 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
246 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
247 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
248 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
249 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
250 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
251 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
252 { "CSMSG_SET_MODES", "$bModes $b %s" },
253 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
254 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
255 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
256 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
257 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
258 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
259 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
260 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
261 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
262 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
263 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
264 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
265 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
266 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
267 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
268 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
269 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
270 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
271 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
272 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
273 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
274 { "CSMSG_USET_INFO", "$bInfo $b %s" },
276 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
277 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
278 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
279 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
280 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
281 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
282 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
283 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
284 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
286 { "CSMSG_PROTECT_NONE", "No users will be protected." },
287 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
288 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
289 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
290 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
291 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
292 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
293 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
294 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
295 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
300 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
301 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
302 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
303 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
304 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
305 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
306 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
307 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
309 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
310 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
311 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
313 /* Channel userlist */
314 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
315 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
316 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
317 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
319 /* Channel note list */
320 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
321 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
322 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
323 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
324 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
325 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
326 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
327 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
328 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
329 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
330 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
331 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
332 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
333 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
334 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
336 /* Channel [un]suspension */
337 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
338 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
339 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
340 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
341 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
342 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
343 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
345 /* Access information */
346 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
347 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
348 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
349 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
350 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
351 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
352 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
353 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
354 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
355 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
356 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
358 /* Seen information */
359 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
360 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
361 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
362 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
364 /* Names information */
365 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
366 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
368 /* Channel information */
369 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
370 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
371 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
372 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
373 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
374 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
375 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
376 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
377 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
378 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
379 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
380 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
388 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
389 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
391 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
392 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
393 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
394 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
395 { "CSMSG_PEEK_OPS", "$bOps:$b" },
396 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
398 /* Network information */
399 { "CSMSG_NETWORK_INFO", "Network Information:" },
400 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
401 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
402 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
403 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
404 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
405 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
406 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
407 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
410 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
411 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
412 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
414 /* Channel searches */
415 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
416 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
417 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
418 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
420 /* Channel configuration */
421 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
422 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
423 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
426 { "CSMSG_USER_OPTIONS", "User Options:" },
427 { "CSMSG_USER_PROTECTED", "That user is protected." },
430 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
431 { "CSMSG_PING_RESPONSE", "Pong!" },
432 { "CSMSG_WUT_RESPONSE", "wut" },
433 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
434 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
435 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
436 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
437 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
438 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
439 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
442 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
446 /* eject_user and unban_user flags */
447 #define ACTION_KICK 0x0001
448 #define ACTION_BAN 0x0002
449 #define ACTION_ADD_BAN 0x0004
450 #define ACTION_ADD_TIMED_BAN 0x0008
451 #define ACTION_UNBAN 0x0010
452 #define ACTION_DEL_BAN 0x0020
454 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
455 #define MODELEN 40 + KEYLEN
459 #define CSFUNC_ARGS user, channel, argc, argv, cmd
461 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
462 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
463 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
464 reply("MSG_MISSING_PARAMS", argv[0]); \
468 DECLARE_LIST(dnrList, struct do_not_register *);
469 DEFINE_LIST(dnrList, struct do_not_register *);
471 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
473 struct userNode *chanserv;
476 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
477 static struct log_type *CS_LOG;
481 struct channelList support_channels;
482 struct mod_chanmode default_modes;
484 unsigned long db_backup_frequency;
485 unsigned long channel_expire_frequency;
488 unsigned int adjust_delay;
489 long channel_expire_delay;
490 unsigned int nodelete_level;
492 unsigned int adjust_threshold;
493 int join_flood_threshold;
495 unsigned int greeting_length;
496 unsigned int refresh_period;
498 unsigned int max_owned;
499 unsigned int max_chan_users;
500 unsigned int max_chan_bans;
501 unsigned int max_userinfo_length;
503 unsigned int use_registered_mode;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->flags = CHANNEL_DEFAULT_FLAGS;
1084 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1085 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1086 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1087 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1089 channel->prev = NULL;
1090 channel->next = channelList;
1093 channelList->prev = channel;
1094 channelList = channel;
1095 registered_channels++;
1097 channel->channel = cNode;
1099 cNode->channel_info = channel;
1104 static struct userData*
1105 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1107 struct userData *ud;
1109 if(access > UL_OWNER)
1112 ud = calloc(1, sizeof(*ud));
1113 ud->channel = channel;
1114 ud->handle = handle;
1116 ud->access = access;
1117 ud->info = info ? strdup(info) : NULL;
1120 ud->next = channel->users;
1122 channel->users->prev = ud;
1123 channel->users = ud;
1125 channel->userCount++;
1129 ud->u_next = ud->handle->channels;
1131 ud->u_next->u_prev = ud;
1132 ud->handle->channels = ud;
1137 static void unregister_channel(struct chanData *channel, const char *reason);
1140 del_channel_user(struct userData *user, int do_gc)
1142 struct chanData *channel = user->channel;
1144 channel->userCount--;
1148 user->prev->next = user->next;
1150 channel->users = user->next;
1152 user->next->prev = user->prev;
1155 user->u_prev->u_next = user->u_next;
1157 user->handle->channels = user->u_next;
1159 user->u_next->u_prev = user->u_prev;
1163 if(do_gc && !channel->users && !IsProtected(channel))
1164 unregister_channel(channel, "lost all users.");
1167 static void expire_ban(void *data);
1169 static struct banData*
1170 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1173 unsigned int ii, l1, l2;
1178 bd = malloc(sizeof(struct banData));
1180 bd->channel = channel;
1182 bd->triggered = triggered;
1183 bd->expires = expires;
1185 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1187 extern const char *hidden_host_suffix;
1188 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1192 l2 = strlen(old_name);
1195 if(irccasecmp(mask + l1 - l2, old_name))
1197 new_mask = alloca(MAXLEN);
1198 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1201 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1203 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1204 bd->reason = reason ? strdup(reason) : NULL;
1207 timeq_add(expires, expire_ban, bd);
1210 bd->next = channel->bans;
1212 channel->bans->prev = bd;
1214 channel->banCount++;
1221 del_channel_ban(struct banData *ban)
1223 ban->channel->banCount--;
1227 ban->prev->next = ban->next;
1229 ban->channel->bans = ban->next;
1232 ban->next->prev = ban->prev;
1235 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1244 expire_ban(void *data)
1246 struct banData *bd = data;
1247 if(!IsSuspended(bd->channel))
1249 struct banList bans;
1250 struct mod_chanmode change;
1252 bans = bd->channel->channel->banlist;
1253 mod_chanmode_init(&change);
1254 for(ii=0; ii<bans.used; ii++)
1256 if(!strcmp(bans.list[ii]->ban, bd->mask))
1259 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1260 change.args[0].hostmask = bd->mask;
1261 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1267 del_channel_ban(bd);
1270 static void chanserv_expire_suspension(void *data);
1273 unregister_channel(struct chanData *channel, const char *reason)
1275 struct mod_chanmode change;
1276 char msgbuf[MAXLEN];
1278 /* After channel unregistration, the following must be cleaned
1280 - Channel information.
1283 - Channel suspension data.
1284 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1290 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1292 if (chanserv_conf.use_registered_mode) {
1293 mod_chanmode_init(&change);
1294 change.modes_clear |= MODE_REGISTERED;
1295 mod_chanmode_announce(chanserv, channel->channel, &change);
1298 while(channel->users)
1299 del_channel_user(channel->users, 0);
1301 while(channel->bans)
1302 del_channel_ban(channel->bans);
1304 free(channel->topic);
1305 free(channel->registrar);
1306 free(channel->greeting);
1307 free(channel->user_greeting);
1308 free(channel->topic_mask);
1311 channel->prev->next = channel->next;
1313 channelList = channel->next;
1316 channel->next->prev = channel->prev;
1318 if(channel->suspended)
1320 struct chanNode *cNode = channel->channel;
1321 struct suspended *suspended, *next_suspended;
1323 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1325 next_suspended = suspended->previous;
1326 free(suspended->suspender);
1327 free(suspended->reason);
1328 if(suspended->expires)
1329 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1334 cNode->channel_info = NULL;
1336 channel->channel->channel_info = NULL;
1338 dict_delete(channel->notes);
1339 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1340 if(!IsSuspended(channel))
1341 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1342 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1343 UnlockChannel(channel->channel);
1345 registered_channels--;
1349 expire_channels(UNUSED_ARG(void *data))
1351 struct chanData *channel, *next;
1352 struct userData *user;
1353 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1355 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1356 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1358 for(channel = channelList; channel; channel = next)
1360 next = channel->next;
1362 /* See if the channel can be expired. */
1363 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1364 || IsProtected(channel))
1367 /* Make sure there are no high-ranking users still in the channel. */
1368 for(user=channel->users; user; user=user->next)
1369 if(user->present && (user->access >= UL_PRESENT))
1374 /* Unregister the channel */
1375 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1376 unregister_channel(channel, "registration expired.");
1379 if(chanserv_conf.channel_expire_frequency)
1380 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1384 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1386 char protect = channel->chOpts[chProtect];
1387 struct userData *cs_victim, *cs_aggressor;
1389 /* Don't protect if no one is to be protected, someone is attacking
1390 himself, or if the aggressor is an IRC Operator. */
1391 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1394 /* Don't protect if the victim isn't authenticated (because they
1395 can't be a channel user), unless we are to protect non-users
1397 cs_victim = GetChannelAccess(channel, victim->handle_info);
1398 if(protect != 'a' && !cs_victim)
1401 /* Protect if the aggressor isn't a user because at this point,
1402 the aggressor can only be less than or equal to the victim. */
1403 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1407 /* If the aggressor was a user, then the victim can't be helped. */
1414 if(cs_victim->access > cs_aggressor->access)
1419 if(cs_victim->access >= cs_aggressor->access)
1428 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1430 struct chanData *cData = channel->channel_info;
1431 struct userData *cs_victim;
1433 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1434 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1435 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1437 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1445 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1447 if(IsService(victim))
1449 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1453 if(protect_user(victim, user, channel->channel_info))
1455 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1462 static struct do_not_register *
1463 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1465 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1466 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1467 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1468 strcpy(dnr->reason, reason);
1470 if(dnr->chan_name[0] == '*')
1471 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1472 else if(strpbrk(dnr->chan_name, "*?"))
1473 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1475 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1479 static struct dnrList
1480 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1482 struct dnrList list;
1484 struct do_not_register *dnr;
1486 dnrList_init(&list);
1487 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1488 dnrList_append(&list, dnr);
1489 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1490 dnrList_append(&list, dnr);
1492 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1493 if(match_ircglob(chan_name, iter_key(it)))
1494 dnrList_append(&list, iter_data(it));
1499 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1501 struct dnrList list;
1502 struct do_not_register *dnr;
1504 char buf[INTERVALLEN];
1506 list = chanserv_find_dnrs(chan_name, handle);
1507 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1509 dnr = list.list[ii];
1512 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1513 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1516 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1519 reply("CSMSG_MORE_DNRS", list.used - ii);
1524 struct do_not_register *
1525 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1527 struct do_not_register *dnr;
1530 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1534 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1536 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1537 if(match_ircglob(chan_name, iter_key(it)))
1538 return iter_data(it);
1543 static CHANSERV_FUNC(cmd_noregister)
1546 struct do_not_register *dnr;
1547 char buf[INTERVALLEN];
1548 unsigned int matches;
1554 reply("CSMSG_DNR_SEARCH_RESULTS");
1556 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1558 dnr = iter_data(it);
1560 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1562 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1565 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1567 dnr = iter_data(it);
1569 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1571 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1574 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1576 dnr = iter_data(it);
1578 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1580 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1585 reply("MSG_MATCH_COUNT", matches);
1587 reply("MSG_NO_MATCHES");
1593 if(!IsChannelName(target) && (*target != '*'))
1595 reply("CSMSG_NOT_DNR", target);
1601 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1602 if((*target == '*') && !get_handle_info(target + 1))
1604 reply("MSG_HANDLE_UNKNOWN", target + 1);
1607 chanserv_add_dnr(target, user->handle_info->handle, reason);
1608 reply("CSMSG_NOREGISTER_CHANNEL", target);
1612 reply("CSMSG_DNR_SEARCH_RESULTS");
1614 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1616 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1618 reply("MSG_NO_MATCHES");
1622 static CHANSERV_FUNC(cmd_allowregister)
1624 const char *chan_name = argv[1];
1626 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1628 dict_remove(handle_dnrs, chan_name+1);
1629 reply("CSMSG_DNR_REMOVED", chan_name);
1631 else if(dict_find(plain_dnrs, chan_name, NULL))
1633 dict_remove(plain_dnrs, chan_name);
1634 reply("CSMSG_DNR_REMOVED", chan_name);
1636 else if(dict_find(mask_dnrs, chan_name, NULL))
1638 dict_remove(mask_dnrs, chan_name);
1639 reply("CSMSG_DNR_REMOVED", chan_name);
1643 reply("CSMSG_NO_SUCH_DNR", chan_name);
1650 chanserv_get_owned_count(struct handle_info *hi)
1652 struct userData *cList;
1655 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1656 if(cList->access == UL_OWNER)
1661 static CHANSERV_FUNC(cmd_register)
1663 struct mod_chanmode *change;
1664 struct handle_info *handle;
1665 struct chanData *cData;
1666 struct modeNode *mn;
1667 char reason[MAXLEN];
1669 unsigned int new_channel, force=0;
1670 struct do_not_register *dnr;
1674 if(channel->channel_info)
1676 reply("CSMSG_ALREADY_REGGED", channel->name);
1680 if(channel->bad_channel)
1682 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1686 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1688 reply("CSMSG_MUST_BE_OPPED", channel->name);
1693 chan_name = channel->name;
1697 if((argc < 2) || !IsChannelName(argv[1]))
1699 reply("MSG_NOT_CHANNEL_NAME");
1703 if(opserv_bad_channel(argv[1]))
1705 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1710 chan_name = argv[1];
1713 if(argc >= (new_channel+2))
1715 if(!IsHelping(user))
1717 reply("CSMSG_PROXY_FORBIDDEN");
1721 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1723 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1724 dnr = chanserv_is_dnr(chan_name, handle);
1728 handle = user->handle_info;
1729 dnr = chanserv_is_dnr(chan_name, handle);
1733 if(!IsHelping(user))
1734 reply("CSMSG_DNR_CHANNEL", chan_name);
1736 chanserv_show_dnrs(user, cmd, chan_name, handle);
1740 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1742 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1747 channel = AddChannel(argv[1], now, NULL, NULL);
1749 cData = register_channel(channel, user->handle_info->handle);
1750 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1751 cData->modes = chanserv_conf.default_modes;
1752 if (chanserv_conf.use_registered_mode)
1753 cData->modes.modes_set |= MODE_REGISTERED;
1754 change = mod_chanmode_dup(&cData->modes, 1);
1755 change->args[change->argc].mode = MODE_CHANOP;
1756 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1758 mod_chanmode_announce(chanserv, channel, change);
1759 mod_chanmode_free(change);
1761 /* Initialize the channel's max user record. */
1762 cData->max = channel->members.used;
1764 if(handle != user->handle_info)
1765 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1767 reply("CSMSG_REG_SUCCESS", channel->name);
1769 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1770 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1775 make_confirmation_string(struct userData *uData)
1777 static char strbuf[16];
1782 for(src = uData->handle->handle; *src; )
1783 accum = accum * 31 + toupper(*src++);
1785 for(src = uData->channel->channel->name; *src; )
1786 accum = accum * 31 + toupper(*src++);
1787 sprintf(strbuf, "%08x", accum);
1791 static CHANSERV_FUNC(cmd_unregister)
1794 char reason[MAXLEN];
1795 struct chanData *cData;
1796 struct userData *uData;
1798 cData = channel->channel_info;
1801 reply("CSMSG_NOT_REGISTERED", channel->name);
1805 uData = GetChannelUser(cData, user->handle_info);
1806 if(!uData || (uData->access < UL_OWNER))
1808 reply("CSMSG_NO_ACCESS");
1812 if(IsProtected(cData))
1814 reply("CSMSG_UNREG_NODELETE", channel->name);
1818 if(!IsHelping(user))
1820 const char *confirm_string;
1821 if(IsSuspended(cData))
1823 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1826 confirm_string = make_confirmation_string(uData);
1827 if((argc < 2) || strcmp(argv[1], confirm_string))
1829 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1834 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1835 name = strdup(channel->name);
1836 unregister_channel(cData, reason);
1837 reply("CSMSG_UNREG_SUCCESS", name);
1842 static CHANSERV_FUNC(cmd_move)
1844 struct chanNode *target;
1845 struct modeNode *mn;
1846 struct userData *uData;
1847 char reason[MAXLEN];
1848 struct do_not_register *dnr;
1852 if(IsProtected(channel->channel_info))
1854 reply("CSMSG_MOVE_NODELETE", channel->name);
1858 if(!IsChannelName(argv[1]))
1860 reply("MSG_NOT_CHANNEL_NAME");
1864 if(opserv_bad_channel(argv[1]))
1866 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1870 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1872 for(uData = channel->channel_info->users; uData; uData = uData->next)
1874 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1876 if(!IsHelping(user))
1877 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1879 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1885 if(!(target = GetChannel(argv[1])))
1887 target = AddChannel(argv[1], now, NULL, NULL);
1888 if(!IsSuspended(channel->channel_info))
1889 AddChannelUser(chanserv, target);
1891 else if(target->channel_info)
1893 reply("CSMSG_ALREADY_REGGED", target->name);
1896 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1897 && !IsHelping(user))
1899 reply("CSMSG_MUST_BE_OPPED", target->name);
1902 else if(!IsSuspended(channel->channel_info))
1904 struct mod_chanmode change;
1905 mod_chanmode_init(&change);
1907 change.args[0].mode = MODE_CHANOP;
1908 change.args[0].member = AddChannelUser(chanserv, target);
1909 mod_chanmode_announce(chanserv, target, &change);
1912 /* Move the channel_info to the target channel; it
1913 shouldn't be necessary to clear timeq callbacks
1914 for the old channel. */
1915 target->channel_info = channel->channel_info;
1916 target->channel_info->channel = target;
1917 channel->channel_info = NULL;
1919 reply("CSMSG_MOVE_SUCCESS", target->name);
1921 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1922 if(!IsSuspended(target->channel_info))
1924 char reason2[MAXLEN];
1925 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1926 DelChannelUser(chanserv, channel, reason2, 0);
1928 UnlockChannel(channel);
1929 LockChannel(target);
1930 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1935 merge_users(struct chanData *source, struct chanData *target)
1937 struct userData *suData, *tuData, *next;
1943 /* Insert the source's users into the scratch area. */
1944 for(suData = source->users; suData; suData = suData->next)
1945 dict_insert(merge, suData->handle->handle, suData);
1947 /* Iterate through the target's users, looking for
1948 users common to both channels. The lower access is
1949 removed from either the scratch area or target user
1951 for(tuData = target->users; tuData; tuData = next)
1953 struct userData *choice;
1955 next = tuData->next;
1957 /* If a source user exists with the same handle as a target
1958 channel's user, resolve the conflict by removing one. */
1959 suData = dict_find(merge, tuData->handle->handle, NULL);
1963 /* Pick the data we want to keep. */
1964 /* If the access is the same, use the later seen time. */
1965 if(suData->access == tuData->access)
1966 choice = (suData->seen > tuData->seen) ? suData : tuData;
1967 else /* Otherwise, keep the higher access level. */
1968 choice = (suData->access > tuData->access) ? suData : tuData;
1970 /* Remove the user that wasn't picked. */
1971 if(choice == tuData)
1973 dict_remove(merge, suData->handle->handle);
1974 del_channel_user(suData, 0);
1977 del_channel_user(tuData, 0);
1980 /* Move the remaining users to the target channel. */
1981 for(it = dict_first(merge); it; it = iter_next(it))
1983 suData = iter_data(it);
1985 /* Insert the user into the target channel's linked list. */
1986 suData->prev = NULL;
1987 suData->next = target->users;
1988 suData->channel = target;
1991 target->users->prev = suData;
1992 target->users = suData;
1994 /* Update the user counts for the target channel; the
1995 source counts are left alone. */
1996 target->userCount++;
1999 /* Possible to assert (source->users == NULL) here. */
2000 source->users = NULL;
2005 merge_bans(struct chanData *source, struct chanData *target)
2007 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2009 /* Hold on to the original head of the target ban list
2010 to avoid comparing source bans with source bans. */
2011 tFront = target->bans;
2013 /* Perform a totally expensive O(n*m) merge, ick. */
2014 for(sbData = source->bans; sbData; sbData = sNext)
2016 /* Flag to track whether the ban's been moved
2017 to the destination yet. */
2020 /* Possible to assert (sbData->prev == NULL) here. */
2021 sNext = sbData->next;
2023 for(tbData = tFront; tbData; tbData = tNext)
2025 tNext = tbData->next;
2027 /* Perform two comparisons between each source
2028 and target ban, conflicts are resolved by
2029 keeping the broader ban and copying the later
2030 expiration and triggered time. */
2031 if(match_ircglobs(tbData->mask, sbData->mask))
2033 /* There is a broader ban in the target channel that
2034 overrides one in the source channel; remove the
2035 source ban and break. */
2036 if(sbData->expires > tbData->expires)
2037 tbData->expires = sbData->expires;
2038 if(sbData->triggered > tbData->triggered)
2039 tbData->triggered = sbData->triggered;
2040 del_channel_ban(sbData);
2043 else if(match_ircglobs(sbData->mask, tbData->mask))
2045 /* There is a broader ban in the source channel that
2046 overrides one in the target channel; remove the
2047 target ban, fall through and move the source over. */
2048 if(tbData->expires > sbData->expires)
2049 sbData->expires = tbData->expires;
2050 if(tbData->triggered > sbData->triggered)
2051 sbData->triggered = tbData->triggered;
2052 if(tbData == tFront)
2054 del_channel_ban(tbData);
2057 /* Source bans can override multiple target bans, so
2058 we allow a source to run through this loop multiple
2059 times, but we can only move it once. */
2064 /* Remove the source ban from the source ban list. */
2066 sbData->next->prev = sbData->prev;
2068 /* Modify the source ban's associated channel. */
2069 sbData->channel = target;
2071 /* Insert the ban into the target channel's linked list. */
2072 sbData->prev = NULL;
2073 sbData->next = target->bans;
2076 target->bans->prev = sbData;
2077 target->bans = sbData;
2079 /* Update the user counts for the target channel. */
2084 /* Possible to assert (source->bans == NULL) here. */
2085 source->bans = NULL;
2089 merge_data(struct chanData *source, struct chanData *target)
2091 if(source->visited > target->visited)
2092 target->visited = source->visited;
2096 merge_channel(struct chanData *source, struct chanData *target)
2098 merge_users(source, target);
2099 merge_bans(source, target);
2100 merge_data(source, target);
2103 static CHANSERV_FUNC(cmd_merge)
2105 struct userData *target_user;
2106 struct chanNode *target;
2107 char reason[MAXLEN];
2111 /* Make sure the target channel exists and is registered to the user
2112 performing the command. */
2113 if(!(target = GetChannel(argv[1])))
2115 reply("MSG_INVALID_CHANNEL");
2119 if(!target->channel_info)
2121 reply("CSMSG_NOT_REGISTERED", target->name);
2125 if(IsProtected(channel->channel_info))
2127 reply("CSMSG_MERGE_NODELETE");
2131 if(IsSuspended(target->channel_info))
2133 reply("CSMSG_MERGE_SUSPENDED");
2137 if(channel == target)
2139 reply("CSMSG_MERGE_SELF");
2143 target_user = GetChannelUser(target->channel_info, user->handle_info);
2144 if(!target_user || (target_user->access < UL_OWNER))
2146 reply("CSMSG_MERGE_NOT_OWNER");
2150 /* Merge the channel structures and associated data. */
2151 merge_channel(channel->channel_info, target->channel_info);
2152 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2153 unregister_channel(channel->channel_info, reason);
2154 reply("CSMSG_MERGE_SUCCESS", target->name);
2158 static CHANSERV_FUNC(cmd_opchan)
2160 struct mod_chanmode change;
2161 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2163 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2166 channel->channel_info->may_opchan = 0;
2167 mod_chanmode_init(&change);
2169 change.args[0].mode = MODE_CHANOP;
2170 change.args[0].member = GetUserMode(channel, chanserv);
2171 mod_chanmode_announce(chanserv, channel, &change);
2172 reply("CSMSG_OPCHAN_DONE", channel->name);
2176 static CHANSERV_FUNC(cmd_adduser)
2178 struct userData *actee;
2179 struct userData *actor;
2180 struct handle_info *handle;
2181 unsigned short access;
2185 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2187 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2191 access = user_level_from_name(argv[2], UL_OWNER);
2194 reply("CSMSG_INVALID_ACCESS", argv[2]);
2198 actor = GetChannelUser(channel->channel_info, user->handle_info);
2199 if(actor->access <= access)
2201 reply("CSMSG_NO_BUMP_ACCESS");
2205 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2208 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2210 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2214 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2215 scan_user_presence(actee, NULL);
2216 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2220 static CHANSERV_FUNC(cmd_clvl)
2222 struct handle_info *handle;
2223 struct userData *victim;
2224 struct userData *actor;
2225 unsigned short new_access;
2226 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2230 actor = GetChannelUser(channel->channel_info, user->handle_info);
2232 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2235 if(handle == user->handle_info && !privileged)
2237 reply("CSMSG_NO_SELF_CLVL");
2241 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2243 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2247 if(actor->access <= victim->access && !privileged)
2249 reply("MSG_USER_OUTRANKED", handle->handle);
2253 new_access = user_level_from_name(argv[2], UL_OWNER);
2257 reply("CSMSG_INVALID_ACCESS", argv[2]);
2261 if(new_access >= actor->access && !privileged)
2263 reply("CSMSG_NO_BUMP_ACCESS");
2267 victim->access = new_access;
2268 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2272 static CHANSERV_FUNC(cmd_deluser)
2274 struct handle_info *handle;
2275 struct userData *victim;
2276 struct userData *actor;
2277 unsigned short access;
2282 actor = GetChannelUser(channel->channel_info, user->handle_info);
2284 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2287 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2289 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2295 access = user_level_from_name(argv[1], UL_OWNER);
2298 reply("CSMSG_INVALID_ACCESS", argv[1]);
2301 if(access != victim->access)
2303 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2309 access = victim->access;
2312 if((actor->access <= victim->access) && !IsHelping(user))
2314 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2318 chan_name = strdup(channel->name);
2319 del_channel_user(victim, 1);
2320 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2326 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2328 struct userData *actor, *uData, *next;
2330 actor = GetChannelUser(channel->channel_info, user->handle_info);
2332 if(min_access > max_access)
2334 reply("CSMSG_BAD_RANGE", min_access, max_access);
2338 if((actor->access <= max_access) && !IsHelping(user))
2340 reply("CSMSG_NO_ACCESS");
2344 for(uData = channel->channel_info->users; uData; uData = next)
2348 if((uData->access >= min_access)
2349 && (uData->access <= max_access)
2350 && match_ircglob(uData->handle->handle, mask))
2351 del_channel_user(uData, 1);
2354 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2358 static CHANSERV_FUNC(cmd_mdelowner)
2360 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2363 static CHANSERV_FUNC(cmd_mdelcoowner)
2365 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2368 static CHANSERV_FUNC(cmd_mdelmaster)
2370 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2373 static CHANSERV_FUNC(cmd_mdelop)
2375 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2378 static CHANSERV_FUNC(cmd_mdelpeon)
2380 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2384 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2386 struct banData *bData, *next;
2387 char interval[INTERVALLEN];
2392 limit = now - duration;
2393 for(bData = channel->channel_info->bans; bData; bData = next)
2397 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2400 del_channel_ban(bData);
2404 intervalString(interval, duration, user->handle_info);
2405 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2410 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2412 struct userData *actor, *uData, *next;
2413 char interval[INTERVALLEN];
2417 actor = GetChannelUser(channel->channel_info, user->handle_info);
2418 if(min_access > max_access)
2420 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2424 if((actor->access <= max_access) && !IsHelping(user))
2426 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2431 limit = now - duration;
2432 for(uData = channel->channel_info->users; uData; uData = next)
2436 if((uData->seen > limit) || uData->present)
2439 if(((uData->access >= min_access) && (uData->access <= max_access))
2440 || (!max_access && (uData->access < actor->access)))
2442 del_channel_user(uData, 1);
2450 max_access = UL_OWNER;
2452 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2456 static CHANSERV_FUNC(cmd_trim)
2458 unsigned long duration;
2459 unsigned short min_level, max_level;
2463 duration = ParseInterval(argv[2]);
2466 reply("CSMSG_CANNOT_TRIM");
2470 if(!irccasecmp(argv[1], "bans"))
2472 cmd_trim_bans(user, channel, duration);
2475 else if(!irccasecmp(argv[1], "users"))
2477 cmd_trim_users(user, channel, 0, 0, duration);
2480 else if(parse_level_range(&min_level, &max_level, argv[1]))
2482 cmd_trim_users(user, channel, min_level, max_level, duration);
2485 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2487 cmd_trim_users(user, channel, min_level, min_level, duration);
2492 reply("CSMSG_INVALID_TRIM", argv[1]);
2497 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2498 to the user. cmd_all takes advantage of this. */
2499 static CHANSERV_FUNC(cmd_up)
2501 struct mod_chanmode change;
2502 struct userData *uData;
2505 mod_chanmode_init(&change);
2507 change.args[0].member = GetUserMode(channel, user);
2508 if(!change.args[0].member)
2511 reply("MSG_CHANNEL_ABSENT", channel->name);
2515 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2519 reply("CSMSG_GODMODE_UP", argv[0]);
2522 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2524 change.args[0].mode = MODE_CHANOP;
2525 errmsg = "CSMSG_ALREADY_OPPED";
2527 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2529 change.args[0].mode = MODE_VOICE;
2530 errmsg = "CSMSG_ALREADY_VOICED";
2535 reply("CSMSG_NO_ACCESS");
2538 change.args[0].mode &= ~change.args[0].member->modes;
2539 if(!change.args[0].mode)
2542 reply(errmsg, channel->name);
2545 modcmd_chanmode_announce(&change);
2549 static CHANSERV_FUNC(cmd_down)
2551 struct mod_chanmode change;
2553 mod_chanmode_init(&change);
2555 change.args[0].member = GetUserMode(channel, user);
2556 if(!change.args[0].member)
2559 reply("MSG_CHANNEL_ABSENT", channel->name);
2563 if(!change.args[0].member->modes)
2566 reply("CSMSG_ALREADY_DOWN", channel->name);
2570 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2571 modcmd_chanmode_announce(&change);
2575 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)
2577 struct userData *cList;
2579 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2581 if(IsSuspended(cList->channel)
2582 || IsUserSuspended(cList)
2583 || !GetUserMode(cList->channel->channel, user))
2586 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2592 static CHANSERV_FUNC(cmd_upall)
2594 return cmd_all(CSFUNC_ARGS, cmd_up);
2597 static CHANSERV_FUNC(cmd_downall)
2599 return cmd_all(CSFUNC_ARGS, cmd_down);
2602 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2603 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2606 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)
2608 unsigned int ii, valid;
2609 struct userNode *victim;
2610 struct mod_chanmode *change;
2612 change = mod_chanmode_alloc(argc - 1);
2614 for(ii=valid=0; ++ii < argc; )
2616 if(!(victim = GetUserH(argv[ii])))
2618 change->args[valid].mode = mode;
2619 change->args[valid].member = GetUserMode(channel, victim);
2620 if(!change->args[valid].member)
2622 if(validate && !validate(user, channel, victim))
2627 change->argc = valid;
2628 if(valid < (argc-1))
2629 reply("CSMSG_PROCESS_FAILED");
2632 modcmd_chanmode_announce(change);
2633 reply(action, channel->name);
2635 mod_chanmode_free(change);
2639 static CHANSERV_FUNC(cmd_op)
2641 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2644 static CHANSERV_FUNC(cmd_deop)
2646 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2649 static CHANSERV_FUNC(cmd_voice)
2651 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2654 static CHANSERV_FUNC(cmd_devoice)
2656 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2660 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2666 for(ii=0; ii<channel->members.used; ii++)
2668 struct modeNode *mn = channel->members.list[ii];
2670 if(IsService(mn->user))
2673 if(!user_matches_glob(mn->user, ban, 1))
2676 if(protect_user(mn->user, user, channel->channel_info))
2680 victims[(*victimCount)++] = mn;
2686 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2688 struct userNode *victim;
2689 struct modeNode **victims;
2690 unsigned int offset, n, victimCount, duration = 0;
2691 char *reason = "Bye.", *ban, *name;
2692 char interval[INTERVALLEN];
2694 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2695 REQUIRE_PARAMS(offset);
2698 reason = unsplit_string(argv + offset, argc - offset, NULL);
2699 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2701 /* Truncate the reason to a length of TOPICLEN, as
2702 the ircd does; however, leave room for an ellipsis
2703 and the kicker's nick. */
2704 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2708 if((victim = GetUserH(argv[1])))
2710 victims = alloca(sizeof(victims[0]));
2711 victims[0] = GetUserMode(channel, victim);
2712 /* XXX: The comparison with ACTION_KICK is just because all
2713 * other actions can work on users outside the channel, and we
2714 * want to allow those (e.g. unbans) in that case. If we add
2715 * some other ejection action for in-channel users, change
2717 victimCount = victims[0] ? 1 : 0;
2719 if(IsService(victim))
2721 reply("MSG_SERVICE_IMMUNE", victim->nick);
2725 if((action == ACTION_KICK) && !victimCount)
2727 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2731 if(protect_user(victim, user, channel->channel_info))
2733 reply("CSMSG_USER_PROTECTED", victim->nick);
2737 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2738 name = victim->nick;
2742 if(!is_ircmask(argv[1]))
2744 reply("MSG_NICK_UNKNOWN", argv[1]);
2748 victims = alloca(sizeof(victims[0]) * channel->members.used);
2750 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2752 reply("CSMSG_MASK_PROTECTED", argv[1]);
2756 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2758 reply("CSMSG_LAME_MASK", argv[1]);
2762 if((action == ACTION_KICK) && (victimCount == 0))
2764 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2768 name = ban = strdup(argv[1]);
2771 /* Truncate the ban in place if necessary; we must ensure
2772 that 'ban' is a valid ban mask before sanitizing it. */
2773 sanitize_ircmask(ban);
2775 if(action & ACTION_ADD_BAN)
2777 struct banData *bData, *next;
2779 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2781 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2786 if(action & ACTION_ADD_TIMED_BAN)
2788 duration = ParseInterval(argv[2]);
2792 reply("CSMSG_DURATION_TOO_LOW");
2796 else if(duration > (86400 * 365 * 2))
2798 reply("CSMSG_DURATION_TOO_HIGH");
2804 for(bData = channel->channel_info->bans; bData; bData = next)
2806 if(match_ircglobs(bData->mask, ban))
2808 int exact = !irccasecmp(bData->mask, ban);
2810 /* The ban is redundant; there is already a ban
2811 with the same effect in place. */
2815 free(bData->reason);
2816 bData->reason = strdup(reason);
2817 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2819 reply("CSMSG_REASON_CHANGE", ban);
2823 if(exact && bData->expires)
2827 /* If the ban matches an existing one exactly,
2828 extend the expiration time if the provided
2829 duration is longer. */
2830 if(duration && ((time_t)(now + duration) > bData->expires))
2832 bData->expires = now + duration;
2843 /* Delete the expiration timeq entry and
2844 requeue if necessary. */
2845 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2848 timeq_add(bData->expires, expire_ban, bData);
2852 /* automated kickban */
2855 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2857 reply("CSMSG_BAN_ADDED", name, channel->name);
2863 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2870 if(match_ircglobs(ban, bData->mask))
2872 /* The ban we are adding makes previously existing
2873 bans redundant; silently remove them. */
2874 del_channel_ban(bData);
2878 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);
2880 name = ban = strdup(bData->mask);
2884 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2886 extern const char *hidden_host_suffix;
2887 const char *old_name = chanserv_conf.old_ban_names->list[n];
2889 unsigned int l1, l2;
2892 l2 = strlen(old_name);
2895 if(irccasecmp(ban + l1 - l2, old_name))
2897 new_mask = malloc(MAXLEN);
2898 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2900 name = ban = new_mask;
2905 if(action & ACTION_BAN)
2907 unsigned int exists;
2908 struct mod_chanmode *change;
2910 if(channel->banlist.used >= MAXBANS)
2913 reply("CSMSG_BANLIST_FULL", channel->name);
2918 exists = ChannelBanExists(channel, ban);
2919 change = mod_chanmode_alloc(victimCount + 1);
2920 for(n = 0; n < victimCount; ++n)
2922 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2923 change->args[n].member = victims[n];
2927 change->args[n].mode = MODE_BAN;
2928 change->args[n++].hostmask = ban;
2932 modcmd_chanmode_announce(change);
2934 mod_chanmode_announce(chanserv, channel, change);
2935 mod_chanmode_free(change);
2937 if(exists && (action == ACTION_BAN))
2940 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2946 if(action & ACTION_KICK)
2948 char kick_reason[MAXLEN];
2949 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2951 for(n = 0; n < victimCount; n++)
2952 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2957 /* No response, since it was automated. */
2959 else if(action & ACTION_ADD_BAN)
2962 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2964 reply("CSMSG_BAN_ADDED", name, channel->name);
2966 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2967 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2968 else if(action & ACTION_BAN)
2969 reply("CSMSG_BAN_DONE", name, channel->name);
2970 else if(action & ACTION_KICK && victimCount)
2971 reply("CSMSG_KICK_DONE", name, channel->name);
2977 static CHANSERV_FUNC(cmd_kickban)
2979 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2982 static CHANSERV_FUNC(cmd_kick)
2984 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2987 static CHANSERV_FUNC(cmd_ban)
2989 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2992 static CHANSERV_FUNC(cmd_addban)
2994 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2997 static CHANSERV_FUNC(cmd_addtimedban)
2999 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3002 static struct mod_chanmode *
3003 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3005 struct mod_chanmode *change;
3006 unsigned char *match;
3007 unsigned int ii, count;
3009 match = alloca(bans->used);
3012 for(ii = count = 0; ii < bans->used; ++ii)
3014 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3021 for(ii = count = 0; ii < bans->used; ++ii)
3023 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3030 change = mod_chanmode_alloc(count);
3031 for(ii = count = 0; ii < bans->used; ++ii)
3035 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3036 change->args[count++].hostmask = bans->list[ii]->ban;
3042 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3044 struct userNode *actee;
3050 /* may want to allow a comma delimited list of users... */
3051 if(!(actee = GetUserH(argv[1])))
3053 if(!is_ircmask(argv[1]))
3055 reply("MSG_NICK_UNKNOWN", argv[1]);
3059 mask = strdup(argv[1]);
3062 /* We don't sanitize the mask here because ircu
3064 if(action & ACTION_UNBAN)
3066 struct mod_chanmode *change;
3067 change = find_matching_bans(&channel->banlist, actee, mask);
3070 modcmd_chanmode_announce(change);
3071 mod_chanmode_free(change);
3076 if(action & ACTION_DEL_BAN)
3078 struct banData *ban, *next;
3080 ban = channel->channel_info->bans;
3084 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3087 for( ; ban && !match_ircglobs(mask, ban->mask);
3092 del_channel_ban(ban);
3099 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3101 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3107 static CHANSERV_FUNC(cmd_unban)
3109 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3112 static CHANSERV_FUNC(cmd_delban)
3114 /* it doesn't necessarily have to remove the channel ban - may want
3115 to make that an option. */
3116 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3119 static CHANSERV_FUNC(cmd_unbanme)
3121 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3122 long flags = ACTION_UNBAN;
3124 /* remove permanent bans if the user has the proper access. */
3125 if(uData->access >= UL_MASTER)
3126 flags |= ACTION_DEL_BAN;
3128 argv[1] = user->nick;
3129 return unban_user(user, channel, 2, argv, cmd, flags);
3132 static CHANSERV_FUNC(cmd_unbanall)
3134 struct mod_chanmode *change;
3137 if(!channel->banlist.used)
3139 reply("CSMSG_NO_BANS", channel->name);
3143 change = mod_chanmode_alloc(channel->banlist.used);
3144 for(ii=0; ii<channel->banlist.used; ii++)
3146 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3147 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3149 modcmd_chanmode_announce(change);
3150 mod_chanmode_free(change);
3151 reply("CSMSG_BANS_REMOVED", channel->name);
3155 static CHANSERV_FUNC(cmd_open)
3157 struct mod_chanmode *change;
3159 change = find_matching_bans(&channel->banlist, user, NULL);
3161 change = mod_chanmode_alloc(0);
3162 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3163 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3164 && channel->channel_info->modes.modes_set)
3165 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3166 modcmd_chanmode_announce(change);
3167 reply("CSMSG_CHANNEL_OPENED", channel->name);
3168 mod_chanmode_free(change);
3172 static CHANSERV_FUNC(cmd_myaccess)
3174 static struct string_buffer sbuf;
3175 struct handle_info *target_handle;
3176 struct userData *uData;
3179 target_handle = user->handle_info;
3180 else if(!IsHelping(user))
3182 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3185 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3188 if(!target_handle->channels)
3190 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3194 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3195 for(uData = target_handle->channels; uData; uData = uData->u_next)
3197 struct chanData *cData = uData->channel;
3199 if(uData->access > UL_OWNER)
3201 if(IsProtected(cData)
3202 && (target_handle != user->handle_info)
3203 && !GetTrueChannelAccess(cData, user->handle_info))
3206 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3207 if(uData->flags != USER_AUTO_OP)
3208 string_buffer_append(&sbuf, ',');
3209 if(IsUserSuspended(uData))
3210 string_buffer_append(&sbuf, 's');
3211 if(IsUserAutoOp(uData))
3213 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3214 string_buffer_append(&sbuf, 'o');
3215 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3216 string_buffer_append(&sbuf, 'v');
3218 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3219 string_buffer_append(&sbuf, 'i');
3221 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3223 string_buffer_append_string(&sbuf, ")]");
3224 string_buffer_append(&sbuf, '\0');
3225 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3231 static CHANSERV_FUNC(cmd_access)
3233 struct userNode *target;
3234 struct handle_info *target_handle;
3235 struct userData *uData;
3237 char prefix[MAXLEN];
3242 target_handle = target->handle_info;
3244 else if((target = GetUserH(argv[1])))
3246 target_handle = target->handle_info;
3248 else if(argv[1][0] == '*')
3250 if(!(target_handle = get_handle_info(argv[1]+1)))
3252 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3258 reply("MSG_NICK_UNKNOWN", argv[1]);
3262 assert(target || target_handle);
3264 if(target == chanserv)
3266 reply("CSMSG_IS_CHANSERV");
3274 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3279 reply("MSG_USER_AUTHENTICATE", target->nick);
3282 reply("MSG_AUTHENTICATE");
3288 const char *epithet = NULL, *type = NULL;
3291 epithet = chanserv_conf.irc_operator_epithet;
3294 else if(IsNetworkHelper(target))
3296 epithet = chanserv_conf.network_helper_epithet;
3297 type = "network helper";
3299 else if(IsSupportHelper(target))
3301 epithet = chanserv_conf.support_helper_epithet;
3302 type = "support helper";
3306 if(target_handle->epithet)
3307 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3309 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3311 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3315 sprintf(prefix, "%s", target_handle->handle);
3318 if(!channel->channel_info)
3320 reply("CSMSG_NOT_REGISTERED", channel->name);
3324 helping = HANDLE_FLAGGED(target_handle, HELPING)
3325 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3326 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3328 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3329 /* To prevent possible information leaks, only show infolines
3330 * if the requestor is in the channel or it's their own
3332 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3334 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3336 /* Likewise, only say it's suspended if the user has active
3337 * access in that channel or it's their own entry. */
3338 if(IsUserSuspended(uData)
3339 && (GetChannelUser(channel->channel_info, user->handle_info)
3340 || (user->handle_info == uData->handle)))
3342 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3347 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3354 zoot_list(struct listData *list)
3356 struct userData *uData;
3357 unsigned int start, curr, highest, lowest;
3358 struct helpfile_table tmp_table;
3359 const char **temp, *msg;
3361 if(list->table.length == 1)
3364 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3366 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3367 msg = user_find_message(list->user, "MSG_NONE");
3368 send_message_type(4, list->user, list->bot, " %s", msg);
3370 tmp_table.width = list->table.width;
3371 tmp_table.flags = list->table.flags;
3372 list->table.contents[0][0] = " ";
3373 highest = list->highest;
3374 if(list->lowest != 0)
3375 lowest = list->lowest;
3376 else if(highest < 100)
3379 lowest = highest - 100;
3380 for(start = curr = 1; curr < list->table.length; )
3382 uData = list->users[curr-1];
3383 list->table.contents[curr++][0] = " ";
3384 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3387 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3389 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3390 temp = list->table.contents[--start];
3391 list->table.contents[start] = list->table.contents[0];
3392 tmp_table.contents = list->table.contents + start;
3393 tmp_table.length = curr - start;
3394 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3395 list->table.contents[start] = temp;
3397 highest = lowest - 1;
3398 lowest = (highest < 100) ? 0 : (highest - 99);
3404 def_list(struct listData *list)
3408 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3410 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3411 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3412 if(list->table.length == 1)
3414 msg = user_find_message(list->user, "MSG_NONE");
3415 send_message_type(4, list->user, list->bot, " %s", msg);
3420 userData_access_comp(const void *arg_a, const void *arg_b)
3422 const struct userData *a = *(struct userData**)arg_a;
3423 const struct userData *b = *(struct userData**)arg_b;
3425 if(a->access != b->access)
3426 res = b->access - a->access;
3428 res = irccasecmp(a->handle->handle, b->handle->handle);
3433 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3435 void (*send_list)(struct listData *);
3436 struct userData *uData;
3437 struct listData lData;
3438 unsigned int matches;
3442 lData.bot = cmd->parent->bot;
3443 lData.channel = channel;
3444 lData.lowest = lowest;
3445 lData.highest = highest;
3446 lData.search = (argc > 1) ? argv[1] : NULL;
3447 send_list = def_list;
3448 (void)zoot_list; /* since it doesn't show user levels */
3450 if(user->handle_info)
3452 switch(user->handle_info->userlist_style)
3454 case HI_STYLE_DEF: send_list = def_list; break;
3455 case HI_STYLE_ZOOT: send_list = def_list; break;
3459 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3461 for(uData = channel->channel_info->users; uData; uData = uData->next)
3463 if((uData->access < lowest)
3464 || (uData->access > highest)
3465 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3467 lData.users[matches++] = uData;
3469 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3471 lData.table.length = matches+1;
3472 lData.table.width = 4;
3473 lData.table.flags = TABLE_NO_FREE;
3474 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3475 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3476 lData.table.contents[0] = ary;
3479 ary[2] = "Last Seen";
3481 for(matches = 1; matches < lData.table.length; ++matches)
3483 struct userData *uData = lData.users[matches-1];
3484 char seen[INTERVALLEN];
3486 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3487 lData.table.contents[matches] = ary;
3488 ary[0] = strtab(uData->access);
3489 ary[1] = uData->handle->handle;
3492 else if(!uData->seen)
3495 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3496 ary[2] = strdup(ary[2]);
3497 if(IsUserSuspended(uData))
3498 ary[3] = "Suspended";
3499 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3500 ary[3] = "Vacation";
3505 for(matches = 1; matches < lData.table.length; ++matches)
3507 free((char*)lData.table.contents[matches][2]);
3508 free(lData.table.contents[matches]);
3510 free(lData.table.contents[0]);
3511 free(lData.table.contents);
3515 static CHANSERV_FUNC(cmd_users)
3517 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3520 static CHANSERV_FUNC(cmd_wlist)
3522 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3525 static CHANSERV_FUNC(cmd_clist)
3527 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3530 static CHANSERV_FUNC(cmd_mlist)
3532 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3535 static CHANSERV_FUNC(cmd_olist)
3537 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3540 static CHANSERV_FUNC(cmd_plist)
3542 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3545 static CHANSERV_FUNC(cmd_bans)
3547 struct helpfile_table tbl;
3548 unsigned int matches = 0, timed = 0, ii;
3549 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3550 const char *msg_never, *triggered, *expires;
3551 struct banData *ban, **bans;
3558 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3560 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3562 if(search && !match_ircglobs(search, ban->mask))
3564 bans[matches++] = ban;
3569 tbl.length = matches + 1;
3570 tbl.width = 4 + timed;
3572 tbl.flags = TABLE_NO_FREE;
3573 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3574 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3575 tbl.contents[0][0] = "Mask";
3576 tbl.contents[0][1] = "Set By";
3577 tbl.contents[0][2] = "Triggered";
3580 tbl.contents[0][3] = "Expires";
3581 tbl.contents[0][4] = "Reason";
3584 tbl.contents[0][3] = "Reason";
3587 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3589 free(tbl.contents[0]);
3594 msg_never = user_find_message(user, "MSG_NEVER");
3595 for(ii = 0; ii < matches; )
3601 else if(ban->expires)
3602 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3604 expires = msg_never;
3607 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3609 triggered = msg_never;
3611 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3612 tbl.contents[ii][0] = ban->mask;
3613 tbl.contents[ii][1] = ban->owner;
3614 tbl.contents[ii][2] = strdup(triggered);
3617 tbl.contents[ii][3] = strdup(expires);
3618 tbl.contents[ii][4] = ban->reason;
3621 tbl.contents[ii][3] = ban->reason;
3623 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3624 reply("MSG_MATCH_COUNT", matches);
3625 for(ii = 1; ii < tbl.length; ++ii)
3627 free((char*)tbl.contents[ii][2]);
3629 free((char*)tbl.contents[ii][3]);
3630 free(tbl.contents[ii]);
3632 free(tbl.contents[0]);
3638 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3640 struct chanData *cData = channel->channel_info;
3641 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3643 if(cData->topic_mask)
3644 return !match_ircglob(new_topic, cData->topic_mask);
3645 else if(cData->topic)
3646 return irccasecmp(new_topic, cData->topic);
3651 static CHANSERV_FUNC(cmd_topic)
3653 struct chanData *cData;
3656 cData = channel->channel_info;
3661 SetChannelTopic(channel, chanserv, cData->topic, 1);
3662 reply("CSMSG_TOPIC_SET", cData->topic);
3666 reply("CSMSG_NO_TOPIC", channel->name);
3670 topic = unsplit_string(argv + 1, argc - 1, NULL);
3671 /* If they say "!topic *", use an empty topic. */
3672 if((topic[0] == '*') && (topic[1] == 0))
3674 if(bad_topic(channel, user, topic))
3676 char *topic_mask = cData->topic_mask;
3679 char new_topic[TOPICLEN+1], tchar;
3680 int pos=0, starpos=-1, dpos=0, len;
3682 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3689 len = strlen(topic);
3690 if((dpos + len) > TOPICLEN)
3691 len = TOPICLEN + 1 - dpos;
3692 memcpy(new_topic+dpos, topic, len);
3696 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3697 default: new_topic[dpos++] = tchar; break;
3700 if((dpos > TOPICLEN) || tchar)
3703 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3704 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3707 new_topic[dpos] = 0;
3708 SetChannelTopic(channel, chanserv, new_topic, 1);
3710 reply("CSMSG_TOPIC_LOCKED", channel->name);
3715 SetChannelTopic(channel, chanserv, topic, 1);
3717 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3719 /* Grab the topic and save it as the default topic. */
3721 cData->topic = strdup(channel->topic);
3727 static CHANSERV_FUNC(cmd_mode)
3729 struct mod_chanmode *change;
3733 change = &channel->channel_info->modes;
3734 if(change->modes_set || change->modes_clear) {
3735 modcmd_chanmode_announce(change);
3736 reply("CSMSG_DEFAULTED_MODES", channel->name);
3738 reply("CSMSG_NO_MODES", channel->name);
3742 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3745 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3749 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3750 && mode_lock_violated(&channel->channel_info->modes, change))
3753 mod_chanmode_format(&channel->channel_info->modes, modes);
3754 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3758 modcmd_chanmode_announce(change);
3759 mod_chanmode_free(change);
3760 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3764 static CHANSERV_FUNC(cmd_invite)
3766 struct userData *uData;
3767 struct userNode *invite;
3769 uData = GetChannelUser(channel->channel_info, user->handle_info);
3773 if(!(invite = GetUserH(argv[1])))
3775 reply("MSG_NICK_UNKNOWN", argv[1]);
3782 if(GetUserMode(channel, invite))
3784 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3792 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3793 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3796 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3798 irc_invite(chanserv, invite, channel);
3800 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3805 static CHANSERV_FUNC(cmd_inviteme)
3807 if(GetUserMode(channel, user))
3809 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3812 if(channel->channel_info
3813 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3815 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3818 irc_invite(cmd->parent->bot, user, channel);
3823 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3826 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3828 /* We display things based on two dimensions:
3829 * - Issue time: present or absent
3830 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3831 * (in order of precedence, so something both expired and revoked
3832 * only counts as revoked)
3834 combo = (suspended->issued ? 4 : 0)
3835 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3837 case 0: /* no issue time, indefinite expiration */
3838 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3840 case 1: /* no issue time, expires in future */
3841 intervalString(buf1, suspended->expires-now, user->handle_info);
3842 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3844 case 2: /* no issue time, expired */
3845 intervalString(buf1, now-suspended->expires, user->handle_info);
3846 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3848 case 3: /* no issue time, revoked */
3849 intervalString(buf1, now-suspended->revoked, user->handle_info);
3850 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3852 case 4: /* issue time set, indefinite expiration */
3853 intervalString(buf1, now-suspended->issued, user->handle_info);
3854 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3856 case 5: /* issue time set, expires in future */
3857 intervalString(buf1, now-suspended->issued, user->handle_info);
3858 intervalString(buf2, suspended->expires-now, user->handle_info);
3859 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3861 case 6: /* issue time set, expired */
3862 intervalString(buf1, now-suspended->issued, user->handle_info);
3863 intervalString(buf2, now-suspended->expires, user->handle_info);
3864 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3866 case 7: /* issue time set, revoked */
3867 intervalString(buf1, now-suspended->issued, user->handle_info);
3868 intervalString(buf2, now-suspended->revoked, user->handle_info);
3869 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3872 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3877 static CHANSERV_FUNC(cmd_info)
3879 char modes[MAXLEN], buffer[INTERVALLEN];
3880 struct userData *uData, *owner;
3881 struct chanData *cData;
3882 struct do_not_register *dnr;
3887 cData = channel->channel_info;
3888 reply("CSMSG_CHANNEL_INFO", channel->name);
3890 uData = GetChannelUser(cData, user->handle_info);
3891 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3893 mod_chanmode_format(&cData->modes, modes);
3894 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3895 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3898 for(it = dict_first(cData->notes); it; it = iter_next(it))
3902 note = iter_data(it);
3903 if(!note_type_visible_to_user(cData, note->type, user))
3906 padding = PADLEN - 1 - strlen(iter_key(it));
3907 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3910 reply("CSMSG_CHANNEL_MAX", cData->max);
3911 for(owner = cData->users; owner; owner = owner->next)
3912 if(owner->access == UL_OWNER)
3913 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3914 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3915 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3916 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3917 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3919 privileged = IsStaff(user);
3920 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3921 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3923 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3924 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3926 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3928 struct suspended *suspended;
3929 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3930 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3931 show_suspension_info(cmd, user, suspended);
3933 else if(IsSuspended(cData))
3935 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3936 show_suspension_info(cmd, user, cData->suspended);
3941 static CHANSERV_FUNC(cmd_netinfo)
3943 extern time_t boot_time;
3944 extern unsigned long burst_length;
3945 char interval[INTERVALLEN];
3947 reply("CSMSG_NETWORK_INFO");
3948 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3949 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3950 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3951 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3952 reply("CSMSG_NETWORK_BANS", banCount);
3953 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3954 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3955 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3960 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3962 struct helpfile_table table;
3964 struct userNode *user;
3969 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3970 table.contents = alloca(list->used*sizeof(*table.contents));
3971 for(nn=0; nn<list->used; nn++)
3973 user = list->list[nn];
3974 if(user->modes & skip_flags)
3978 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3981 nick = alloca(strlen(user->nick)+3);
3982 sprintf(nick, "(%s)", user->nick);
3986 table.contents[table.length][0] = nick;
3989 table_send(chanserv, to->nick, 0, NULL, table);
3992 static CHANSERV_FUNC(cmd_ircops)
3994 reply("CSMSG_STAFF_OPERS");
3995 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3999 static CHANSERV_FUNC(cmd_helpers)
4001 reply("CSMSG_STAFF_HELPERS");
4002 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4006 static CHANSERV_FUNC(cmd_staff)
4008 reply("CSMSG_NETWORK_STAFF");
4009 cmd_ircops(CSFUNC_ARGS);
4010 cmd_helpers(CSFUNC_ARGS);
4014 static CHANSERV_FUNC(cmd_peek)
4016 struct modeNode *mn;
4017 char modes[MODELEN];
4019 struct helpfile_table table;
4021 irc_make_chanmode(channel, modes);
4023 reply("CSMSG_PEEK_INFO", channel->name);
4024 reply("CSMSG_PEEK_TOPIC", channel->topic);
4025 reply("CSMSG_PEEK_MODES", modes);
4026 reply("CSMSG_PEEK_USERS", channel->members.used);
4030 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4031 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4032 for(n = 0; n < channel->members.used; n++)
4034 mn = channel->members.list[n];
4035 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4037 table.contents[table.length] = alloca(sizeof(**table.contents));
4038 table.contents[table.length][0] = mn->user->nick;
4043 reply("CSMSG_PEEK_OPS");
4044 table_send(chanserv, user->nick, 0, NULL, table);
4047 reply("CSMSG_PEEK_NO_OPS");
4051 static MODCMD_FUNC(cmd_wipeinfo)
4053 struct handle_info *victim;
4054 struct userData *ud, *actor;
4057 actor = GetChannelUser(channel->channel_info, user->handle_info);
4058 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4060 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4062 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4065 if((ud->access >= actor->access) && (ud != actor))
4067 reply("MSG_USER_OUTRANKED", victim->handle);
4073 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4077 static CHANSERV_FUNC(cmd_resync)
4079 struct mod_chanmode *changes;
4080 struct chanData *cData = channel->channel_info;
4081 unsigned int ii, used;
4083 changes = mod_chanmode_alloc(channel->members.used * 2);
4084 for(ii = used = 0; ii < channel->members.used; ++ii)
4086 struct modeNode *mn = channel->members.list[ii];
4087 struct userData *uData;
4089 if(IsService(mn->user))
4092 uData = GetChannelAccess(cData, mn->user->handle_info);
4093 if(!cData->lvlOpts[lvlGiveOps]
4094 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4096 if(!(mn->modes & MODE_CHANOP))
4098 changes->args[used].mode = MODE_CHANOP;
4099 changes->args[used++].member = mn;
4102 else if(!cData->lvlOpts[lvlGiveVoice]
4103 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4105 if(mn->modes & MODE_CHANOP)
4107 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4108 changes->args[used++].member = mn;
4110 if(!(mn->modes & MODE_VOICE))
4112 changes->args[used].mode = MODE_VOICE;
4113 changes->args[used++].member = mn;
4120 changes->args[used].mode = MODE_REMOVE | mn->modes;
4121 changes->args[used++].member = mn;
4125 changes->argc = used;
4126 modcmd_chanmode_announce(changes);
4127 mod_chanmode_free(changes);
4128 reply("CSMSG_RESYNCED_USERS", channel->name);
4132 static CHANSERV_FUNC(cmd_seen)
4134 struct userData *uData;
4135 struct handle_info *handle;
4136 char seen[INTERVALLEN];
4140 if(!irccasecmp(argv[1], chanserv->nick))
4142 reply("CSMSG_IS_CHANSERV");
4146 if(!(handle = get_handle_info(argv[1])))
4148 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4152 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4154 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4159 reply("CSMSG_USER_PRESENT", handle->handle);
4160 else if(uData->seen)
4161 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4163 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4165 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4166 reply("CSMSG_USER_VACATION", handle->handle);
4171 static MODCMD_FUNC(cmd_names)
4173 struct userNode *targ;
4174 struct userData *targData;
4175 unsigned int ii, pos;
4178 for(ii=pos=0; ii<channel->members.used; ++ii)
4180 targ = channel->members.list[ii]->user;
4181 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4184 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4187 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4191 if(IsUserSuspended(targData))
4193 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4196 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4197 reply("CSMSG_END_NAMES", channel->name);
4202 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4204 switch(ntype->visible_type)
4206 case NOTE_VIS_ALL: return 1;
4207 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4208 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4213 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4215 struct userData *uData;
4217 switch(ntype->set_access_type)
4219 case NOTE_SET_CHANNEL_ACCESS:
4220 if(!user->handle_info)
4222 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4224 return uData->access >= ntype->set_access.min_ulevel;
4225 case NOTE_SET_CHANNEL_SETTER:
4226 return check_user_level(channel, user, lvlSetters, 1, 0);
4227 case NOTE_SET_PRIVILEGED: default:
4228 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4232 static CHANSERV_FUNC(cmd_note)
4234 struct chanData *cData;
4236 struct note_type *ntype;
4238 cData = channel->channel_info;
4241 reply("CSMSG_NOT_REGISTERED", channel->name);
4245 /* If no arguments, show all visible notes for the channel. */
4251 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4253 note = iter_data(it);
4254 if(!note_type_visible_to_user(cData, note->type, user))
4257 reply("CSMSG_NOTELIST_HEADER", channel->name);
4258 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4261 reply("CSMSG_NOTELIST_END", channel->name);
4263 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4265 /* If one argument, show the named note. */
4268 if((note = dict_find(cData->notes, argv[1], NULL))
4269 && note_type_visible_to_user(cData, note->type, user))
4271 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4273 else if((ntype = dict_find(note_types, argv[1], NULL))
4274 && note_type_visible_to_user(NULL, ntype, user))
4276 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4281 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4285 /* Assume they're trying to set a note. */
4289 ntype = dict_find(note_types, argv[1], NULL);
4292 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4295 else if(note_type_settable_by_user(channel, ntype, user))
4297 note_text = unsplit_string(argv+2, argc-2, NULL);
4298 if((note = dict_find(cData->notes, argv[1], NULL)))
4299 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4300 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4301 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4303 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4305 /* The note is viewable to staff only, so return 0
4306 to keep the invocation from getting logged (or
4307 regular users can see it in !events). */
4313 reply("CSMSG_NO_ACCESS");
4320 static CHANSERV_FUNC(cmd_delnote)
4325 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4326 || !note_type_settable_by_user(channel, note->type, user))
4328 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4331 dict_remove(channel->channel_info->notes, note->type->name);
4332 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4336 static CHANSERV_FUNC(cmd_events)
4338 struct logSearch discrim;
4339 struct logReport report;
4340 unsigned int matches, limit;
4342 limit = (argc > 1) ? atoi(argv[1]) : 10;
4343 if(limit < 1 || limit > 200)
4346 memset(&discrim, 0, sizeof(discrim));
4347 discrim.masks.bot = chanserv;
4348 discrim.masks.channel_name = channel->name;
4350 discrim.masks.command = argv[2];
4351 discrim.limit = limit;
4352 discrim.max_time = INT_MAX;
4353 discrim.severities = 1 << LOG_COMMAND;
4354 report.reporter = chanserv;
4356 reply("CSMSG_EVENT_SEARCH_RESULTS");
4357 matches = log_entry_search(&discrim, log_report_entry, &report);
4359 reply("MSG_MATCH_COUNT", matches);
4361 reply("MSG_NO_MATCHES");
4365 static CHANSERV_FUNC(cmd_say)
4371 msg = unsplit_string(argv + 1, argc - 1, NULL);
4372 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4374 else if(GetUserH(argv[1]))
4377 msg = unsplit_string(argv + 2, argc - 2, NULL);
4378 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4382 reply("MSG_NOT_TARGET_NAME");
4388 static CHANSERV_FUNC(cmd_emote)
4394 /* CTCP is so annoying. */
4395 msg = unsplit_string(argv + 1, argc - 1, NULL);
4396 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4398 else if(GetUserH(argv[1]))
4400 msg = unsplit_string(argv + 2, argc - 2, NULL);
4401 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4405 reply("MSG_NOT_TARGET_NAME");
4411 struct channelList *
4412 chanserv_support_channels(void)
4414 return &chanserv_conf.support_channels;
4417 static CHANSERV_FUNC(cmd_expire)
4419 int channel_count = registered_channels;
4420 expire_channels(NULL);
4421 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4426 chanserv_expire_suspension(void *data)
4428 struct suspended *suspended = data;
4429 struct chanNode *channel;
4430 struct mod_chanmode change;
4432 if(!suspended->expires || (now < suspended->expires))
4433 suspended->revoked = now;
4434 channel = suspended->cData->channel;
4435 suspended->cData->channel = channel;
4436 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4437 mod_chanmode_init(&change);
4439 change.args[0].mode = MODE_CHANOP;
4440 change.args[0].member = AddChannelUser(chanserv, channel);
4441 mod_chanmode_announce(chanserv, channel, &change);
4444 static CHANSERV_FUNC(cmd_csuspend)
4446 struct suspended *suspended;
4447 char reason[MAXLEN];
4448 time_t expiry, duration;
4449 struct userData *uData;
4453 if(IsProtected(channel->channel_info))
4455 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4459 if(argv[1][0] == '!')
4461 else if(IsSuspended(channel->channel_info))
4463 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4464 show_suspension_info(cmd, user, channel->channel_info->suspended);
4468 if(!strcmp(argv[1], "0"))
4470 else if((duration = ParseInterval(argv[1])))
4471 expiry = now + duration;
4474 reply("MSG_INVALID_DURATION", argv[1]);
4478 unsplit_string(argv + 2, argc - 2, reason);
4480 suspended = calloc(1, sizeof(*suspended));
4481 suspended->revoked = 0;
4482 suspended->issued = now;
4483 suspended->suspender = strdup(user->handle_info->handle);
4484 suspended->expires = expiry;
4485 suspended->reason = strdup(reason);
4486 suspended->cData = channel->channel_info;
4487 suspended->previous = suspended->cData->suspended;
4488 suspended->cData->suspended = suspended;
4490 if(suspended->expires)
4491 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4493 if(IsSuspended(channel->channel_info))
4495 suspended->previous->revoked = now;
4496 if(suspended->previous->expires)
4497 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4498 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4499 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4503 /* Mark all users in channel as absent. */
4504 for(uData = channel->channel_info->users; uData; uData = uData->next)
4513 /* Mark the channel as suspended, then part. */
4514 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4515 DelChannelUser(chanserv, channel, suspended->reason, 0);
4516 reply("CSMSG_SUSPENDED", channel->name);
4517 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4518 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4523 static CHANSERV_FUNC(cmd_cunsuspend)
4525 struct suspended *suspended;
4526 char message[MAXLEN];
4528 if(!IsSuspended(channel->channel_info))
4530 reply("CSMSG_NOT_SUSPENDED", channel->name);
4534 suspended = channel->channel_info->suspended;
4536 /* Expire the suspension and join ChanServ to the channel. */
4537 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4538 chanserv_expire_suspension(suspended);
4539 reply("CSMSG_UNSUSPENDED", channel->name);
4540 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4541 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4545 typedef struct chanservSearch
4553 unsigned long flags;
4557 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4560 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4565 search = malloc(sizeof(struct chanservSearch));
4566 memset(search, 0, sizeof(*search));
4569 for(i = 0; i < argc; i++)
4571 /* Assume all criteria require arguments. */
4574 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4578 if(!irccasecmp(argv[i], "name"))
4579 search->name = argv[++i];
4580 else if(!irccasecmp(argv[i], "registrar"))
4581 search->registrar = argv[++i];
4582 else if(!irccasecmp(argv[i], "unvisited"))
4583 search->unvisited = ParseInterval(argv[++i]);
4584 else if(!irccasecmp(argv[i], "registered"))
4585 search->registered = ParseInterval(argv[++i]);
4586 else if(!irccasecmp(argv[i], "flags"))
4589 if(!irccasecmp(argv[i], "nodelete"))
4590 search->flags |= CHANNEL_NODELETE;
4591 else if(!irccasecmp(argv[i], "suspended"))
4592 search->flags |= CHANNEL_SUSPENDED;
4595 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4599 else if(!irccasecmp(argv[i], "limit"))
4600 search->limit = strtoul(argv[++i], NULL, 10);
4603 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4608 if(search->name && !strcmp(search->name, "*"))
4610 if(search->registrar && !strcmp(search->registrar, "*"))
4611 search->registrar = 0;
4620 chanserv_channel_match(struct chanData *channel, search_t search)
4622 const char *name = channel->channel->name;
4623 if((search->name && !match_ircglob(name, search->name)) ||
4624 (search->registrar && !channel->registrar) ||
4625 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4626 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4627 (search->registered && (now - channel->registered) > search->registered) ||
4628 (search->flags && ((search->flags & channel->flags) != search->flags)))
4635 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4637 struct chanData *channel;
4638 unsigned int matches = 0;
4640 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4642 if(!chanserv_channel_match(channel, search))
4652 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4657 search_print(struct chanData *channel, void *data)
4659 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4662 static CHANSERV_FUNC(cmd_search)
4665 unsigned int matches;
4666 channel_search_func action;
4670 if(!irccasecmp(argv[1], "count"))
4671 action = search_count;
4672 else if(!irccasecmp(argv[1], "print"))
4673 action = search_print;
4676 reply("CSMSG_ACTION_INVALID", argv[1]);
4680 search = chanserv_search_create(user, argc - 2, argv + 2);
4684 if(action == search_count)
4685 search->limit = INT_MAX;
4687 if(action == search_print)
4688 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4690 matches = chanserv_channel_search(search, action, user);
4693 reply("MSG_MATCH_COUNT", matches);
4695 reply("MSG_NO_MATCHES");
4701 static CHANSERV_FUNC(cmd_unvisited)
4703 struct chanData *cData;
4704 time_t interval = chanserv_conf.channel_expire_delay;
4705 char buffer[INTERVALLEN];
4706 unsigned int limit = 25, matches = 0;
4710 interval = ParseInterval(argv[1]);
4712 limit = atoi(argv[2]);
4715 intervalString(buffer, interval, user->handle_info);
4716 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4718 for(cData = channelList; cData && matches < limit; cData = cData->next)
4720 if((now - cData->visited) < interval)
4723 intervalString(buffer, now - cData->visited, user->handle_info);
4724 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4731 static MODCMD_FUNC(chan_opt_defaulttopic)
4737 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4739 reply("CSMSG_TOPIC_LOCKED", channel->name);
4743 topic = unsplit_string(argv+1, argc-1, NULL);
4745 free(channel->channel_info->topic);
4746 if(topic[0] == '*' && topic[1] == 0)
4748 topic = channel->channel_info->topic = NULL;
4752 topic = channel->channel_info->topic = strdup(topic);
4753 if(channel->channel_info->topic_mask
4754 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4755 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4757 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4760 if(channel->channel_info->topic)
4761 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4763 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4767 static MODCMD_FUNC(chan_opt_topicmask)
4771 struct chanData *cData = channel->channel_info;
4774 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4776 reply("CSMSG_TOPIC_LOCKED", channel->name);
4780 mask = unsplit_string(argv+1, argc-1, NULL);
4782 if(cData->topic_mask)
4783 free(cData->topic_mask);
4784 if(mask[0] == '*' && mask[1] == 0)
4786 cData->topic_mask = 0;
4790 cData->topic_mask = strdup(mask);
4792 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4793 else if(!match_ircglob(cData->topic, cData->topic_mask))
4794 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4798 if(channel->channel_info->topic_mask)
4799 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4801 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4805 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4809 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4813 if(greeting[0] == '*' && greeting[1] == 0)
4817 unsigned int length = strlen(greeting);
4818 if(length > chanserv_conf.greeting_length)
4820 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4823 *data = strdup(greeting);
4832 reply(name, user_find_message(user, "MSG_NONE"));
4836 static MODCMD_FUNC(chan_opt_greeting)
4838 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4841 static MODCMD_FUNC(chan_opt_usergreeting)
4843 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4846 static MODCMD_FUNC(chan_opt_modes)
4848 struct mod_chanmode *new_modes;
4849 char modes[MODELEN];
4853 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4855 reply("CSMSG_NO_ACCESS");
4858 if(argv[1][0] == '*' && argv[1][1] == 0)
4860 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4862 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4864 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4867 else if(new_modes->argc > 1)
4869 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4870 mod_chanmode_free(new_modes);
4875 channel->channel_info->modes = *new_modes;
4876 modcmd_chanmode_announce(new_modes);
4877 mod_chanmode_free(new_modes);
4881 mod_chanmode_format(&channel->channel_info->modes, modes);
4883 reply("CSMSG_SET_MODES", modes);
4885 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4889 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4891 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4893 struct chanData *cData = channel->channel_info;
4898 /* Set flag according to value. */
4899 if(enabled_string(argv[1]))
4901 cData->flags |= mask;
4904 else if(disabled_string(argv[1]))
4906 cData->flags &= ~mask;
4911 reply("MSG_INVALID_BINARY", argv[1]);
4917 /* Find current option value. */
4918 value = (cData->flags & mask) ? 1 : 0;
4922 reply(name, user_find_message(user, "MSG_ON"));
4924 reply(name, user_find_message(user, "MSG_OFF"));
4928 static MODCMD_FUNC(chan_opt_nodelete)
4930 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4932 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4936 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4939 static MODCMD_FUNC(chan_opt_dynlimit)
4941 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4944 static MODCMD_FUNC(chan_opt_offchannel)
4946 struct chanData *cData = channel->channel_info;
4951 /* Set flag according to value. */
4952 if(enabled_string(argv[1]))
4954 if(!IsOffChannel(cData))
4955 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4956 cData->flags |= CHANNEL_OFFCHANNEL;
4959 else if(disabled_string(argv[1]))
4961 if(IsOffChannel(cData))
4963 struct mod_chanmode change;
4964 mod_chanmode_init(&change);
4966 change.args[0].mode = MODE_CHANOP;
4967 change.args[0].member = AddChannelUser(chanserv, channel);
4968 mod_chanmode_announce(chanserv, channel, &change);
4970 cData->flags &= ~CHANNEL_OFFCHANNEL;
4975 reply("MSG_INVALID_BINARY", argv[1]);
4981 /* Find current option value. */
4982 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4986 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4988 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4992 static MODCMD_FUNC(chan_opt_defaults)
4994 struct userData *uData;
4995 struct chanData *cData;
4996 const char *confirm;
4997 enum levelOption lvlOpt;
4998 enum charOption chOpt;
5000 cData = channel->channel_info;
5001 uData = GetChannelUser(cData, user->handle_info);
5002 if(!uData || (uData->access < UL_OWNER))
5004 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5007 confirm = make_confirmation_string(uData);
5008 if((argc < 2) || strcmp(argv[1], confirm))
5010 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5013 cData->flags = CHANNEL_DEFAULT_FLAGS;
5014 cData->modes = chanserv_conf.default_modes;
5015 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5016 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5017 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5018 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5019 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5024 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5026 struct chanData *cData = channel->channel_info;
5027 struct userData *uData;
5028 unsigned short value;
5032 if(!check_user_level(channel, user, option, 1, 1))
5034 reply("CSMSG_CANNOT_SET");
5037 value = user_level_from_name(argv[1], UL_OWNER+1);
5038 if(!value && strcmp(argv[1], "0"))
5040 reply("CSMSG_INVALID_ACCESS", argv[1]);
5043 uData = GetChannelUser(cData, user->handle_info);
5044 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5046 reply("CSMSG_BAD_SETLEVEL");
5052 if(value > cData->lvlOpts[lvlGiveOps])
5054 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5059 if(value < cData->lvlOpts[lvlGiveVoice])
5061 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5066 /* This test only applies to owners, since non-owners
5067 * trying to set an option to above their level get caught
5068 * by the CSMSG_BAD_SETLEVEL test above.
5070 if(value > uData->access)
5072 reply("CSMSG_BAD_SETTERS");
5079 cData->lvlOpts[option] = value;
5081 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5085 static MODCMD_FUNC(chan_opt_enfops)
5087 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5090 static MODCMD_FUNC(chan_opt_giveops)
5092 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5095 static MODCMD_FUNC(chan_opt_enfmodes)
5097 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5100 static MODCMD_FUNC(chan_opt_enftopic)
5102 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5105 static MODCMD_FUNC(chan_opt_pubcmd)
5107 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5110 static MODCMD_FUNC(chan_opt_setters)
5112 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5115 static MODCMD_FUNC(chan_opt_ctcpusers)
5117 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5120 static MODCMD_FUNC(chan_opt_userinfo)
5122 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5125 static MODCMD_FUNC(chan_opt_givevoice)
5127 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5130 static MODCMD_FUNC(chan_opt_topicsnarf)
5132 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5135 static MODCMD_FUNC(chan_opt_inviteme)
5137 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5141 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5143 struct chanData *cData = channel->channel_info;
5144 int count = charOptions[option].count, index;
5148 index = atoi(argv[1]);
5150 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5152 reply("CSMSG_INVALID_NUMERIC", index);
5153 /* Show possible values. */
5154 for(index = 0; index < count; index++)
5155 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5159 cData->chOpts[option] = charOptions[option].values[index].value;
5163 /* Find current option value. */
5166 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5170 /* Somehow, the option value is corrupt; reset it to the default. */
5171 cData->chOpts[option] = charOptions[option].default_value;
5176 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5180 static MODCMD_FUNC(chan_opt_protect)
5182 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5185 static MODCMD_FUNC(chan_opt_toys)
5187 return channel_multiple_option(chToys, CSFUNC_ARGS);
5190 static MODCMD_FUNC(chan_opt_ctcpreaction)
5192 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5195 static MODCMD_FUNC(chan_opt_topicrefresh)
5197 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5200 static struct svccmd_list set_shows_list;
5203 handle_svccmd_unbind(struct svccmd *target) {
5205 for(ii=0; ii<set_shows_list.used; ++ii)
5206 if(target == set_shows_list.list[ii])
5207 set_shows_list.used = 0;
5210 static CHANSERV_FUNC(cmd_set)
5212 struct svccmd *subcmd;
5216 /* Check if we need to (re-)initialize set_shows_list. */
5217 if(!set_shows_list.used)
5219 if(!set_shows_list.size)
5221 set_shows_list.size = chanserv_conf.set_shows->used;
5222 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5224 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5226 const char *name = chanserv_conf.set_shows->list[ii];
5227 sprintf(buf, "%s %s", argv[0], name);
5228 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5231 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5234 svccmd_list_append(&set_shows_list, subcmd);
5240 reply("CSMSG_CHANNEL_OPTIONS");
5241 for(ii = 0; ii < set_shows_list.used; ii++)
5243 subcmd = set_shows_list.list[ii];
5244 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5249 sprintf(buf, "%s %s", argv[0], argv[1]);
5250 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5253 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5256 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5258 reply("CSMSG_NO_ACCESS");
5262 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5266 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5268 struct userData *uData;
5270 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5273 reply("CSMSG_NOT_USER", channel->name);
5279 /* Just show current option value. */
5281 else if(enabled_string(argv[1]))
5283 uData->flags |= mask;
5285 else if(disabled_string(argv[1]))
5287 uData->flags &= ~mask;
5291 reply("MSG_INVALID_BINARY", argv[1]);
5295 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5299 static MODCMD_FUNC(user_opt_noautoop)
5301 struct userData *uData;
5303 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5306 reply("CSMSG_NOT_USER", channel->name);
5309 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5310 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5312 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5315 static MODCMD_FUNC(user_opt_autoinvite)
5317 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5320 static MODCMD_FUNC(user_opt_info)
5322 struct userData *uData;
5325 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5329 /* If they got past the command restrictions (which require access)
5330 * but fail this test, we have some fool with security override on.
5332 reply("CSMSG_NOT_USER", channel->name);
5339 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5340 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5342 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5345 bp = strcspn(infoline, "\001");
5348 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5353 if(infoline[0] == '*' && infoline[1] == 0)
5356 uData->info = strdup(infoline);
5359 reply("CSMSG_USET_INFO", uData->info);
5361 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5365 struct svccmd_list uset_shows_list;
5367 static CHANSERV_FUNC(cmd_uset)
5369 struct svccmd *subcmd;
5373 /* Check if we need to (re-)initialize uset_shows_list. */
5374 if(!uset_shows_list.used)
5378 "NoAutoOp", "AutoInvite", "Info"
5381 if(!uset_shows_list.size)
5383 uset_shows_list.size = ArrayLength(options);
5384 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5386 for(ii = 0; ii < ArrayLength(options); ii++)
5388 const char *name = options[ii];
5389 sprintf(buf, "%s %s", argv[0], name);
5390 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5393 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5396 svccmd_list_append(&uset_shows_list, subcmd);
5402 /* Do this so options are presented in a consistent order. */
5403 reply("CSMSG_USER_OPTIONS");
5404 for(ii = 0; ii < uset_shows_list.used; ii++)
5405 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5409 sprintf(buf, "%s %s", argv[0], argv[1]);
5410 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5413 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5417 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5420 static CHANSERV_FUNC(cmd_giveownership)
5422 struct handle_info *new_owner_hi;
5423 struct userData *new_owner, *curr_user;
5424 struct chanData *cData = channel->channel_info;
5425 struct do_not_register *dnr;
5427 unsigned short co_access;
5428 char reason[MAXLEN];
5431 curr_user = GetChannelAccess(cData, user->handle_info);
5432 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5433 if(!curr_user || (curr_user->access != UL_OWNER))
5435 struct userData *owner = NULL;
5436 for(curr_user = channel->channel_info->users;
5438 curr_user = curr_user->next)
5440 if(curr_user->access != UL_OWNER)
5444 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5451 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5453 if(new_owner_hi == user->handle_info)
5455 reply("CSMSG_NO_TRANSFER_SELF");
5458 new_owner = GetChannelAccess(cData, new_owner_hi);
5461 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5464 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5466 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5469 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5470 if(!IsHelping(user))
5471 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5473 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5476 if(new_owner->access >= UL_COOWNER)
5477 co_access = new_owner->access;
5479 co_access = UL_COOWNER;
5480 new_owner->access = UL_OWNER;
5482 curr_user->access = co_access;
5483 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5484 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5485 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5489 static CHANSERV_FUNC(cmd_suspend)
5491 struct handle_info *hi;
5492 struct userData *self, *target;
5495 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5496 self = GetChannelUser(channel->channel_info, user->handle_info);
5497 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5499 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5502 if(target->access >= self->access)
5504 reply("MSG_USER_OUTRANKED", hi->handle);
5507 if(target->flags & USER_SUSPENDED)
5509 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5514 target->present = 0;
5517 target->flags |= USER_SUSPENDED;
5518 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5522 static CHANSERV_FUNC(cmd_unsuspend)
5524 struct handle_info *hi;
5525 struct userData *self, *target;
5528 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5529 self = GetChannelUser(channel->channel_info, user->handle_info);
5530 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5532 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5535 if(target->access >= self->access)
5537 reply("MSG_USER_OUTRANKED", hi->handle);
5540 if(!(target->flags & USER_SUSPENDED))
5542 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5545 target->flags &= ~USER_SUSPENDED;
5546 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5550 static MODCMD_FUNC(cmd_deleteme)
5552 struct handle_info *hi;
5553 struct userData *target;
5554 const char *confirm_string;
5555 unsigned short access;
5558 hi = user->handle_info;
5559 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5561 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5564 if(target->access == UL_OWNER)
5566 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5569 confirm_string = make_confirmation_string(target);
5570 if((argc < 2) || strcmp(argv[1], confirm_string))
5572 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5575 access = target->access;
5576 channel_name = strdup(channel->name);
5577 del_channel_user(target, 1);
5578 reply("CSMSG_DELETED_YOU", access, channel_name);
5584 chanserv_refresh_topics(UNUSED_ARG(void *data))
5586 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5587 struct chanData *cData;
5590 for(cData = channelList; cData; cData = cData->next)
5592 if(IsSuspended(cData))
5594 opt = cData->chOpts[chTopicRefresh];
5597 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5600 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5601 cData->last_refresh = refresh_num;
5603 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5606 static CHANSERV_FUNC(cmd_unf)
5610 char response[MAXLEN];
5611 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5612 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5613 irc_privmsg(cmd->parent->bot, channel->name, response);
5616 reply("CSMSG_UNF_RESPONSE");
5620 static CHANSERV_FUNC(cmd_ping)
5624 char response[MAXLEN];
5625 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5626 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5627 irc_privmsg(cmd->parent->bot, channel->name, response);
5630 reply("CSMSG_PING_RESPONSE");
5634 static CHANSERV_FUNC(cmd_wut)
5638 char response[MAXLEN];
5639 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5640 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5641 irc_privmsg(cmd->parent->bot, channel->name, response);
5644 reply("CSMSG_WUT_RESPONSE");
5648 static CHANSERV_FUNC(cmd_8ball)
5650 unsigned int i, j, accum;
5655 for(i=1; i<argc; i++)
5656 for(j=0; argv[i][j]; j++)
5657 accum = (accum << 5) - accum + toupper(argv[i][j]);
5658 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5661 char response[MAXLEN];
5662 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5663 irc_privmsg(cmd->parent->bot, channel->name, response);
5666 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5670 static CHANSERV_FUNC(cmd_d)
5672 unsigned long sides, count, modifier, ii, total;
5673 char response[MAXLEN], *sep;
5677 if((count = strtoul(argv[1], &sep, 10)) < 1)
5687 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5688 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5692 else if((sep[0] == '-') && isdigit(sep[1]))
5693 modifier = strtoul(sep, NULL, 10);
5694 else if((sep[0] == '+') && isdigit(sep[1]))
5695 modifier = strtoul(sep+1, NULL, 10);
5702 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5707 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5710 for(total = ii = 0; ii < count; ++ii)
5711 total += (rand() % sides) + 1;
5714 if((count > 1) || modifier)
5716 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5717 sprintf(response, fmt, total, count, sides, modifier);
5721 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5722 sprintf(response, fmt, total, sides);
5725 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5727 send_message_type(4, user, cmd->parent->bot, "%s", response);
5731 static CHANSERV_FUNC(cmd_huggle)
5733 /* CTCP must be via PRIVMSG, never notice */
5735 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5737 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5742 chanserv_adjust_limit(void *data)
5744 struct mod_chanmode change;
5745 struct chanData *cData = data;
5746 struct chanNode *channel = cData->channel;
5749 if(IsSuspended(cData))
5752 cData->limitAdjusted = now;
5753 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5754 if(cData->modes.modes_set & MODE_LIMIT)
5756 if(limit > cData->modes.new_limit)
5757 limit = cData->modes.new_limit;
5758 else if(limit == cData->modes.new_limit)
5762 mod_chanmode_init(&change);
5763 change.modes_set = MODE_LIMIT;
5764 change.new_limit = limit;
5765 mod_chanmode_announce(chanserv, channel, &change);
5769 handle_new_channel(struct chanNode *channel)
5771 struct chanData *cData;
5773 if(!(cData = channel->channel_info))
5776 if(cData->modes.modes_set || cData->modes.modes_clear)
5777 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5779 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5780 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5783 /* Welcome to my worst nightmare. Warning: Read (or modify)
5784 the code below at your own risk. */
5786 handle_join(struct modeNode *mNode)
5788 struct mod_chanmode change;
5789 struct userNode *user = mNode->user;
5790 struct chanNode *channel = mNode->channel;
5791 struct chanData *cData;
5792 struct userData *uData = NULL;
5793 struct banData *bData;
5794 struct handle_info *handle;
5795 unsigned int modes = 0, info = 0;
5798 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5801 cData = channel->channel_info;
5802 if(channel->members.used > cData->max)
5803 cData->max = channel->members.used;
5805 /* Check for bans. If they're joining through a ban, one of two
5807 * 1: Join during a netburst, by riding the break. Kick them
5808 * unless they have ops or voice in the channel.
5809 * 2: They're allowed to join through the ban (an invite in
5810 * ircu2.10, or a +e on Hybrid, or something).
5811 * If they're not joining through a ban, and the banlist is not
5812 * full, see if they're on the banlist for the channel. If so,
5815 if(user->uplink->burst && !mNode->modes)
5818 for(ii = 0; ii < channel->banlist.used; ii++)
5820 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5822 /* Riding a netburst. Naughty. */
5823 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5829 mod_chanmode_init(&change);
5831 if(channel->banlist.used < MAXBANS)
5833 /* Not joining through a ban. */
5834 for(bData = cData->bans;
5835 bData && !user_matches_glob(user, bData->mask, 1);
5836 bData = bData->next);
5840 char kick_reason[MAXLEN];
5841 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5843 bData->triggered = now;
5844 if(bData != cData->bans)
5846 /* Shuffle the ban to the head of the list. */
5848 bData->next->prev = bData->prev;
5850 bData->prev->next = bData->next;
5853 bData->next = cData->bans;
5856 cData->bans->prev = bData;
5857 cData->bans = bData;
5860 change.args[0].mode = MODE_BAN;
5861 change.args[0].hostmask = bData->mask;
5862 mod_chanmode_announce(chanserv, channel, &change);
5863 KickChannelUser(user, channel, chanserv, kick_reason);
5868 /* ChanServ will not modify the limits in join-flooded channels.
5869 It will also skip DynLimit processing when the user (or srvx)
5870 is bursting in, because there are likely more incoming. */
5871 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5872 && !user->uplink->burst
5873 && !channel->join_flooded
5874 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5876 /* The user count has begun "bumping" into the channel limit,
5877 so set a timer to raise the limit a bit. Any previous
5878 timers are removed so three incoming users within the delay
5879 results in one limit change, not three. */
5881 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5882 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5885 if(channel->join_flooded)
5887 /* don't automatically give ops or voice during a join flood */
5889 else if(cData->lvlOpts[lvlGiveOps] == 0)
5890 modes |= MODE_CHANOP;
5891 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5892 modes |= MODE_VOICE;
5894 greeting = cData->greeting;
5895 if(user->handle_info)
5897 handle = user->handle_info;
5899 if(IsHelper(user) && !IsHelping(user))
5902 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5904 if(channel == chanserv_conf.support_channels.list[ii])
5906 HANDLE_SET_FLAG(user->handle_info, HELPING);
5912 uData = GetTrueChannelAccess(cData, handle);
5913 if(uData && !IsUserSuspended(uData))
5915 /* Ops and above were handled by the above case. */
5916 if(IsUserAutoOp(uData))
5918 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5919 modes |= MODE_CHANOP;
5920 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5921 modes |= MODE_VOICE;
5923 if(uData->access >= UL_PRESENT)
5924 cData->visited = now;
5925 if(cData->user_greeting)
5926 greeting = cData->user_greeting;
5928 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5929 && ((now - uData->seen) >= chanserv_conf.info_delay)
5936 if(!user->uplink->burst)
5940 if(modes & MODE_CHANOP)
5941 modes &= ~MODE_VOICE;
5942 change.args[0].mode = modes;
5943 change.args[0].member = mNode;
5944 mod_chanmode_announce(chanserv, channel, &change);
5946 if(greeting && !user->uplink->burst)
5947 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5949 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5955 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5957 struct mod_chanmode change;
5958 struct userData *channel;
5959 unsigned int ii, jj;
5961 if(!user->handle_info)
5964 mod_chanmode_init(&change);
5966 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5968 struct chanNode *cn;
5969 struct modeNode *mn;
5970 if(IsUserSuspended(channel)
5971 || IsSuspended(channel->channel)
5972 || !(cn = channel->channel->channel))
5975 mn = GetUserMode(cn, user);
5978 if(!IsUserSuspended(channel)
5979 && IsUserAutoInvite(channel)
5980 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5982 && !user->uplink->burst)
5983 irc_invite(chanserv, user, cn);
5987 if(channel->access >= UL_PRESENT)
5988 channel->channel->visited = now;
5990 if(IsUserAutoOp(channel))
5992 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5993 change.args[0].mode = MODE_CHANOP;
5994 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5995 change.args[0].mode = MODE_VOICE;
5997 change.args[0].mode = 0;
5998 change.args[0].member = mn;
5999 if(change.args[0].mode)
6000 mod_chanmode_announce(chanserv, cn, &change);
6003 channel->seen = now;
6004 channel->present = 1;
6007 for(ii = 0; ii < user->channels.used; ++ii)
6009 struct chanNode *channel = user->channels.list[ii]->channel;
6010 struct banData *ban;
6012 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6013 || !channel->channel_info)
6015 for(jj = 0; jj < channel->banlist.used; ++jj)
6016 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6018 if(jj < channel->banlist.used)
6020 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6022 char kick_reason[MAXLEN];
6023 if(!user_matches_glob(user, ban->mask, 1))
6025 change.args[0].mode = MODE_BAN;
6026 change.args[0].hostmask = ban->mask;
6027 mod_chanmode_announce(chanserv, channel, &change);
6028 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6029 KickChannelUser(user, channel, chanserv, kick_reason);
6030 ban->triggered = now;
6035 if(IsSupportHelper(user))
6037 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6039 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6041 HANDLE_SET_FLAG(user->handle_info, HELPING);
6049 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6051 struct chanData *cData;
6052 struct userData *uData;
6054 cData = mn->channel->channel_info;
6055 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6058 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6060 /* Allow for a bit of padding so that the limit doesn't
6061 track the user count exactly, which could get annoying. */
6062 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6064 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6065 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6069 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6071 scan_user_presence(uData, mn->user);
6075 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6077 unsigned int ii, jj;
6078 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6080 for(jj = 0; jj < mn->user->channels.used; ++jj)
6081 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6083 if(jj < mn->user->channels.used)
6086 if(ii == chanserv_conf.support_channels.used)
6087 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6092 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6094 struct userData *uData;
6096 if(!channel->channel_info || !kicker || IsService(kicker)
6097 || (kicker == victim) || IsSuspended(channel->channel_info)
6098 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6101 if(protect_user(victim, kicker, channel->channel_info))
6103 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6104 KickChannelUser(kicker, channel, chanserv, reason);
6107 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6112 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6114 struct chanData *cData;
6116 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6119 cData = channel->channel_info;
6120 if(bad_topic(channel, user, channel->topic))
6122 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6123 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6124 SetChannelTopic(channel, chanserv, old_topic, 1);
6125 else if(cData->topic)
6126 SetChannelTopic(channel, chanserv, cData->topic, 1);
6129 /* With topicsnarf, grab the topic and save it as the default topic. */
6130 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6133 cData->topic = strdup(channel->topic);
6139 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6141 struct mod_chanmode *bounce = NULL;
6142 unsigned int bnc, ii;
6145 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6148 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6149 && mode_lock_violated(&channel->channel_info->modes, change))
6151 char correct[MAXLEN];
6152 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6153 mod_chanmode_format(&channel->channel_info->modes, correct);
6154 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6156 for(ii = bnc = 0; ii < change->argc; ++ii)
6158 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6160 const struct userNode *victim = change->args[ii].member->user;
6161 if(!protect_user(victim, user, channel->channel_info))
6164 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6167 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6168 bounce->args[bnc].member = GetUserMode(channel, user);
6169 if(bounce->args[bnc].member)
6173 bounce->args[bnc].mode = MODE_CHANOP;
6174 bounce->args[bnc].member = change->args[ii].member;
6176 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6178 else if(change->args[ii].mode & MODE_CHANOP)
6180 const struct userNode *victim = change->args[ii].member->user;
6181 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6184 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6185 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6186 bounce->args[bnc].member = change->args[ii].member;
6189 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6191 const char *ban = change->args[ii].hostmask;
6192 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6195 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6196 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6197 bounce->args[bnc].hostmask = ban;
6199 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6204 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6205 mod_chanmode_announce(chanserv, channel, bounce);
6206 mod_chanmode_free(bounce);
6211 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6213 struct chanNode *channel;
6214 struct banData *bData;
6215 struct mod_chanmode change;
6216 unsigned int ii, jj;
6217 char kick_reason[MAXLEN];
6219 mod_chanmode_init(&change);
6221 change.args[0].mode = MODE_BAN;
6222 for(ii = 0; ii < user->channels.used; ++ii)
6224 channel = user->channels.list[ii]->channel;
6225 /* Need not check for bans if they're opped or voiced. */
6226 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6228 /* Need not check for bans unless channel registration is active. */
6229 if(!channel->channel_info || IsSuspended(channel->channel_info))
6231 /* Look for a matching ban already on the channel. */
6232 for(jj = 0; jj < channel->banlist.used; ++jj)
6233 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6235 /* Need not act if we found one. */
6236 if(jj < channel->banlist.used)
6238 /* Look for a matching ban in this channel. */
6239 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6241 if(!user_matches_glob(user, bData->mask, 1))
6243 change.args[0].hostmask = bData->mask;
6244 mod_chanmode_announce(chanserv, channel, &change);
6245 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6246 KickChannelUser(user, channel, chanserv, kick_reason);
6247 bData->triggered = now;
6248 break; /* we don't need to check any more bans in the channel */
6253 static void handle_rename(struct handle_info *handle, const char *old_handle)
6255 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6259 dict_remove2(handle_dnrs, old_handle, 1);
6260 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6261 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6266 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6268 struct userNode *h_user;
6270 if(handle->channels)
6272 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6273 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6275 while(handle->channels)
6276 del_channel_user(handle->channels, 1);
6281 handle_server_link(UNUSED_ARG(struct server *server))
6283 struct chanData *cData;
6285 for(cData = channelList; cData; cData = cData->next)
6287 if(!IsSuspended(cData))
6288 cData->may_opchan = 1;
6289 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6290 && !cData->channel->join_flooded
6291 && ((cData->channel->limit - cData->channel->members.used)
6292 < chanserv_conf.adjust_threshold))
6294 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6295 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6301 chanserv_conf_read(void)
6305 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6306 struct mod_chanmode *change;
6307 struct string_list *strlist;
6308 struct chanNode *chan;
6311 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6313 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6316 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6317 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6318 chanserv_conf.support_channels.used = 0;
6319 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6321 for(ii = 0; ii < strlist->used; ++ii)
6323 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6326 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6328 channelList_append(&chanserv_conf.support_channels, chan);
6331 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6334 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6337 chan = AddChannel(str, now, str2, NULL);
6339 channelList_append(&chanserv_conf.support_channels, chan);
6341 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6342 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6343 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6344 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6345 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6346 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6347 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6348 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6349 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6350 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6351 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6352 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6353 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6354 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6355 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6356 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6357 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6358 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6359 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6360 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6361 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6362 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6363 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6365 NickChange(chanserv, str, 0);
6366 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6367 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6368 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6369 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6370 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6371 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6372 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6373 chanserv_conf.max_owned = str ? atoi(str) : 5;
6374 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6375 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6376 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6377 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6378 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6379 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6380 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6383 safestrncpy(mode_line, str, sizeof(mode_line));
6384 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6385 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6387 chanserv_conf.default_modes = *change;
6388 mod_chanmode_free(change);
6390 free_string_list(chanserv_conf.set_shows);
6391 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6393 strlist = string_list_copy(strlist);
6396 static const char *list[] = {
6397 /* free form text */
6398 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6399 /* options based on user level */
6400 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6401 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6402 /* multiple choice options */
6403 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6404 /* binary options */
6405 "DynLimit", "NoDelete",
6410 strlist = alloc_string_list(ArrayLength(list)-1);
6411 for(ii=0; list[ii]; ii++)
6412 string_list_append(strlist, strdup(list[ii]));
6414 chanserv_conf.set_shows = strlist;
6415 /* We don't look things up now, in case the list refers to options
6416 * defined by modules initialized after this point. Just mark the
6417 * function list as invalid, so it will be initialized.
6419 set_shows_list.used = 0;
6420 free_string_list(chanserv_conf.eightball);
6421 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6424 strlist = string_list_copy(strlist);
6428 strlist = alloc_string_list(4);
6429 string_list_append(strlist, strdup("Yes."));
6430 string_list_append(strlist, strdup("No."));
6431 string_list_append(strlist, strdup("Maybe so."));
6433 chanserv_conf.eightball = strlist;
6434 free_string_list(chanserv_conf.old_ban_names);
6435 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6437 strlist = string_list_copy(strlist);
6439 strlist = alloc_string_list(2);
6440 chanserv_conf.old_ban_names = strlist;
6441 /* the variable itself is actually declared in proto-common.c; this is equally
6443 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6444 off_channel = (str && enabled_string(str)) ? 1 : 0;
6445 str = database_get_data(conf_node, "use_registered_mode", RECDB_QSTRING);
6446 chanserv_conf.use_registered_mode = (str && enabled_string(str)) ? 1 : 0;
6450 chanserv_note_type_read(const char *key, struct record_data *rd)
6453 struct note_type *ntype;
6456 if(!(obj = GET_RECORD_OBJECT(rd)))
6458 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6461 if(!(ntype = chanserv_create_note_type(key)))
6463 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6467 /* Figure out set access */
6468 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6470 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6471 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6473 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6475 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6476 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6478 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6480 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6484 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6485 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6486 ntype->set_access.min_opserv = 0;
6489 /* Figure out visibility */
6490 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6491 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6492 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6493 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6494 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6495 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6496 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6497 ntype->visible_type = NOTE_VIS_ALL;
6499 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6501 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6502 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6506 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6508 struct handle_info *handle;
6509 struct userData *uData;
6510 char *seen, *inf, *flags;
6512 unsigned short access;
6514 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6516 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6520 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6521 if(access > UL_OWNER)
6523 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6527 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6528 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6529 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6530 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6531 handle = get_handle_info(key);
6534 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6538 uData = add_channel_user(chan, handle, access, last_seen, inf);
6539 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6543 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6545 struct banData *bData;
6546 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6547 time_t set_time, triggered_time, expires_time;
6549 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6551 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6555 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6556 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6557 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6558 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6559 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6560 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6562 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6563 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6565 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6567 expires_time = set_time + atoi(s_duration);
6571 if(expires_time && (expires_time < now))
6574 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6577 static struct suspended *
6578 chanserv_read_suspended(dict_t obj)
6580 struct suspended *suspended = calloc(1, sizeof(*suspended));
6584 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6585 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6586 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6587 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6588 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6589 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6590 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6591 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6592 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6593 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6598 chanserv_channel_read(const char *key, struct record_data *hir)
6600 struct suspended *suspended;
6601 struct mod_chanmode *modes;
6602 struct chanNode *cNode;
6603 struct chanData *cData;
6604 struct dict *channel, *obj;
6605 char *str, *argv[10];
6609 channel = hir->d.object;
6611 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6614 cNode = AddChannel(key, now, NULL, NULL);
6617 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6620 cData = register_channel(cNode, str);
6623 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6627 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6629 enum levelOption lvlOpt;
6630 enum charOption chOpt;
6632 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6633 cData->flags = atoi(str);
6635 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6637 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6639 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6640 else if(levelOptions[lvlOpt].old_flag)
6642 if(cData->flags & levelOptions[lvlOpt].old_flag)
6643 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6645 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6649 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6651 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6653 cData->chOpts[chOpt] = str[0];
6656 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6658 enum levelOption lvlOpt;
6659 enum charOption chOpt;
6662 cData->flags = base64toint(str, 5);
6663 count = strlen(str += 5);
6664 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6667 if(levelOptions[lvlOpt].old_flag)
6669 if(cData->flags & levelOptions[lvlOpt].old_flag)
6670 lvl = levelOptions[lvlOpt].flag_value;
6672 lvl = levelOptions[lvlOpt].default_value;
6674 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6676 case 'c': lvl = UL_COOWNER; break;
6677 case 'm': lvl = UL_MASTER; break;
6678 case 'n': lvl = UL_OWNER+1; break;
6679 case 'o': lvl = UL_OP; break;
6680 case 'p': lvl = UL_PEON; break;
6681 case 'w': lvl = UL_OWNER; break;
6682 default: lvl = 0; break;
6684 cData->lvlOpts[lvlOpt] = lvl;
6686 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6687 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6690 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6692 suspended = chanserv_read_suspended(obj);
6693 cData->suspended = suspended;
6694 suspended->cData = cData;
6695 /* We could use suspended->expires and suspended->revoked to
6696 * set the CHANNEL_SUSPENDED flag, but we don't. */
6698 else if(IsSuspended(cData))
6700 suspended = calloc(1, sizeof(*suspended));
6701 suspended->issued = 0;
6702 suspended->revoked = 0;
6703 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6704 suspended->expires = str ? atoi(str) : 0;
6705 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6706 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6707 suspended->reason = strdup(str ? str : "No reason");
6708 suspended->previous = NULL;
6709 cData->suspended = suspended;
6710 suspended->cData = cData;
6713 suspended = NULL; /* to squelch a warning */
6715 if(IsSuspended(cData)) {
6716 if(suspended->expires > now)
6717 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6718 else if(suspended->expires)
6719 cData->flags &= ~CHANNEL_SUSPENDED;
6722 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6723 struct mod_chanmode change;
6724 mod_chanmode_init(&change);
6726 change.args[0].mode = MODE_CHANOP;
6727 change.args[0].member = AddChannelUser(chanserv, cNode);
6728 mod_chanmode_announce(chanserv, cNode, &change);
6731 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6732 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6733 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6734 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6735 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6736 cData->max = str ? atoi(str) : 0;
6737 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6738 cData->greeting = str ? strdup(str) : NULL;
6739 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6740 cData->user_greeting = str ? strdup(str) : NULL;
6741 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6742 cData->topic_mask = str ? strdup(str) : NULL;
6743 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6744 cData->topic = str ? strdup(str) : NULL;
6746 if(!IsSuspended(cData)
6747 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6748 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6749 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6750 cData->modes = *modes;
6751 if (chanserv_conf.use_registered_mode)
6752 cData->modes.modes_set |= MODE_REGISTERED;
6753 if(cData->modes.argc > 1)
6754 cData->modes.argc = 1;
6755 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6756 mod_chanmode_free(modes);
6759 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6760 for(it = dict_first(obj); it; it = iter_next(it))
6761 user_read_helper(iter_key(it), iter_data(it), cData);
6763 if(!cData->users && !IsProtected(cData))
6765 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6766 unregister_channel(cData, "has empty user list.");
6770 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6771 for(it = dict_first(obj); it; it = iter_next(it))
6772 ban_read_helper(iter_key(it), iter_data(it), cData);
6774 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6775 for(it = dict_first(obj); it; it = iter_next(it))
6777 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6778 struct record_data *rd = iter_data(it);
6779 const char *note, *setter;
6781 if(rd->type != RECDB_OBJECT)
6783 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6787 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6789 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6791 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6795 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6796 if(!setter) setter = "<unknown>";
6797 chanserv_add_channel_note(cData, ntype, setter, note);
6805 chanserv_dnr_read(const char *key, struct record_data *hir)
6807 const char *setter, *reason, *str;
6808 struct do_not_register *dnr;
6810 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6813 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6816 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6819 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6822 dnr = chanserv_add_dnr(key, setter, reason);
6825 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6827 dnr->set = atoi(str);
6833 chanserv_saxdb_read(struct dict *database)
6835 struct dict *section;
6838 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6839 for(it = dict_first(section); it; it = iter_next(it))
6840 chanserv_note_type_read(iter_key(it), iter_data(it));
6842 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6843 for(it = dict_first(section); it; it = iter_next(it))
6844 chanserv_channel_read(iter_key(it), iter_data(it));
6846 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6847 for(it = dict_first(section); it; it = iter_next(it))
6848 chanserv_dnr_read(iter_key(it), iter_data(it));
6854 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6856 int high_present = 0;
6857 saxdb_start_record(ctx, KEY_USERS, 1);
6858 for(; uData; uData = uData->next)
6860 if((uData->access >= UL_PRESENT) && uData->present)
6862 saxdb_start_record(ctx, uData->handle->handle, 0);
6863 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6864 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6866 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6868 saxdb_write_string(ctx, KEY_INFO, uData->info);
6869 saxdb_end_record(ctx);
6871 saxdb_end_record(ctx);
6872 return high_present;
6876 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6880 saxdb_start_record(ctx, KEY_BANS, 1);
6881 for(; bData; bData = bData->next)
6883 saxdb_start_record(ctx, bData->mask, 0);
6884 saxdb_write_int(ctx, KEY_SET, bData->set);
6885 if(bData->triggered)
6886 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6888 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6890 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6892 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6893 saxdb_end_record(ctx);
6895 saxdb_end_record(ctx);
6899 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6901 saxdb_start_record(ctx, name, 0);
6902 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6903 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6905 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6907 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6909 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6911 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6912 saxdb_end_record(ctx);
6916 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6920 enum levelOption lvlOpt;
6921 enum charOption chOpt;
6923 saxdb_start_record(ctx, channel->channel->name, 1);
6925 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6926 saxdb_write_int(ctx, KEY_MAX, channel->max);
6928 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6929 if(channel->registrar)
6930 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6931 if(channel->greeting)
6932 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6933 if(channel->user_greeting)
6934 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6935 if(channel->topic_mask)
6936 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6937 if(channel->suspended)
6938 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6940 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6941 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6942 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6943 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6944 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6946 buf[0] = channel->chOpts[chOpt];
6948 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6950 saxdb_end_record(ctx);
6952 if(channel->modes.modes_set || channel->modes.modes_clear)
6954 mod_chanmode_format(&channel->modes, buf);
6955 saxdb_write_string(ctx, KEY_MODES, buf);
6958 high_present = chanserv_write_users(ctx, channel->users);
6959 chanserv_write_bans(ctx, channel->bans);
6961 if(dict_size(channel->notes))
6965 saxdb_start_record(ctx, KEY_NOTES, 1);
6966 for(it = dict_first(channel->notes); it; it = iter_next(it))
6968 struct note *note = iter_data(it);
6969 saxdb_start_record(ctx, iter_key(it), 0);
6970 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6971 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6972 saxdb_end_record(ctx);
6974 saxdb_end_record(ctx);
6977 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6978 saxdb_end_record(ctx);
6982 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6986 saxdb_start_record(ctx, ntype->name, 0);
6987 switch(ntype->set_access_type)
6989 case NOTE_SET_CHANNEL_ACCESS:
6990 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6992 case NOTE_SET_CHANNEL_SETTER:
6993 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6995 case NOTE_SET_PRIVILEGED: default:
6996 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6999 switch(ntype->visible_type)
7001 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7002 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7003 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7005 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7006 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7007 saxdb_end_record(ctx);
7011 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7013 struct do_not_register *dnr;
7016 for(it = dict_first(dnrs); it; it = iter_next(it))
7018 dnr = iter_data(it);
7019 saxdb_start_record(ctx, dnr->chan_name, 0);
7021 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7022 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7023 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7024 saxdb_end_record(ctx);
7029 chanserv_saxdb_write(struct saxdb_context *ctx)
7032 struct chanData *channel;
7035 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7036 for(it = dict_first(note_types); it; it = iter_next(it))
7037 chanserv_write_note_type(ctx, iter_data(it));
7038 saxdb_end_record(ctx);
7041 saxdb_start_record(ctx, KEY_DNR, 1);
7042 write_dnrs_helper(ctx, handle_dnrs);
7043 write_dnrs_helper(ctx, plain_dnrs);
7044 write_dnrs_helper(ctx, mask_dnrs);
7045 saxdb_end_record(ctx);
7048 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7049 for(channel = channelList; channel; channel = channel->next)
7050 chanserv_write_channel(ctx, channel);
7051 saxdb_end_record(ctx);
7057 chanserv_db_cleanup(void) {
7059 unreg_part_func(handle_part);
7061 unregister_channel(channelList, "terminating.");
7062 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7063 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7064 free(chanserv_conf.support_channels.list);
7065 dict_delete(handle_dnrs);
7066 dict_delete(plain_dnrs);
7067 dict_delete(mask_dnrs);
7068 dict_delete(note_types);
7069 free_string_list(chanserv_conf.eightball);
7070 free_string_list(chanserv_conf.old_ban_names);
7071 free_string_list(chanserv_conf.set_shows);
7072 free(set_shows_list.list);
7073 free(uset_shows_list.list);
7076 struct userData *helper = helperList;
7077 helperList = helperList->next;
7082 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7083 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7084 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7087 init_chanserv(const char *nick)
7089 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7090 conf_register_reload(chanserv_conf_read);
7092 reg_server_link_func(handle_server_link);
7094 reg_new_channel_func(handle_new_channel);
7095 reg_join_func(handle_join);
7096 reg_part_func(handle_part);
7097 reg_kick_func(handle_kick);
7098 reg_topic_func(handle_topic);
7099 reg_mode_change_func(handle_mode);
7100 reg_nick_change_func(handle_nick_change);
7102 reg_auth_func(handle_auth);
7103 reg_handle_rename_func(handle_rename);
7104 reg_unreg_func(handle_unreg);
7106 handle_dnrs = dict_new();
7107 dict_set_free_data(handle_dnrs, free);
7108 plain_dnrs = dict_new();
7109 dict_set_free_data(plain_dnrs, free);
7110 mask_dnrs = dict_new();
7111 dict_set_free_data(mask_dnrs, free);
7113 reg_svccmd_unbind_func(handle_svccmd_unbind);
7114 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7115 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7116 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7117 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7118 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7119 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7120 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7121 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7122 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7124 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7125 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7127 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7128 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7129 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7130 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7131 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7133 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7134 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7135 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7136 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7137 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7139 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7140 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7141 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7142 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7144 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7145 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7146 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7147 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7148 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7149 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7150 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7151 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7153 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7154 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7155 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7156 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7157 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7158 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7159 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7160 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7161 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7162 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7163 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7164 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7165 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7166 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7168 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7169 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7170 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7171 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7172 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7174 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7175 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7177 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7178 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7179 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7180 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7181 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7182 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7183 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7184 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7185 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7186 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7187 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7189 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7190 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7192 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7193 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7194 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7195 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7197 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7198 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7199 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7200 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7201 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7203 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7204 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7205 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7206 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7207 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7208 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7210 /* Channel options */
7211 DEFINE_CHANNEL_OPTION(defaulttopic);
7212 DEFINE_CHANNEL_OPTION(topicmask);
7213 DEFINE_CHANNEL_OPTION(greeting);
7214 DEFINE_CHANNEL_OPTION(usergreeting);
7215 DEFINE_CHANNEL_OPTION(modes);
7216 DEFINE_CHANNEL_OPTION(enfops);
7217 DEFINE_CHANNEL_OPTION(giveops);
7218 DEFINE_CHANNEL_OPTION(protect);
7219 DEFINE_CHANNEL_OPTION(enfmodes);
7220 DEFINE_CHANNEL_OPTION(enftopic);
7221 DEFINE_CHANNEL_OPTION(pubcmd);
7222 DEFINE_CHANNEL_OPTION(givevoice);
7223 DEFINE_CHANNEL_OPTION(userinfo);
7224 DEFINE_CHANNEL_OPTION(dynlimit);
7225 DEFINE_CHANNEL_OPTION(topicsnarf);
7226 DEFINE_CHANNEL_OPTION(nodelete);
7227 DEFINE_CHANNEL_OPTION(toys);
7228 DEFINE_CHANNEL_OPTION(setters);
7229 DEFINE_CHANNEL_OPTION(topicrefresh);
7230 DEFINE_CHANNEL_OPTION(ctcpusers);
7231 DEFINE_CHANNEL_OPTION(ctcpreaction);
7232 DEFINE_CHANNEL_OPTION(inviteme);
7234 DEFINE_CHANNEL_OPTION(offchannel);
7235 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7237 /* Alias set topic to set defaulttopic for compatibility. */
7238 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7241 DEFINE_USER_OPTION(noautoop);
7242 DEFINE_USER_OPTION(autoinvite);
7243 DEFINE_USER_OPTION(info);
7245 /* Alias uset autovoice to uset autoop. */
7246 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7248 note_types = dict_new();
7249 dict_set_free_data(note_types, chanserv_deref_note_type);
7252 chanserv = AddService(nick, "Channel Services", NULL);
7253 service_register(chanserv)->trigger = '!';
7254 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7256 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7258 if(chanserv_conf.channel_expire_frequency)
7259 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7261 if(chanserv_conf.refresh_period)
7263 time_t next_refresh;
7264 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7265 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7268 reg_exit_func(chanserv_db_cleanup);
7269 message_register_table(msgtab);