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 struct string_list *set_shows;
504 struct string_list *eightball;
505 struct string_list *old_ban_names;
507 const char *ctcp_short_ban_duration;
508 const char *ctcp_long_ban_duration;
510 const char *irc_operator_epithet;
511 const char *network_helper_epithet;
512 const char *support_helper_epithet;
517 struct userNode *user;
518 struct userNode *bot;
519 struct chanNode *channel;
521 unsigned short lowest;
522 unsigned short highest;
523 struct userData **users;
524 struct helpfile_table table;
527 enum note_access_type
529 NOTE_SET_CHANNEL_ACCESS,
530 NOTE_SET_CHANNEL_SETTER,
534 enum note_visible_type
537 NOTE_VIS_CHANNEL_USERS,
543 enum note_access_type set_access_type;
545 unsigned int min_opserv;
546 unsigned short min_ulevel;
548 enum note_visible_type visible_type;
549 unsigned int max_length;
556 struct note_type *type;
557 char setter[NICKSERV_HANDLE_LEN+1];
561 static unsigned int registered_channels;
562 static unsigned int banCount;
564 static const struct {
567 unsigned short level;
570 { "peon", "Peon", UL_PEON, '+' },
571 { "op", "Op", UL_OP, '@' },
572 { "master", "Master", UL_MASTER, '%' },
573 { "coowner", "Coowner", UL_COOWNER, '*' },
574 { "owner", "Owner", UL_OWNER, '!' },
575 { "helper", "BUG:", UL_HELPER, 'X' }
578 static const struct {
581 unsigned short default_value;
582 unsigned int old_idx;
583 unsigned int old_flag;
584 unsigned short flag_value;
586 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
587 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
588 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
589 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
590 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
591 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
592 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
593 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
594 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
595 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
596 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
599 struct charOptionValues {
602 } protectValues[] = {
603 { 'a', "CSMSG_PROTECT_ALL" },
604 { 'e', "CSMSG_PROTECT_EQUAL" },
605 { 'l', "CSMSG_PROTECT_LOWER" },
606 { 'n', "CSMSG_PROTECT_NONE" }
608 { 'd', "CSMSG_TOYS_DISABLED" },
609 { 'n', "CSMSG_TOYS_PRIVATE" },
610 { 'p', "CSMSG_TOYS_PUBLIC" }
611 }, topicRefreshValues[] = {
612 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
613 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
614 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
615 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
616 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
617 }, ctcpReactionValues[] = {
618 { 'k', "CSMSG_CTCPREACTION_KICK" },
619 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
620 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
621 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
624 static const struct {
628 unsigned int old_idx;
630 struct charOptionValues *values;
632 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
633 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
634 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
635 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
638 struct userData *helperList;
639 struct chanData *channelList;
640 static struct module *chanserv_module;
641 static unsigned int userCount;
643 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
644 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
645 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
648 user_level_from_name(const char *name, unsigned short clamp_level)
650 unsigned int level = 0, ii;
652 level = strtoul(name, NULL, 10);
653 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
654 if(!irccasecmp(name, accessLevels[ii].name))
655 level = accessLevels[ii].level;
656 if(level > clamp_level)
662 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
665 *minl = strtoul(arg, &sep, 10);
673 *maxl = strtoul(sep+1, &sep, 10);
681 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
683 struct userData *uData, **head;
685 if(!channel || !handle)
688 if(override && HANDLE_FLAGGED(handle, HELPING)
689 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
691 for(uData = helperList;
692 uData && uData->handle != handle;
693 uData = uData->next);
697 uData = calloc(1, sizeof(struct userData));
698 uData->handle = handle;
700 uData->access = UL_HELPER;
706 uData->next = helperList;
708 helperList->prev = uData;
716 for(uData = channel->users; uData; uData = uData->next)
717 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
720 head = &(channel->users);
723 if(uData && (uData != *head))
725 /* Shuffle the user to the head of whatever list he was in. */
727 uData->next->prev = uData->prev;
729 uData->prev->next = uData->next;
735 (**head).prev = uData;
742 /* Returns non-zero if user has at least the minimum access.
743 * exempt_owner is set when handling !set, so the owner can set things
746 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
748 struct userData *uData;
749 struct chanData *cData = channel->channel_info;
750 unsigned short minimum = cData->lvlOpts[opt];
753 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
756 if(minimum <= uData->access)
758 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
763 /* Scan for other users authenticated to the same handle
764 still in the channel. If so, keep them listed as present.
766 user is optional, if not null, it skips checking that userNode
767 (for the handle_part function) */
769 scan_user_presence(struct userData *uData, struct userNode *user)
773 if(IsSuspended(uData->channel)
774 || IsUserSuspended(uData)
775 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
787 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
789 unsigned int eflags, argc;
791 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
793 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
794 if(!channel->channel_info
795 || IsSuspended(channel->channel_info)
797 || !ircncasecmp(text, "ACTION ", 7))
799 /* Figure out the minimum level needed to CTCP the channel */
800 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
802 /* We need to enforce against them; do so. */
805 argv[1] = user->nick;
807 if(GetUserMode(channel, user))
808 eflags |= ACTION_KICK;
809 switch(channel->channel_info->chOpts[chCTCPReaction]) {
810 default: case 'k': /* just do the kick */ break;
812 eflags |= ACTION_BAN;
815 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
816 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
823 argv[argc++] = bad_ctcp_reason;
824 eject_user(chanserv, channel, argc, argv, NULL, eflags);
828 chanserv_create_note_type(const char *name)
830 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
831 strcpy(ntype->name, name);
833 dict_insert(note_types, ntype->name, ntype);
838 chanserv_deref_note_type(void *data)
840 struct note_type *ntype = data;
842 if(--ntype->refs > 0)
848 chanserv_flush_note_type(struct note_type *ntype)
850 struct chanData *cData;
851 for(cData = channelList; cData; cData = cData->next)
852 dict_remove(cData->notes, ntype->name);
856 chanserv_truncate_notes(struct note_type *ntype)
858 struct chanData *cData;
860 unsigned int size = sizeof(*note) + ntype->max_length;
862 for(cData = channelList; cData; cData = cData->next) {
863 note = dict_find(cData->notes, ntype->name, NULL);
866 if(strlen(note->note) <= ntype->max_length)
868 dict_remove2(cData->notes, ntype->name, 1);
869 note = realloc(note, size);
870 note->note[ntype->max_length] = 0;
871 dict_insert(cData->notes, ntype->name, note);
875 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
878 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
881 unsigned int len = strlen(text);
883 if(len > type->max_length) len = type->max_length;
884 note = calloc(1, sizeof(*note) + len);
886 strncpy(note->setter, setter, sizeof(note->setter)-1);
887 memcpy(note->note, text, len);
889 dict_insert(channel->notes, type->name, note);
895 chanserv_free_note(void *data)
897 struct note *note = data;
899 chanserv_deref_note_type(note->type);
900 assert(note->type->refs > 0); /* must use delnote to remove the type */
904 static MODCMD_FUNC(cmd_createnote) {
905 struct note_type *ntype;
906 unsigned int arg = 1, existed = 0, max_length;
908 if((ntype = dict_find(note_types, argv[1], NULL)))
911 ntype = chanserv_create_note_type(argv[arg]);
912 if(!irccasecmp(argv[++arg], "privileged"))
915 ntype->set_access_type = NOTE_SET_PRIVILEGED;
916 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
918 else if(!irccasecmp(argv[arg], "channel"))
920 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
923 reply("CSMSG_INVALID_ACCESS", argv[arg]);
926 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
927 ntype->set_access.min_ulevel = ulvl;
929 else if(!irccasecmp(argv[arg], "setter"))
931 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
935 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
939 if(!irccasecmp(argv[++arg], "privileged"))
940 ntype->visible_type = NOTE_VIS_PRIVILEGED;
941 else if(!irccasecmp(argv[arg], "channel_users"))
942 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
943 else if(!irccasecmp(argv[arg], "all"))
944 ntype->visible_type = NOTE_VIS_ALL;
946 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
950 if((arg+1) >= argc) {
951 reply("MSG_MISSING_PARAMS", argv[0]);
954 max_length = strtoul(argv[++arg], NULL, 0);
955 if(max_length < 20 || max_length > 450)
957 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
960 if(existed && (max_length < ntype->max_length))
962 ntype->max_length = max_length;
963 chanserv_truncate_notes(ntype);
965 ntype->max_length = max_length;
968 reply("CSMSG_NOTE_MODIFIED", ntype->name);
970 reply("CSMSG_NOTE_CREATED", ntype->name);
975 dict_remove(note_types, ntype->name);
979 static MODCMD_FUNC(cmd_removenote) {
980 struct note_type *ntype;
983 ntype = dict_find(note_types, argv[1], NULL);
984 force = (argc > 2) && !irccasecmp(argv[2], "force");
987 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
994 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
997 chanserv_flush_note_type(ntype);
999 dict_remove(note_types, argv[1]);
1000 reply("CSMSG_NOTE_DELETED", argv[1]);
1005 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1009 if(orig->modes_set & change->modes_clear)
1011 if(orig->modes_clear & change->modes_set)
1013 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1014 && strcmp(orig->new_key, change->new_key))
1016 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1017 && (orig->new_limit != change->new_limit))
1022 static char max_length_text[MAXLEN+1][16];
1024 static struct helpfile_expansion
1025 chanserv_expand_variable(const char *variable)
1027 struct helpfile_expansion exp;
1029 if(!irccasecmp(variable, "notes"))
1032 exp.type = HF_TABLE;
1033 exp.value.table.length = 1;
1034 exp.value.table.width = 3;
1035 exp.value.table.flags = 0;
1036 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1037 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1038 exp.value.table.contents[0][0] = "Note Type";
1039 exp.value.table.contents[0][1] = "Visibility";
1040 exp.value.table.contents[0][2] = "Max Length";
1041 for(it=dict_first(note_types); it; it=iter_next(it))
1043 struct note_type *ntype = iter_data(it);
1046 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1047 row = exp.value.table.length++;
1048 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1049 exp.value.table.contents[row][0] = ntype->name;
1050 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1051 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1053 if(!max_length_text[ntype->max_length][0])
1054 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1055 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1060 exp.type = HF_STRING;
1061 exp.value.str = NULL;
1065 static struct chanData*
1066 register_channel(struct chanNode *cNode, char *registrar)
1068 struct chanData *channel;
1069 enum levelOption lvlOpt;
1070 enum charOption chOpt;
1072 channel = calloc(1, sizeof(struct chanData));
1074 channel->notes = dict_new();
1075 dict_set_free_data(channel->notes, chanserv_free_note);
1077 channel->registrar = strdup(registrar);
1078 channel->registered = now;
1079 channel->visited = now;
1080 channel->limitAdjusted = now;
1081 channel->flags = CHANNEL_DEFAULT_FLAGS;
1082 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1083 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1084 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1085 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1087 channel->prev = NULL;
1088 channel->next = channelList;
1091 channelList->prev = channel;
1092 channelList = channel;
1093 registered_channels++;
1095 channel->channel = cNode;
1097 cNode->channel_info = channel;
1102 static struct userData*
1103 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1105 struct userData *ud;
1107 if(access > UL_OWNER)
1110 ud = calloc(1, sizeof(*ud));
1111 ud->channel = channel;
1112 ud->handle = handle;
1114 ud->access = access;
1115 ud->info = info ? strdup(info) : NULL;
1118 ud->next = channel->users;
1120 channel->users->prev = ud;
1121 channel->users = ud;
1123 channel->userCount++;
1127 ud->u_next = ud->handle->channels;
1129 ud->u_next->u_prev = ud;
1130 ud->handle->channels = ud;
1135 static void unregister_channel(struct chanData *channel, const char *reason);
1138 del_channel_user(struct userData *user, int do_gc)
1140 struct chanData *channel = user->channel;
1142 channel->userCount--;
1146 user->prev->next = user->next;
1148 channel->users = user->next;
1150 user->next->prev = user->prev;
1153 user->u_prev->u_next = user->u_next;
1155 user->handle->channels = user->u_next;
1157 user->u_next->u_prev = user->u_prev;
1161 if(do_gc && !channel->users && !IsProtected(channel))
1162 unregister_channel(channel, "lost all users.");
1165 static void expire_ban(void *data);
1167 static struct banData*
1168 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1171 unsigned int ii, l1, l2;
1176 bd = malloc(sizeof(struct banData));
1178 bd->channel = channel;
1180 bd->triggered = triggered;
1181 bd->expires = expires;
1183 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1185 extern const char *hidden_host_suffix;
1186 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1190 l2 = strlen(old_name);
1193 if(irccasecmp(mask + l1 - l2, old_name))
1195 new_mask = alloca(MAXLEN);
1196 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1199 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1201 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1202 bd->reason = reason ? strdup(reason) : NULL;
1205 timeq_add(expires, expire_ban, bd);
1208 bd->next = channel->bans;
1210 channel->bans->prev = bd;
1212 channel->banCount++;
1219 del_channel_ban(struct banData *ban)
1221 ban->channel->banCount--;
1225 ban->prev->next = ban->next;
1227 ban->channel->bans = ban->next;
1230 ban->next->prev = ban->prev;
1233 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1242 expire_ban(void *data)
1244 struct banData *bd = data;
1245 if(!IsSuspended(bd->channel))
1247 struct banList bans;
1248 struct mod_chanmode change;
1250 bans = bd->channel->channel->banlist;
1251 mod_chanmode_init(&change);
1252 for(ii=0; ii<bans.used; ii++)
1254 if(!strcmp(bans.list[ii]->ban, bd->mask))
1257 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1258 change.args[0].hostmask = bd->mask;
1259 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1265 del_channel_ban(bd);
1268 static void chanserv_expire_suspension(void *data);
1271 unregister_channel(struct chanData *channel, const char *reason)
1273 struct mod_chanmode change;
1274 char msgbuf[MAXLEN];
1276 /* After channel unregistration, the following must be cleaned
1278 - Channel information.
1281 - Channel suspension data.
1282 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1288 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1290 mod_chanmode_init(&change);
1291 change.modes_clear |= MODE_REGISTERED;
1292 mod_chanmode_announce(chanserv, channel->channel, &change);
1294 while(channel->users)
1295 del_channel_user(channel->users, 0);
1297 while(channel->bans)
1298 del_channel_ban(channel->bans);
1300 free(channel->topic);
1301 free(channel->registrar);
1302 free(channel->greeting);
1303 free(channel->user_greeting);
1304 free(channel->topic_mask);
1307 channel->prev->next = channel->next;
1309 channelList = channel->next;
1312 channel->next->prev = channel->prev;
1314 if(channel->suspended)
1316 struct chanNode *cNode = channel->channel;
1317 struct suspended *suspended, *next_suspended;
1319 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1321 next_suspended = suspended->previous;
1322 free(suspended->suspender);
1323 free(suspended->reason);
1324 if(suspended->expires)
1325 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1330 cNode->channel_info = NULL;
1332 channel->channel->channel_info = NULL;
1334 dict_delete(channel->notes);
1335 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1336 if(!IsSuspended(channel))
1337 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1338 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1339 UnlockChannel(channel->channel);
1341 registered_channels--;
1345 expire_channels(UNUSED_ARG(void *data))
1347 struct chanData *channel, *next;
1348 struct userData *user;
1349 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1351 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1352 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1354 for(channel = channelList; channel; channel = next)
1356 next = channel->next;
1358 /* See if the channel can be expired. */
1359 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1360 || IsProtected(channel))
1363 /* Make sure there are no high-ranking users still in the channel. */
1364 for(user=channel->users; user; user=user->next)
1365 if(user->present && (user->access >= UL_PRESENT))
1370 /* Unregister the channel */
1371 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1372 unregister_channel(channel, "registration expired.");
1375 if(chanserv_conf.channel_expire_frequency)
1376 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1380 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1382 char protect = channel->chOpts[chProtect];
1383 struct userData *cs_victim, *cs_aggressor;
1385 /* Don't protect if no one is to be protected, someone is attacking
1386 himself, or if the aggressor is an IRC Operator. */
1387 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1390 /* Don't protect if the victim isn't authenticated (because they
1391 can't be a channel user), unless we are to protect non-users
1393 cs_victim = GetChannelAccess(channel, victim->handle_info);
1394 if(protect != 'a' && !cs_victim)
1397 /* Protect if the aggressor isn't a user because at this point,
1398 the aggressor can only be less than or equal to the victim. */
1399 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1403 /* If the aggressor was a user, then the victim can't be helped. */
1410 if(cs_victim->access > cs_aggressor->access)
1415 if(cs_victim->access >= cs_aggressor->access)
1424 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1426 struct chanData *cData = channel->channel_info;
1427 struct userData *cs_victim;
1429 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1430 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1431 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1433 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1441 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1443 if(IsService(victim))
1445 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1449 if(protect_user(victim, user, channel->channel_info))
1451 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1458 static struct do_not_register *
1459 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1461 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1462 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1463 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1464 strcpy(dnr->reason, reason);
1466 if(dnr->chan_name[0] == '*')
1467 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1468 else if(strpbrk(dnr->chan_name, "*?"))
1469 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1471 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1475 static struct dnrList
1476 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1478 struct dnrList list;
1480 struct do_not_register *dnr;
1482 dnrList_init(&list);
1483 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1484 dnrList_append(&list, dnr);
1485 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1486 dnrList_append(&list, dnr);
1488 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1489 if(match_ircglob(chan_name, iter_key(it)))
1490 dnrList_append(&list, iter_data(it));
1495 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1497 struct dnrList list;
1498 struct do_not_register *dnr;
1500 char buf[INTERVALLEN];
1502 list = chanserv_find_dnrs(chan_name, handle);
1503 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1505 dnr = list.list[ii];
1508 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1509 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1512 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1515 reply("CSMSG_MORE_DNRS", list.used - ii);
1520 struct do_not_register *
1521 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1523 struct do_not_register *dnr;
1526 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1530 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1532 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1533 if(match_ircglob(chan_name, iter_key(it)))
1534 return iter_data(it);
1539 static CHANSERV_FUNC(cmd_noregister)
1542 struct do_not_register *dnr;
1543 char buf[INTERVALLEN];
1544 unsigned int matches;
1550 reply("CSMSG_DNR_SEARCH_RESULTS");
1552 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1554 dnr = iter_data(it);
1556 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1558 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1561 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1563 dnr = iter_data(it);
1565 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1567 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1570 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1572 dnr = iter_data(it);
1574 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1576 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1581 reply("MSG_MATCH_COUNT", matches);
1583 reply("MSG_NO_MATCHES");
1589 if(!IsChannelName(target) && (*target != '*'))
1591 reply("CSMSG_NOT_DNR", target);
1597 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1598 if((*target == '*') && !get_handle_info(target + 1))
1600 reply("MSG_HANDLE_UNKNOWN", target + 1);
1603 chanserv_add_dnr(target, user->handle_info->handle, reason);
1604 reply("CSMSG_NOREGISTER_CHANNEL", target);
1608 reply("CSMSG_DNR_SEARCH_RESULTS");
1610 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1612 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1614 reply("MSG_NO_MATCHES");
1618 static CHANSERV_FUNC(cmd_allowregister)
1620 const char *chan_name = argv[1];
1622 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1624 dict_remove(handle_dnrs, chan_name+1);
1625 reply("CSMSG_DNR_REMOVED", chan_name);
1627 else if(dict_find(plain_dnrs, chan_name, NULL))
1629 dict_remove(plain_dnrs, chan_name);
1630 reply("CSMSG_DNR_REMOVED", chan_name);
1632 else if(dict_find(mask_dnrs, chan_name, NULL))
1634 dict_remove(mask_dnrs, chan_name);
1635 reply("CSMSG_DNR_REMOVED", chan_name);
1639 reply("CSMSG_NO_SUCH_DNR", chan_name);
1646 chanserv_get_owned_count(struct handle_info *hi)
1648 struct userData *cList;
1651 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1652 if(cList->access == UL_OWNER)
1657 static CHANSERV_FUNC(cmd_register)
1659 struct mod_chanmode *change;
1660 struct handle_info *handle;
1661 struct chanData *cData;
1662 struct modeNode *mn;
1663 char reason[MAXLEN];
1665 unsigned int new_channel, force=0;
1666 struct do_not_register *dnr;
1670 if(channel->channel_info)
1672 reply("CSMSG_ALREADY_REGGED", channel->name);
1676 if(channel->bad_channel)
1678 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1682 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1684 reply("CSMSG_MUST_BE_OPPED", channel->name);
1689 chan_name = channel->name;
1693 if((argc < 2) || !IsChannelName(argv[1]))
1695 reply("MSG_NOT_CHANNEL_NAME");
1699 if(opserv_bad_channel(argv[1]))
1701 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1706 chan_name = argv[1];
1709 if(argc >= (new_channel+2))
1711 if(!IsHelping(user))
1713 reply("CSMSG_PROXY_FORBIDDEN");
1717 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1719 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1720 dnr = chanserv_is_dnr(chan_name, handle);
1724 handle = user->handle_info;
1725 dnr = chanserv_is_dnr(chan_name, handle);
1729 if(!IsHelping(user))
1730 reply("CSMSG_DNR_CHANNEL", chan_name);
1732 chanserv_show_dnrs(user, cmd, chan_name, handle);
1736 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1738 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1743 channel = AddChannel(argv[1], now, NULL, NULL);
1745 cData = register_channel(channel, user->handle_info->handle);
1746 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1747 cData->modes = chanserv_conf.default_modes;
1748 change = mod_chanmode_dup(&cData->modes, 1);
1749 change->args[change->argc].mode = MODE_CHANOP;
1750 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1752 mod_chanmode_announce(chanserv, channel, change);
1753 mod_chanmode_free(change);
1755 /* Initialize the channel's max user record. */
1756 cData->max = channel->members.used;
1758 if(handle != user->handle_info)
1759 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1761 reply("CSMSG_REG_SUCCESS", channel->name);
1763 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1764 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1769 make_confirmation_string(struct userData *uData)
1771 static char strbuf[16];
1776 for(src = uData->handle->handle; *src; )
1777 accum = accum * 31 + toupper(*src++);
1779 for(src = uData->channel->channel->name; *src; )
1780 accum = accum * 31 + toupper(*src++);
1781 sprintf(strbuf, "%08x", accum);
1785 static CHANSERV_FUNC(cmd_unregister)
1788 char reason[MAXLEN];
1789 struct chanData *cData;
1790 struct userData *uData;
1792 cData = channel->channel_info;
1795 reply("CSMSG_NOT_REGISTERED", channel->name);
1799 uData = GetChannelUser(cData, user->handle_info);
1800 if(!uData || (uData->access < UL_OWNER))
1802 reply("CSMSG_NO_ACCESS");
1806 if(IsProtected(cData))
1808 reply("CSMSG_UNREG_NODELETE", channel->name);
1812 if(!IsHelping(user))
1814 const char *confirm_string;
1815 if(IsSuspended(cData))
1817 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1820 confirm_string = make_confirmation_string(uData);
1821 if((argc < 2) || strcmp(argv[1], confirm_string))
1823 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1828 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1829 name = strdup(channel->name);
1830 unregister_channel(cData, reason);
1831 reply("CSMSG_UNREG_SUCCESS", name);
1836 static CHANSERV_FUNC(cmd_move)
1838 struct chanNode *target;
1839 struct modeNode *mn;
1840 struct userData *uData;
1841 char reason[MAXLEN];
1842 struct do_not_register *dnr;
1846 if(IsProtected(channel->channel_info))
1848 reply("CSMSG_MOVE_NODELETE", channel->name);
1852 if(!IsChannelName(argv[1]))
1854 reply("MSG_NOT_CHANNEL_NAME");
1858 if(opserv_bad_channel(argv[1]))
1860 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1864 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1866 for(uData = channel->channel_info->users; uData; uData = uData->next)
1868 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1870 if(!IsHelping(user))
1871 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1873 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1879 if(!(target = GetChannel(argv[1])))
1881 target = AddChannel(argv[1], now, NULL, NULL);
1882 if(!IsSuspended(channel->channel_info))
1883 AddChannelUser(chanserv, target);
1885 else if(target->channel_info)
1887 reply("CSMSG_ALREADY_REGGED", target->name);
1890 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1891 && !IsHelping(user))
1893 reply("CSMSG_MUST_BE_OPPED", target->name);
1896 else if(!IsSuspended(channel->channel_info))
1898 struct mod_chanmode change;
1899 mod_chanmode_init(&change);
1901 change.args[0].mode = MODE_CHANOP;
1902 change.args[0].member = AddChannelUser(chanserv, target);
1903 mod_chanmode_announce(chanserv, target, &change);
1906 /* Move the channel_info to the target channel; it
1907 shouldn't be necessary to clear timeq callbacks
1908 for the old channel. */
1909 target->channel_info = channel->channel_info;
1910 target->channel_info->channel = target;
1911 channel->channel_info = NULL;
1913 reply("CSMSG_MOVE_SUCCESS", target->name);
1915 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1916 if(!IsSuspended(target->channel_info))
1918 char reason2[MAXLEN];
1919 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1920 DelChannelUser(chanserv, channel, reason2, 0);
1922 UnlockChannel(channel);
1923 LockChannel(target);
1924 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1929 merge_users(struct chanData *source, struct chanData *target)
1931 struct userData *suData, *tuData, *next;
1937 /* Insert the source's users into the scratch area. */
1938 for(suData = source->users; suData; suData = suData->next)
1939 dict_insert(merge, suData->handle->handle, suData);
1941 /* Iterate through the target's users, looking for
1942 users common to both channels. The lower access is
1943 removed from either the scratch area or target user
1945 for(tuData = target->users; tuData; tuData = next)
1947 struct userData *choice;
1949 next = tuData->next;
1951 /* If a source user exists with the same handle as a target
1952 channel's user, resolve the conflict by removing one. */
1953 suData = dict_find(merge, tuData->handle->handle, NULL);
1957 /* Pick the data we want to keep. */
1958 /* If the access is the same, use the later seen time. */
1959 if(suData->access == tuData->access)
1960 choice = (suData->seen > tuData->seen) ? suData : tuData;
1961 else /* Otherwise, keep the higher access level. */
1962 choice = (suData->access > tuData->access) ? suData : tuData;
1964 /* Remove the user that wasn't picked. */
1965 if(choice == tuData)
1967 dict_remove(merge, suData->handle->handle);
1968 del_channel_user(suData, 0);
1971 del_channel_user(tuData, 0);
1974 /* Move the remaining users to the target channel. */
1975 for(it = dict_first(merge); it; it = iter_next(it))
1977 suData = iter_data(it);
1979 /* Insert the user into the target channel's linked list. */
1980 suData->prev = NULL;
1981 suData->next = target->users;
1982 suData->channel = target;
1985 target->users->prev = suData;
1986 target->users = suData;
1988 /* Update the user counts for the target channel; the
1989 source counts are left alone. */
1990 target->userCount++;
1993 /* Possible to assert (source->users == NULL) here. */
1994 source->users = NULL;
1999 merge_bans(struct chanData *source, struct chanData *target)
2001 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2003 /* Hold on to the original head of the target ban list
2004 to avoid comparing source bans with source bans. */
2005 tFront = target->bans;
2007 /* Perform a totally expensive O(n*m) merge, ick. */
2008 for(sbData = source->bans; sbData; sbData = sNext)
2010 /* Flag to track whether the ban's been moved
2011 to the destination yet. */
2014 /* Possible to assert (sbData->prev == NULL) here. */
2015 sNext = sbData->next;
2017 for(tbData = tFront; tbData; tbData = tNext)
2019 tNext = tbData->next;
2021 /* Perform two comparisons between each source
2022 and target ban, conflicts are resolved by
2023 keeping the broader ban and copying the later
2024 expiration and triggered time. */
2025 if(match_ircglobs(tbData->mask, sbData->mask))
2027 /* There is a broader ban in the target channel that
2028 overrides one in the source channel; remove the
2029 source ban and break. */
2030 if(sbData->expires > tbData->expires)
2031 tbData->expires = sbData->expires;
2032 if(sbData->triggered > tbData->triggered)
2033 tbData->triggered = sbData->triggered;
2034 del_channel_ban(sbData);
2037 else if(match_ircglobs(sbData->mask, tbData->mask))
2039 /* There is a broader ban in the source channel that
2040 overrides one in the target channel; remove the
2041 target ban, fall through and move the source over. */
2042 if(tbData->expires > sbData->expires)
2043 sbData->expires = tbData->expires;
2044 if(tbData->triggered > sbData->triggered)
2045 sbData->triggered = tbData->triggered;
2046 if(tbData == tFront)
2048 del_channel_ban(tbData);
2051 /* Source bans can override multiple target bans, so
2052 we allow a source to run through this loop multiple
2053 times, but we can only move it once. */
2058 /* Remove the source ban from the source ban list. */
2060 sbData->next->prev = sbData->prev;
2062 /* Modify the source ban's associated channel. */
2063 sbData->channel = target;
2065 /* Insert the ban into the target channel's linked list. */
2066 sbData->prev = NULL;
2067 sbData->next = target->bans;
2070 target->bans->prev = sbData;
2071 target->bans = sbData;
2073 /* Update the user counts for the target channel. */
2078 /* Possible to assert (source->bans == NULL) here. */
2079 source->bans = NULL;
2083 merge_data(struct chanData *source, struct chanData *target)
2085 if(source->visited > target->visited)
2086 target->visited = source->visited;
2090 merge_channel(struct chanData *source, struct chanData *target)
2092 merge_users(source, target);
2093 merge_bans(source, target);
2094 merge_data(source, target);
2097 static CHANSERV_FUNC(cmd_merge)
2099 struct userData *target_user;
2100 struct chanNode *target;
2101 char reason[MAXLEN];
2105 /* Make sure the target channel exists and is registered to the user
2106 performing the command. */
2107 if(!(target = GetChannel(argv[1])))
2109 reply("MSG_INVALID_CHANNEL");
2113 if(!target->channel_info)
2115 reply("CSMSG_NOT_REGISTERED", target->name);
2119 if(IsProtected(channel->channel_info))
2121 reply("CSMSG_MERGE_NODELETE");
2125 if(IsSuspended(target->channel_info))
2127 reply("CSMSG_MERGE_SUSPENDED");
2131 if(channel == target)
2133 reply("CSMSG_MERGE_SELF");
2137 target_user = GetChannelUser(target->channel_info, user->handle_info);
2138 if(!target_user || (target_user->access < UL_OWNER))
2140 reply("CSMSG_MERGE_NOT_OWNER");
2144 /* Merge the channel structures and associated data. */
2145 merge_channel(channel->channel_info, target->channel_info);
2146 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2147 unregister_channel(channel->channel_info, reason);
2148 reply("CSMSG_MERGE_SUCCESS", target->name);
2152 static CHANSERV_FUNC(cmd_opchan)
2154 struct mod_chanmode change;
2155 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2157 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2160 channel->channel_info->may_opchan = 0;
2161 mod_chanmode_init(&change);
2163 change.args[0].mode = MODE_CHANOP;
2164 change.args[0].member = GetUserMode(channel, chanserv);
2165 mod_chanmode_announce(chanserv, channel, &change);
2166 reply("CSMSG_OPCHAN_DONE", channel->name);
2170 static CHANSERV_FUNC(cmd_adduser)
2172 struct userData *actee;
2173 struct userData *actor;
2174 struct handle_info *handle;
2175 unsigned short access;
2179 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2181 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2185 access = user_level_from_name(argv[2], UL_OWNER);
2188 reply("CSMSG_INVALID_ACCESS", argv[2]);
2192 actor = GetChannelUser(channel->channel_info, user->handle_info);
2193 if(actor->access <= access)
2195 reply("CSMSG_NO_BUMP_ACCESS");
2199 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2202 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2204 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2208 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2209 scan_user_presence(actee, NULL);
2210 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2214 static CHANSERV_FUNC(cmd_clvl)
2216 struct handle_info *handle;
2217 struct userData *victim;
2218 struct userData *actor;
2219 unsigned short new_access;
2220 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2224 actor = GetChannelUser(channel->channel_info, user->handle_info);
2226 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2229 if(handle == user->handle_info && !privileged)
2231 reply("CSMSG_NO_SELF_CLVL");
2235 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2237 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2241 if(actor->access <= victim->access && !privileged)
2243 reply("MSG_USER_OUTRANKED", handle->handle);
2247 new_access = user_level_from_name(argv[2], UL_OWNER);
2251 reply("CSMSG_INVALID_ACCESS", argv[2]);
2255 if(new_access >= actor->access && !privileged)
2257 reply("CSMSG_NO_BUMP_ACCESS");
2261 victim->access = new_access;
2262 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2266 static CHANSERV_FUNC(cmd_deluser)
2268 struct handle_info *handle;
2269 struct userData *victim;
2270 struct userData *actor;
2271 unsigned short access;
2276 actor = GetChannelUser(channel->channel_info, user->handle_info);
2278 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2281 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2283 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2289 access = user_level_from_name(argv[1], UL_OWNER);
2292 reply("CSMSG_INVALID_ACCESS", argv[1]);
2295 if(access != victim->access)
2297 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2303 access = victim->access;
2306 if((actor->access <= victim->access) && !IsHelping(user))
2308 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2312 chan_name = strdup(channel->name);
2313 del_channel_user(victim, 1);
2314 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2320 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2322 struct userData *actor, *uData, *next;
2324 actor = GetChannelUser(channel->channel_info, user->handle_info);
2326 if(min_access > max_access)
2328 reply("CSMSG_BAD_RANGE", min_access, max_access);
2332 if((actor->access <= max_access) && !IsHelping(user))
2334 reply("CSMSG_NO_ACCESS");
2338 for(uData = channel->channel_info->users; uData; uData = next)
2342 if((uData->access >= min_access)
2343 && (uData->access <= max_access)
2344 && match_ircglob(uData->handle->handle, mask))
2345 del_channel_user(uData, 1);
2348 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2352 static CHANSERV_FUNC(cmd_mdelowner)
2354 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2357 static CHANSERV_FUNC(cmd_mdelcoowner)
2359 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2362 static CHANSERV_FUNC(cmd_mdelmaster)
2364 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2367 static CHANSERV_FUNC(cmd_mdelop)
2369 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2372 static CHANSERV_FUNC(cmd_mdelpeon)
2374 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2378 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2380 struct banData *bData, *next;
2381 char interval[INTERVALLEN];
2386 limit = now - duration;
2387 for(bData = channel->channel_info->bans; bData; bData = next)
2391 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2394 del_channel_ban(bData);
2398 intervalString(interval, duration, user->handle_info);
2399 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2404 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2406 struct userData *actor, *uData, *next;
2407 char interval[INTERVALLEN];
2411 actor = GetChannelUser(channel->channel_info, user->handle_info);
2412 if(min_access > max_access)
2414 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2418 if((actor->access <= max_access) && !IsHelping(user))
2420 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2425 limit = now - duration;
2426 for(uData = channel->channel_info->users; uData; uData = next)
2430 if((uData->seen > limit) || uData->present)
2433 if(((uData->access >= min_access) && (uData->access <= max_access))
2434 || (!max_access && (uData->access < actor->access)))
2436 del_channel_user(uData, 1);
2444 max_access = UL_OWNER;
2446 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2450 static CHANSERV_FUNC(cmd_trim)
2452 unsigned long duration;
2453 unsigned short min_level, max_level;
2457 duration = ParseInterval(argv[2]);
2460 reply("CSMSG_CANNOT_TRIM");
2464 if(!irccasecmp(argv[1], "bans"))
2466 cmd_trim_bans(user, channel, duration);
2469 else if(!irccasecmp(argv[1], "users"))
2471 cmd_trim_users(user, channel, 0, 0, duration);
2474 else if(parse_level_range(&min_level, &max_level, argv[1]))
2476 cmd_trim_users(user, channel, min_level, max_level, duration);
2479 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2481 cmd_trim_users(user, channel, min_level, min_level, duration);
2486 reply("CSMSG_INVALID_TRIM", argv[1]);
2491 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2492 to the user. cmd_all takes advantage of this. */
2493 static CHANSERV_FUNC(cmd_up)
2495 struct mod_chanmode change;
2496 struct userData *uData;
2499 mod_chanmode_init(&change);
2501 change.args[0].member = GetUserMode(channel, user);
2502 if(!change.args[0].member)
2505 reply("MSG_CHANNEL_ABSENT", channel->name);
2509 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2513 reply("CSMSG_GODMODE_UP", argv[0]);
2516 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2518 change.args[0].mode = MODE_CHANOP;
2519 errmsg = "CSMSG_ALREADY_OPPED";
2521 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2523 change.args[0].mode = MODE_VOICE;
2524 errmsg = "CSMSG_ALREADY_VOICED";
2529 reply("CSMSG_NO_ACCESS");
2532 change.args[0].mode &= ~change.args[0].member->modes;
2533 if(!change.args[0].mode)
2536 reply(errmsg, channel->name);
2539 modcmd_chanmode_announce(&change);
2543 static CHANSERV_FUNC(cmd_down)
2545 struct mod_chanmode change;
2547 mod_chanmode_init(&change);
2549 change.args[0].member = GetUserMode(channel, user);
2550 if(!change.args[0].member)
2553 reply("MSG_CHANNEL_ABSENT", channel->name);
2557 if(!change.args[0].member->modes)
2560 reply("CSMSG_ALREADY_DOWN", channel->name);
2564 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2565 modcmd_chanmode_announce(&change);
2569 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)
2571 struct userData *cList;
2573 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2575 if(IsSuspended(cList->channel)
2576 || IsUserSuspended(cList)
2577 || !GetUserMode(cList->channel->channel, user))
2580 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2586 static CHANSERV_FUNC(cmd_upall)
2588 return cmd_all(CSFUNC_ARGS, cmd_up);
2591 static CHANSERV_FUNC(cmd_downall)
2593 return cmd_all(CSFUNC_ARGS, cmd_down);
2596 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2597 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2600 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)
2602 unsigned int ii, valid;
2603 struct userNode *victim;
2604 struct mod_chanmode *change;
2606 change = mod_chanmode_alloc(argc - 1);
2608 for(ii=valid=0; ++ii < argc; )
2610 if(!(victim = GetUserH(argv[ii])))
2612 change->args[valid].mode = mode;
2613 change->args[valid].member = GetUserMode(channel, victim);
2614 if(!change->args[valid].member)
2616 if(validate && !validate(user, channel, victim))
2621 change->argc = valid;
2622 if(valid < (argc-1))
2623 reply("CSMSG_PROCESS_FAILED");
2626 modcmd_chanmode_announce(change);
2627 reply(action, channel->name);
2629 mod_chanmode_free(change);
2633 static CHANSERV_FUNC(cmd_op)
2635 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2638 static CHANSERV_FUNC(cmd_deop)
2640 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2643 static CHANSERV_FUNC(cmd_voice)
2645 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2648 static CHANSERV_FUNC(cmd_devoice)
2650 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2654 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2660 for(ii=0; ii<channel->members.used; ii++)
2662 struct modeNode *mn = channel->members.list[ii];
2664 if(IsService(mn->user))
2667 if(!user_matches_glob(mn->user, ban, 1))
2670 if(protect_user(mn->user, user, channel->channel_info))
2674 victims[(*victimCount)++] = mn;
2680 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2682 struct userNode *victim;
2683 struct modeNode **victims;
2684 unsigned int offset, n, victimCount, duration = 0;
2685 char *reason = "Bye.", *ban, *name;
2686 char interval[INTERVALLEN];
2688 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2689 REQUIRE_PARAMS(offset);
2692 reason = unsplit_string(argv + offset, argc - offset, NULL);
2693 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2695 /* Truncate the reason to a length of TOPICLEN, as
2696 the ircd does; however, leave room for an ellipsis
2697 and the kicker's nick. */
2698 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2702 if((victim = GetUserH(argv[1])))
2704 victims = alloca(sizeof(victims[0]));
2705 victims[0] = GetUserMode(channel, victim);
2706 /* XXX: The comparison with ACTION_KICK is just because all
2707 * other actions can work on users outside the channel, and we
2708 * want to allow those (e.g. unbans) in that case. If we add
2709 * some other ejection action for in-channel users, change
2711 victimCount = victims[0] ? 1 : 0;
2713 if(IsService(victim))
2715 reply("MSG_SERVICE_IMMUNE", victim->nick);
2719 if((action == ACTION_KICK) && !victimCount)
2721 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2725 if(protect_user(victim, user, channel->channel_info))
2727 reply("CSMSG_USER_PROTECTED", victim->nick);
2731 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2732 name = victim->nick;
2736 if(!is_ircmask(argv[1]))
2738 reply("MSG_NICK_UNKNOWN", argv[1]);
2742 victims = alloca(sizeof(victims[0]) * channel->members.used);
2744 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2746 reply("CSMSG_MASK_PROTECTED", argv[1]);
2750 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2752 reply("CSMSG_LAME_MASK", argv[1]);
2756 if((action == ACTION_KICK) && (victimCount == 0))
2758 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2762 name = ban = strdup(argv[1]);
2765 /* Truncate the ban in place if necessary; we must ensure
2766 that 'ban' is a valid ban mask before sanitizing it. */
2767 sanitize_ircmask(ban);
2769 if(action & ACTION_ADD_BAN)
2771 struct banData *bData, *next;
2773 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2775 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2780 if(action & ACTION_ADD_TIMED_BAN)
2782 duration = ParseInterval(argv[2]);
2786 reply("CSMSG_DURATION_TOO_LOW");
2790 else if(duration > (86400 * 365 * 2))
2792 reply("CSMSG_DURATION_TOO_HIGH");
2798 for(bData = channel->channel_info->bans; bData; bData = next)
2800 if(match_ircglobs(bData->mask, ban))
2802 int exact = !irccasecmp(bData->mask, ban);
2804 /* The ban is redundant; there is already a ban
2805 with the same effect in place. */
2809 free(bData->reason);
2810 bData->reason = strdup(reason);
2811 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2813 reply("CSMSG_REASON_CHANGE", ban);
2817 if(exact && bData->expires)
2821 /* If the ban matches an existing one exactly,
2822 extend the expiration time if the provided
2823 duration is longer. */
2824 if(duration && ((time_t)(now + duration) > bData->expires))
2826 bData->expires = now + duration;
2837 /* Delete the expiration timeq entry and
2838 requeue if necessary. */
2839 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2842 timeq_add(bData->expires, expire_ban, bData);
2846 /* automated kickban */
2849 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2851 reply("CSMSG_BAN_ADDED", name, channel->name);
2857 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2864 if(match_ircglobs(ban, bData->mask))
2866 /* The ban we are adding makes previously existing
2867 bans redundant; silently remove them. */
2868 del_channel_ban(bData);
2872 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);
2874 name = ban = strdup(bData->mask);
2878 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2880 extern const char *hidden_host_suffix;
2881 const char *old_name = chanserv_conf.old_ban_names->list[n];
2883 unsigned int l1, l2;
2886 l2 = strlen(old_name);
2889 if(irccasecmp(ban + l1 - l2, old_name))
2891 new_mask = malloc(MAXLEN);
2892 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2894 name = ban = new_mask;
2899 if(action & ACTION_BAN)
2901 unsigned int exists;
2902 struct mod_chanmode *change;
2904 if(channel->banlist.used >= MAXBANS)
2907 reply("CSMSG_BANLIST_FULL", channel->name);
2912 exists = ChannelBanExists(channel, ban);
2913 change = mod_chanmode_alloc(victimCount + 1);
2914 for(n = 0; n < victimCount; ++n)
2916 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2917 change->args[n].member = victims[n];
2921 change->args[n].mode = MODE_BAN;
2922 change->args[n++].hostmask = ban;
2926 modcmd_chanmode_announce(change);
2928 mod_chanmode_announce(chanserv, channel, change);
2929 mod_chanmode_free(change);
2931 if(exists && (action == ACTION_BAN))
2934 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2940 if(action & ACTION_KICK)
2942 char kick_reason[MAXLEN];
2943 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2945 for(n = 0; n < victimCount; n++)
2946 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2951 /* No response, since it was automated. */
2953 else if(action & ACTION_ADD_BAN)
2956 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2958 reply("CSMSG_BAN_ADDED", name, channel->name);
2960 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2961 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2962 else if(action & ACTION_BAN)
2963 reply("CSMSG_BAN_DONE", name, channel->name);
2964 else if(action & ACTION_KICK && victimCount)
2965 reply("CSMSG_KICK_DONE", name, channel->name);
2971 static CHANSERV_FUNC(cmd_kickban)
2973 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2976 static CHANSERV_FUNC(cmd_kick)
2978 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2981 static CHANSERV_FUNC(cmd_ban)
2983 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2986 static CHANSERV_FUNC(cmd_addban)
2988 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2991 static CHANSERV_FUNC(cmd_addtimedban)
2993 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2996 static struct mod_chanmode *
2997 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2999 struct mod_chanmode *change;
3000 unsigned char *match;
3001 unsigned int ii, count;
3003 match = alloca(bans->used);
3006 for(ii = count = 0; ii < bans->used; ++ii)
3008 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3015 for(ii = count = 0; ii < bans->used; ++ii)
3017 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3024 change = mod_chanmode_alloc(count);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3029 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3030 change->args[count++].hostmask = bans->list[ii]->ban;
3036 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3038 struct userNode *actee;
3044 /* may want to allow a comma delimited list of users... */
3045 if(!(actee = GetUserH(argv[1])))
3047 if(!is_ircmask(argv[1]))
3049 reply("MSG_NICK_UNKNOWN", argv[1]);
3053 mask = strdup(argv[1]);
3056 /* We don't sanitize the mask here because ircu
3058 if(action & ACTION_UNBAN)
3060 struct mod_chanmode *change;
3061 change = find_matching_bans(&channel->banlist, actee, mask);
3064 modcmd_chanmode_announce(change);
3065 mod_chanmode_free(change);
3070 if(action & ACTION_DEL_BAN)
3072 struct banData *ban, *next;
3074 ban = channel->channel_info->bans;
3078 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3081 for( ; ban && !match_ircglobs(mask, ban->mask);
3086 del_channel_ban(ban);
3093 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3095 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3101 static CHANSERV_FUNC(cmd_unban)
3103 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3106 static CHANSERV_FUNC(cmd_delban)
3108 /* it doesn't necessarily have to remove the channel ban - may want
3109 to make that an option. */
3110 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3113 static CHANSERV_FUNC(cmd_unbanme)
3115 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3116 long flags = ACTION_UNBAN;
3118 /* remove permanent bans if the user has the proper access. */
3119 if(uData->access >= UL_MASTER)
3120 flags |= ACTION_DEL_BAN;
3122 argv[1] = user->nick;
3123 return unban_user(user, channel, 2, argv, cmd, flags);
3126 static CHANSERV_FUNC(cmd_unbanall)
3128 struct mod_chanmode *change;
3131 if(!channel->banlist.used)
3133 reply("CSMSG_NO_BANS", channel->name);
3137 change = mod_chanmode_alloc(channel->banlist.used);
3138 for(ii=0; ii<channel->banlist.used; ii++)
3140 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3141 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3143 modcmd_chanmode_announce(change);
3144 mod_chanmode_free(change);
3145 reply("CSMSG_BANS_REMOVED", channel->name);
3149 static CHANSERV_FUNC(cmd_open)
3151 struct mod_chanmode *change;
3153 change = find_matching_bans(&channel->banlist, user, NULL);
3155 change = mod_chanmode_alloc(0);
3156 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3157 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3158 && channel->channel_info->modes.modes_set)
3159 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3160 modcmd_chanmode_announce(change);
3161 reply("CSMSG_CHANNEL_OPENED", channel->name);
3162 mod_chanmode_free(change);
3166 static CHANSERV_FUNC(cmd_myaccess)
3168 static struct string_buffer sbuf;
3169 struct handle_info *target_handle;
3170 struct userData *uData;
3173 target_handle = user->handle_info;
3174 else if(!IsHelping(user))
3176 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3179 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3182 if(!target_handle->channels)
3184 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3188 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3189 for(uData = target_handle->channels; uData; uData = uData->u_next)
3191 struct chanData *cData = uData->channel;
3193 if(uData->access > UL_OWNER)
3195 if(IsProtected(cData)
3196 && (target_handle != user->handle_info)
3197 && !GetTrueChannelAccess(cData, user->handle_info))
3200 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3201 if(uData->flags != USER_AUTO_OP)
3202 string_buffer_append(&sbuf, ',');
3203 if(IsUserSuspended(uData))
3204 string_buffer_append(&sbuf, 's');
3205 if(IsUserAutoOp(uData))
3207 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3208 string_buffer_append(&sbuf, 'o');
3209 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3210 string_buffer_append(&sbuf, 'v');
3212 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3213 string_buffer_append(&sbuf, 'i');
3215 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3217 string_buffer_append_string(&sbuf, ")]");
3218 string_buffer_append(&sbuf, '\0');
3219 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3225 static CHANSERV_FUNC(cmd_access)
3227 struct userNode *target;
3228 struct handle_info *target_handle;
3229 struct userData *uData;
3231 char prefix[MAXLEN];
3236 target_handle = target->handle_info;
3238 else if((target = GetUserH(argv[1])))
3240 target_handle = target->handle_info;
3242 else if(argv[1][0] == '*')
3244 if(!(target_handle = get_handle_info(argv[1]+1)))
3246 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3252 reply("MSG_NICK_UNKNOWN", argv[1]);
3256 assert(target || target_handle);
3258 if(target == chanserv)
3260 reply("CSMSG_IS_CHANSERV");
3268 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3273 reply("MSG_USER_AUTHENTICATE", target->nick);
3276 reply("MSG_AUTHENTICATE");
3282 const char *epithet = NULL, *type = NULL;
3285 epithet = chanserv_conf.irc_operator_epithet;
3288 else if(IsNetworkHelper(target))
3290 epithet = chanserv_conf.network_helper_epithet;
3291 type = "network helper";
3293 else if(IsSupportHelper(target))
3295 epithet = chanserv_conf.support_helper_epithet;
3296 type = "support helper";
3300 if(target_handle->epithet)
3301 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3303 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3305 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3309 sprintf(prefix, "%s", target_handle->handle);
3312 if(!channel->channel_info)
3314 reply("CSMSG_NOT_REGISTERED", channel->name);
3318 helping = HANDLE_FLAGGED(target_handle, HELPING)
3319 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3320 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3322 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3323 /* To prevent possible information leaks, only show infolines
3324 * if the requestor is in the channel or it's their own
3326 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3328 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3330 /* Likewise, only say it's suspended if the user has active
3331 * access in that channel or it's their own entry. */
3332 if(IsUserSuspended(uData)
3333 && (GetChannelUser(channel->channel_info, user->handle_info)
3334 || (user->handle_info == uData->handle)))
3336 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3341 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3348 zoot_list(struct listData *list)
3350 struct userData *uData;
3351 unsigned int start, curr, highest, lowest;
3352 struct helpfile_table tmp_table;
3353 const char **temp, *msg;
3355 if(list->table.length == 1)
3358 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3360 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3361 msg = user_find_message(list->user, "MSG_NONE");
3362 send_message_type(4, list->user, list->bot, " %s", msg);
3364 tmp_table.width = list->table.width;
3365 tmp_table.flags = list->table.flags;
3366 list->table.contents[0][0] = " ";
3367 highest = list->highest;
3368 if(list->lowest != 0)
3369 lowest = list->lowest;
3370 else if(highest < 100)
3373 lowest = highest - 100;
3374 for(start = curr = 1; curr < list->table.length; )
3376 uData = list->users[curr-1];
3377 list->table.contents[curr++][0] = " ";
3378 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3381 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3383 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3384 temp = list->table.contents[--start];
3385 list->table.contents[start] = list->table.contents[0];
3386 tmp_table.contents = list->table.contents + start;
3387 tmp_table.length = curr - start;
3388 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3389 list->table.contents[start] = temp;
3391 highest = lowest - 1;
3392 lowest = (highest < 100) ? 0 : (highest - 99);
3398 def_list(struct listData *list)
3402 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3404 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3405 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3406 if(list->table.length == 1)
3408 msg = user_find_message(list->user, "MSG_NONE");
3409 send_message_type(4, list->user, list->bot, " %s", msg);
3414 userData_access_comp(const void *arg_a, const void *arg_b)
3416 const struct userData *a = *(struct userData**)arg_a;
3417 const struct userData *b = *(struct userData**)arg_b;
3419 if(a->access != b->access)
3420 res = b->access - a->access;
3422 res = irccasecmp(a->handle->handle, b->handle->handle);
3427 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3429 void (*send_list)(struct listData *);
3430 struct userData *uData;
3431 struct listData lData;
3432 unsigned int matches;
3436 lData.bot = cmd->parent->bot;
3437 lData.channel = channel;
3438 lData.lowest = lowest;
3439 lData.highest = highest;
3440 lData.search = (argc > 1) ? argv[1] : NULL;
3441 send_list = def_list;
3442 (void)zoot_list; /* since it doesn't show user levels */
3444 if(user->handle_info)
3446 switch(user->handle_info->userlist_style)
3448 case HI_STYLE_DEF: send_list = def_list; break;
3449 case HI_STYLE_ZOOT: send_list = def_list; break;
3453 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3455 for(uData = channel->channel_info->users; uData; uData = uData->next)
3457 if((uData->access < lowest)
3458 || (uData->access > highest)
3459 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3461 lData.users[matches++] = uData;
3463 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3465 lData.table.length = matches+1;
3466 lData.table.width = 4;
3467 lData.table.flags = TABLE_NO_FREE;
3468 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3469 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3470 lData.table.contents[0] = ary;
3473 ary[2] = "Last Seen";
3475 for(matches = 1; matches < lData.table.length; ++matches)
3477 struct userData *uData = lData.users[matches-1];
3478 char seen[INTERVALLEN];
3480 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3481 lData.table.contents[matches] = ary;
3482 ary[0] = strtab(uData->access);
3483 ary[1] = uData->handle->handle;
3486 else if(!uData->seen)
3489 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3490 ary[2] = strdup(ary[2]);
3491 if(IsUserSuspended(uData))
3492 ary[3] = "Suspended";
3493 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3494 ary[3] = "Vacation";
3499 for(matches = 1; matches < lData.table.length; ++matches)
3501 free((char*)lData.table.contents[matches][2]);
3502 free(lData.table.contents[matches]);
3504 free(lData.table.contents[0]);
3505 free(lData.table.contents);
3509 static CHANSERV_FUNC(cmd_users)
3511 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3514 static CHANSERV_FUNC(cmd_wlist)
3516 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3519 static CHANSERV_FUNC(cmd_clist)
3521 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3524 static CHANSERV_FUNC(cmd_mlist)
3526 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3529 static CHANSERV_FUNC(cmd_olist)
3531 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3534 static CHANSERV_FUNC(cmd_plist)
3536 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3539 static CHANSERV_FUNC(cmd_bans)
3541 struct helpfile_table tbl;
3542 unsigned int matches = 0, timed = 0, ii;
3543 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3544 const char *msg_never, *triggered, *expires;
3545 struct banData *ban, **bans;
3552 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3554 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3556 if(search && !match_ircglobs(search, ban->mask))
3558 bans[matches++] = ban;
3563 tbl.length = matches + 1;
3564 tbl.width = 4 + timed;
3566 tbl.flags = TABLE_NO_FREE;
3567 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3568 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3569 tbl.contents[0][0] = "Mask";
3570 tbl.contents[0][1] = "Set By";
3571 tbl.contents[0][2] = "Triggered";
3574 tbl.contents[0][3] = "Expires";
3575 tbl.contents[0][4] = "Reason";
3578 tbl.contents[0][3] = "Reason";
3581 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3583 free(tbl.contents[0]);
3588 msg_never = user_find_message(user, "MSG_NEVER");
3589 for(ii = 0; ii < matches; )
3595 else if(ban->expires)
3596 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3598 expires = msg_never;
3601 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3603 triggered = msg_never;
3605 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3606 tbl.contents[ii][0] = ban->mask;
3607 tbl.contents[ii][1] = ban->owner;
3608 tbl.contents[ii][2] = strdup(triggered);
3611 tbl.contents[ii][3] = strdup(expires);
3612 tbl.contents[ii][4] = ban->reason;
3615 tbl.contents[ii][3] = ban->reason;
3617 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3618 reply("MSG_MATCH_COUNT", matches);
3619 for(ii = 1; ii < tbl.length; ++ii)
3621 free((char*)tbl.contents[ii][2]);
3623 free((char*)tbl.contents[ii][3]);
3624 free(tbl.contents[ii]);
3626 free(tbl.contents[0]);
3632 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3634 struct chanData *cData = channel->channel_info;
3635 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3637 if(cData->topic_mask)
3638 return !match_ircglob(new_topic, cData->topic_mask);
3639 else if(cData->topic)
3640 return irccasecmp(new_topic, cData->topic);
3645 static CHANSERV_FUNC(cmd_topic)
3647 struct chanData *cData;
3650 cData = channel->channel_info;
3655 SetChannelTopic(channel, chanserv, cData->topic, 1);
3656 reply("CSMSG_TOPIC_SET", cData->topic);
3660 reply("CSMSG_NO_TOPIC", channel->name);
3664 topic = unsplit_string(argv + 1, argc - 1, NULL);
3665 /* If they say "!topic *", use an empty topic. */
3666 if((topic[0] == '*') && (topic[1] == 0))
3668 if(bad_topic(channel, user, topic))
3670 char *topic_mask = cData->topic_mask;
3673 char new_topic[TOPICLEN+1], tchar;
3674 int pos=0, starpos=-1, dpos=0, len;
3676 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3683 len = strlen(topic);
3684 if((dpos + len) > TOPICLEN)
3685 len = TOPICLEN + 1 - dpos;
3686 memcpy(new_topic+dpos, topic, len);
3690 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3691 default: new_topic[dpos++] = tchar; break;
3694 if((dpos > TOPICLEN) || tchar)
3697 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3698 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3701 new_topic[dpos] = 0;
3702 SetChannelTopic(channel, chanserv, new_topic, 1);
3704 reply("CSMSG_TOPIC_LOCKED", channel->name);
3709 SetChannelTopic(channel, chanserv, topic, 1);
3711 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3713 /* Grab the topic and save it as the default topic. */
3715 cData->topic = strdup(channel->topic);
3721 static CHANSERV_FUNC(cmd_mode)
3723 struct mod_chanmode *change;
3727 change = &channel->channel_info->modes;
3728 if(change->modes_set || change->modes_clear) {
3729 modcmd_chanmode_announce(change);
3730 reply("CSMSG_DEFAULTED_MODES", channel->name);
3732 reply("CSMSG_NO_MODES", channel->name);
3736 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3739 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3743 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3744 && mode_lock_violated(&channel->channel_info->modes, change))
3747 mod_chanmode_format(&channel->channel_info->modes, modes);
3748 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3752 modcmd_chanmode_announce(change);
3753 mod_chanmode_free(change);
3754 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3758 static CHANSERV_FUNC(cmd_invite)
3760 struct userData *uData;
3761 struct userNode *invite;
3763 uData = GetChannelUser(channel->channel_info, user->handle_info);
3767 if(!(invite = GetUserH(argv[1])))
3769 reply("MSG_NICK_UNKNOWN", argv[1]);
3776 if(GetUserMode(channel, invite))
3778 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3786 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3787 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3790 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3792 irc_invite(chanserv, invite, channel);
3794 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3799 static CHANSERV_FUNC(cmd_inviteme)
3801 if(GetUserMode(channel, user))
3803 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3806 if(channel->channel_info
3807 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3809 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3812 irc_invite(cmd->parent->bot, user, channel);
3817 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3820 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3822 /* We display things based on two dimensions:
3823 * - Issue time: present or absent
3824 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3825 * (in order of precedence, so something both expired and revoked
3826 * only counts as revoked)
3828 combo = (suspended->issued ? 4 : 0)
3829 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3831 case 0: /* no issue time, indefinite expiration */
3832 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3834 case 1: /* no issue time, expires in future */
3835 intervalString(buf1, suspended->expires-now, user->handle_info);
3836 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3838 case 2: /* no issue time, expired */
3839 intervalString(buf1, now-suspended->expires, user->handle_info);
3840 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3842 case 3: /* no issue time, revoked */
3843 intervalString(buf1, now-suspended->revoked, user->handle_info);
3844 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3846 case 4: /* issue time set, indefinite expiration */
3847 intervalString(buf1, now-suspended->issued, user->handle_info);
3848 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3850 case 5: /* issue time set, expires in future */
3851 intervalString(buf1, now-suspended->issued, user->handle_info);
3852 intervalString(buf2, suspended->expires-now, user->handle_info);
3853 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3855 case 6: /* issue time set, expired */
3856 intervalString(buf1, now-suspended->issued, user->handle_info);
3857 intervalString(buf2, now-suspended->expires, user->handle_info);
3858 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3860 case 7: /* issue time set, revoked */
3861 intervalString(buf1, now-suspended->issued, user->handle_info);
3862 intervalString(buf2, now-suspended->revoked, user->handle_info);
3863 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3866 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3871 static CHANSERV_FUNC(cmd_info)
3873 char modes[MAXLEN], buffer[INTERVALLEN];
3874 struct userData *uData, *owner;
3875 struct chanData *cData;
3876 struct do_not_register *dnr;
3881 cData = channel->channel_info;
3882 reply("CSMSG_CHANNEL_INFO", channel->name);
3884 uData = GetChannelUser(cData, user->handle_info);
3885 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3887 mod_chanmode_format(&cData->modes, modes);
3888 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3889 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3892 for(it = dict_first(cData->notes); it; it = iter_next(it))
3896 note = iter_data(it);
3897 if(!note_type_visible_to_user(cData, note->type, user))
3900 padding = PADLEN - 1 - strlen(iter_key(it));
3901 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3904 reply("CSMSG_CHANNEL_MAX", cData->max);
3905 for(owner = cData->users; owner; owner = owner->next)
3906 if(owner->access == UL_OWNER)
3907 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3908 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3909 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3910 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3911 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3913 privileged = IsStaff(user);
3914 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3915 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3917 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3918 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3920 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3922 struct suspended *suspended;
3923 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3924 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3925 show_suspension_info(cmd, user, suspended);
3927 else if(IsSuspended(cData))
3929 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3930 show_suspension_info(cmd, user, cData->suspended);
3935 static CHANSERV_FUNC(cmd_netinfo)
3937 extern time_t boot_time;
3938 extern unsigned long burst_length;
3939 char interval[INTERVALLEN];
3941 reply("CSMSG_NETWORK_INFO");
3942 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3943 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3944 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3945 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3946 reply("CSMSG_NETWORK_BANS", banCount);
3947 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3948 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3949 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3954 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3956 struct helpfile_table table;
3958 struct userNode *user;
3963 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3964 table.contents = alloca(list->used*sizeof(*table.contents));
3965 for(nn=0; nn<list->used; nn++)
3967 user = list->list[nn];
3968 if(user->modes & skip_flags)
3972 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3975 nick = alloca(strlen(user->nick)+3);
3976 sprintf(nick, "(%s)", user->nick);
3980 table.contents[table.length][0] = nick;
3983 table_send(chanserv, to->nick, 0, NULL, table);
3986 static CHANSERV_FUNC(cmd_ircops)
3988 reply("CSMSG_STAFF_OPERS");
3989 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3993 static CHANSERV_FUNC(cmd_helpers)
3995 reply("CSMSG_STAFF_HELPERS");
3996 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4000 static CHANSERV_FUNC(cmd_staff)
4002 reply("CSMSG_NETWORK_STAFF");
4003 cmd_ircops(CSFUNC_ARGS);
4004 cmd_helpers(CSFUNC_ARGS);
4008 static CHANSERV_FUNC(cmd_peek)
4010 struct modeNode *mn;
4011 char modes[MODELEN];
4013 struct helpfile_table table;
4015 irc_make_chanmode(channel, modes);
4017 reply("CSMSG_PEEK_INFO", channel->name);
4018 reply("CSMSG_PEEK_TOPIC", channel->topic);
4019 reply("CSMSG_PEEK_MODES", modes);
4020 reply("CSMSG_PEEK_USERS", channel->members.used);
4024 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4025 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4026 for(n = 0; n < channel->members.used; n++)
4028 mn = channel->members.list[n];
4029 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4031 table.contents[table.length] = alloca(sizeof(**table.contents));
4032 table.contents[table.length][0] = mn->user->nick;
4037 reply("CSMSG_PEEK_OPS");
4038 table_send(chanserv, user->nick, 0, NULL, table);
4041 reply("CSMSG_PEEK_NO_OPS");
4045 static MODCMD_FUNC(cmd_wipeinfo)
4047 struct handle_info *victim;
4048 struct userData *ud, *actor;
4051 actor = GetChannelUser(channel->channel_info, user->handle_info);
4052 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4054 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4056 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4059 if((ud->access >= actor->access) && (ud != actor))
4061 reply("MSG_USER_OUTRANKED", victim->handle);
4067 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4071 static CHANSERV_FUNC(cmd_resync)
4073 struct mod_chanmode *changes;
4074 struct chanData *cData = channel->channel_info;
4075 unsigned int ii, used;
4077 changes = mod_chanmode_alloc(channel->members.used * 2);
4078 for(ii = used = 0; ii < channel->members.used; ++ii)
4080 struct modeNode *mn = channel->members.list[ii];
4081 struct userData *uData;
4083 if(IsService(mn->user))
4086 uData = GetChannelAccess(cData, mn->user->handle_info);
4087 if(!cData->lvlOpts[lvlGiveOps]
4088 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4090 if(!(mn->modes & MODE_CHANOP))
4092 changes->args[used].mode = MODE_CHANOP;
4093 changes->args[used++].member = mn;
4096 else if(!cData->lvlOpts[lvlGiveVoice]
4097 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4099 if(mn->modes & MODE_CHANOP)
4101 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4102 changes->args[used++].member = mn;
4104 if(!(mn->modes & MODE_VOICE))
4106 changes->args[used].mode = MODE_VOICE;
4107 changes->args[used++].member = mn;
4114 changes->args[used].mode = MODE_REMOVE | mn->modes;
4115 changes->args[used++].member = mn;
4119 changes->argc = used;
4120 modcmd_chanmode_announce(changes);
4121 mod_chanmode_free(changes);
4122 reply("CSMSG_RESYNCED_USERS", channel->name);
4126 static CHANSERV_FUNC(cmd_seen)
4128 struct userData *uData;
4129 struct handle_info *handle;
4130 char seen[INTERVALLEN];
4134 if(!irccasecmp(argv[1], chanserv->nick))
4136 reply("CSMSG_IS_CHANSERV");
4140 if(!(handle = get_handle_info(argv[1])))
4142 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4146 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4148 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4153 reply("CSMSG_USER_PRESENT", handle->handle);
4154 else if(uData->seen)
4155 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4157 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4159 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4160 reply("CSMSG_USER_VACATION", handle->handle);
4165 static MODCMD_FUNC(cmd_names)
4167 struct userNode *targ;
4168 struct userData *targData;
4169 unsigned int ii, pos;
4172 for(ii=pos=0; ii<channel->members.used; ++ii)
4174 targ = channel->members.list[ii]->user;
4175 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4178 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4181 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4185 if(IsUserSuspended(targData))
4187 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4190 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4191 reply("CSMSG_END_NAMES", channel->name);
4196 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4198 switch(ntype->visible_type)
4200 case NOTE_VIS_ALL: return 1;
4201 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4202 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4207 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4209 struct userData *uData;
4211 switch(ntype->set_access_type)
4213 case NOTE_SET_CHANNEL_ACCESS:
4214 if(!user->handle_info)
4216 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4218 return uData->access >= ntype->set_access.min_ulevel;
4219 case NOTE_SET_CHANNEL_SETTER:
4220 return check_user_level(channel, user, lvlSetters, 1, 0);
4221 case NOTE_SET_PRIVILEGED: default:
4222 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4226 static CHANSERV_FUNC(cmd_note)
4228 struct chanData *cData;
4230 struct note_type *ntype;
4232 cData = channel->channel_info;
4235 reply("CSMSG_NOT_REGISTERED", channel->name);
4239 /* If no arguments, show all visible notes for the channel. */
4245 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4247 note = iter_data(it);
4248 if(!note_type_visible_to_user(cData, note->type, user))
4251 reply("CSMSG_NOTELIST_HEADER", channel->name);
4252 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4255 reply("CSMSG_NOTELIST_END", channel->name);
4257 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4259 /* If one argument, show the named note. */
4262 if((note = dict_find(cData->notes, argv[1], NULL))
4263 && note_type_visible_to_user(cData, note->type, user))
4265 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4267 else if((ntype = dict_find(note_types, argv[1], NULL))
4268 && note_type_visible_to_user(NULL, ntype, user))
4270 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4275 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4279 /* Assume they're trying to set a note. */
4283 ntype = dict_find(note_types, argv[1], NULL);
4286 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4289 else if(note_type_settable_by_user(channel, ntype, user))
4291 note_text = unsplit_string(argv+2, argc-2, NULL);
4292 if((note = dict_find(cData->notes, argv[1], NULL)))
4293 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4294 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4295 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4297 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4299 /* The note is viewable to staff only, so return 0
4300 to keep the invocation from getting logged (or
4301 regular users can see it in !events). */
4307 reply("CSMSG_NO_ACCESS");
4314 static CHANSERV_FUNC(cmd_delnote)
4319 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4320 || !note_type_settable_by_user(channel, note->type, user))
4322 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4325 dict_remove(channel->channel_info->notes, note->type->name);
4326 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4330 static CHANSERV_FUNC(cmd_events)
4332 struct logSearch discrim;
4333 struct logReport report;
4334 unsigned int matches, limit;
4336 limit = (argc > 1) ? atoi(argv[1]) : 10;
4337 if(limit < 1 || limit > 200)
4340 memset(&discrim, 0, sizeof(discrim));
4341 discrim.masks.bot = chanserv;
4342 discrim.masks.channel_name = channel->name;
4344 discrim.masks.command = argv[2];
4345 discrim.limit = limit;
4346 discrim.max_time = INT_MAX;
4347 discrim.severities = 1 << LOG_COMMAND;
4348 report.reporter = chanserv;
4350 reply("CSMSG_EVENT_SEARCH_RESULTS");
4351 matches = log_entry_search(&discrim, log_report_entry, &report);
4353 reply("MSG_MATCH_COUNT", matches);
4355 reply("MSG_NO_MATCHES");
4359 static CHANSERV_FUNC(cmd_say)
4365 msg = unsplit_string(argv + 1, argc - 1, NULL);
4366 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4368 else if(GetUserH(argv[1]))
4371 msg = unsplit_string(argv + 2, argc - 2, NULL);
4372 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4376 reply("MSG_NOT_TARGET_NAME");
4382 static CHANSERV_FUNC(cmd_emote)
4388 /* CTCP is so annoying. */
4389 msg = unsplit_string(argv + 1, argc - 1, NULL);
4390 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4392 else if(GetUserH(argv[1]))
4394 msg = unsplit_string(argv + 2, argc - 2, NULL);
4395 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4399 reply("MSG_NOT_TARGET_NAME");
4405 struct channelList *
4406 chanserv_support_channels(void)
4408 return &chanserv_conf.support_channels;
4411 static CHANSERV_FUNC(cmd_expire)
4413 int channel_count = registered_channels;
4414 expire_channels(NULL);
4415 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4420 chanserv_expire_suspension(void *data)
4422 struct suspended *suspended = data;
4423 struct chanNode *channel;
4424 struct mod_chanmode change;
4426 if(!suspended->expires || (now < suspended->expires))
4427 suspended->revoked = now;
4428 channel = suspended->cData->channel;
4429 suspended->cData->channel = channel;
4430 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4431 mod_chanmode_init(&change);
4433 change.args[0].mode = MODE_CHANOP;
4434 change.args[0].member = AddChannelUser(chanserv, channel);
4435 mod_chanmode_announce(chanserv, channel, &change);
4438 static CHANSERV_FUNC(cmd_csuspend)
4440 struct suspended *suspended;
4441 char reason[MAXLEN];
4442 time_t expiry, duration;
4443 struct userData *uData;
4447 if(IsProtected(channel->channel_info))
4449 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4453 if(argv[1][0] == '!')
4455 else if(IsSuspended(channel->channel_info))
4457 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4458 show_suspension_info(cmd, user, channel->channel_info->suspended);
4462 if(!strcmp(argv[1], "0"))
4464 else if((duration = ParseInterval(argv[1])))
4465 expiry = now + duration;
4468 reply("MSG_INVALID_DURATION", argv[1]);
4472 unsplit_string(argv + 2, argc - 2, reason);
4474 suspended = calloc(1, sizeof(*suspended));
4475 suspended->revoked = 0;
4476 suspended->issued = now;
4477 suspended->suspender = strdup(user->handle_info->handle);
4478 suspended->expires = expiry;
4479 suspended->reason = strdup(reason);
4480 suspended->cData = channel->channel_info;
4481 suspended->previous = suspended->cData->suspended;
4482 suspended->cData->suspended = suspended;
4484 if(suspended->expires)
4485 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4487 if(IsSuspended(channel->channel_info))
4489 suspended->previous->revoked = now;
4490 if(suspended->previous->expires)
4491 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4492 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4493 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4497 /* Mark all users in channel as absent. */
4498 for(uData = channel->channel_info->users; uData; uData = uData->next)
4507 /* Mark the channel as suspended, then part. */
4508 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4509 DelChannelUser(chanserv, channel, suspended->reason, 0);
4510 reply("CSMSG_SUSPENDED", channel->name);
4511 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4512 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4517 static CHANSERV_FUNC(cmd_cunsuspend)
4519 struct suspended *suspended;
4520 char message[MAXLEN];
4522 if(!IsSuspended(channel->channel_info))
4524 reply("CSMSG_NOT_SUSPENDED", channel->name);
4528 suspended = channel->channel_info->suspended;
4530 /* Expire the suspension and join ChanServ to the channel. */
4531 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4532 chanserv_expire_suspension(suspended);
4533 reply("CSMSG_UNSUSPENDED", channel->name);
4534 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4535 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4539 typedef struct chanservSearch
4547 unsigned long flags;
4551 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4554 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4559 search = malloc(sizeof(struct chanservSearch));
4560 memset(search, 0, sizeof(*search));
4563 for(i = 0; i < argc; i++)
4565 /* Assume all criteria require arguments. */
4568 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4572 if(!irccasecmp(argv[i], "name"))
4573 search->name = argv[++i];
4574 else if(!irccasecmp(argv[i], "registrar"))
4575 search->registrar = argv[++i];
4576 else if(!irccasecmp(argv[i], "unvisited"))
4577 search->unvisited = ParseInterval(argv[++i]);
4578 else if(!irccasecmp(argv[i], "registered"))
4579 search->registered = ParseInterval(argv[++i]);
4580 else if(!irccasecmp(argv[i], "flags"))
4583 if(!irccasecmp(argv[i], "nodelete"))
4584 search->flags |= CHANNEL_NODELETE;
4585 else if(!irccasecmp(argv[i], "suspended"))
4586 search->flags |= CHANNEL_SUSPENDED;
4589 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4593 else if(!irccasecmp(argv[i], "limit"))
4594 search->limit = strtoul(argv[++i], NULL, 10);
4597 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4602 if(search->name && !strcmp(search->name, "*"))
4604 if(search->registrar && !strcmp(search->registrar, "*"))
4605 search->registrar = 0;
4614 chanserv_channel_match(struct chanData *channel, search_t search)
4616 const char *name = channel->channel->name;
4617 if((search->name && !match_ircglob(name, search->name)) ||
4618 (search->registrar && !channel->registrar) ||
4619 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4620 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4621 (search->registered && (now - channel->registered) > search->registered) ||
4622 (search->flags && ((search->flags & channel->flags) != search->flags)))
4629 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4631 struct chanData *channel;
4632 unsigned int matches = 0;
4634 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4636 if(!chanserv_channel_match(channel, search))
4646 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4651 search_print(struct chanData *channel, void *data)
4653 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4656 static CHANSERV_FUNC(cmd_search)
4659 unsigned int matches;
4660 channel_search_func action;
4664 if(!irccasecmp(argv[1], "count"))
4665 action = search_count;
4666 else if(!irccasecmp(argv[1], "print"))
4667 action = search_print;
4670 reply("CSMSG_ACTION_INVALID", argv[1]);
4674 search = chanserv_search_create(user, argc - 2, argv + 2);
4678 if(action == search_count)
4679 search->limit = INT_MAX;
4681 if(action == search_print)
4682 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4684 matches = chanserv_channel_search(search, action, user);
4687 reply("MSG_MATCH_COUNT", matches);
4689 reply("MSG_NO_MATCHES");
4695 static CHANSERV_FUNC(cmd_unvisited)
4697 struct chanData *cData;
4698 time_t interval = chanserv_conf.channel_expire_delay;
4699 char buffer[INTERVALLEN];
4700 unsigned int limit = 25, matches = 0;
4704 interval = ParseInterval(argv[1]);
4706 limit = atoi(argv[2]);
4709 intervalString(buffer, interval, user->handle_info);
4710 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4712 for(cData = channelList; cData && matches < limit; cData = cData->next)
4714 if((now - cData->visited) < interval)
4717 intervalString(buffer, now - cData->visited, user->handle_info);
4718 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4725 static MODCMD_FUNC(chan_opt_defaulttopic)
4731 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4733 reply("CSMSG_TOPIC_LOCKED", channel->name);
4737 topic = unsplit_string(argv+1, argc-1, NULL);
4739 free(channel->channel_info->topic);
4740 if(topic[0] == '*' && topic[1] == 0)
4742 topic = channel->channel_info->topic = NULL;
4746 topic = channel->channel_info->topic = strdup(topic);
4747 if(channel->channel_info->topic_mask
4748 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4749 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4751 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4754 if(channel->channel_info->topic)
4755 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4757 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4761 static MODCMD_FUNC(chan_opt_topicmask)
4765 struct chanData *cData = channel->channel_info;
4768 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4770 reply("CSMSG_TOPIC_LOCKED", channel->name);
4774 mask = unsplit_string(argv+1, argc-1, NULL);
4776 if(cData->topic_mask)
4777 free(cData->topic_mask);
4778 if(mask[0] == '*' && mask[1] == 0)
4780 cData->topic_mask = 0;
4784 cData->topic_mask = strdup(mask);
4786 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4787 else if(!match_ircglob(cData->topic, cData->topic_mask))
4788 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4792 if(channel->channel_info->topic_mask)
4793 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4795 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4799 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4803 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4807 if(greeting[0] == '*' && greeting[1] == 0)
4811 unsigned int length = strlen(greeting);
4812 if(length > chanserv_conf.greeting_length)
4814 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4817 *data = strdup(greeting);
4826 reply(name, user_find_message(user, "MSG_NONE"));
4830 static MODCMD_FUNC(chan_opt_greeting)
4832 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4835 static MODCMD_FUNC(chan_opt_usergreeting)
4837 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4840 static MODCMD_FUNC(chan_opt_modes)
4842 struct mod_chanmode *new_modes;
4843 char modes[MODELEN];
4847 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4849 reply("CSMSG_NO_ACCESS");
4852 if(argv[1][0] == '*' && argv[1][1] == 0)
4854 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4856 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4858 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4861 else if(new_modes->argc > 1)
4863 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4864 mod_chanmode_free(new_modes);
4869 channel->channel_info->modes = *new_modes;
4870 modcmd_chanmode_announce(new_modes);
4871 mod_chanmode_free(new_modes);
4875 mod_chanmode_format(&channel->channel_info->modes, modes);
4877 reply("CSMSG_SET_MODES", modes);
4879 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4883 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4885 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4887 struct chanData *cData = channel->channel_info;
4892 /* Set flag according to value. */
4893 if(enabled_string(argv[1]))
4895 cData->flags |= mask;
4898 else if(disabled_string(argv[1]))
4900 cData->flags &= ~mask;
4905 reply("MSG_INVALID_BINARY", argv[1]);
4911 /* Find current option value. */
4912 value = (cData->flags & mask) ? 1 : 0;
4916 reply(name, user_find_message(user, "MSG_ON"));
4918 reply(name, user_find_message(user, "MSG_OFF"));
4922 static MODCMD_FUNC(chan_opt_nodelete)
4924 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4926 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4930 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4933 static MODCMD_FUNC(chan_opt_dynlimit)
4935 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4938 static MODCMD_FUNC(chan_opt_offchannel)
4940 struct chanData *cData = channel->channel_info;
4945 /* Set flag according to value. */
4946 if(enabled_string(argv[1]))
4948 if(!IsOffChannel(cData))
4949 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4950 cData->flags |= CHANNEL_OFFCHANNEL;
4953 else if(disabled_string(argv[1]))
4955 if(IsOffChannel(cData))
4957 struct mod_chanmode change;
4958 mod_chanmode_init(&change);
4960 change.args[0].mode = MODE_CHANOP;
4961 change.args[0].member = AddChannelUser(chanserv, channel);
4962 mod_chanmode_announce(chanserv, channel, &change);
4964 cData->flags &= ~CHANNEL_OFFCHANNEL;
4969 reply("MSG_INVALID_BINARY", argv[1]);
4975 /* Find current option value. */
4976 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4980 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4982 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4986 static MODCMD_FUNC(chan_opt_defaults)
4988 struct userData *uData;
4989 struct chanData *cData;
4990 const char *confirm;
4991 enum levelOption lvlOpt;
4992 enum charOption chOpt;
4994 cData = channel->channel_info;
4995 uData = GetChannelUser(cData, user->handle_info);
4996 if(!uData || (uData->access < UL_OWNER))
4998 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5001 confirm = make_confirmation_string(uData);
5002 if((argc < 2) || strcmp(argv[1], confirm))
5004 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5007 cData->flags = CHANNEL_DEFAULT_FLAGS;
5008 cData->modes = chanserv_conf.default_modes;
5009 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5010 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5011 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5012 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5013 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5018 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5020 struct chanData *cData = channel->channel_info;
5021 struct userData *uData;
5022 unsigned short value;
5026 if(!check_user_level(channel, user, option, 1, 1))
5028 reply("CSMSG_CANNOT_SET");
5031 value = user_level_from_name(argv[1], UL_OWNER+1);
5032 if(!value && strcmp(argv[1], "0"))
5034 reply("CSMSG_INVALID_ACCESS", argv[1]);
5037 uData = GetChannelUser(cData, user->handle_info);
5038 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5040 reply("CSMSG_BAD_SETLEVEL");
5046 if(value > cData->lvlOpts[lvlGiveOps])
5048 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5053 if(value < cData->lvlOpts[lvlGiveVoice])
5055 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5060 /* This test only applies to owners, since non-owners
5061 * trying to set an option to above their level get caught
5062 * by the CSMSG_BAD_SETLEVEL test above.
5064 if(value > uData->access)
5066 reply("CSMSG_BAD_SETTERS");
5073 cData->lvlOpts[option] = value;
5075 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5079 static MODCMD_FUNC(chan_opt_enfops)
5081 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5084 static MODCMD_FUNC(chan_opt_giveops)
5086 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5089 static MODCMD_FUNC(chan_opt_enfmodes)
5091 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5094 static MODCMD_FUNC(chan_opt_enftopic)
5096 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5099 static MODCMD_FUNC(chan_opt_pubcmd)
5101 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5104 static MODCMD_FUNC(chan_opt_setters)
5106 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5109 static MODCMD_FUNC(chan_opt_ctcpusers)
5111 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5114 static MODCMD_FUNC(chan_opt_userinfo)
5116 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5119 static MODCMD_FUNC(chan_opt_givevoice)
5121 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5124 static MODCMD_FUNC(chan_opt_topicsnarf)
5126 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5129 static MODCMD_FUNC(chan_opt_inviteme)
5131 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5135 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5137 struct chanData *cData = channel->channel_info;
5138 int count = charOptions[option].count, index;
5142 index = atoi(argv[1]);
5144 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5146 reply("CSMSG_INVALID_NUMERIC", index);
5147 /* Show possible values. */
5148 for(index = 0; index < count; index++)
5149 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5153 cData->chOpts[option] = charOptions[option].values[index].value;
5157 /* Find current option value. */
5160 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5164 /* Somehow, the option value is corrupt; reset it to the default. */
5165 cData->chOpts[option] = charOptions[option].default_value;
5170 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5174 static MODCMD_FUNC(chan_opt_protect)
5176 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5179 static MODCMD_FUNC(chan_opt_toys)
5181 return channel_multiple_option(chToys, CSFUNC_ARGS);
5184 static MODCMD_FUNC(chan_opt_ctcpreaction)
5186 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5189 static MODCMD_FUNC(chan_opt_topicrefresh)
5191 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5194 static struct svccmd_list set_shows_list;
5197 handle_svccmd_unbind(struct svccmd *target) {
5199 for(ii=0; ii<set_shows_list.used; ++ii)
5200 if(target == set_shows_list.list[ii])
5201 set_shows_list.used = 0;
5204 static CHANSERV_FUNC(cmd_set)
5206 struct svccmd *subcmd;
5210 /* Check if we need to (re-)initialize set_shows_list. */
5211 if(!set_shows_list.used)
5213 if(!set_shows_list.size)
5215 set_shows_list.size = chanserv_conf.set_shows->used;
5216 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5218 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5220 const char *name = chanserv_conf.set_shows->list[ii];
5221 sprintf(buf, "%s %s", argv[0], name);
5222 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5225 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5228 svccmd_list_append(&set_shows_list, subcmd);
5234 reply("CSMSG_CHANNEL_OPTIONS");
5235 for(ii = 0; ii < set_shows_list.used; ii++)
5237 subcmd = set_shows_list.list[ii];
5238 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5243 sprintf(buf, "%s %s", argv[0], argv[1]);
5244 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5247 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5250 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5252 reply("CSMSG_NO_ACCESS");
5256 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5260 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5262 struct userData *uData;
5264 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5267 reply("CSMSG_NOT_USER", channel->name);
5273 /* Just show current option value. */
5275 else if(enabled_string(argv[1]))
5277 uData->flags |= mask;
5279 else if(disabled_string(argv[1]))
5281 uData->flags &= ~mask;
5285 reply("MSG_INVALID_BINARY", argv[1]);
5289 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5293 static MODCMD_FUNC(user_opt_noautoop)
5295 struct userData *uData;
5297 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5300 reply("CSMSG_NOT_USER", channel->name);
5303 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5304 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5306 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5309 static MODCMD_FUNC(user_opt_autoinvite)
5311 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5314 static MODCMD_FUNC(user_opt_info)
5316 struct userData *uData;
5319 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5323 /* If they got past the command restrictions (which require access)
5324 * but fail this test, we have some fool with security override on.
5326 reply("CSMSG_NOT_USER", channel->name);
5333 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5334 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5336 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5339 bp = strcspn(infoline, "\001");
5342 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5347 if(infoline[0] == '*' && infoline[1] == 0)
5350 uData->info = strdup(infoline);
5353 reply("CSMSG_USET_INFO", uData->info);
5355 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5359 struct svccmd_list uset_shows_list;
5361 static CHANSERV_FUNC(cmd_uset)
5363 struct svccmd *subcmd;
5367 /* Check if we need to (re-)initialize uset_shows_list. */
5368 if(!uset_shows_list.used)
5372 "NoAutoOp", "AutoInvite", "Info"
5375 if(!uset_shows_list.size)
5377 uset_shows_list.size = ArrayLength(options);
5378 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5380 for(ii = 0; ii < ArrayLength(options); ii++)
5382 const char *name = options[ii];
5383 sprintf(buf, "%s %s", argv[0], name);
5384 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5387 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5390 svccmd_list_append(&uset_shows_list, subcmd);
5396 /* Do this so options are presented in a consistent order. */
5397 reply("CSMSG_USER_OPTIONS");
5398 for(ii = 0; ii < uset_shows_list.used; ii++)
5399 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5403 sprintf(buf, "%s %s", argv[0], argv[1]);
5404 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5407 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5411 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5414 static CHANSERV_FUNC(cmd_giveownership)
5416 struct handle_info *new_owner_hi;
5417 struct userData *new_owner, *curr_user;
5418 struct chanData *cData = channel->channel_info;
5419 struct do_not_register *dnr;
5421 unsigned short co_access;
5422 char reason[MAXLEN];
5425 curr_user = GetChannelAccess(cData, user->handle_info);
5426 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5427 if(!curr_user || (curr_user->access != UL_OWNER))
5429 struct userData *owner = NULL;
5430 for(curr_user = channel->channel_info->users;
5432 curr_user = curr_user->next)
5434 if(curr_user->access != UL_OWNER)
5438 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5445 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5447 if(new_owner_hi == user->handle_info)
5449 reply("CSMSG_NO_TRANSFER_SELF");
5452 new_owner = GetChannelAccess(cData, new_owner_hi);
5455 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5458 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5460 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5463 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5464 if(!IsHelping(user))
5465 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5467 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5470 if(new_owner->access >= UL_COOWNER)
5471 co_access = new_owner->access;
5473 co_access = UL_COOWNER;
5474 new_owner->access = UL_OWNER;
5476 curr_user->access = co_access;
5477 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5478 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5479 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5483 static CHANSERV_FUNC(cmd_suspend)
5485 struct handle_info *hi;
5486 struct userData *self, *target;
5489 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5490 self = GetChannelUser(channel->channel_info, user->handle_info);
5491 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5493 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5496 if(target->access >= self->access)
5498 reply("MSG_USER_OUTRANKED", hi->handle);
5501 if(target->flags & USER_SUSPENDED)
5503 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5508 target->present = 0;
5511 target->flags |= USER_SUSPENDED;
5512 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5516 static CHANSERV_FUNC(cmd_unsuspend)
5518 struct handle_info *hi;
5519 struct userData *self, *target;
5522 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5523 self = GetChannelUser(channel->channel_info, user->handle_info);
5524 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5526 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5529 if(target->access >= self->access)
5531 reply("MSG_USER_OUTRANKED", hi->handle);
5534 if(!(target->flags & USER_SUSPENDED))
5536 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5539 target->flags &= ~USER_SUSPENDED;
5540 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5544 static MODCMD_FUNC(cmd_deleteme)
5546 struct handle_info *hi;
5547 struct userData *target;
5548 const char *confirm_string;
5549 unsigned short access;
5552 hi = user->handle_info;
5553 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5555 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5558 if(target->access == UL_OWNER)
5560 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5563 confirm_string = make_confirmation_string(target);
5564 if((argc < 2) || strcmp(argv[1], confirm_string))
5566 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5569 access = target->access;
5570 channel_name = strdup(channel->name);
5571 del_channel_user(target, 1);
5572 reply("CSMSG_DELETED_YOU", access, channel_name);
5578 chanserv_refresh_topics(UNUSED_ARG(void *data))
5580 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5581 struct chanData *cData;
5584 for(cData = channelList; cData; cData = cData->next)
5586 if(IsSuspended(cData))
5588 opt = cData->chOpts[chTopicRefresh];
5591 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5594 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5595 cData->last_refresh = refresh_num;
5597 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5600 static CHANSERV_FUNC(cmd_unf)
5604 char response[MAXLEN];
5605 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5606 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5607 irc_privmsg(cmd->parent->bot, channel->name, response);
5610 reply("CSMSG_UNF_RESPONSE");
5614 static CHANSERV_FUNC(cmd_ping)
5618 char response[MAXLEN];
5619 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5620 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5621 irc_privmsg(cmd->parent->bot, channel->name, response);
5624 reply("CSMSG_PING_RESPONSE");
5628 static CHANSERV_FUNC(cmd_wut)
5632 char response[MAXLEN];
5633 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5634 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5635 irc_privmsg(cmd->parent->bot, channel->name, response);
5638 reply("CSMSG_WUT_RESPONSE");
5642 static CHANSERV_FUNC(cmd_8ball)
5644 unsigned int i, j, accum;
5649 for(i=1; i<argc; i++)
5650 for(j=0; argv[i][j]; j++)
5651 accum = (accum << 5) - accum + toupper(argv[i][j]);
5652 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5655 char response[MAXLEN];
5656 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5657 irc_privmsg(cmd->parent->bot, channel->name, response);
5660 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5664 static CHANSERV_FUNC(cmd_d)
5666 unsigned long sides, count, modifier, ii, total;
5667 char response[MAXLEN], *sep;
5671 if((count = strtoul(argv[1], &sep, 10)) < 1)
5681 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5682 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5686 else if((sep[0] == '-') && isdigit(sep[1]))
5687 modifier = strtoul(sep, NULL, 10);
5688 else if((sep[0] == '+') && isdigit(sep[1]))
5689 modifier = strtoul(sep+1, NULL, 10);
5696 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5701 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5704 for(total = ii = 0; ii < count; ++ii)
5705 total += (rand() % sides) + 1;
5708 if((count > 1) || modifier)
5710 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5711 sprintf(response, fmt, total, count, sides, modifier);
5715 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5716 sprintf(response, fmt, total, sides);
5719 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5721 send_message_type(4, user, cmd->parent->bot, "%s", response);
5725 static CHANSERV_FUNC(cmd_huggle)
5727 /* CTCP must be via PRIVMSG, never notice */
5729 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5731 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5736 chanserv_adjust_limit(void *data)
5738 struct mod_chanmode change;
5739 struct chanData *cData = data;
5740 struct chanNode *channel = cData->channel;
5743 if(IsSuspended(cData))
5746 cData->limitAdjusted = now;
5747 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5748 if(cData->modes.modes_set & MODE_LIMIT)
5750 if(limit > cData->modes.new_limit)
5751 limit = cData->modes.new_limit;
5752 else if(limit == cData->modes.new_limit)
5756 mod_chanmode_init(&change);
5757 change.modes_set = MODE_LIMIT;
5758 change.new_limit = limit;
5759 mod_chanmode_announce(chanserv, channel, &change);
5763 handle_new_channel(struct chanNode *channel)
5765 struct chanData *cData;
5767 if(!(cData = channel->channel_info))
5770 if(cData->modes.modes_set || cData->modes.modes_clear)
5771 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5773 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5774 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5777 /* Welcome to my worst nightmare. Warning: Read (or modify)
5778 the code below at your own risk. */
5780 handle_join(struct modeNode *mNode)
5782 struct mod_chanmode change;
5783 struct userNode *user = mNode->user;
5784 struct chanNode *channel = mNode->channel;
5785 struct chanData *cData;
5786 struct userData *uData = NULL;
5787 struct banData *bData;
5788 struct handle_info *handle;
5789 unsigned int modes = 0, info = 0;
5792 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5795 cData = channel->channel_info;
5796 if(channel->members.used > cData->max)
5797 cData->max = channel->members.used;
5799 /* Check for bans. If they're joining through a ban, one of two
5801 * 1: Join during a netburst, by riding the break. Kick them
5802 * unless they have ops or voice in the channel.
5803 * 2: They're allowed to join through the ban (an invite in
5804 * ircu2.10, or a +e on Hybrid, or something).
5805 * If they're not joining through a ban, and the banlist is not
5806 * full, see if they're on the banlist for the channel. If so,
5809 if(user->uplink->burst && !mNode->modes)
5812 for(ii = 0; ii < channel->banlist.used; ii++)
5814 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5816 /* Riding a netburst. Naughty. */
5817 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5823 mod_chanmode_init(&change);
5825 if(channel->banlist.used < MAXBANS)
5827 /* Not joining through a ban. */
5828 for(bData = cData->bans;
5829 bData && !user_matches_glob(user, bData->mask, 1);
5830 bData = bData->next);
5834 char kick_reason[MAXLEN];
5835 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5837 bData->triggered = now;
5838 if(bData != cData->bans)
5840 /* Shuffle the ban to the head of the list. */
5842 bData->next->prev = bData->prev;
5844 bData->prev->next = bData->next;
5847 bData->next = cData->bans;
5850 cData->bans->prev = bData;
5851 cData->bans = bData;
5854 change.args[0].mode = MODE_BAN;
5855 change.args[0].hostmask = bData->mask;
5856 mod_chanmode_announce(chanserv, channel, &change);
5857 KickChannelUser(user, channel, chanserv, kick_reason);
5862 /* ChanServ will not modify the limits in join-flooded channels.
5863 It will also skip DynLimit processing when the user (or srvx)
5864 is bursting in, because there are likely more incoming. */
5865 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5866 && !user->uplink->burst
5867 && !channel->join_flooded
5868 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5870 /* The user count has begun "bumping" into the channel limit,
5871 so set a timer to raise the limit a bit. Any previous
5872 timers are removed so three incoming users within the delay
5873 results in one limit change, not three. */
5875 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5876 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5879 if(channel->join_flooded)
5881 /* don't automatically give ops or voice during a join flood */
5883 else if(cData->lvlOpts[lvlGiveOps] == 0)
5884 modes |= MODE_CHANOP;
5885 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5886 modes |= MODE_VOICE;
5888 greeting = cData->greeting;
5889 if(user->handle_info)
5891 handle = user->handle_info;
5893 if(IsHelper(user) && !IsHelping(user))
5896 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5898 if(channel == chanserv_conf.support_channels.list[ii])
5900 HANDLE_SET_FLAG(user->handle_info, HELPING);
5906 uData = GetTrueChannelAccess(cData, handle);
5907 if(uData && !IsUserSuspended(uData))
5909 /* Ops and above were handled by the above case. */
5910 if(IsUserAutoOp(uData))
5912 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5913 modes |= MODE_CHANOP;
5914 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5915 modes |= MODE_VOICE;
5917 if(uData->access >= UL_PRESENT)
5918 cData->visited = now;
5919 if(cData->user_greeting)
5920 greeting = cData->user_greeting;
5922 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5923 && ((now - uData->seen) >= chanserv_conf.info_delay)
5930 if(!user->uplink->burst)
5934 if(modes & MODE_CHANOP)
5935 modes &= ~MODE_VOICE;
5936 change.args[0].mode = modes;
5937 change.args[0].member = mNode;
5938 mod_chanmode_announce(chanserv, channel, &change);
5940 if(greeting && !user->uplink->burst)
5941 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5943 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5949 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5951 struct mod_chanmode change;
5952 struct userData *channel;
5953 unsigned int ii, jj;
5955 if(!user->handle_info)
5958 mod_chanmode_init(&change);
5960 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5962 struct chanNode *cn;
5963 struct modeNode *mn;
5964 if(IsUserSuspended(channel)
5965 || IsSuspended(channel->channel)
5966 || !(cn = channel->channel->channel))
5969 mn = GetUserMode(cn, user);
5972 if(!IsUserSuspended(channel)
5973 && IsUserAutoInvite(channel)
5974 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5976 && !user->uplink->burst)
5977 irc_invite(chanserv, user, cn);
5981 if(channel->access >= UL_PRESENT)
5982 channel->channel->visited = now;
5984 if(IsUserAutoOp(channel))
5986 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5987 change.args[0].mode = MODE_CHANOP;
5988 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5989 change.args[0].mode = MODE_VOICE;
5991 change.args[0].mode = 0;
5992 change.args[0].member = mn;
5993 if(change.args[0].mode)
5994 mod_chanmode_announce(chanserv, cn, &change);
5997 channel->seen = now;
5998 channel->present = 1;
6001 for(ii = 0; ii < user->channels.used; ++ii)
6003 struct chanNode *channel = user->channels.list[ii]->channel;
6004 struct banData *ban;
6006 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6007 || !channel->channel_info)
6009 for(jj = 0; jj < channel->banlist.used; ++jj)
6010 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6012 if(jj < channel->banlist.used)
6014 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6016 char kick_reason[MAXLEN];
6017 if(!user_matches_glob(user, ban->mask, 1))
6019 change.args[0].mode = MODE_BAN;
6020 change.args[0].hostmask = ban->mask;
6021 mod_chanmode_announce(chanserv, channel, &change);
6022 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6023 KickChannelUser(user, channel, chanserv, kick_reason);
6024 ban->triggered = now;
6029 if(IsSupportHelper(user))
6031 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6033 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6035 HANDLE_SET_FLAG(user->handle_info, HELPING);
6043 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6045 struct chanData *cData;
6046 struct userData *uData;
6048 cData = mn->channel->channel_info;
6049 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6052 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6054 /* Allow for a bit of padding so that the limit doesn't
6055 track the user count exactly, which could get annoying. */
6056 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6058 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6059 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6063 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6065 scan_user_presence(uData, mn->user);
6069 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6071 unsigned int ii, jj;
6072 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6074 for(jj = 0; jj < mn->user->channels.used; ++jj)
6075 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6077 if(jj < mn->user->channels.used)
6080 if(ii == chanserv_conf.support_channels.used)
6081 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6086 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6088 struct userData *uData;
6090 if(!channel->channel_info || !kicker || IsService(kicker)
6091 || (kicker == victim) || IsSuspended(channel->channel_info)
6092 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6095 if(protect_user(victim, kicker, channel->channel_info))
6097 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6098 KickChannelUser(kicker, channel, chanserv, reason);
6101 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6106 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6108 struct chanData *cData;
6110 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6113 cData = channel->channel_info;
6114 if(bad_topic(channel, user, channel->topic))
6116 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6117 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6118 SetChannelTopic(channel, chanserv, old_topic, 1);
6119 else if(cData->topic)
6120 SetChannelTopic(channel, chanserv, cData->topic, 1);
6123 /* With topicsnarf, grab the topic and save it as the default topic. */
6124 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6127 cData->topic = strdup(channel->topic);
6133 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6135 struct mod_chanmode *bounce = NULL;
6136 unsigned int bnc, ii;
6139 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6142 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6143 && mode_lock_violated(&channel->channel_info->modes, change))
6145 char correct[MAXLEN];
6146 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6147 mod_chanmode_format(&channel->channel_info->modes, correct);
6148 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6150 for(ii = bnc = 0; ii < change->argc; ++ii)
6152 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6154 const struct userNode *victim = change->args[ii].member->user;
6155 if(!protect_user(victim, user, channel->channel_info))
6158 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6161 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6162 bounce->args[bnc].member = GetUserMode(channel, user);
6163 if(bounce->args[bnc].member)
6167 bounce->args[bnc].mode = MODE_CHANOP;
6168 bounce->args[bnc].member = change->args[ii].member;
6170 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6172 else if(change->args[ii].mode & MODE_CHANOP)
6174 const struct userNode *victim = change->args[ii].member->user;
6175 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6178 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6179 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6180 bounce->args[bnc].member = change->args[ii].member;
6183 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6185 const char *ban = change->args[ii].hostmask;
6186 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6189 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6190 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6191 bounce->args[bnc].hostmask = ban;
6193 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6198 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6199 mod_chanmode_announce(chanserv, channel, bounce);
6200 mod_chanmode_free(bounce);
6205 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6207 struct chanNode *channel;
6208 struct banData *bData;
6209 struct mod_chanmode change;
6210 unsigned int ii, jj;
6211 char kick_reason[MAXLEN];
6213 mod_chanmode_init(&change);
6215 change.args[0].mode = MODE_BAN;
6216 for(ii = 0; ii < user->channels.used; ++ii)
6218 channel = user->channels.list[ii]->channel;
6219 /* Need not check for bans if they're opped or voiced. */
6220 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6222 /* Need not check for bans unless channel registration is active. */
6223 if(!channel->channel_info || IsSuspended(channel->channel_info))
6225 /* Look for a matching ban already on the channel. */
6226 for(jj = 0; jj < channel->banlist.used; ++jj)
6227 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6229 /* Need not act if we found one. */
6230 if(jj < channel->banlist.used)
6232 /* Look for a matching ban in this channel. */
6233 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6235 if(!user_matches_glob(user, bData->mask, 1))
6237 change.args[0].hostmask = bData->mask;
6238 mod_chanmode_announce(chanserv, channel, &change);
6239 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6240 KickChannelUser(user, channel, chanserv, kick_reason);
6241 bData->triggered = now;
6242 break; /* we don't need to check any more bans in the channel */
6247 static void handle_rename(struct handle_info *handle, const char *old_handle)
6249 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6253 dict_remove2(handle_dnrs, old_handle, 1);
6254 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6255 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6260 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6262 struct userNode *h_user;
6264 if(handle->channels)
6266 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6267 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6269 while(handle->channels)
6270 del_channel_user(handle->channels, 1);
6275 handle_server_link(UNUSED_ARG(struct server *server))
6277 struct chanData *cData;
6279 for(cData = channelList; cData; cData = cData->next)
6281 if(!IsSuspended(cData))
6282 cData->may_opchan = 1;
6283 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6284 && !cData->channel->join_flooded
6285 && ((cData->channel->limit - cData->channel->members.used)
6286 < chanserv_conf.adjust_threshold))
6288 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6289 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6295 chanserv_conf_read(void)
6299 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6300 struct mod_chanmode *change;
6301 struct string_list *strlist;
6302 struct chanNode *chan;
6305 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6307 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6310 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6311 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6312 chanserv_conf.support_channels.used = 0;
6313 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6315 for(ii = 0; ii < strlist->used; ++ii)
6317 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6320 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6322 channelList_append(&chanserv_conf.support_channels, chan);
6325 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6328 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6331 chan = AddChannel(str, now, str2, NULL);
6333 channelList_append(&chanserv_conf.support_channels, chan);
6335 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6336 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6337 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6338 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6339 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6340 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6341 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6342 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6343 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6344 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6345 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6346 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6347 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6348 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6349 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6350 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6351 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6352 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6353 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6354 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6355 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6356 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6357 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6359 NickChange(chanserv, str, 0);
6360 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6361 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6362 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6363 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6364 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6365 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6366 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6367 chanserv_conf.max_owned = str ? atoi(str) : 5;
6368 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6369 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6370 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6371 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6372 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6373 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6374 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6377 safestrncpy(mode_line, str, sizeof(mode_line));
6378 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6379 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6381 chanserv_conf.default_modes = *change;
6382 mod_chanmode_free(change);
6384 free_string_list(chanserv_conf.set_shows);
6385 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6387 strlist = string_list_copy(strlist);
6390 static const char *list[] = {
6391 /* free form text */
6392 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6393 /* options based on user level */
6394 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6395 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6396 /* multiple choice options */
6397 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6398 /* binary options */
6399 "DynLimit", "NoDelete",
6404 strlist = alloc_string_list(ArrayLength(list)-1);
6405 for(ii=0; list[ii]; ii++)
6406 string_list_append(strlist, strdup(list[ii]));
6408 chanserv_conf.set_shows = strlist;
6409 /* We don't look things up now, in case the list refers to options
6410 * defined by modules initialized after this point. Just mark the
6411 * function list as invalid, so it will be initialized.
6413 set_shows_list.used = 0;
6414 free_string_list(chanserv_conf.eightball);
6415 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6418 strlist = string_list_copy(strlist);
6422 strlist = alloc_string_list(4);
6423 string_list_append(strlist, strdup("Yes."));
6424 string_list_append(strlist, strdup("No."));
6425 string_list_append(strlist, strdup("Maybe so."));
6427 chanserv_conf.eightball = strlist;
6428 free_string_list(chanserv_conf.old_ban_names);
6429 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6431 strlist = string_list_copy(strlist);
6433 strlist = alloc_string_list(2);
6434 chanserv_conf.old_ban_names = strlist;
6435 /* the variable itself is actually declared in proto-common.c; this is equally
6437 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6438 off_channel = (str && enabled_string(str)) ? 1 : 0;
6442 chanserv_note_type_read(const char *key, struct record_data *rd)
6445 struct note_type *ntype;
6448 if(!(obj = GET_RECORD_OBJECT(rd)))
6450 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6453 if(!(ntype = chanserv_create_note_type(key)))
6455 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6459 /* Figure out set access */
6460 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6462 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6463 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6465 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6467 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6468 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6470 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6472 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6476 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6477 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6478 ntype->set_access.min_opserv = 0;
6481 /* Figure out visibility */
6482 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6483 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6484 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6485 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6486 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6487 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6488 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6489 ntype->visible_type = NOTE_VIS_ALL;
6491 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6493 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6494 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6498 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6500 struct handle_info *handle;
6501 struct userData *uData;
6502 char *seen, *inf, *flags;
6504 unsigned short access;
6506 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6508 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6512 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6513 if(access > UL_OWNER)
6515 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6519 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6520 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6521 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6522 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6523 handle = get_handle_info(key);
6526 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6530 uData = add_channel_user(chan, handle, access, last_seen, inf);
6531 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6535 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6537 struct banData *bData;
6538 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6539 time_t set_time, triggered_time, expires_time;
6541 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6543 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6547 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6548 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6549 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6550 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6551 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6552 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6554 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6555 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6557 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6559 expires_time = set_time + atoi(s_duration);
6563 if(expires_time && (expires_time < now))
6566 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6569 static struct suspended *
6570 chanserv_read_suspended(dict_t obj)
6572 struct suspended *suspended = calloc(1, sizeof(*suspended));
6576 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6577 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6578 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6579 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6580 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6581 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6582 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6583 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6584 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6585 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6590 chanserv_channel_read(const char *key, struct record_data *hir)
6592 struct suspended *suspended;
6593 struct mod_chanmode *modes;
6594 struct chanNode *cNode;
6595 struct chanData *cData;
6596 struct dict *channel, *obj;
6597 char *str, *argv[10];
6601 channel = hir->d.object;
6603 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6606 cNode = AddChannel(key, now, NULL, NULL);
6609 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6612 cData = register_channel(cNode, str);
6615 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6619 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6621 enum levelOption lvlOpt;
6622 enum charOption chOpt;
6624 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6625 cData->flags = atoi(str);
6627 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6629 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6631 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6632 else if(levelOptions[lvlOpt].old_flag)
6634 if(cData->flags & levelOptions[lvlOpt].old_flag)
6635 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6637 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6641 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6643 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6645 cData->chOpts[chOpt] = str[0];
6648 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6650 enum levelOption lvlOpt;
6651 enum charOption chOpt;
6654 cData->flags = base64toint(str, 5);
6655 count = strlen(str += 5);
6656 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6659 if(levelOptions[lvlOpt].old_flag)
6661 if(cData->flags & levelOptions[lvlOpt].old_flag)
6662 lvl = levelOptions[lvlOpt].flag_value;
6664 lvl = levelOptions[lvlOpt].default_value;
6666 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6668 case 'c': lvl = UL_COOWNER; break;
6669 case 'm': lvl = UL_MASTER; break;
6670 case 'n': lvl = UL_OWNER+1; break;
6671 case 'o': lvl = UL_OP; break;
6672 case 'p': lvl = UL_PEON; break;
6673 case 'w': lvl = UL_OWNER; break;
6674 default: lvl = 0; break;
6676 cData->lvlOpts[lvlOpt] = lvl;
6678 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6679 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6682 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6684 suspended = chanserv_read_suspended(obj);
6685 cData->suspended = suspended;
6686 suspended->cData = cData;
6687 /* We could use suspended->expires and suspended->revoked to
6688 * set the CHANNEL_SUSPENDED flag, but we don't. */
6690 else if(IsSuspended(cData))
6692 suspended = calloc(1, sizeof(*suspended));
6693 suspended->issued = 0;
6694 suspended->revoked = 0;
6695 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6696 suspended->expires = str ? atoi(str) : 0;
6697 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6698 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6699 suspended->reason = strdup(str ? str : "No reason");
6700 suspended->previous = NULL;
6701 cData->suspended = suspended;
6702 suspended->cData = cData;
6705 suspended = NULL; /* to squelch a warning */
6707 if(IsSuspended(cData)) {
6708 if(suspended->expires > now)
6709 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6710 else if(suspended->expires)
6711 cData->flags &= ~CHANNEL_SUSPENDED;
6714 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6715 struct mod_chanmode change;
6716 mod_chanmode_init(&change);
6718 change.args[0].mode = MODE_CHANOP;
6719 change.args[0].member = AddChannelUser(chanserv, cNode);
6720 mod_chanmode_announce(chanserv, cNode, &change);
6723 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6724 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6725 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6726 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6727 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6728 cData->max = str ? atoi(str) : 0;
6729 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6730 cData->greeting = str ? strdup(str) : NULL;
6731 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6732 cData->user_greeting = str ? strdup(str) : NULL;
6733 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6734 cData->topic_mask = str ? strdup(str) : NULL;
6735 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6736 cData->topic = str ? strdup(str) : NULL;
6738 if(!IsSuspended(cData)
6739 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6740 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6741 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6742 cData->modes = *modes;
6743 cData->modes.modes_set |= MODE_REGISTERED;
6744 if(cData->modes.argc > 1)
6745 cData->modes.argc = 1;
6746 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6747 mod_chanmode_free(modes);
6750 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6751 for(it = dict_first(obj); it; it = iter_next(it))
6752 user_read_helper(iter_key(it), iter_data(it), cData);
6754 if(!cData->users && !IsProtected(cData))
6756 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6757 unregister_channel(cData, "has empty user list.");
6761 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6762 for(it = dict_first(obj); it; it = iter_next(it))
6763 ban_read_helper(iter_key(it), iter_data(it), cData);
6765 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6766 for(it = dict_first(obj); it; it = iter_next(it))
6768 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6769 struct record_data *rd = iter_data(it);
6770 const char *note, *setter;
6772 if(rd->type != RECDB_OBJECT)
6774 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6778 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6780 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6782 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6786 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6787 if(!setter) setter = "<unknown>";
6788 chanserv_add_channel_note(cData, ntype, setter, note);
6796 chanserv_dnr_read(const char *key, struct record_data *hir)
6798 const char *setter, *reason, *str;
6799 struct do_not_register *dnr;
6801 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6804 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6807 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6810 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6813 dnr = chanserv_add_dnr(key, setter, reason);
6816 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6818 dnr->set = atoi(str);
6824 chanserv_saxdb_read(struct dict *database)
6826 struct dict *section;
6829 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6830 for(it = dict_first(section); it; it = iter_next(it))
6831 chanserv_note_type_read(iter_key(it), iter_data(it));
6833 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6834 for(it = dict_first(section); it; it = iter_next(it))
6835 chanserv_channel_read(iter_key(it), iter_data(it));
6837 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6838 for(it = dict_first(section); it; it = iter_next(it))
6839 chanserv_dnr_read(iter_key(it), iter_data(it));
6845 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6847 int high_present = 0;
6848 saxdb_start_record(ctx, KEY_USERS, 1);
6849 for(; uData; uData = uData->next)
6851 if((uData->access >= UL_PRESENT) && uData->present)
6853 saxdb_start_record(ctx, uData->handle->handle, 0);
6854 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6855 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6857 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6859 saxdb_write_string(ctx, KEY_INFO, uData->info);
6860 saxdb_end_record(ctx);
6862 saxdb_end_record(ctx);
6863 return high_present;
6867 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6871 saxdb_start_record(ctx, KEY_BANS, 1);
6872 for(; bData; bData = bData->next)
6874 saxdb_start_record(ctx, bData->mask, 0);
6875 saxdb_write_int(ctx, KEY_SET, bData->set);
6876 if(bData->triggered)
6877 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6879 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6881 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6883 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6884 saxdb_end_record(ctx);
6886 saxdb_end_record(ctx);
6890 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6892 saxdb_start_record(ctx, name, 0);
6893 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6894 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6896 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6898 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6900 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6902 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6903 saxdb_end_record(ctx);
6907 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6911 enum levelOption lvlOpt;
6912 enum charOption chOpt;
6914 saxdb_start_record(ctx, channel->channel->name, 1);
6916 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6917 saxdb_write_int(ctx, KEY_MAX, channel->max);
6919 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6920 if(channel->registrar)
6921 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6922 if(channel->greeting)
6923 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6924 if(channel->user_greeting)
6925 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6926 if(channel->topic_mask)
6927 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6928 if(channel->suspended)
6929 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6931 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6932 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6933 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6934 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6935 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6937 buf[0] = channel->chOpts[chOpt];
6939 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6941 saxdb_end_record(ctx);
6943 if(channel->modes.modes_set || channel->modes.modes_clear)
6945 mod_chanmode_format(&channel->modes, buf);
6946 saxdb_write_string(ctx, KEY_MODES, buf);
6949 high_present = chanserv_write_users(ctx, channel->users);
6950 chanserv_write_bans(ctx, channel->bans);
6952 if(dict_size(channel->notes))
6956 saxdb_start_record(ctx, KEY_NOTES, 1);
6957 for(it = dict_first(channel->notes); it; it = iter_next(it))
6959 struct note *note = iter_data(it);
6960 saxdb_start_record(ctx, iter_key(it), 0);
6961 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6962 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6963 saxdb_end_record(ctx);
6965 saxdb_end_record(ctx);
6968 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6969 saxdb_end_record(ctx);
6973 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6977 saxdb_start_record(ctx, ntype->name, 0);
6978 switch(ntype->set_access_type)
6980 case NOTE_SET_CHANNEL_ACCESS:
6981 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6983 case NOTE_SET_CHANNEL_SETTER:
6984 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6986 case NOTE_SET_PRIVILEGED: default:
6987 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6990 switch(ntype->visible_type)
6992 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6993 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6994 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6996 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6997 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6998 saxdb_end_record(ctx);
7002 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7004 struct do_not_register *dnr;
7007 for(it = dict_first(dnrs); it; it = iter_next(it))
7009 dnr = iter_data(it);
7010 saxdb_start_record(ctx, dnr->chan_name, 0);
7012 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7013 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7014 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7015 saxdb_end_record(ctx);
7020 chanserv_saxdb_write(struct saxdb_context *ctx)
7023 struct chanData *channel;
7026 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7027 for(it = dict_first(note_types); it; it = iter_next(it))
7028 chanserv_write_note_type(ctx, iter_data(it));
7029 saxdb_end_record(ctx);
7032 saxdb_start_record(ctx, KEY_DNR, 1);
7033 write_dnrs_helper(ctx, handle_dnrs);
7034 write_dnrs_helper(ctx, plain_dnrs);
7035 write_dnrs_helper(ctx, mask_dnrs);
7036 saxdb_end_record(ctx);
7039 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7040 for(channel = channelList; channel; channel = channel->next)
7041 chanserv_write_channel(ctx, channel);
7042 saxdb_end_record(ctx);
7048 chanserv_db_cleanup(void) {
7050 unreg_part_func(handle_part);
7052 unregister_channel(channelList, "terminating.");
7053 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7054 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7055 free(chanserv_conf.support_channels.list);
7056 dict_delete(handle_dnrs);
7057 dict_delete(plain_dnrs);
7058 dict_delete(mask_dnrs);
7059 dict_delete(note_types);
7060 free_string_list(chanserv_conf.eightball);
7061 free_string_list(chanserv_conf.old_ban_names);
7062 free_string_list(chanserv_conf.set_shows);
7063 free(set_shows_list.list);
7064 free(uset_shows_list.list);
7067 struct userData *helper = helperList;
7068 helperList = helperList->next;
7073 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7074 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7075 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7078 init_chanserv(const char *nick)
7080 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7081 conf_register_reload(chanserv_conf_read);
7083 reg_server_link_func(handle_server_link);
7085 reg_new_channel_func(handle_new_channel);
7086 reg_join_func(handle_join);
7087 reg_part_func(handle_part);
7088 reg_kick_func(handle_kick);
7089 reg_topic_func(handle_topic);
7090 reg_mode_change_func(handle_mode);
7091 reg_nick_change_func(handle_nick_change);
7093 reg_auth_func(handle_auth);
7094 reg_handle_rename_func(handle_rename);
7095 reg_unreg_func(handle_unreg);
7097 handle_dnrs = dict_new();
7098 dict_set_free_data(handle_dnrs, free);
7099 plain_dnrs = dict_new();
7100 dict_set_free_data(plain_dnrs, free);
7101 mask_dnrs = dict_new();
7102 dict_set_free_data(mask_dnrs, free);
7104 reg_svccmd_unbind_func(handle_svccmd_unbind);
7105 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7106 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7107 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7108 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7109 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7110 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7111 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7112 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7113 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7115 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7116 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7118 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7119 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7120 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7121 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7122 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7124 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7125 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7126 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7127 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7128 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7130 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7131 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7132 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7133 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7135 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7136 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7137 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7138 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7139 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7140 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7141 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7142 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7144 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7145 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7146 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7147 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7148 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7149 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7150 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7151 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7152 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7153 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7154 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7155 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7156 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7157 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7159 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7160 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7161 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7162 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7163 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7165 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7166 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7168 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7169 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7170 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7171 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7172 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7173 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7174 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7175 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7176 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7177 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7178 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7180 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7181 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7183 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7184 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7185 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7186 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7188 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7189 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7190 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7191 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7192 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7194 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7195 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7196 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7197 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7198 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7199 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7201 /* Channel options */
7202 DEFINE_CHANNEL_OPTION(defaulttopic);
7203 DEFINE_CHANNEL_OPTION(topicmask);
7204 DEFINE_CHANNEL_OPTION(greeting);
7205 DEFINE_CHANNEL_OPTION(usergreeting);
7206 DEFINE_CHANNEL_OPTION(modes);
7207 DEFINE_CHANNEL_OPTION(enfops);
7208 DEFINE_CHANNEL_OPTION(giveops);
7209 DEFINE_CHANNEL_OPTION(protect);
7210 DEFINE_CHANNEL_OPTION(enfmodes);
7211 DEFINE_CHANNEL_OPTION(enftopic);
7212 DEFINE_CHANNEL_OPTION(pubcmd);
7213 DEFINE_CHANNEL_OPTION(givevoice);
7214 DEFINE_CHANNEL_OPTION(userinfo);
7215 DEFINE_CHANNEL_OPTION(dynlimit);
7216 DEFINE_CHANNEL_OPTION(topicsnarf);
7217 DEFINE_CHANNEL_OPTION(nodelete);
7218 DEFINE_CHANNEL_OPTION(toys);
7219 DEFINE_CHANNEL_OPTION(setters);
7220 DEFINE_CHANNEL_OPTION(topicrefresh);
7221 DEFINE_CHANNEL_OPTION(ctcpusers);
7222 DEFINE_CHANNEL_OPTION(ctcpreaction);
7223 DEFINE_CHANNEL_OPTION(inviteme);
7225 DEFINE_CHANNEL_OPTION(offchannel);
7226 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7228 /* Alias set topic to set defaulttopic for compatibility. */
7229 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7232 DEFINE_USER_OPTION(noautoop);
7233 DEFINE_USER_OPTION(autoinvite);
7234 DEFINE_USER_OPTION(info);
7236 /* Alias uset autovoice to uset autoop. */
7237 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7239 note_types = dict_new();
7240 dict_set_free_data(note_types, chanserv_deref_note_type);
7243 chanserv = AddService(nick, "Channel Services", NULL);
7244 service_register(chanserv)->trigger = '!';
7245 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7247 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7249 if(chanserv_conf.channel_expire_frequency)
7250 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7252 if(chanserv_conf.refresh_period)
7254 time_t next_refresh;
7255 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7256 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7259 reg_exit_func(chanserv_db_cleanup);
7260 message_register_table(msgtab);