1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for opserv_bad_channel() */
27 #define CHANSERV_CONF_NAME "services/chanserv"
29 /* ChanServ options */
30 #define KEY_SUPPORT_CHANNEL "support_channel"
31 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
32 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
33 #define KEY_INFO_DELAY "info_delay"
34 #define KEY_MAX_GREETLEN "max_greetlen"
35 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
36 #define KEY_ADJUST_DELAY "adjust_delay"
37 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
38 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
39 #define KEY_MAX_CHAN_USERS "max_chan_users"
40 #define KEY_MAX_CHAN_BANS "max_chan_bans"
41 #define KEY_NICK "nick"
42 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
43 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
44 #define KEY_SWITCH_TIMEOUT "switch_timeout"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
56 /* ChanServ database */
57 #define KEY_CHANNELS "channels"
58 #define KEY_NOTE_TYPES "note_types"
60 /* Note type parameters */
61 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
62 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
63 #define KEY_NOTE_SETTER_ACCESS "setter_access"
64 #define KEY_NOTE_VISIBILITY "visibility"
65 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
66 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
67 #define KEY_NOTE_VIS_ALL "all"
68 #define KEY_NOTE_MAX_LENGTH "max_length"
69 #define KEY_NOTE_SETTER "setter"
70 #define KEY_NOTE_NOTE "note"
72 /* Do-not-register channels */
74 #define KEY_DNR_SET "set"
75 #define KEY_DNR_SETTER "setter"
76 #define KEY_DNR_REASON "reason"
79 #define KEY_REGISTERED "registered"
80 #define KEY_REGISTRAR "registrar"
81 #define KEY_SUSPENDED "suspended"
82 #define KEY_PREVIOUS "previous"
83 #define KEY_SUSPENDER "suspender"
84 #define KEY_ISSUED "issued"
85 #define KEY_REVOKED "revoked"
86 #define KEY_SUSPEND_EXPIRES "suspend_expires"
87 #define KEY_SUSPEND_REASON "suspend_reason"
88 #define KEY_VISITED "visited"
89 #define KEY_TOPIC "topic"
90 #define KEY_GREETING "greeting"
91 #define KEY_USER_GREETING "user_greeting"
92 #define KEY_MODES "modes"
93 #define KEY_FLAGS "flags"
94 #define KEY_OPTIONS "options"
95 #define KEY_USERS "users"
96 #define KEY_BANS "bans"
98 #define KEY_NOTES "notes"
99 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_LEVEL "level"
103 #define KEY_INFO "info"
104 #define KEY_SEEN "seen"
107 #define KEY_OWNER "owner"
108 #define KEY_REASON "reason"
109 #define KEY_SET "set"
110 #define KEY_DURATION "duration"
111 #define KEY_EXPIRES "expires"
112 #define KEY_TRIGGERED "triggered"
114 #define CHANNEL_DEFAULT_FLAGS (0)
115 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
117 /* Administrative messages */
118 static const struct message_entry msgtab[] = {
119 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
121 /* Channel registration */
122 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
123 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
124 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
125 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
126 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
127 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
129 /* Do-not-register channels */
130 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
131 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
132 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
133 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
134 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
135 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
136 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
137 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
138 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
139 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
140 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
142 /* Channel unregistration */
143 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
144 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
145 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
146 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
149 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
150 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
152 /* Channel merging */
153 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
154 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
155 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
156 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
157 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
159 /* Handle unregistration */
160 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
163 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
164 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
165 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
166 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
167 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
168 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
169 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
170 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
171 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
172 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
173 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
174 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
175 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
177 /* Removing yourself from a channel. */
178 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
179 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
180 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
182 /* User management */
183 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
241 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
242 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
243 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
244 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
245 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
246 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
247 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
248 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
249 { "CSMSG_SET_MODES", "$bModes $b %s" },
250 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
251 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
252 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
253 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
254 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
255 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
256 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
257 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
258 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
259 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
260 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
261 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
262 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
263 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
264 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
265 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
266 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
267 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
268 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
269 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
270 { "CSMSG_USET_INFO", "$bInfo $b %s" },
272 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
273 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
274 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
275 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
276 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
277 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
278 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
279 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
280 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
281 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
282 { "CSMSG_PROTECT_NONE", "No users will be protected." },
283 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
284 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
285 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
286 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
287 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
288 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
289 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
290 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
291 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
292 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
293 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
294 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
296 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
297 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
298 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
299 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
300 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
302 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
303 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
304 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
306 /* Channel userlist */
307 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
308 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
309 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
310 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
312 /* Channel note list */
313 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
314 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
315 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
316 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
317 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
318 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
319 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
320 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
321 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
322 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
323 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
324 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
325 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
326 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
327 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
329 /* Channel [un]suspension */
330 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
331 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
332 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
333 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
334 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
335 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
336 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
338 /* Access information */
339 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
340 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
341 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
342 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
343 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
344 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
345 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
346 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
347 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
348 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
349 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
351 /* Seen information */
352 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
353 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
354 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
355 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
357 /* Names information */
358 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
359 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
361 /* Channel information */
362 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
363 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
364 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
365 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
366 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
367 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
368 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
369 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
370 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
371 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
372 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
373 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
374 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
375 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
378 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
379 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
380 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
381 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
382 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
384 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
385 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
386 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
387 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
388 { "CSMSG_PEEK_OPS", "$bOps:$b" },
389 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
391 /* Network information */
392 { "CSMSG_NETWORK_INFO", "Network Information:" },
393 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
394 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
395 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
396 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
397 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
398 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
399 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
402 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
403 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
404 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
406 /* Channel searches */
407 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
408 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
409 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
410 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
412 /* Channel configuration */
413 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
414 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
415 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
418 { "CSMSG_USER_OPTIONS", "User Options:" },
419 { "CSMSG_USER_PROTECTED", "That user is protected." },
422 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
423 { "CSMSG_PING_RESPONSE", "Pong!" },
424 { "CSMSG_WUT_RESPONSE", "wut" },
425 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
426 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
427 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
428 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
429 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
430 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
431 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
434 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
438 /* eject_user and unban_user flags */
439 #define ACTION_KICK 0x0001
440 #define ACTION_BAN 0x0002
441 #define ACTION_ADD_BAN 0x0004
442 #define ACTION_ADD_TIMED_BAN 0x0008
443 #define ACTION_UNBAN 0x0010
444 #define ACTION_DEL_BAN 0x0020
446 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
447 #define MODELEN 40 + KEYLEN
451 #define CSFUNC_ARGS user, channel, argc, argv, cmd
453 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
454 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
455 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
456 reply("MSG_MISSING_PARAMS", argv[0]); \
460 DECLARE_LIST(dnrList, struct do_not_register *);
461 DEFINE_LIST(dnrList, struct do_not_register *);
463 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
465 struct userNode *chanserv;
467 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
468 static struct log_type *CS_LOG;
472 struct channelList support_channels;
473 struct mod_chanmode default_modes;
475 unsigned long db_backup_frequency;
476 unsigned long channel_expire_frequency;
479 unsigned int adjust_delay;
480 long channel_expire_delay;
481 unsigned int nodelete_level;
483 unsigned int adjust_threshold;
484 int join_flood_threshold;
486 unsigned int greeting_length;
487 unsigned int refresh_period;
489 unsigned int max_owned;
490 unsigned int max_chan_users;
491 unsigned int max_chan_bans;
493 struct string_list *set_shows;
494 struct string_list *eightball;
495 struct string_list *old_ban_names;
497 const char *ctcp_short_ban_duration;
498 const char *ctcp_long_ban_duration;
500 const char *irc_operator_epithet;
501 const char *network_helper_epithet;
502 const char *support_helper_epithet;
507 struct userNode *user;
508 struct userNode *bot;
509 struct chanNode *channel;
511 unsigned short lowest;
512 unsigned short highest;
513 struct userData **users;
514 struct helpfile_table table;
517 enum note_access_type
519 NOTE_SET_CHANNEL_ACCESS,
520 NOTE_SET_CHANNEL_SETTER,
524 enum note_visible_type
527 NOTE_VIS_CHANNEL_USERS,
533 enum note_access_type set_access_type;
535 unsigned int min_opserv;
536 unsigned short min_ulevel;
538 enum note_visible_type visible_type;
539 unsigned int max_length;
546 struct note_type *type;
547 char setter[NICKSERV_HANDLE_LEN+1];
551 static unsigned int registered_channels;
552 static unsigned int banCount;
554 static const struct {
557 unsigned short level;
560 { "peon", "Peon", UL_PEON, '+' },
561 { "op", "Op", UL_OP, '@' },
562 { "master", "Master", UL_MASTER, '%' },
563 { "coowner", "Coowner", UL_COOWNER, '*' },
564 { "owner", "Owner", UL_OWNER, '!' },
565 { "helper", "BUG:", UL_HELPER, 'X' }
568 static const struct {
571 unsigned short default_value;
572 unsigned int old_idx;
573 unsigned int old_flag;
574 unsigned short flag_value;
576 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
577 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
578 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
579 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
580 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
581 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
582 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
583 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
584 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
585 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
586 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
589 struct charOptionValues {
592 } protectValues[] = {
593 { 'a', "CSMSG_PROTECT_ALL" },
594 { 'e', "CSMSG_PROTECT_EQUAL" },
595 { 'l', "CSMSG_PROTECT_LOWER" },
596 { 'n', "CSMSG_PROTECT_NONE" }
598 { 'd', "CSMSG_TOYS_DISABLED" },
599 { 'n', "CSMSG_TOYS_PRIVATE" },
600 { 'p', "CSMSG_TOYS_PUBLIC" }
601 }, topicRefreshValues[] = {
602 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
603 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
604 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
605 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
606 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
607 }, ctcpReactionValues[] = {
608 { 'k', "CSMSG_CTCPREACTION_KICK" },
609 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
610 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
611 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
614 static const struct {
618 unsigned int old_idx;
620 struct charOptionValues *values;
622 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
623 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
624 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
625 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
628 struct userData *helperList;
629 struct chanData *channelList;
630 static struct module *chanserv_module;
631 static unsigned int userCount;
633 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
634 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
635 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
638 user_level_from_name(const char *name, unsigned short clamp_level)
640 unsigned int level = 0, ii;
643 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
644 if(!irccasecmp(name, accessLevels[ii].name))
645 level = accessLevels[ii].level;
646 if(level > clamp_level)
652 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
655 *minl = strtoul(arg, &sep, 10);
663 *maxl = strtoul(sep+1, &sep, 10);
671 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
673 struct userData *uData, **head;
675 if(!channel || !handle)
678 if(override && HANDLE_FLAGGED(handle, HELPING)
679 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
681 for(uData = helperList;
682 uData && uData->handle != handle;
683 uData = uData->next);
687 uData = calloc(1, sizeof(struct userData));
688 uData->handle = handle;
690 uData->access = UL_HELPER;
696 uData->next = helperList;
698 helperList->prev = uData;
706 for(uData = channel->users; uData; uData = uData->next)
707 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
710 head = &(channel->users);
713 if(uData && (uData != *head))
715 /* Shuffle the user to the head of whatever list he was in. */
717 uData->next->prev = uData->prev;
719 uData->prev->next = uData->next;
725 (**head).prev = uData;
732 /* Returns non-zero if user has at least the minimum access.
733 * exempt_owner is set when handling !set, so the owner can set things
736 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
738 struct userData *uData;
739 struct chanData *cData = channel->channel_info;
740 unsigned short minimum = cData->lvlOpts[opt];
743 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
746 if(minimum <= uData->access)
748 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
753 /* Scan for other users authenticated to the same handle
754 still in the channel. If so, keep them listed as present.
756 user is optional, if not null, it skips checking that userNode
757 (for the handle_part function) */
759 scan_user_presence(struct userData *uData, struct userNode *user)
763 if(IsSuspended(uData->channel)
764 || IsUserSuspended(uData)
765 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
777 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
779 unsigned int eflags, argc;
781 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
783 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
784 if(!channel->channel_info
785 || IsSuspended(channel->channel_info)
787 || !ircncasecmp(text, "ACTION ", 7))
789 /* Figure out the minimum level needed to CTCP the channel */
790 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
792 /* We need to enforce against them; do so. */
795 argv[1] = user->nick;
797 if(GetUserMode(channel, user))
798 eflags |= ACTION_KICK;
799 switch(channel->channel_info->chOpts[chCTCPReaction]) {
800 default: case 'k': /* just do the kick */ break;
802 eflags |= ACTION_BAN;
805 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
806 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
809 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
810 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
813 argv[argc++] = bad_ctcp_reason;
814 eject_user(chanserv, channel, argc, argv, NULL, eflags);
818 chanserv_create_note_type(const char *name)
820 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
821 strcpy(ntype->name, name);
823 dict_insert(note_types, ntype->name, ntype);
828 chanserv_deref_note_type(void *data)
830 struct note_type *ntype = data;
832 if(--ntype->refs > 0)
838 chanserv_flush_note_type(struct note_type *ntype)
840 struct chanData *cData;
841 for(cData = channelList; cData; cData = cData->next)
842 dict_remove(cData->notes, ntype->name);
846 chanserv_truncate_notes(struct note_type *ntype)
848 struct chanData *cData;
850 unsigned int size = sizeof(*note) + ntype->max_length;
852 for(cData = channelList; cData; cData = cData->next) {
853 note = dict_find(cData->notes, ntype->name, NULL);
856 if(strlen(note->note) <= ntype->max_length)
858 dict_remove2(cData->notes, ntype->name, 1);
859 note = realloc(note, size);
860 note->note[ntype->max_length] = 0;
861 dict_insert(cData->notes, ntype->name, note);
865 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
868 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
871 unsigned int len = strlen(text);
873 if(len > type->max_length) len = type->max_length;
874 note = calloc(1, sizeof(*note) + len);
876 strncpy(note->setter, setter, sizeof(note->setter)-1);
877 memcpy(note->note, text, len);
879 dict_insert(channel->notes, type->name, note);
885 chanserv_free_note(void *data)
887 struct note *note = data;
889 chanserv_deref_note_type(note->type);
890 assert(note->type->refs > 0); /* must use delnote to remove the type */
894 static MODCMD_FUNC(cmd_createnote) {
895 struct note_type *ntype;
896 unsigned int arg = 1, existed = 0, max_length;
898 if((ntype = dict_find(note_types, argv[1], NULL)))
901 ntype = chanserv_create_note_type(argv[arg]);
902 if(!irccasecmp(argv[++arg], "privileged"))
905 ntype->set_access_type = NOTE_SET_PRIVILEGED;
906 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
908 else if(!irccasecmp(argv[arg], "channel"))
910 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
913 reply("CSMSG_INVALID_ACCESS", argv[arg]);
916 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
917 ntype->set_access.min_ulevel = ulvl;
919 else if(!irccasecmp(argv[arg], "setter"))
921 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
925 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
929 if(!irccasecmp(argv[++arg], "privileged"))
930 ntype->visible_type = NOTE_VIS_PRIVILEGED;
931 else if(!irccasecmp(argv[arg], "channel_users"))
932 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
933 else if(!irccasecmp(argv[arg], "all"))
934 ntype->visible_type = NOTE_VIS_ALL;
936 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
940 if((arg+1) >= argc) {
941 reply("MSG_MISSING_PARAMS", argv[0]);
944 max_length = strtoul(argv[++arg], NULL, 0);
945 if(max_length < 20 || max_length > 450)
947 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
950 if(existed && (max_length < ntype->max_length))
952 ntype->max_length = max_length;
953 chanserv_truncate_notes(ntype);
955 ntype->max_length = max_length;
958 reply("CSMSG_NOTE_MODIFIED", ntype->name);
960 reply("CSMSG_NOTE_CREATED", ntype->name);
965 dict_remove(note_types, ntype->name);
969 static MODCMD_FUNC(cmd_removenote) {
970 struct note_type *ntype;
973 ntype = dict_find(note_types, argv[1], NULL);
974 force = (argc > 2) && !irccasecmp(argv[2], "force");
977 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
984 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
987 chanserv_flush_note_type(ntype);
989 dict_remove(note_types, argv[1]);
990 reply("CSMSG_NOTE_DELETED", argv[1]);
995 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
999 if(orig->modes_set & change->modes_clear)
1001 if(orig->modes_clear & change->modes_set)
1003 if((orig->modes_set & MODE_KEY)
1004 && strcmp(orig->new_key, change->new_key))
1006 if((orig->modes_set & MODE_LIMIT)
1007 && (orig->new_limit != change->new_limit))
1012 static char max_length_text[MAXLEN+1][16];
1014 static struct helpfile_expansion
1015 chanserv_expand_variable(const char *variable)
1017 struct helpfile_expansion exp;
1019 if(!irccasecmp(variable, "notes"))
1022 exp.type = HF_TABLE;
1023 exp.value.table.length = 1;
1024 exp.value.table.width = 3;
1025 exp.value.table.flags = 0;
1026 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1027 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1028 exp.value.table.contents[0][0] = "Note Type";
1029 exp.value.table.contents[0][1] = "Visibility";
1030 exp.value.table.contents[0][2] = "Max Length";
1031 for(it=dict_first(note_types); it; it=iter_next(it))
1033 struct note_type *ntype = iter_data(it);
1036 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1037 row = exp.value.table.length++;
1038 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1039 exp.value.table.contents[row][0] = ntype->name;
1040 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1041 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1043 if(!max_length_text[ntype->max_length][0])
1044 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1045 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1050 exp.type = HF_STRING;
1051 exp.value.str = NULL;
1055 static struct chanData*
1056 register_channel(struct chanNode *cNode, char *registrar)
1058 struct chanData *channel;
1059 enum levelOption lvlOpt;
1060 enum charOption chOpt;
1062 channel = calloc(1, sizeof(struct chanData));
1064 channel->notes = dict_new();
1065 dict_set_free_data(channel->notes, chanserv_free_note);
1067 channel->registrar = strdup(registrar);
1068 channel->registered = now;
1069 channel->visited = now;
1070 channel->limitAdjusted = now;
1071 channel->flags = CHANNEL_DEFAULT_FLAGS;
1072 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1073 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1074 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1075 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1077 channel->prev = NULL;
1078 channel->next = channelList;
1081 channelList->prev = channel;
1082 channelList = channel;
1083 registered_channels++;
1085 channel->channel = cNode;
1087 cNode->channel_info = channel;
1092 static struct userData*
1093 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1095 struct userData *ud;
1097 if(access > UL_OWNER)
1100 ud = calloc(1, sizeof(*ud));
1101 ud->channel = channel;
1102 ud->handle = handle;
1104 ud->access = access;
1105 ud->info = info ? strdup(info) : NULL;
1108 ud->next = channel->users;
1110 channel->users->prev = ud;
1111 channel->users = ud;
1113 channel->userCount++;
1117 ud->u_next = ud->handle->channels;
1119 ud->u_next->u_prev = ud;
1120 ud->handle->channels = ud;
1125 static void unregister_channel(struct chanData *channel, const char *reason);
1128 del_channel_user(struct userData *user, int do_gc)
1130 struct chanData *channel = user->channel;
1132 channel->userCount--;
1136 user->prev->next = user->next;
1138 channel->users = user->next;
1140 user->next->prev = user->prev;
1143 user->u_prev->u_next = user->u_next;
1145 user->handle->channels = user->u_next;
1147 user->u_next->u_prev = user->u_prev;
1151 if(do_gc && !channel->users && !IsProtected(channel))
1152 unregister_channel(channel, "lost all users.");
1155 static void expire_ban(void *data);
1157 static struct banData*
1158 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1161 unsigned int ii, l1, l2;
1166 bd = malloc(sizeof(struct banData));
1168 bd->channel = channel;
1170 bd->triggered = triggered;
1171 bd->expires = expires;
1173 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1175 extern const char *hidden_host_suffix;
1176 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1180 l2 = strlen(old_name);
1183 if(irccasecmp(mask + l1 - l2, old_name))
1185 new_mask = alloca(MAXLEN);
1186 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1189 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1191 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1192 bd->reason = reason ? strdup(reason) : NULL;
1195 timeq_add(expires, expire_ban, bd);
1198 bd->next = channel->bans;
1200 channel->bans->prev = bd;
1202 channel->banCount++;
1209 del_channel_ban(struct banData *ban)
1211 ban->channel->banCount--;
1215 ban->prev->next = ban->next;
1217 ban->channel->bans = ban->next;
1220 ban->next->prev = ban->prev;
1223 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1232 expire_ban(void *data)
1234 struct banData *bd = data;
1235 if(!IsSuspended(bd->channel))
1237 struct banList bans;
1238 struct mod_chanmode change;
1240 bans = bd->channel->channel->banlist;
1241 mod_chanmode_init(&change);
1242 for(ii=0; ii<bans.used; ii++)
1244 if(!strcmp(bans.list[ii]->ban, bd->mask))
1247 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1248 change.args[0].hostmask = bd->mask;
1249 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1255 del_channel_ban(bd);
1258 static void chanserv_expire_suspension(void *data);
1261 unregister_channel(struct chanData *channel, const char *reason)
1263 char msgbuf[MAXLEN];
1265 /* After channel unregistration, the following must be cleaned
1267 - Channel information.
1270 - Channel suspension data.
1271 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1277 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1279 while(channel->users)
1280 del_channel_user(channel->users, 0);
1282 while(channel->bans)
1283 del_channel_ban(channel->bans);
1285 if(channel->topic) free(channel->topic);
1286 if(channel->registrar) free(channel->registrar);
1287 if(channel->greeting) free(channel->greeting);
1288 if(channel->user_greeting) free(channel->user_greeting);
1289 if(channel->topic_mask) free(channel->topic_mask);
1291 if(channel->prev) channel->prev->next = channel->next;
1292 else channelList = channel->next;
1294 if(channel->next) channel->next->prev = channel->prev;
1296 if(channel->suspended)
1298 struct chanNode *cNode = channel->channel;
1299 struct suspended *suspended, *next_suspended;
1301 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1303 next_suspended = suspended->previous;
1304 free(suspended->suspender);
1305 free(suspended->reason);
1306 if(suspended->expires)
1307 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1312 cNode->channel_info = NULL;
1314 channel->channel->channel_info = NULL;
1317 dict_delete(channel->notes);
1318 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1319 if(!IsSuspended(channel))
1320 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1321 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1322 UnlockChannel(channel->channel);
1324 registered_channels--;
1328 expire_channels(UNUSED_ARG(void *data))
1330 struct chanData *channel, *next;
1331 struct userData *user;
1332 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1334 intervalString(delay, chanserv_conf.channel_expire_delay);
1335 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1337 for(channel = channelList; channel; channel = next)
1339 next = channel->next;
1341 /* See if the channel can be expired. */
1342 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1343 || IsProtected(channel))
1346 /* Make sure there are no high-ranking users still in the channel. */
1347 for(user=channel->users; user; user=user->next)
1348 if(user->present && (user->access >= UL_PRESENT))
1353 /* Unregister the channel */
1354 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1355 unregister_channel(channel, "registration expired.");
1358 if(chanserv_conf.channel_expire_frequency)
1359 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1363 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1365 char protect = channel->chOpts[chProtect];
1366 struct userData *cs_victim, *cs_aggressor;
1368 /* Don't protect if no one is to be protected, someone is attacking
1369 himself, or if the aggressor is an IRC Operator. */
1370 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1373 /* Don't protect if the victim isn't authenticated (because they
1374 can't be a channel user), unless we are to protect non-users
1376 cs_victim = GetChannelAccess(channel, victim->handle_info);
1377 if(protect != 'a' && !cs_victim)
1380 /* Protect if the aggressor isn't a user because at this point,
1381 the aggressor can only be less than or equal to the victim. */
1382 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1386 /* If the aggressor was a user, then the victim can't be helped. */
1393 if(cs_victim->access > cs_aggressor->access)
1398 if(cs_victim->access >= cs_aggressor->access)
1407 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1409 struct chanData *cData = channel->channel_info;
1410 struct userData *cs_victim;
1412 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1413 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1414 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1416 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1424 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1426 if(IsService(victim))
1428 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1432 if(protect_user(victim, user, channel->channel_info))
1434 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1441 static struct do_not_register *
1442 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1444 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1445 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1446 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1447 strcpy(dnr->reason, reason);
1449 if(dnr->chan_name[0] == '*')
1450 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1451 else if(strpbrk(dnr->chan_name, "*?"))
1452 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1454 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1458 static struct dnrList
1459 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1461 struct dnrList list;
1463 struct do_not_register *dnr;
1465 dnrList_init(&list);
1466 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1467 dnrList_append(&list, dnr);
1468 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1469 dnrList_append(&list, dnr);
1471 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1472 if(match_ircglob(chan_name, iter_key(it)))
1473 dnrList_append(&list, iter_data(it));
1478 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1480 struct dnrList list;
1481 struct do_not_register *dnr;
1483 char buf[INTERVALLEN];
1485 list = chanserv_find_dnrs(chan_name, handle);
1486 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1488 dnr = list.list[ii];
1491 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1492 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1495 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1498 reply("CSMSG_MORE_DNRS", list.used - ii);
1503 struct do_not_register *
1504 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1506 struct do_not_register *dnr;
1509 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1513 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1515 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1516 if(match_ircglob(chan_name, iter_key(it)))
1517 return iter_data(it);
1522 static CHANSERV_FUNC(cmd_noregister)
1525 struct do_not_register *dnr;
1526 char buf[INTERVALLEN];
1527 unsigned int matches;
1533 reply("CSMSG_DNR_SEARCH_RESULTS");
1535 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1537 dnr = iter_data(it);
1539 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1541 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1544 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1546 dnr = iter_data(it);
1548 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1550 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1553 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1555 dnr = iter_data(it);
1557 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1559 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1564 reply("MSG_MATCH_COUNT", matches);
1566 reply("MSG_NO_MATCHES");
1572 if(!IsChannelName(target) && (*target != '*'))
1574 reply("CSMSG_NOT_DNR", target);
1580 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1581 if((*target == '*') && !get_handle_info(target + 1))
1583 reply("MSG_HANDLE_UNKNOWN", target + 1);
1586 chanserv_add_dnr(target, user->handle_info->handle, reason);
1587 reply("CSMSG_NOREGISTER_CHANNEL", target);
1591 reply("CSMSG_DNR_SEARCH_RESULTS");
1593 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1595 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1597 reply("MSG_NO_MATCHES");
1601 static CHANSERV_FUNC(cmd_allowregister)
1603 const char *chan_name = argv[1];
1605 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1607 dict_remove(handle_dnrs, chan_name+1);
1608 reply("CSMSG_DNR_REMOVED", chan_name);
1610 else if(dict_find(plain_dnrs, chan_name, NULL))
1612 dict_remove(plain_dnrs, chan_name);
1613 reply("CSMSG_DNR_REMOVED", chan_name);
1615 else if(dict_find(mask_dnrs, chan_name, NULL))
1617 dict_remove(mask_dnrs, chan_name);
1618 reply("CSMSG_DNR_REMOVED", chan_name);
1622 reply("CSMSG_NO_SUCH_DNR", chan_name);
1629 chanserv_get_owned_count(struct handle_info *hi)
1631 struct userData *cList;
1634 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1635 if(cList->access == UL_OWNER)
1640 static CHANSERV_FUNC(cmd_register)
1642 struct mod_chanmode *change;
1643 struct handle_info *handle;
1644 struct chanData *cData;
1645 struct modeNode *mn;
1646 char reason[MAXLEN];
1648 unsigned int new_channel, force=0;
1649 struct do_not_register *dnr;
1653 if(channel->channel_info)
1655 reply("CSMSG_ALREADY_REGGED", channel->name);
1659 if(channel->bad_channel)
1661 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1665 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1667 reply("CSMSG_MUST_BE_OPPED", channel->name);
1672 chan_name = channel->name;
1676 if((argc < 2) || !IsChannelName(argv[1]))
1678 reply("MSG_NOT_CHANNEL_NAME");
1682 if(opserv_bad_channel(argv[1]))
1684 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1689 chan_name = argv[1];
1692 if(argc >= (new_channel+2))
1694 if(!IsHelping(user))
1696 reply("CSMSG_PROXY_FORBIDDEN");
1700 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1702 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1703 dnr = chanserv_is_dnr(chan_name, handle);
1707 handle = user->handle_info;
1708 dnr = chanserv_is_dnr(chan_name, handle);
1712 if(!IsHelping(user))
1713 reply("CSMSG_DNR_CHANNEL", chan_name);
1715 chanserv_show_dnrs(user, cmd, chan_name, handle);
1719 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1721 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1726 channel = AddChannel(argv[1], now, NULL, NULL);
1728 cData = register_channel(channel, user->handle_info->handle);
1729 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1730 cData->modes = chanserv_conf.default_modes;
1731 change = mod_chanmode_dup(&cData->modes, 1);
1732 change->args[change->argc].mode = MODE_CHANOP;
1733 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1735 mod_chanmode_announce(chanserv, channel, change);
1736 mod_chanmode_free(change);
1738 /* Initialize the channel's max user record. */
1739 cData->max = channel->members.used;
1741 if(handle != user->handle_info)
1742 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1744 reply("CSMSG_REG_SUCCESS", channel->name);
1746 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1747 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1752 make_confirmation_string(struct userData *uData)
1754 static char strbuf[16];
1759 for(src = uData->handle->handle; *src; )
1760 accum = accum * 31 + toupper(*src++);
1762 for(src = uData->channel->channel->name; *src; )
1763 accum = accum * 31 + toupper(*src++);
1764 sprintf(strbuf, "%08x", accum);
1768 static CHANSERV_FUNC(cmd_unregister)
1771 char reason[MAXLEN];
1772 struct chanData *cData;
1773 struct userData *uData;
1775 cData = channel->channel_info;
1778 reply("CSMSG_NOT_REGISTERED", channel->name);
1782 uData = GetChannelUser(cData, user->handle_info);
1783 if(!uData || (uData->access < UL_OWNER))
1785 reply("CSMSG_NO_ACCESS");
1789 if(IsProtected(cData))
1791 reply("CSMSG_UNREG_NODELETE", channel->name);
1795 if(!IsHelping(user))
1797 const char *confirm_string;
1798 if(IsSuspended(cData))
1800 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1803 confirm_string = make_confirmation_string(uData);
1804 if((argc < 2) || strcmp(argv[1], confirm_string))
1806 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1811 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1812 name = strdup(channel->name);
1813 unregister_channel(cData, reason);
1814 reply("CSMSG_UNREG_SUCCESS", name);
1819 static CHANSERV_FUNC(cmd_move)
1821 struct chanNode *target;
1822 struct modeNode *mn;
1823 struct userData *uData;
1824 char reason[MAXLEN];
1825 struct do_not_register *dnr;
1829 if(IsProtected(channel->channel_info))
1831 reply("CSMSG_MOVE_NODELETE", channel->name);
1835 if(!IsChannelName(argv[1]))
1837 reply("MSG_NOT_CHANNEL_NAME");
1841 if(opserv_bad_channel(argv[1]))
1843 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1847 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1849 for(uData = channel->channel_info->users; uData; uData = uData->next)
1851 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1853 if(!IsHelping(user))
1854 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1856 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1862 if(!(target = GetChannel(argv[1])))
1864 target = AddChannel(argv[1], now, NULL, NULL);
1865 if(!IsSuspended(channel->channel_info))
1866 AddChannelUser(chanserv, target);
1868 else if(target->channel_info)
1870 reply("CSMSG_ALREADY_REGGED", target->name);
1873 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1874 && !IsHelping(user))
1876 reply("CSMSG_MUST_BE_OPPED", target->name);
1879 else if(!IsSuspended(channel->channel_info))
1881 struct mod_chanmode change;
1882 mod_chanmode_init(&change);
1884 change.args[0].mode = MODE_CHANOP;
1885 change.args[0].member = AddChannelUser(chanserv, target);
1886 mod_chanmode_announce(chanserv, target, &change);
1889 /* Move the channel_info to the target channel; it
1890 shouldn't be necessary to clear timeq callbacks
1891 for the old channel. */
1892 target->channel_info = channel->channel_info;
1893 target->channel_info->channel = target;
1894 channel->channel_info = NULL;
1896 reply("CSMSG_MOVE_SUCCESS", target->name);
1898 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1899 if(!IsSuspended(target->channel_info))
1901 char reason2[MAXLEN];
1902 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1903 DelChannelUser(chanserv, channel, reason2, 0);
1905 UnlockChannel(channel);
1906 LockChannel(target);
1907 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1912 merge_users(struct chanData *source, struct chanData *target)
1914 struct userData *suData, *tuData, *next;
1920 /* Insert the source's users into the scratch area. */
1921 for(suData = source->users; suData; suData = suData->next)
1922 dict_insert(merge, suData->handle->handle, suData);
1924 /* Iterate through the target's users, looking for
1925 users common to both channels. The lower access is
1926 removed from either the scratch area or target user
1928 for(tuData = target->users; tuData; tuData = next)
1930 struct userData *choice;
1932 next = tuData->next;
1934 /* If a source user exists with the same handle as a target
1935 channel's user, resolve the conflict by removing one. */
1936 suData = dict_find(merge, tuData->handle->handle, NULL);
1940 /* Pick the data we want to keep. */
1941 /* If the access is the same, use the later seen time. */
1942 if(suData->access == tuData->access)
1943 choice = (suData->seen > tuData->seen) ? suData : tuData;
1944 else /* Otherwise, keep the higher access level. */
1945 choice = (suData->access > tuData->access) ? suData : tuData;
1947 /* Remove the user that wasn't picked. */
1948 if(choice == tuData)
1950 dict_remove(merge, suData->handle->handle);
1951 del_channel_user(suData, 0);
1954 del_channel_user(tuData, 0);
1957 /* Move the remaining users to the target channel. */
1958 for(it = dict_first(merge); it; it = iter_next(it))
1960 suData = iter_data(it);
1962 /* Insert the user into the target channel's linked list. */
1963 suData->prev = NULL;
1964 suData->next = target->users;
1965 suData->channel = target;
1968 target->users->prev = suData;
1969 target->users = suData;
1971 /* Update the user counts for the target channel; the
1972 source counts are left alone. */
1973 target->userCount++;
1976 /* Possible to assert (source->users == NULL) here. */
1977 source->users = NULL;
1982 merge_bans(struct chanData *source, struct chanData *target)
1984 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1986 /* Hold on to the original head of the target ban list
1987 to avoid comparing source bans with source bans. */
1988 tFront = target->bans;
1990 /* Perform a totally expensive O(n*m) merge, ick. */
1991 for(sbData = source->bans; sbData; sbData = sNext)
1993 /* Flag to track whether the ban's been moved
1994 to the destination yet. */
1997 /* Possible to assert (sbData->prev == NULL) here. */
1998 sNext = sbData->next;
2000 for(tbData = tFront; tbData; tbData = tNext)
2002 tNext = tbData->next;
2004 /* Perform two comparisons between each source
2005 and target ban, conflicts are resolved by
2006 keeping the broader ban and copying the later
2007 expiration and triggered time. */
2008 if(match_ircglobs(tbData->mask, sbData->mask))
2010 /* There is a broader ban in the target channel that
2011 overrides one in the source channel; remove the
2012 source ban and break. */
2013 if(sbData->expires > tbData->expires)
2014 tbData->expires = sbData->expires;
2015 if(sbData->triggered > tbData->triggered)
2016 tbData->triggered = sbData->triggered;
2017 del_channel_ban(sbData);
2020 else if(match_ircglobs(sbData->mask, tbData->mask))
2022 /* There is a broader ban in the source channel that
2023 overrides one in the target channel; remove the
2024 target ban, fall through and move the source over. */
2025 if(tbData->expires > sbData->expires)
2026 sbData->expires = tbData->expires;
2027 if(tbData->triggered > sbData->triggered)
2028 sbData->triggered = tbData->triggered;
2029 if(tbData == tFront)
2031 del_channel_ban(tbData);
2034 /* Source bans can override multiple target bans, so
2035 we allow a source to run through this loop multiple
2036 times, but we can only move it once. */
2041 /* Remove the source ban from the source ban list. */
2043 sbData->next->prev = sbData->prev;
2045 /* Modify the source ban's associated channel. */
2046 sbData->channel = target;
2048 /* Insert the ban into the target channel's linked list. */
2049 sbData->prev = NULL;
2050 sbData->next = target->bans;
2053 target->bans->prev = sbData;
2054 target->bans = sbData;
2056 /* Update the user counts for the target channel. */
2061 /* Possible to assert (source->bans == NULL) here. */
2062 source->bans = NULL;
2066 merge_data(struct chanData *source, struct chanData *target)
2068 if(source->visited > target->visited)
2069 target->visited = source->visited;
2073 merge_channel(struct chanData *source, struct chanData *target)
2075 merge_users(source, target);
2076 merge_bans(source, target);
2077 merge_data(source, target);
2080 static CHANSERV_FUNC(cmd_merge)
2082 struct userData *target_user;
2083 struct chanNode *target;
2084 char reason[MAXLEN];
2088 /* Make sure the target channel exists and is registered to the user
2089 performing the command. */
2090 if(!(target = GetChannel(argv[1])))
2092 reply("MSG_INVALID_CHANNEL");
2096 if(!target->channel_info)
2098 reply("CSMSG_NOT_REGISTERED", target->name);
2102 if(IsProtected(channel->channel_info))
2104 reply("CSMSG_MERGE_NODELETE");
2108 if(IsSuspended(target->channel_info))
2110 reply("CSMSG_MERGE_SUSPENDED");
2114 if(channel == target)
2116 reply("CSMSG_MERGE_SELF");
2120 target_user = GetChannelUser(target->channel_info, user->handle_info);
2121 if(!target_user || (target_user->access < UL_OWNER))
2123 reply("CSMSG_MERGE_NOT_OWNER");
2127 /* Merge the channel structures and associated data. */
2128 merge_channel(channel->channel_info, target->channel_info);
2129 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2130 unregister_channel(channel->channel_info, reason);
2131 reply("CSMSG_MERGE_SUCCESS", target->name);
2135 static CHANSERV_FUNC(cmd_opchan)
2137 struct mod_chanmode change;
2138 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2140 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2143 channel->channel_info->may_opchan = 0;
2144 mod_chanmode_init(&change);
2146 change.args[0].mode = MODE_CHANOP;
2147 change.args[0].member = GetUserMode(channel, chanserv);
2148 mod_chanmode_announce(chanserv, channel, &change);
2149 reply("CSMSG_OPCHAN_DONE", channel->name);
2153 static CHANSERV_FUNC(cmd_adduser)
2155 struct userData *actee;
2156 struct userData *actor;
2157 struct handle_info *handle;
2158 unsigned short access;
2162 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2164 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2168 access = user_level_from_name(argv[2], UL_OWNER);
2171 reply("CSMSG_INVALID_ACCESS", argv[2]);
2175 actor = GetChannelUser(channel->channel_info, user->handle_info);
2176 if(actor->access <= access)
2178 reply("CSMSG_NO_BUMP_ACCESS");
2182 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2185 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2187 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2191 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2192 scan_user_presence(actee, NULL);
2193 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2197 static CHANSERV_FUNC(cmd_clvl)
2199 struct handle_info *handle;
2200 struct userData *victim;
2201 struct userData *actor;
2202 unsigned short new_access;
2203 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2207 actor = GetChannelUser(channel->channel_info, user->handle_info);
2209 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2212 if(handle == user->handle_info && !privileged)
2214 reply("CSMSG_NO_SELF_CLVL");
2218 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2220 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2224 if(actor->access <= victim->access && !privileged)
2226 reply("MSG_USER_OUTRANKED", handle->handle);
2230 new_access = user_level_from_name(argv[2], UL_OWNER);
2234 reply("CSMSG_INVALID_ACCESS", argv[2]);
2238 if(new_access >= actor->access && !privileged)
2240 reply("CSMSG_NO_BUMP_ACCESS");
2244 victim->access = new_access;
2245 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2249 static CHANSERV_FUNC(cmd_deluser)
2251 struct handle_info *handle;
2252 struct userData *victim;
2253 struct userData *actor;
2254 unsigned short access;
2259 actor = GetChannelUser(channel->channel_info, user->handle_info);
2261 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2264 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2266 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2272 access = user_level_from_name(argv[1], UL_OWNER);
2275 reply("CSMSG_INVALID_ACCESS", argv[1]);
2278 if(access != victim->access)
2280 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2286 access = victim->access;
2289 if((actor->access <= victim->access) && !IsHelping(user))
2291 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2295 chan_name = strdup(channel->name);
2296 del_channel_user(victim, 1);
2297 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2303 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2305 struct userData *actor, *uData, *next;
2307 actor = GetChannelUser(channel->channel_info, user->handle_info);
2309 if(min_access > max_access)
2311 reply("CSMSG_BAD_RANGE", min_access, max_access);
2315 if((actor->access <= max_access) && !IsHelping(user))
2317 reply("CSMSG_NO_ACCESS");
2321 for(uData = channel->channel_info->users; uData; uData = next)
2325 if((uData->access >= min_access)
2326 && (uData->access <= max_access)
2327 && match_ircglob(uData->handle->handle, mask))
2328 del_channel_user(uData, 1);
2331 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2335 static CHANSERV_FUNC(cmd_mdelowner)
2337 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2340 static CHANSERV_FUNC(cmd_mdelcoowner)
2342 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2345 static CHANSERV_FUNC(cmd_mdelmaster)
2347 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2350 static CHANSERV_FUNC(cmd_mdelop)
2352 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2355 static CHANSERV_FUNC(cmd_mdelpeon)
2357 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2361 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2363 struct banData *bData, *next;
2364 char interval[INTERVALLEN];
2369 limit = now - duration;
2370 for(bData = channel->channel_info->bans; bData; bData = next)
2374 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2377 del_channel_ban(bData);
2381 intervalString(interval, duration);
2382 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2387 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2389 struct userData *actor, *uData, *next;
2390 char interval[INTERVALLEN];
2394 actor = GetChannelUser(channel->channel_info, user->handle_info);
2395 if(min_access > max_access)
2397 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2401 if((actor->access <= max_access) && !IsHelping(user))
2403 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2408 limit = now - duration;
2409 for(uData = channel->channel_info->users; uData; uData = next)
2413 if((uData->seen > limit) || uData->present)
2416 if(((uData->access >= min_access) && (uData->access <= max_access))
2417 || (!max_access && (uData->access < actor->access)))
2419 del_channel_user(uData, 1);
2427 max_access = UL_OWNER;
2429 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2433 static CHANSERV_FUNC(cmd_trim)
2435 unsigned long duration;
2436 unsigned short min_level, max_level;
2440 duration = ParseInterval(argv[2]);
2443 reply("CSMSG_CANNOT_TRIM");
2447 if(!irccasecmp(argv[1], "bans"))
2449 cmd_trim_bans(user, channel, duration);
2452 else if(!irccasecmp(argv[1], "users"))
2454 cmd_trim_users(user, channel, 0, 0, duration);
2457 else if(parse_level_range(&min_level, &max_level, argv[1]))
2459 cmd_trim_users(user, channel, min_level, max_level, duration);
2462 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2464 cmd_trim_users(user, channel, min_level, min_level, duration);
2469 reply("CSMSG_INVALID_TRIM", argv[1]);
2474 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2475 to the user. cmd_all takes advantage of this. */
2476 static CHANSERV_FUNC(cmd_up)
2478 struct mod_chanmode change;
2479 struct userData *uData;
2482 mod_chanmode_init(&change);
2484 change.args[0].member = GetUserMode(channel, user);
2485 if(!change.args[0].member)
2488 reply("MSG_CHANNEL_ABSENT", channel->name);
2492 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2496 reply("CSMSG_GODMODE_UP", argv[0]);
2499 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2501 change.args[0].mode = MODE_CHANOP;
2502 errmsg = "CSMSG_ALREADY_OPPED";
2506 change.args[0].mode = MODE_VOICE;
2507 errmsg = "CSMSG_ALREADY_VOICED";
2509 change.args[0].mode &= ~change.args[0].member->modes;
2510 if(!change.args[0].mode)
2513 reply(errmsg, channel->name);
2516 modcmd_chanmode_announce(&change);
2520 static CHANSERV_FUNC(cmd_down)
2522 struct mod_chanmode change;
2524 mod_chanmode_init(&change);
2526 change.args[0].member = GetUserMode(channel, user);
2527 if(!change.args[0].member)
2530 reply("MSG_CHANNEL_ABSENT", channel->name);
2534 if(!change.args[0].member->modes)
2537 reply("CSMSG_ALREADY_DOWN", channel->name);
2541 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2542 modcmd_chanmode_announce(&change);
2546 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)
2548 struct userData *cList;
2550 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2552 if(IsSuspended(cList->channel)
2553 || IsUserSuspended(cList)
2554 || !GetUserMode(cList->channel->channel, user))
2557 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2563 static CHANSERV_FUNC(cmd_upall)
2565 return cmd_all(CSFUNC_ARGS, cmd_up);
2568 static CHANSERV_FUNC(cmd_downall)
2570 return cmd_all(CSFUNC_ARGS, cmd_down);
2573 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2574 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2577 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)
2579 unsigned int ii, valid;
2580 struct userNode *victim;
2581 struct mod_chanmode *change;
2583 change = mod_chanmode_alloc(argc - 1);
2585 for(ii=valid=0; ++ii < argc; )
2587 if(!(victim = GetUserH(argv[ii])))
2589 change->args[valid].mode = mode;
2590 change->args[valid].member = GetUserMode(channel, victim);
2591 if(!change->args[valid].member)
2593 if(validate && !validate(user, channel, victim))
2598 change->argc = valid;
2599 if(valid < (argc-1))
2600 reply("CSMSG_PROCESS_FAILED");
2603 modcmd_chanmode_announce(change);
2604 reply(action, channel->name);
2606 mod_chanmode_free(change);
2610 static CHANSERV_FUNC(cmd_op)
2612 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2615 static CHANSERV_FUNC(cmd_deop)
2617 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2620 static CHANSERV_FUNC(cmd_voice)
2622 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2625 static CHANSERV_FUNC(cmd_devoice)
2627 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2631 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2637 for(ii=0; ii<channel->members.used; ii++)
2639 struct modeNode *mn = channel->members.list[ii];
2641 if(IsService(mn->user))
2644 if(!user_matches_glob(mn->user, ban, 1))
2647 if(protect_user(mn->user, user, channel->channel_info))
2651 victims[(*victimCount)++] = mn;
2657 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2659 struct userNode *victim;
2660 struct modeNode **victims;
2661 unsigned int offset, n, victimCount, duration = 0;
2662 char *reason = "Bye.", *ban, *name;
2663 char interval[INTERVALLEN];
2665 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2666 REQUIRE_PARAMS(offset);
2669 reason = unsplit_string(argv + offset, argc - offset, NULL);
2670 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2672 /* Truncate the reason to a length of TOPICLEN, as
2673 the ircd does; however, leave room for an ellipsis
2674 and the kicker's nick. */
2675 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2679 if((victim = GetUserH(argv[1])))
2681 victims = alloca(sizeof(victims[0]));
2682 victims[0] = GetUserMode(channel, victim);
2683 /* XXX: The comparison with ACTION_KICK is just because all
2684 * other actions can work on users outside the channel, and we
2685 * want to allow those (e.g. unbans) in that case. If we add
2686 * some other ejection action for in-channel users, change
2688 victimCount = victims[0] ? 1 : 0;
2690 if(IsService(victim))
2692 reply("MSG_SERVICE_IMMUNE", victim->nick);
2696 if((action == ACTION_KICK) && !victimCount)
2698 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2702 if(protect_user(victim, user, channel->channel_info))
2704 reply("CSMSG_USER_PROTECTED", victim->nick);
2708 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2709 name = victim->nick;
2713 if(!is_ircmask(argv[1]))
2715 reply("MSG_NICK_UNKNOWN", argv[1]);
2719 victims = alloca(sizeof(victims[0]) * channel->members.used);
2721 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2723 reply("CSMSG_MASK_PROTECTED", argv[1]);
2727 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2729 reply("CSMSG_LAME_MASK", argv[1]);
2733 if((action == ACTION_KICK) && (victimCount == 0))
2735 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2739 name = ban = strdup(argv[1]);
2742 /* Truncate the ban in place if necessary; we must ensure
2743 that 'ban' is a valid ban mask before sanitizing it. */
2744 sanitize_ircmask(ban);
2746 if(action & ACTION_ADD_BAN)
2748 struct banData *bData, *next;
2750 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2752 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2757 if(action & ACTION_ADD_TIMED_BAN)
2759 duration = ParseInterval(argv[2]);
2763 reply("CSMSG_DURATION_TOO_LOW");
2767 else if(duration > (86400 * 365 * 2))
2769 reply("CSMSG_DURATION_TOO_HIGH");
2775 for(bData = channel->channel_info->bans; bData; bData = next)
2777 if(match_ircglobs(bData->mask, ban))
2779 int exact = !irccasecmp(bData->mask, ban);
2781 /* The ban is redundant; there is already a ban
2782 with the same effect in place. */
2786 free(bData->reason);
2787 bData->reason = strdup(reason);
2788 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2790 reply("CSMSG_REASON_CHANGE", ban);
2794 if(exact && bData->expires)
2798 /* If the ban matches an existing one exactly,
2799 extend the expiration time if the provided
2800 duration is longer. */
2801 if(duration && ((time_t)(now + duration) > bData->expires))
2803 bData->expires = now + duration;
2814 /* Delete the expiration timeq entry and
2815 requeue if necessary. */
2816 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2819 timeq_add(bData->expires, expire_ban, bData);
2823 /* automated kickban */
2826 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2828 reply("CSMSG_BAN_ADDED", name, channel->name);
2834 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2841 if(match_ircglobs(ban, bData->mask))
2843 /* The ban we are adding makes previously existing
2844 bans redundant; silently remove them. */
2845 del_channel_ban(bData);
2849 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);
2851 name = ban = strdup(bData->mask);
2855 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2857 extern const char *hidden_host_suffix;
2858 const char *old_name = chanserv_conf.old_ban_names->list[n];
2860 unsigned int l1, l2;
2863 l2 = strlen(old_name);
2866 if(irccasecmp(ban + l1 - l2, old_name))
2868 new_mask = malloc(MAXLEN);
2869 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2871 name = ban = new_mask;
2876 if(action & ACTION_BAN)
2878 unsigned int exists;
2879 struct mod_chanmode *change;
2881 if(channel->banlist.used >= MAXBANS)
2884 reply("CSMSG_BANLIST_FULL", channel->name);
2889 exists = ChannelBanExists(channel, ban);
2890 change = mod_chanmode_alloc(victimCount + 1);
2891 for(n = 0; n < victimCount; ++n)
2893 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2894 change->args[n].member = victims[n];
2898 change->args[n].mode = MODE_BAN;
2899 change->args[n++].hostmask = ban;
2903 modcmd_chanmode_announce(change);
2905 mod_chanmode_announce(chanserv, channel, change);
2906 mod_chanmode_free(change);
2908 if(exists && (action == ACTION_BAN))
2911 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2917 if(action & ACTION_KICK)
2919 char kick_reason[MAXLEN];
2920 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2922 for(n = 0; n < victimCount; n++)
2923 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2928 /* No response, since it was automated. */
2930 else if(action & ACTION_ADD_BAN)
2933 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2935 reply("CSMSG_BAN_ADDED", name, channel->name);
2937 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2938 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2939 else if(action & ACTION_BAN)
2940 reply("CSMSG_BAN_DONE", name, channel->name);
2941 else if(action & ACTION_KICK && victimCount)
2942 reply("CSMSG_KICK_DONE", name, channel->name);
2948 static CHANSERV_FUNC(cmd_kickban)
2950 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2953 static CHANSERV_FUNC(cmd_kick)
2955 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2958 static CHANSERV_FUNC(cmd_ban)
2960 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2963 static CHANSERV_FUNC(cmd_addban)
2965 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2968 static CHANSERV_FUNC(cmd_addtimedban)
2970 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2973 static struct mod_chanmode *
2974 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2976 struct mod_chanmode *change;
2977 unsigned char *match;
2978 unsigned int ii, count;
2980 match = alloca(bans->used);
2983 for(ii = count = 0; ii < bans->used; ++ii)
2985 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2992 for(ii = count = 0; ii < bans->used; ++ii)
2994 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3001 change = mod_chanmode_alloc(count);
3002 for(ii = count = 0; ii < bans->used; ++ii)
3006 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3007 change->args[count++].hostmask = bans->list[ii]->ban;
3013 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3015 struct userNode *actee;
3021 /* may want to allow a comma delimited list of users... */
3022 if(!(actee = GetUserH(argv[1])))
3024 if(!is_ircmask(argv[1]))
3026 reply("MSG_NICK_UNKNOWN", argv[1]);
3030 mask = strdup(argv[1]);
3033 /* We don't sanitize the mask here because ircu
3035 if(action & ACTION_UNBAN)
3037 struct mod_chanmode *change;
3038 change = find_matching_bans(&channel->banlist, actee, mask);
3041 modcmd_chanmode_announce(change);
3042 mod_chanmode_free(change);
3047 if(action & ACTION_DEL_BAN)
3049 struct banData *ban, *next;
3051 ban = channel->channel_info->bans;
3055 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3058 for( ; ban && !match_ircglobs(mask, ban->mask);
3063 del_channel_ban(ban);
3070 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3072 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3078 static CHANSERV_FUNC(cmd_unban)
3080 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3083 static CHANSERV_FUNC(cmd_delban)
3085 /* it doesn't necessarily have to remove the channel ban - may want
3086 to make that an option. */
3087 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3090 static CHANSERV_FUNC(cmd_unbanme)
3092 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3093 long flags = ACTION_UNBAN;
3095 /* remove permanent bans if the user has the proper access. */
3096 if(uData->access >= UL_MASTER)
3097 flags |= ACTION_DEL_BAN;
3099 argv[1] = user->nick;
3100 return unban_user(user, channel, 2, argv, cmd, flags);
3103 static CHANSERV_FUNC(cmd_unbanall)
3105 struct mod_chanmode *change;
3108 if(!channel->banlist.used)
3110 reply("CSMSG_NO_BANS", channel->name);
3114 change = mod_chanmode_alloc(channel->banlist.used);
3115 for(ii=0; ii<channel->banlist.used; ii++)
3117 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3118 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3120 modcmd_chanmode_announce(change);
3121 mod_chanmode_free(change);
3122 reply("CSMSG_BANS_REMOVED", channel->name);
3126 static CHANSERV_FUNC(cmd_open)
3128 struct mod_chanmode *change;
3130 change = find_matching_bans(&channel->banlist, user, NULL);
3132 change = mod_chanmode_alloc(0);
3133 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3134 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3135 && channel->channel_info->modes.modes_set)
3136 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3137 modcmd_chanmode_announce(change);
3138 reply("CSMSG_CHANNEL_OPENED", channel->name);
3139 mod_chanmode_free(change);
3143 static CHANSERV_FUNC(cmd_myaccess)
3145 struct handle_info *target_handle;
3146 struct userData *uData;
3147 const char *chanName;
3150 target_handle = user->handle_info;
3151 else if(!IsHelping(user))
3153 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3156 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3159 if(!target_handle->channels)
3161 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3165 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3166 for(uData = target_handle->channels; uData; uData = uData->u_next)
3168 struct chanData *cData = uData->channel;
3170 if(uData->access > UL_OWNER)
3172 if(IsProtected(cData)
3173 && (target_handle != user->handle_info)
3174 && !GetTrueChannelAccess(cData, user->handle_info))
3176 chanName = cData->channel->name;
3178 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3180 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3186 static CHANSERV_FUNC(cmd_access)
3188 struct userNode *target;
3189 struct handle_info *target_handle;
3190 struct userData *uData;
3192 char prefix[MAXLEN];
3197 target_handle = target->handle_info;
3199 else if((target = GetUserH(argv[1])))
3201 target_handle = target->handle_info;
3203 else if(argv[1][0] == '*')
3205 if(!(target_handle = get_handle_info(argv[1]+1)))
3207 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3213 reply("MSG_NICK_UNKNOWN", argv[1]);
3217 assert(target || target_handle);
3219 if(target == chanserv)
3221 reply("CSMSG_IS_CHANSERV");
3229 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3234 reply("MSG_USER_AUTHENTICATE", target->nick);
3237 reply("MSG_AUTHENTICATE");
3243 const char *epithet = NULL, *type = NULL;
3246 epithet = chanserv_conf.irc_operator_epithet;
3249 else if(IsNetworkHelper(target))
3251 epithet = chanserv_conf.network_helper_epithet;
3252 type = "network helper";
3254 else if(IsSupportHelper(target))
3256 epithet = chanserv_conf.support_helper_epithet;
3257 type = "support helper";
3261 if(target_handle->epithet)
3262 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3264 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3266 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3270 sprintf(prefix, "%s", target_handle->handle);
3273 if(!channel->channel_info)
3275 reply("CSMSG_NOT_REGISTERED", channel->name);
3279 helping = HANDLE_FLAGGED(target_handle, HELPING)
3280 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3281 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3283 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3284 /* To prevent possible information leaks, only show infolines
3285 * if the requestor is in the channel or it's their own
3287 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3289 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3291 /* Likewise, only say it's suspended if the user has active
3292 * access in that channel or it's their own entry. */
3293 if(IsUserSuspended(uData)
3294 && (GetChannelUser(channel->channel_info, user->handle_info)
3295 || (user->handle_info == uData->handle)))
3297 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3302 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3309 zoot_list(struct listData *list)
3311 struct userData *uData;
3312 unsigned int start, curr, highest, lowest;
3313 struct helpfile_table tmp_table;
3314 const char **temp, *msg;
3316 if(list->table.length == 1)
3319 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3321 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3322 msg = user_find_message(list->user, "MSG_NONE");
3323 send_message_type(4, list->user, list->bot, " %s", msg);
3325 tmp_table.width = list->table.width;
3326 tmp_table.flags = list->table.flags;
3327 list->table.contents[0][0] = " ";
3328 highest = list->highest;
3329 if(list->lowest != 0)
3330 lowest = list->lowest;
3331 else if(highest < 100)
3334 lowest = highest - 100;
3335 for(start = curr = 1; curr < list->table.length; )
3337 uData = list->users[curr-1];
3338 list->table.contents[curr++][0] = " ";
3339 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3342 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3344 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3345 temp = list->table.contents[--start];
3346 list->table.contents[start] = list->table.contents[0];
3347 tmp_table.contents = list->table.contents + start;
3348 tmp_table.length = curr - start;
3349 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3350 list->table.contents[start] = temp;
3352 highest = lowest - 1;
3353 lowest = (highest < 100) ? 0 : (highest - 99);
3359 def_list(struct listData *list)
3363 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3365 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3366 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3367 if(list->table.length == 1)
3369 msg = user_find_message(list->user, "MSG_NONE");
3370 send_message_type(4, list->user, list->bot, " %s", msg);
3375 userData_access_comp(const void *arg_a, const void *arg_b)
3377 const struct userData *a = *(struct userData**)arg_a;
3378 const struct userData *b = *(struct userData**)arg_b;
3380 if(a->access != b->access)
3381 res = b->access - a->access;
3383 res = irccasecmp(a->handle->handle, b->handle->handle);
3388 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3390 void (*send_list)(struct listData *);
3391 struct userData *uData;
3392 struct listData lData;
3393 unsigned int matches;
3397 lData.bot = cmd->parent->bot;
3398 lData.channel = channel;
3399 lData.lowest = lowest;
3400 lData.highest = highest;
3401 lData.search = (argc > 1) ? argv[1] : NULL;
3402 send_list = zoot_list;
3404 if(user->handle_info)
3406 switch(user->handle_info->userlist_style)
3408 case HI_STYLE_DEF: send_list = def_list; break;
3409 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3413 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3415 for(uData = channel->channel_info->users; uData; uData = uData->next)
3417 if((uData->access < lowest)
3418 || (uData->access > highest)
3419 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3421 lData.users[matches++] = uData;
3423 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3425 lData.table.length = matches+1;
3426 lData.table.width = 4;
3427 lData.table.flags = TABLE_NO_FREE;
3428 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3429 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3430 lData.table.contents[0] = ary;
3433 ary[2] = "Last Seen";
3435 for(matches = 1; matches < lData.table.length; ++matches)
3437 struct userData *uData = lData.users[matches-1];
3438 char seen[INTERVALLEN];
3440 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3441 lData.table.contents[matches] = ary;
3442 ary[0] = strtab(uData->access);
3443 ary[1] = uData->handle->handle;
3446 else if(!uData->seen)
3449 ary[2] = intervalString(seen, now - uData->seen);
3450 ary[2] = strdup(ary[2]);
3451 if(IsUserSuspended(uData))
3452 ary[3] = "Suspended";
3453 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3454 ary[3] = "Vacation";
3459 for(matches = 1; matches < lData.table.length; ++matches)
3461 free((char*)lData.table.contents[matches][2]);
3462 free(lData.table.contents[matches]);
3464 free(lData.table.contents[0]);
3465 free(lData.table.contents);
3469 static CHANSERV_FUNC(cmd_users)
3471 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3474 static CHANSERV_FUNC(cmd_wlist)
3476 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3479 static CHANSERV_FUNC(cmd_clist)
3481 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3484 static CHANSERV_FUNC(cmd_mlist)
3486 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3489 static CHANSERV_FUNC(cmd_olist)
3491 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3494 static CHANSERV_FUNC(cmd_plist)
3496 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3499 static CHANSERV_FUNC(cmd_bans)
3501 struct helpfile_table tbl;
3502 unsigned int matches = 0, timed = 0, ii;
3503 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3504 const char *msg_never, *triggered, *expires;
3505 struct banData *ban, **bans;
3512 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3514 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3516 if(search && !match_ircglobs(search, ban->mask))
3518 bans[matches++] = ban;
3523 tbl.length = matches + 1;
3524 tbl.width = 4 + timed;
3526 tbl.flags = TABLE_NO_FREE;
3527 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3528 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3529 tbl.contents[0][0] = "Mask";
3530 tbl.contents[0][1] = "Set By";
3531 tbl.contents[0][2] = "Triggered";
3534 tbl.contents[0][3] = "Expires";
3535 tbl.contents[0][4] = "Reason";
3538 tbl.contents[0][3] = "Reason";
3541 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3543 free(tbl.contents[0]);
3548 msg_never = user_find_message(user, "MSG_NEVER");
3549 for(ii = 0; ii < matches; )
3555 else if(ban->expires)
3556 expires = intervalString(e_buffer, ban->expires - now);
3558 expires = msg_never;
3561 triggered = intervalString(t_buffer, now - ban->triggered);
3563 triggered = msg_never;
3565 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3566 tbl.contents[ii][0] = ban->mask;
3567 tbl.contents[ii][1] = ban->owner;
3568 tbl.contents[ii][2] = strdup(triggered);
3571 tbl.contents[ii][3] = strdup(expires);
3572 tbl.contents[ii][4] = ban->reason;
3575 tbl.contents[ii][3] = ban->reason;
3577 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3578 reply("MSG_MATCH_COUNT", matches);
3579 for(ii = 1; ii < tbl.length; ++ii)
3581 free((char*)tbl.contents[ii][2]);
3583 free((char*)tbl.contents[ii][3]);
3584 free(tbl.contents[ii]);
3586 free(tbl.contents[0]);
3592 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3594 struct chanData *cData = channel->channel_info;
3595 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3597 if(cData->topic_mask)
3598 return !match_ircglob(new_topic, cData->topic_mask);
3599 else if(cData->topic)
3600 return irccasecmp(new_topic, cData->topic);
3605 static CHANSERV_FUNC(cmd_topic)
3607 struct chanData *cData;
3610 cData = channel->channel_info;
3615 SetChannelTopic(channel, chanserv, cData->topic, 1);
3616 reply("CSMSG_TOPIC_SET", cData->topic);
3620 reply("CSMSG_NO_TOPIC", channel->name);
3624 topic = unsplit_string(argv + 1, argc - 1, NULL);
3625 /* If they say "!topic *", use an empty topic. */
3626 if((topic[0] == '*') && (topic[1] == 0))
3628 if(bad_topic(channel, user, topic))
3630 char *topic_mask = cData->topic_mask;
3633 char new_topic[TOPICLEN+1], tchar;
3634 int pos=0, starpos=-1, dpos=0, len;
3636 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3643 len = strlen(topic);
3644 if((dpos + len) > TOPICLEN)
3645 len = TOPICLEN + 1 - dpos;
3646 memcpy(new_topic+dpos, topic, len);
3650 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3651 default: new_topic[dpos++] = tchar; break;
3654 if((dpos > TOPICLEN) || tchar)
3657 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3658 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3661 new_topic[dpos] = 0;
3662 SetChannelTopic(channel, chanserv, new_topic, 1);
3664 reply("CSMSG_TOPIC_LOCKED", channel->name);
3669 SetChannelTopic(channel, chanserv, topic, 1);
3671 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3673 /* Grab the topic and save it as the default topic. */
3675 cData->topic = strdup(channel->topic);
3681 static CHANSERV_FUNC(cmd_mode)
3683 struct mod_chanmode *change;
3687 change = &channel->channel_info->modes;
3688 if(change->modes_set || change->modes_clear) {
3689 modcmd_chanmode_announce(change);
3690 reply("CSMSG_DEFAULTED_MODES", channel->name);
3692 reply("CSMSG_NO_MODES", channel->name);
3696 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3699 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3703 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3704 && mode_lock_violated(&channel->channel_info->modes, change))
3707 mod_chanmode_format(&channel->channel_info->modes, modes);
3708 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3712 modcmd_chanmode_announce(change);
3713 mod_chanmode_free(change);
3714 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3718 static CHANSERV_FUNC(cmd_invite)
3720 struct userData *uData;
3721 struct userNode *invite;
3723 uData = GetChannelUser(channel->channel_info, user->handle_info);
3727 if(!(invite = GetUserH(argv[1])))
3729 reply("MSG_NICK_UNKNOWN", argv[1]);
3736 if(GetUserMode(channel, invite))
3738 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3744 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3745 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3747 irc_invite(chanserv, invite, channel);
3749 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3754 static CHANSERV_FUNC(cmd_inviteme)
3756 if(GetUserMode(channel, user))
3758 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3761 if(channel->channel_info
3762 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3764 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3767 irc_invite(cmd->parent->bot, user, channel);
3772 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3775 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3777 /* We display things based on two dimensions:
3778 * - Issue time: present or absent
3779 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3780 * (in order of precedence, so something both expired and revoked
3781 * only counts as revoked)
3783 combo = (suspended->issued ? 4 : 0)
3784 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3786 case 0: /* no issue time, indefinite expiration */
3787 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3789 case 1: /* no issue time, expires in future */
3790 intervalString(buf1, suspended->expires-now);
3791 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3793 case 2: /* no issue time, expired */
3794 intervalString(buf1, now-suspended->expires);
3795 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3797 case 3: /* no issue time, revoked */
3798 intervalString(buf1, now-suspended->revoked);
3799 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3801 case 4: /* issue time set, indefinite expiration */
3802 intervalString(buf1, now-suspended->issued);
3803 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3805 case 5: /* issue time set, expires in future */
3806 intervalString(buf1, now-suspended->issued);
3807 intervalString(buf2, suspended->expires-now);
3808 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3810 case 6: /* issue time set, expired */
3811 intervalString(buf1, now-suspended->issued);
3812 intervalString(buf2, now-suspended->expires);
3813 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3815 case 7: /* issue time set, revoked */
3816 intervalString(buf1, now-suspended->issued);
3817 intervalString(buf2, now-suspended->revoked);
3818 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3821 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3826 static CHANSERV_FUNC(cmd_info)
3828 char modes[MAXLEN], buffer[INTERVALLEN];
3829 struct userData *uData, *owner;
3830 struct chanData *cData;
3831 struct do_not_register *dnr;
3836 cData = channel->channel_info;
3837 reply("CSMSG_CHANNEL_INFO", channel->name);
3839 uData = GetChannelUser(cData, user->handle_info);
3840 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3842 mod_chanmode_format(&cData->modes, modes);
3843 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3844 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3847 for(it = dict_first(cData->notes); it; it = iter_next(it))
3851 note = iter_data(it);
3852 if(!note_type_visible_to_user(cData, note->type, user))
3855 padding = PADLEN - 1 - strlen(iter_key(it));
3856 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3859 reply("CSMSG_CHANNEL_MAX", cData->max);
3860 for(owner = cData->users; owner; owner = owner->next)
3861 if(owner->access == UL_OWNER)
3862 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3863 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3864 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3865 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3866 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3868 privileged = IsStaff(user);
3869 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3870 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3872 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3873 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3875 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3877 struct suspended *suspended;
3878 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3879 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3880 show_suspension_info(cmd, user, suspended);
3882 else if(IsSuspended(cData))
3884 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3885 show_suspension_info(cmd, user, cData->suspended);
3890 static CHANSERV_FUNC(cmd_netinfo)
3892 extern time_t boot_time;
3893 extern unsigned long burst_length;
3894 char interval[INTERVALLEN];
3896 reply("CSMSG_NETWORK_INFO");
3897 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3898 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3899 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3900 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3901 reply("CSMSG_NETWORK_BANS", banCount);
3902 reply("CSMSG_CHANNEL_USERS", userCount);
3903 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3904 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3909 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3911 struct helpfile_table table;
3913 struct userNode *user;
3918 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3919 table.contents = alloca(list->used*sizeof(*table.contents));
3920 for(nn=0; nn<list->used; nn++)
3922 user = list->list[nn];
3923 if(user->modes & skip_flags)
3927 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3930 nick = alloca(strlen(user->nick)+3);
3931 sprintf(nick, "(%s)", user->nick);
3935 table.contents[table.length][0] = nick;
3938 table_send(chanserv, to->nick, 0, NULL, table);
3941 static CHANSERV_FUNC(cmd_ircops)
3943 reply("CSMSG_STAFF_OPERS");
3944 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3948 static CHANSERV_FUNC(cmd_helpers)
3950 reply("CSMSG_STAFF_HELPERS");
3951 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3955 static CHANSERV_FUNC(cmd_staff)
3957 reply("CSMSG_NETWORK_STAFF");
3958 cmd_ircops(CSFUNC_ARGS);
3959 cmd_helpers(CSFUNC_ARGS);
3963 static CHANSERV_FUNC(cmd_peek)
3965 struct modeNode *mn;
3966 char modes[MODELEN];
3968 struct helpfile_table table;
3970 irc_make_chanmode(channel, modes);
3972 reply("CSMSG_PEEK_INFO", channel->name);
3973 reply("CSMSG_PEEK_TOPIC", channel->topic);
3974 reply("CSMSG_PEEK_MODES", modes);
3975 reply("CSMSG_PEEK_USERS", channel->members.used);
3979 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3980 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3981 for(n = 0; n < channel->members.used; n++)
3983 mn = channel->members.list[n];
3984 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3986 table.contents[table.length] = alloca(sizeof(**table.contents));
3987 table.contents[table.length][0] = mn->user->nick;
3992 reply("CSMSG_PEEK_OPS");
3993 table_send(chanserv, user->nick, 0, NULL, table);
3996 reply("CSMSG_PEEK_NO_OPS");
4000 static MODCMD_FUNC(cmd_wipeinfo)
4002 struct handle_info *victim;
4003 struct userData *ud, *actor;
4006 actor = GetChannelUser(channel->channel_info, user->handle_info);
4007 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4009 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4011 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4014 if((ud->access >= actor->access) && (ud != actor))
4016 reply("MSG_USER_OUTRANKED", victim->handle);
4022 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4026 static CHANSERV_FUNC(cmd_resync)
4028 struct mod_chanmode *changes;
4029 struct chanData *cData = channel->channel_info;
4030 unsigned int ii, used;
4032 changes = mod_chanmode_alloc(channel->members.used * 2);
4033 for(ii = used = 0; ii < channel->members.used; ++ii)
4035 struct modeNode *mn = channel->members.list[ii];
4036 struct userData *uData;
4037 if(IsService(mn->user))
4039 /* must not change modes for this user */
4041 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4045 changes->args[used].mode = MODE_REMOVE | mn->modes;
4046 changes->args[used++].member = mn;
4049 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4051 if(mn->modes & MODE_CHANOP)
4053 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4054 changes->args[used++].member = mn;
4056 if(!(mn->modes & MODE_VOICE))
4058 changes->args[used].mode = MODE_VOICE;
4059 changes->args[used++].member = mn;
4064 if(!(mn->modes & MODE_CHANOP))
4066 changes->args[used].mode = MODE_CHANOP;
4067 changes->args[used++].member = mn;
4071 changes->argc = used;
4072 modcmd_chanmode_announce(changes);
4073 mod_chanmode_free(changes);
4074 reply("CSMSG_RESYNCED_USERS", channel->name);
4078 static CHANSERV_FUNC(cmd_seen)
4080 struct userData *uData;
4081 struct handle_info *handle;
4082 char seen[INTERVALLEN];
4086 if(!irccasecmp(argv[1], chanserv->nick))
4088 reply("CSMSG_IS_CHANSERV");
4092 if(!(handle = get_handle_info(argv[1])))
4094 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4098 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4100 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4105 reply("CSMSG_USER_PRESENT", handle->handle);
4106 else if(uData->seen)
4107 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4109 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4111 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4112 reply("CSMSG_USER_VACATION", handle->handle);
4117 static MODCMD_FUNC(cmd_names)
4119 struct userNode *targ;
4120 struct userData *targData;
4121 unsigned int ii, pos;
4124 for(ii=pos=0; ii<channel->members.used; ++ii)
4126 targ = channel->members.list[ii]->user;
4127 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4130 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4133 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4137 if(IsUserSuspended(targData))
4139 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4142 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4143 reply("CSMSG_END_NAMES", channel->name);
4148 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4150 switch(ntype->visible_type)
4152 case NOTE_VIS_ALL: return 1;
4153 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4154 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4159 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4161 struct userData *uData;
4163 switch(ntype->set_access_type)
4165 case NOTE_SET_CHANNEL_ACCESS:
4166 if(!user->handle_info)
4168 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4170 return uData->access >= ntype->set_access.min_ulevel;
4171 case NOTE_SET_CHANNEL_SETTER:
4172 return check_user_level(channel, user, lvlSetters, 1, 0);
4173 case NOTE_SET_PRIVILEGED: default:
4174 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4178 static CHANSERV_FUNC(cmd_note)
4180 struct chanData *cData;
4182 struct note_type *ntype;
4184 cData = channel->channel_info;
4187 reply("CSMSG_NOT_REGISTERED", channel->name);
4191 /* If no arguments, show all visible notes for the channel. */
4197 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4199 note = iter_data(it);
4200 if(!note_type_visible_to_user(cData, note->type, user))
4203 reply("CSMSG_NOTELIST_HEADER", channel->name);
4204 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4207 reply("CSMSG_NOTELIST_END", channel->name);
4209 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4211 /* If one argument, show the named note. */
4214 if((note = dict_find(cData->notes, argv[1], NULL))
4215 && note_type_visible_to_user(cData, note->type, user))
4217 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4219 else if((ntype = dict_find(note_types, argv[1], NULL))
4220 && note_type_visible_to_user(NULL, ntype, user))
4222 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4227 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4231 /* Assume they're trying to set a note. */
4235 ntype = dict_find(note_types, argv[1], NULL);
4238 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4241 else if(note_type_settable_by_user(channel, ntype, user))
4243 note_text = unsplit_string(argv+2, argc-2, NULL);
4244 if((note = dict_find(cData->notes, argv[1], NULL)))
4245 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4246 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4247 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4249 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4251 /* The note is viewable to staff only, so return 0
4252 to keep the invocation from getting logged (or
4253 regular users can see it in !events). */
4259 reply("CSMSG_NO_ACCESS");
4266 static CHANSERV_FUNC(cmd_delnote)
4271 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4272 || !note_type_settable_by_user(channel, note->type, user))
4274 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4277 dict_remove(channel->channel_info->notes, note->type->name);
4278 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4282 static CHANSERV_FUNC(cmd_events)
4284 struct logSearch discrim;
4285 struct logReport report;
4286 unsigned int matches, limit;
4288 limit = (argc > 1) ? atoi(argv[1]) : 10;
4289 if(limit < 1 || limit > 200) limit = 10;
4291 memset(&discrim, 0, sizeof(discrim));
4292 discrim.masks.bot = chanserv;
4293 discrim.masks.channel_name = channel->name;
4294 if(argc > 2) discrim.masks.command = argv[2];
4295 discrim.limit = limit;
4296 discrim.max_time = INT_MAX;
4297 discrim.severities = 1 << LOG_COMMAND;
4298 report.reporter = chanserv;
4300 reply("CSMSG_EVENT_SEARCH_RESULTS");
4301 matches = log_entry_search(&discrim, log_report_entry, &report);
4303 reply("MSG_MATCH_COUNT", matches);
4305 reply("MSG_NO_MATCHES");
4309 static CHANSERV_FUNC(cmd_say)
4315 msg = unsplit_string(argv + 1, argc - 1, NULL);
4316 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4318 else if(GetUserH(argv[1]))
4321 msg = unsplit_string(argv + 2, argc - 2, NULL);
4322 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4326 reply("MSG_NOT_TARGET_NAME");
4332 static CHANSERV_FUNC(cmd_emote)
4338 /* CTCP is so annoying. */
4339 msg = unsplit_string(argv + 1, argc - 1, NULL);
4340 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4342 else if(GetUserH(argv[1]))
4344 msg = unsplit_string(argv + 2, argc - 2, NULL);
4345 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4349 reply("MSG_NOT_TARGET_NAME");
4355 struct channelList *
4356 chanserv_support_channels(void)
4358 return &chanserv_conf.support_channels;
4361 static CHANSERV_FUNC(cmd_expire)
4363 int channel_count = registered_channels;
4364 expire_channels(NULL);
4365 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4370 chanserv_expire_suspension(void *data)
4372 struct suspended *suspended = data;
4373 struct chanNode *channel;
4374 struct mod_chanmode change;
4376 if(!suspended->expires || (now < suspended->expires))
4377 suspended->revoked = now;
4378 channel = suspended->cData->channel;
4379 suspended->cData->channel = channel;
4380 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4381 mod_chanmode_init(&change);
4383 change.args[0].mode = MODE_CHANOP;
4384 change.args[0].member = AddChannelUser(chanserv, channel);
4385 mod_chanmode_announce(chanserv, channel, &change);
4388 static CHANSERV_FUNC(cmd_csuspend)
4390 struct suspended *suspended;
4391 char reason[MAXLEN];
4392 time_t expiry, duration;
4393 struct userData *uData;
4397 if(IsProtected(channel->channel_info))
4399 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4403 if(argv[1][0] == '!')
4405 else if(IsSuspended(channel->channel_info))
4407 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4408 show_suspension_info(cmd, user, channel->channel_info->suspended);
4412 if(!strcmp(argv[1], "0"))
4414 else if((duration = ParseInterval(argv[1])))
4415 expiry = now + duration;
4418 reply("MSG_INVALID_DURATION", argv[1]);
4422 unsplit_string(argv + 2, argc - 2, reason);
4424 suspended = calloc(1, sizeof(*suspended));
4425 suspended->revoked = 0;
4426 suspended->issued = now;
4427 suspended->suspender = strdup(user->handle_info->handle);
4428 suspended->expires = expiry;
4429 suspended->reason = strdup(reason);
4430 suspended->cData = channel->channel_info;
4431 suspended->previous = suspended->cData->suspended;
4432 suspended->cData->suspended = suspended;
4434 if(suspended->expires)
4435 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4437 if(IsSuspended(channel->channel_info))
4439 suspended->previous->revoked = now;
4440 if(suspended->previous->expires)
4441 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4442 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4443 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4447 /* Mark all users in channel as absent. */
4448 for(uData = channel->channel_info->users; uData; uData = uData->next)
4457 /* Mark the channel as suspended, then part. */
4458 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4459 DelChannelUser(chanserv, channel, suspended->reason, 0);
4460 reply("CSMSG_SUSPENDED", channel->name);
4461 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4462 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4467 static CHANSERV_FUNC(cmd_cunsuspend)
4469 struct suspended *suspended;
4470 char message[MAXLEN];
4472 if(!IsSuspended(channel->channel_info))
4474 reply("CSMSG_NOT_SUSPENDED", channel->name);
4478 suspended = channel->channel_info->suspended;
4480 /* Expire the suspension and join ChanServ to the channel. */
4481 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4482 chanserv_expire_suspension(suspended);
4483 reply("CSMSG_UNSUSPENDED", channel->name);
4484 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4485 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4489 typedef struct chanservSearch
4497 unsigned long flags;
4501 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4504 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4509 search = malloc(sizeof(struct chanservSearch));
4510 memset(search, 0, sizeof(*search));
4513 for(i = 0; i < argc; i++)
4515 /* Assume all criteria require arguments. */
4518 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4522 if(!irccasecmp(argv[i], "name"))
4523 search->name = argv[++i];
4524 else if(!irccasecmp(argv[i], "registrar"))
4525 search->registrar = argv[++i];
4526 else if(!irccasecmp(argv[i], "unvisited"))
4527 search->unvisited = ParseInterval(argv[++i]);
4528 else if(!irccasecmp(argv[i], "registered"))
4529 search->registered = ParseInterval(argv[++i]);
4530 else if(!irccasecmp(argv[i], "flags"))
4533 if(!irccasecmp(argv[i], "nodelete"))
4534 search->flags |= CHANNEL_NODELETE;
4535 else if(!irccasecmp(argv[i], "suspended"))
4536 search->flags |= CHANNEL_SUSPENDED;
4539 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4543 else if(!irccasecmp(argv[i], "limit"))
4544 search->limit = strtoul(argv[++i], NULL, 10);
4547 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4552 if(search->name && !strcmp(search->name, "*"))
4554 if(search->registrar && !strcmp(search->registrar, "*"))
4555 search->registrar = 0;
4564 chanserv_channel_match(struct chanData *channel, search_t search)
4566 const char *name = channel->channel->name;
4567 if((search->name && !match_ircglob(name, search->name)) ||
4568 (search->registrar && !channel->registrar) ||
4569 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4570 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4571 (search->registered && (now - channel->registered) > search->registered) ||
4572 (search->flags && ((search->flags & channel->flags) != search->flags)))
4579 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4581 struct chanData *channel;
4582 unsigned int matches = 0;
4584 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4586 if(!chanserv_channel_match(channel, search))
4596 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4601 search_print(struct chanData *channel, void *data)
4603 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4606 static CHANSERV_FUNC(cmd_search)
4609 unsigned int matches;
4610 channel_search_func action;
4614 if(!irccasecmp(argv[1], "count"))
4615 action = search_count;
4616 else if(!irccasecmp(argv[1], "print"))
4617 action = search_print;
4620 reply("CSMSG_ACTION_INVALID", argv[1]);
4624 search = chanserv_search_create(user, argc - 2, argv + 2);
4628 if(action == search_count)
4629 search->limit = INT_MAX;
4631 if(action == search_print)
4632 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4634 matches = chanserv_channel_search(search, action, user);
4637 reply("MSG_MATCH_COUNT", matches);
4639 reply("MSG_NO_MATCHES");
4645 static CHANSERV_FUNC(cmd_unvisited)
4647 struct chanData *cData;
4648 time_t interval = chanserv_conf.channel_expire_delay;
4649 char buffer[INTERVALLEN];
4650 unsigned int limit = 25, matches = 0;
4654 interval = ParseInterval(argv[1]);
4656 limit = atoi(argv[2]);
4659 intervalString(buffer, interval);
4660 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4662 for(cData = channelList; cData && matches < limit; cData = cData->next)
4664 if((now - cData->visited) < interval)
4667 intervalString(buffer, now - cData->visited);
4668 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4675 static MODCMD_FUNC(chan_opt_defaulttopic)
4681 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4683 reply("CSMSG_TOPIC_LOCKED", channel->name);
4687 topic = unsplit_string(argv+1, argc-1, NULL);
4689 free(channel->channel_info->topic);
4690 if(topic[0] == '*' && topic[1] == 0)
4692 topic = channel->channel_info->topic = NULL;
4696 topic = channel->channel_info->topic = strdup(topic);
4697 if(channel->channel_info->topic_mask
4698 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4699 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4701 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4704 if(channel->channel_info->topic)
4705 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4707 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4711 static MODCMD_FUNC(chan_opt_topicmask)
4715 struct chanData *cData = channel->channel_info;
4718 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4720 reply("CSMSG_TOPIC_LOCKED", channel->name);
4724 mask = unsplit_string(argv+1, argc-1, NULL);
4726 if(cData->topic_mask)
4727 free(cData->topic_mask);
4728 if(mask[0] == '*' && mask[1] == 0)
4730 cData->topic_mask = 0;
4734 cData->topic_mask = strdup(mask);
4736 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4737 else if(!match_ircglob(cData->topic, cData->topic_mask))
4738 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4742 if(channel->channel_info->topic_mask)
4743 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4745 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4749 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4753 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4757 if(greeting[0] == '*' && greeting[1] == 0)
4761 unsigned int length = strlen(greeting);
4762 if(length > chanserv_conf.greeting_length)
4764 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4767 *data = strdup(greeting);
4776 reply(name, user_find_message(user, "MSG_NONE"));
4780 static MODCMD_FUNC(chan_opt_greeting)
4782 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4785 static MODCMD_FUNC(chan_opt_usergreeting)
4787 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4790 static MODCMD_FUNC(chan_opt_modes)
4792 struct mod_chanmode *new_modes;
4793 char modes[MODELEN];
4797 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4799 reply("CSMSG_NO_ACCESS");
4802 if(argv[1][0] == '*' && argv[1][1] == 0)
4804 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4806 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4808 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4811 else if(new_modes->argc > 1)
4813 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4814 mod_chanmode_free(new_modes);
4819 channel->channel_info->modes = *new_modes;
4820 modcmd_chanmode_announce(new_modes);
4821 mod_chanmode_free(new_modes);
4825 mod_chanmode_format(&channel->channel_info->modes, modes);
4827 reply("CSMSG_SET_MODES", modes);
4829 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4833 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4835 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4837 struct chanData *cData = channel->channel_info;
4842 /* Set flag according to value. */
4843 if(enabled_string(argv[1]))
4845 cData->flags |= mask;
4848 else if(disabled_string(argv[1]))
4850 cData->flags &= ~mask;
4855 reply("MSG_INVALID_BINARY", argv[1]);
4861 /* Find current option value. */
4862 value = (cData->flags & mask) ? 1 : 0;
4866 reply(name, user_find_message(user, "MSG_ON"));
4868 reply(name, user_find_message(user, "MSG_OFF"));
4872 static MODCMD_FUNC(chan_opt_nodelete)
4874 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4876 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4880 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4883 static MODCMD_FUNC(chan_opt_dynlimit)
4885 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4888 static MODCMD_FUNC(chan_opt_defaults)
4890 struct userData *uData;
4891 struct chanData *cData;
4892 const char *confirm;
4893 enum levelOption lvlOpt;
4894 enum charOption chOpt;
4896 cData = channel->channel_info;
4897 uData = GetChannelUser(cData, user->handle_info);
4898 if(!uData || (uData->access < UL_OWNER))
4900 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4903 confirm = make_confirmation_string(uData);
4904 if((argc < 2) || strcmp(argv[1], confirm))
4906 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4909 cData->flags = CHANNEL_DEFAULT_FLAGS;
4910 cData->modes = chanserv_conf.default_modes;
4911 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4912 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4913 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4914 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4915 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4920 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4922 struct chanData *cData = channel->channel_info;
4923 struct userData *uData;
4924 unsigned short value;
4928 if(!check_user_level(channel, user, option, 1, 1))
4930 reply("CSMSG_CANNOT_SET");
4933 value = user_level_from_name(argv[1], UL_OWNER+1);
4934 if(!value && strcmp(argv[1], "0"))
4936 reply("CSMSG_INVALID_ACCESS", argv[1]);
4939 uData = GetChannelUser(cData, user->handle_info);
4940 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4942 reply("CSMSG_BAD_SETLEVEL");
4948 if(value > cData->lvlOpts[lvlGiveOps])
4950 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4955 if(value < cData->lvlOpts[lvlGiveVoice])
4957 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4962 /* This test only applies to owners, since non-owners
4963 * trying to set an option to above their level get caught
4964 * by the CSMSG_BAD_SETLEVEL test above.
4966 if(value > uData->access)
4968 reply("CSMSG_BAD_SETTERS");
4975 cData->lvlOpts[option] = value;
4977 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4981 static MODCMD_FUNC(chan_opt_enfops)
4983 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4986 static MODCMD_FUNC(chan_opt_giveops)
4988 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4991 static MODCMD_FUNC(chan_opt_enfmodes)
4993 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
4996 static MODCMD_FUNC(chan_opt_enftopic)
4998 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5001 static MODCMD_FUNC(chan_opt_pubcmd)
5003 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5006 static MODCMD_FUNC(chan_opt_setters)
5008 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5011 static MODCMD_FUNC(chan_opt_ctcpusers)
5013 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5016 static MODCMD_FUNC(chan_opt_userinfo)
5018 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5021 static MODCMD_FUNC(chan_opt_givevoice)
5023 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5026 static MODCMD_FUNC(chan_opt_topicsnarf)
5028 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5031 static MODCMD_FUNC(chan_opt_inviteme)
5033 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5037 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5039 struct chanData *cData = channel->channel_info;
5040 int count = charOptions[option].count, index;
5044 index = atoi(argv[1]);
5046 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5048 reply("CSMSG_INVALID_NUMERIC", index);
5049 /* Show possible values. */
5050 for(index = 0; index < count; index++)
5051 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5055 cData->chOpts[option] = charOptions[option].values[index].value;
5059 /* Find current option value. */
5062 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5066 /* Somehow, the option value is corrupt; reset it to the default. */
5067 cData->chOpts[option] = charOptions[option].default_value;
5072 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5076 static MODCMD_FUNC(chan_opt_protect)
5078 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5081 static MODCMD_FUNC(chan_opt_toys)
5083 return channel_multiple_option(chToys, CSFUNC_ARGS);
5086 static MODCMD_FUNC(chan_opt_ctcpreaction)
5088 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5091 static MODCMD_FUNC(chan_opt_topicrefresh)
5093 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5096 static struct svccmd_list set_shows_list;
5099 handle_svccmd_unbind(struct svccmd *target) {
5101 for(ii=0; ii<set_shows_list.used; ++ii)
5102 if(target == set_shows_list.list[ii])
5103 set_shows_list.used = 0;
5106 static CHANSERV_FUNC(cmd_set)
5108 struct svccmd *subcmd;
5112 /* Check if we need to (re-)initialize set_shows_list. */
5113 if(!set_shows_list.used)
5115 if(!set_shows_list.size)
5117 set_shows_list.size = chanserv_conf.set_shows->used;
5118 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5120 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5122 const char *name = chanserv_conf.set_shows->list[ii];
5123 sprintf(buf, "%s %s", argv[0], name);
5124 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5127 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5130 svccmd_list_append(&set_shows_list, subcmd);
5136 reply("CSMSG_CHANNEL_OPTIONS");
5137 for(ii = 0; ii < set_shows_list.used; ii++)
5139 subcmd = set_shows_list.list[ii];
5140 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5145 sprintf(buf, "%s %s", argv[0], argv[1]);
5146 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5149 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5152 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5154 reply("CSMSG_NO_ACCESS");
5158 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5162 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5164 struct userData *uData;
5166 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5169 reply("CSMSG_NOT_USER", channel->name);
5175 /* Just show current option value. */
5177 else if(enabled_string(argv[1]))
5179 uData->flags |= mask;
5181 else if(disabled_string(argv[1]))
5183 uData->flags &= ~mask;
5187 reply("MSG_INVALID_BINARY", argv[1]);
5191 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5195 static MODCMD_FUNC(user_opt_noautoop)
5197 struct userData *uData;
5199 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5202 reply("CSMSG_NOT_USER", channel->name);
5205 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5206 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5208 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5211 static MODCMD_FUNC(user_opt_autoinvite)
5213 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5216 static MODCMD_FUNC(user_opt_info)
5218 struct userData *uData;
5221 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5225 /* If they got past the command restrictions (which require access)
5226 * but fail this test, we have some fool with security override on.
5228 reply("CSMSG_NOT_USER", channel->name);
5234 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5237 if(infoline[0] == '*' && infoline[1] == 0)
5240 uData->info = strdup(infoline);
5243 reply("CSMSG_USET_INFO", uData->info);
5245 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5249 struct svccmd_list uset_shows_list;
5251 static CHANSERV_FUNC(cmd_uset)
5253 struct svccmd *subcmd;
5257 /* Check if we need to (re-)initialize uset_shows_list. */
5258 if(!uset_shows_list.used)
5262 "NoAutoOp", "AutoInvite", "Info"
5265 if(!uset_shows_list.size)
5267 uset_shows_list.size = ArrayLength(options);
5268 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5270 for(ii = 0; ii < ArrayLength(options); ii++)
5272 const char *name = options[ii];
5273 sprintf(buf, "%s %s", argv[0], name);
5274 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5277 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5280 svccmd_list_append(&uset_shows_list, subcmd);
5286 /* Do this so options are presented in a consistent order. */
5287 reply("CSMSG_USER_OPTIONS");
5288 for(ii = 0; ii < uset_shows_list.used; ii++)
5289 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5293 sprintf(buf, "%s %s", argv[0], argv[1]);
5294 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5297 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5301 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5304 static CHANSERV_FUNC(cmd_giveownership)
5306 struct handle_info *new_owner_hi;
5307 struct userData *new_owner, *curr_user;
5308 struct chanData *cData = channel->channel_info;
5309 struct do_not_register *dnr;
5311 unsigned short co_access;
5312 char reason[MAXLEN];
5315 curr_user = GetChannelAccess(cData, user->handle_info);
5316 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5317 if(!curr_user || (curr_user->access != UL_OWNER))
5319 struct userData *owner = NULL;
5320 for(curr_user = channel->channel_info->users;
5322 curr_user = curr_user->next)
5324 if(curr_user->access != UL_OWNER)
5328 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5335 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5337 if(new_owner_hi == user->handle_info)
5339 reply("CSMSG_NO_TRANSFER_SELF");
5342 new_owner = GetChannelAccess(cData, new_owner_hi);
5345 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5348 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5350 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5353 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5354 if(!IsHelping(user))
5355 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5357 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5360 if(new_owner->access >= UL_COOWNER)
5361 co_access = new_owner->access;
5363 co_access = UL_COOWNER;
5364 new_owner->access = UL_OWNER;
5366 curr_user->access = co_access;
5367 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5368 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5369 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5373 static CHANSERV_FUNC(cmd_suspend)
5375 struct handle_info *hi;
5376 struct userData *self, *target;
5379 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5380 self = GetChannelUser(channel->channel_info, user->handle_info);
5381 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5383 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5386 if(target->access >= self->access)
5388 reply("MSG_USER_OUTRANKED", hi->handle);
5391 if(target->flags & USER_SUSPENDED)
5393 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5398 target->present = 0;
5401 target->flags |= USER_SUSPENDED;
5402 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5406 static CHANSERV_FUNC(cmd_unsuspend)
5408 struct handle_info *hi;
5409 struct userData *self, *target;
5412 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5413 self = GetChannelUser(channel->channel_info, user->handle_info);
5414 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5416 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5419 if(target->access >= self->access)
5421 reply("MSG_USER_OUTRANKED", hi->handle);
5424 if(!(target->flags & USER_SUSPENDED))
5426 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5429 target->flags &= ~USER_SUSPENDED;
5430 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5434 static MODCMD_FUNC(cmd_deleteme)
5436 struct handle_info *hi;
5437 struct userData *target;
5438 const char *confirm_string;
5439 unsigned short access;
5442 hi = user->handle_info;
5443 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5445 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5448 if(target->access == UL_OWNER)
5450 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5453 confirm_string = make_confirmation_string(target);
5454 if((argc < 2) || strcmp(argv[1], confirm_string))
5456 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5459 access = target->access;
5460 channel_name = strdup(channel->name);
5461 del_channel_user(target, 1);
5462 reply("CSMSG_DELETED_YOU", access, channel_name);
5468 chanserv_refresh_topics(UNUSED_ARG(void *data))
5470 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5471 struct chanData *cData;
5474 for(cData = channelList; cData; cData = cData->next)
5476 if(IsSuspended(cData))
5478 opt = cData->chOpts[chTopicRefresh];
5481 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5484 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5485 cData->last_refresh = refresh_num;
5487 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5490 static CHANSERV_FUNC(cmd_unf)
5494 char response[MAXLEN];
5495 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5496 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5497 irc_privmsg(cmd->parent->bot, channel->name, response);
5500 reply("CSMSG_UNF_RESPONSE");
5504 static CHANSERV_FUNC(cmd_ping)
5508 char response[MAXLEN];
5509 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5510 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5511 irc_privmsg(cmd->parent->bot, channel->name, response);
5514 reply("CSMSG_PING_RESPONSE");
5518 static CHANSERV_FUNC(cmd_wut)
5522 char response[MAXLEN];
5523 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5524 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5525 irc_privmsg(cmd->parent->bot, channel->name, response);
5528 reply("CSMSG_WUT_RESPONSE");
5532 static CHANSERV_FUNC(cmd_8ball)
5534 unsigned int i, j, accum;
5539 for(i=1; i<argc; i++)
5540 for(j=0; argv[i][j]; j++)
5541 accum = (accum << 5) - accum + toupper(argv[i][j]);
5542 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5545 char response[MAXLEN];
5546 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5547 irc_privmsg(cmd->parent->bot, channel->name, response);
5550 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5554 static CHANSERV_FUNC(cmd_d)
5556 unsigned long sides, count, modifier, ii, total;
5557 char response[MAXLEN], *sep;
5561 if((count = strtoul(argv[1], &sep, 10)) < 1)
5571 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5572 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5576 else if((sep[0] == '-') && isdigit(sep[1]))
5577 modifier = strtoul(sep, NULL, 10);
5578 else if((sep[0] == '+') && isdigit(sep[1]))
5579 modifier = strtoul(sep+1, NULL, 10);
5586 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5591 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5594 for(total = ii = 0; ii < count; ++ii)
5595 total += (rand() % sides) + 1;
5598 if((count > 1) || modifier)
5600 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5601 sprintf(response, fmt, total, count, sides, modifier);
5605 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5606 sprintf(response, fmt, total, sides);
5609 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5611 send_message_type(4, user, cmd->parent->bot, "%s", response);
5615 static CHANSERV_FUNC(cmd_huggle)
5617 /* CTCP must be via PRIVMSG, never notice */
5619 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5621 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5626 chanserv_adjust_limit(void *data)
5628 struct mod_chanmode change;
5629 struct chanData *cData = data;
5630 struct chanNode *channel = cData->channel;
5633 if(IsSuspended(cData))
5636 cData->limitAdjusted = now;
5637 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5638 if(cData->modes.modes_set & MODE_LIMIT)
5640 if(limit > cData->modes.new_limit)
5641 limit = cData->modes.new_limit;
5642 else if(limit == cData->modes.new_limit)
5646 mod_chanmode_init(&change);
5647 change.modes_set = MODE_LIMIT;
5648 change.new_limit = limit;
5649 mod_chanmode_announce(chanserv, channel, &change);
5653 handle_new_channel(struct chanNode *channel)
5655 struct chanData *cData;
5657 if(!(cData = channel->channel_info))
5660 if(cData->modes.modes_set || cData->modes.modes_clear)
5661 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5663 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5664 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5667 /* Welcome to my worst nightmare. Warning: Read (or modify)
5668 the code below at your own risk. */
5670 handle_join(struct modeNode *mNode)
5672 struct mod_chanmode change;
5673 struct userNode *user = mNode->user;
5674 struct chanNode *channel = mNode->channel;
5675 struct chanData *cData;
5676 struct userData *uData = NULL;
5677 struct banData *bData;
5678 struct handle_info *handle;
5679 unsigned int modes = 0, info = 0;
5682 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5685 cData = channel->channel_info;
5686 if(channel->members.used > cData->max)
5687 cData->max = channel->members.used;
5689 /* Check for bans. If they're joining through a ban, one of two
5691 * 1: Join during a netburst, by riding the break. Kick them
5692 * unless they have ops or voice in the channel.
5693 * 2: They're allowed to join through the ban (an invite in
5694 * ircu2.10, or a +e on Hybrid, or something).
5695 * If they're not joining through a ban, and the banlist is not
5696 * full, see if they're on the banlist for the channel. If so,
5699 if(user->uplink->burst && !mNode->modes)
5702 for(ii = 0; ii < channel->banlist.used; ii++)
5704 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5706 /* Riding a netburst. Naughty. */
5707 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5713 mod_chanmode_init(&change);
5715 if(channel->banlist.used < MAXBANS)
5717 /* Not joining through a ban. */
5718 for(bData = cData->bans;
5719 bData && !user_matches_glob(user, bData->mask, 1);
5720 bData = bData->next);
5724 char kick_reason[MAXLEN];
5725 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5727 bData->triggered = now;
5728 if(bData != cData->bans)
5730 /* Shuffle the ban to the head of the list. */
5732 bData->next->prev = bData->prev;
5734 bData->prev->next = bData->next;
5737 bData->next = cData->bans;
5740 cData->bans->prev = bData;
5741 cData->bans = bData;
5744 change.args[0].mode = MODE_BAN;
5745 change.args[0].hostmask = bData->mask;
5746 mod_chanmode_announce(chanserv, channel, &change);
5747 KickChannelUser(user, channel, chanserv, kick_reason);
5752 /* ChanServ will not modify the limits in join-flooded channels.
5753 It will also skip DynLimit processing when the user (or srvx)
5754 is bursting in, because there are likely more incoming. */
5755 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5756 && !user->uplink->burst
5757 && !channel->join_flooded
5758 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5760 /* The user count has begun "bumping" into the channel limit,
5761 so set a timer to raise the limit a bit. Any previous
5762 timers are removed so three incoming users within the delay
5763 results in one limit change, not three. */
5765 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5766 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5769 if(channel->join_flooded)
5771 /* don't automatically give ops or voice during a join flood */
5773 else if(cData->lvlOpts[lvlGiveOps] == 0)
5774 modes |= MODE_CHANOP;
5775 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5776 modes |= MODE_VOICE;
5778 greeting = cData->greeting;
5779 if(user->handle_info)
5781 handle = user->handle_info;
5783 if(IsHelper(user) && !IsHelping(user))
5786 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5788 if(channel == chanserv_conf.support_channels.list[ii])
5790 HANDLE_SET_FLAG(user->handle_info, HELPING);
5796 uData = GetTrueChannelAccess(cData, handle);
5797 if(uData && !IsUserSuspended(uData))
5799 /* Ops and above were handled by the above case. */
5800 if(IsUserAutoOp(uData))
5802 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5803 modes |= MODE_CHANOP;
5804 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5805 modes |= MODE_VOICE;
5807 if(uData->access >= UL_PRESENT)
5808 cData->visited = now;
5809 if(cData->user_greeting)
5810 greeting = cData->user_greeting;
5812 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5813 && ((now - uData->seen) >= chanserv_conf.info_delay)
5820 if(!user->uplink->burst)
5824 if(modes & MODE_CHANOP)
5825 modes &= ~MODE_VOICE;
5826 change.args[0].mode = modes;
5827 change.args[0].member = mNode;
5828 mod_chanmode_announce(chanserv, channel, &change);
5830 if(greeting && !user->uplink->burst)
5831 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5833 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5839 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5841 struct mod_chanmode change;
5842 struct userData *channel;
5843 unsigned int ii, jj;
5845 if(!user->handle_info)
5848 mod_chanmode_init(&change);
5850 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5852 struct chanNode *cn;
5853 struct modeNode *mn;
5854 if(IsUserSuspended(channel)
5855 || IsSuspended(channel->channel)
5856 || !(cn = channel->channel->channel))
5859 mn = GetUserMode(cn, user);
5862 if(!IsUserSuspended(channel)
5863 && IsUserAutoInvite(channel)
5864 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5865 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5867 irc_invite(chanserv, user, cn);
5871 if(channel->access >= UL_PRESENT)
5872 channel->channel->visited = now;
5874 if(IsUserAutoOp(channel))
5876 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5877 change.args[0].mode = MODE_CHANOP;
5878 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5879 change.args[0].mode = MODE_VOICE;
5881 change.args[0].mode = 0;
5882 change.args[0].member = mn;
5883 if(change.args[0].mode)
5884 mod_chanmode_announce(chanserv, cn, &change);
5887 channel->seen = now;
5888 channel->present = 1;
5891 for(ii = 0; ii < user->channels.used; ++ii)
5893 struct chanNode *channel = user->channels.list[ii]->channel;
5894 struct banData *ban;
5896 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5897 || !channel->channel_info)
5899 for(jj = 0; jj < channel->banlist.used; ++jj)
5900 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5902 if(jj < channel->banlist.used)
5904 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5906 char kick_reason[MAXLEN];
5907 if(!user_matches_glob(user, ban->mask, 1))
5909 change.args[0].mode = MODE_BAN;
5910 change.args[0].hostmask = ban->mask;
5911 mod_chanmode_announce(chanserv, channel, &change);
5912 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5913 KickChannelUser(user, channel, chanserv, kick_reason);
5914 ban->triggered = now;
5919 if(IsSupportHelper(user))
5921 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5923 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5925 HANDLE_SET_FLAG(user->handle_info, HELPING);
5933 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5935 struct chanData *cData;
5936 struct userData *uData;
5938 cData = channel->channel_info;
5939 if(!cData || IsSuspended(cData) || IsLocal(user))
5942 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5944 /* Allow for a bit of padding so that the limit doesn't
5945 track the user count exactly, which could get annoying. */
5946 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5948 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5949 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5953 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5954 scan_user_presence(uData, user);
5956 if(IsHelping(user) && IsSupportHelper(user))
5958 unsigned int ii, jj;
5959 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5961 for(jj = 0; jj < user->channels.used; ++jj)
5962 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5964 if(jj < user->channels.used)
5967 if(ii == chanserv_conf.support_channels.used)
5968 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5973 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5975 if(!channel->channel_info || !kicker || IsService(kicker)
5976 || (kicker == victim) || IsSuspended(channel->channel_info)
5977 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5980 if(protect_user(victim, kicker, channel->channel_info))
5982 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
5983 KickChannelUser(kicker, channel, chanserv, reason);
5988 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
5990 struct chanData *cData;
5992 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
5995 cData = channel->channel_info;
5996 if(bad_topic(channel, user, channel->topic))
5998 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
5999 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6000 SetChannelTopic(channel, chanserv, old_topic, 1);
6001 else if(cData->topic)
6002 SetChannelTopic(channel, chanserv, cData->topic, 1);
6005 /* With topicsnarf, grab the topic and save it as the default topic. */
6006 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6009 cData->topic = strdup(channel->topic);
6015 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6017 struct mod_chanmode *bounce = NULL;
6018 unsigned int bnc, ii;
6021 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6024 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6025 && mode_lock_violated(&channel->channel_info->modes, change))
6027 char correct[MAXLEN];
6028 bounce = mod_chanmode_alloc(change->argc + 1);
6029 *bounce = channel->channel_info->modes;
6030 mod_chanmode_format(&channel->channel_info->modes, correct);
6031 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6033 for(ii = bnc = 0; ii < change->argc; ++ii)
6035 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6037 const struct userNode *victim = change->args[ii].member->user;
6038 if(!protect_user(victim, user, channel->channel_info))
6041 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6044 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6045 bounce->args[bnc].member = GetUserMode(channel, user);
6046 if(bounce->args[bnc].member)
6050 bounce->args[bnc].mode = MODE_CHANOP;
6051 bounce->args[bnc].member = change->args[ii].member;
6053 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6055 else if(change->args[ii].mode & MODE_CHANOP)
6057 const struct userNode *victim = change->args[ii].member->user;
6058 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6061 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6062 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6063 bounce->args[bnc].member = change->args[ii].member;
6066 else if(change->args[ii].mode & MODE_BAN)
6068 const char *ban = change->args[ii].hostmask;
6069 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6072 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6073 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6074 bounce->args[bnc].hostmask = ban;
6076 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6081 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6082 mod_chanmode_announce(chanserv, channel, bounce);
6083 mod_chanmode_free(bounce);
6088 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6090 struct chanNode *channel;
6091 struct banData *bData;
6092 struct mod_chanmode change;
6093 unsigned int ii, jj;
6094 char kick_reason[MAXLEN];
6096 mod_chanmode_init(&change);
6098 change.args[0].mode = MODE_BAN;
6099 for(ii = 0; ii < user->channels.used; ++ii)
6101 channel = user->channels.list[ii]->channel;
6102 /* Need not check for bans if they're opped or voiced. */
6103 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6105 /* Need not check for bans unless channel registration is active. */
6106 if(!channel->channel_info || IsSuspended(channel->channel_info))
6108 /* Look for a matching ban already on the channel. */
6109 for(jj = 0; jj < channel->banlist.used; ++jj)
6110 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6112 /* Need not act if we found one. */
6113 if(jj < channel->banlist.used)
6115 /* Look for a matching ban in this channel. */
6116 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6118 if(!user_matches_glob(user, bData->mask, 1))
6120 change.args[0].hostmask = bData->mask;
6121 mod_chanmode_announce(chanserv, channel, &change);
6122 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6123 KickChannelUser(user, channel, chanserv, kick_reason);
6124 bData->triggered = now;
6125 break; /* we don't need to check any more bans in the channel */
6130 static void handle_rename(struct handle_info *handle, const char *old_handle)
6132 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6136 dict_remove2(handle_dnrs, old_handle, 1);
6137 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6138 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6143 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6145 struct userNode *h_user;
6147 if(handle->channels)
6149 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6150 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6152 while(handle->channels)
6153 del_channel_user(handle->channels, 1);
6158 handle_server_link(UNUSED_ARG(struct server *server))
6160 struct chanData *cData;
6162 for(cData = channelList; cData; cData = cData->next)
6164 if(!IsSuspended(cData))
6165 cData->may_opchan = 1;
6166 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6167 && !cData->channel->join_flooded
6168 && ((cData->channel->limit - cData->channel->members.used)
6169 < chanserv_conf.adjust_threshold))
6171 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6172 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6178 chanserv_conf_read(void)
6182 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6183 struct mod_chanmode *change;
6184 struct string_list *strlist;
6185 struct chanNode *chan;
6188 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6190 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6193 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6194 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6195 chanserv_conf.support_channels.used = 0;
6196 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6198 for(ii = 0; ii < strlist->used; ++ii)
6200 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6203 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6205 channelList_append(&chanserv_conf.support_channels, chan);
6208 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6211 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6214 chan = AddChannel(str, now, str2, NULL);
6216 channelList_append(&chanserv_conf.support_channels, chan);
6218 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6219 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6220 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6221 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6222 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6223 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6224 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6225 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6226 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6227 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6228 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6229 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6230 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6231 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6232 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6233 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6234 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6235 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6236 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6237 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6238 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6240 NickChange(chanserv, str, 0);
6241 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6242 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6243 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6244 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6245 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6246 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6247 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6248 chanserv_conf.max_owned = str ? atoi(str) : 5;
6249 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6250 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6251 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6252 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6253 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6254 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6255 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6258 safestrncpy(mode_line, str, sizeof(mode_line));
6259 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6260 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6262 chanserv_conf.default_modes = *change;
6263 mod_chanmode_free(change);
6265 free_string_list(chanserv_conf.set_shows);
6266 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6268 strlist = string_list_copy(strlist);
6271 static const char *list[] = {
6272 /* free form text */
6273 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6274 /* options based on user level */
6275 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6276 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6277 /* multiple choice options */
6278 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6279 /* binary options */
6280 "DynLimit", "NoDelete",
6285 strlist = alloc_string_list(ArrayLength(list)-1);
6286 for(ii=0; list[ii]; ii++)
6287 string_list_append(strlist, strdup(list[ii]));
6289 chanserv_conf.set_shows = strlist;
6290 /* We don't look things up now, in case the list refers to options
6291 * defined by modules initialized after this point. Just mark the
6292 * function list as invalid, so it will be initialized.
6294 set_shows_list.used = 0;
6295 free_string_list(chanserv_conf.eightball);
6296 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6299 strlist = string_list_copy(strlist);
6303 strlist = alloc_string_list(4);
6304 string_list_append(strlist, strdup("Yes."));
6305 string_list_append(strlist, strdup("No."));
6306 string_list_append(strlist, strdup("Maybe so."));
6308 chanserv_conf.eightball = strlist;
6309 free_string_list(chanserv_conf.old_ban_names);
6310 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6312 strlist = string_list_copy(strlist);
6314 strlist = alloc_string_list(2);
6315 chanserv_conf.old_ban_names = strlist;
6319 chanserv_note_type_read(const char *key, struct record_data *rd)
6322 struct note_type *ntype;
6325 if(!(obj = GET_RECORD_OBJECT(rd)))
6327 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6330 if(!(ntype = chanserv_create_note_type(key)))
6332 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6336 /* Figure out set access */
6337 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6339 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6340 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6342 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6344 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6345 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6347 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6349 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6353 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6354 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6355 ntype->set_access.min_opserv = 0;
6358 /* Figure out visibility */
6359 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6360 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6361 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6362 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6363 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6364 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6365 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6366 ntype->visible_type = NOTE_VIS_ALL;
6368 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6370 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6371 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6375 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6377 struct handle_info *handle;
6378 struct userData *uData;
6379 char *seen, *inf, *flags;
6381 unsigned short access;
6383 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6385 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6389 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6390 if(access > UL_OWNER)
6392 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6396 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6397 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6398 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6399 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6400 handle = get_handle_info(key);
6403 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6407 uData = add_channel_user(chan, handle, access, last_seen, inf);
6408 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6412 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6414 struct banData *bData;
6415 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6416 time_t set_time, triggered_time, expires_time;
6418 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6420 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6424 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6425 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6426 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6427 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6428 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6429 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6431 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6432 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6434 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6436 expires_time = set_time + atoi(s_duration);
6440 if(expires_time && (expires_time < now))
6443 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6446 static struct suspended *
6447 chanserv_read_suspended(dict_t obj)
6449 struct suspended *suspended = calloc(1, sizeof(*suspended));
6453 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6454 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6455 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6456 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6457 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6458 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6459 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6460 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6461 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6462 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6467 chanserv_channel_read(const char *key, struct record_data *hir)
6469 struct suspended *suspended;
6470 struct mod_chanmode *modes;
6471 struct chanNode *cNode;
6472 struct chanData *cData;
6473 struct dict *channel, *obj;
6474 char *str, *argv[10];
6478 channel = hir->d.object;
6480 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6483 cNode = AddChannel(key, now, NULL, NULL);
6486 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6489 cData = register_channel(cNode, str);
6492 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6496 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6498 enum levelOption lvlOpt;
6499 enum charOption chOpt;
6501 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6502 cData->flags = atoi(str);
6504 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6506 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6508 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6509 else if(levelOptions[lvlOpt].old_flag)
6511 if(cData->flags & levelOptions[lvlOpt].old_flag)
6512 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6514 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6518 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6520 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6522 cData->chOpts[chOpt] = str[0];
6525 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6527 enum levelOption lvlOpt;
6528 enum charOption chOpt;
6531 cData->flags = base64toint(str, 5);
6532 count = strlen(str += 5);
6533 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6536 if(levelOptions[lvlOpt].old_flag)
6538 if(cData->flags & levelOptions[lvlOpt].old_flag)
6539 lvl = levelOptions[lvlOpt].flag_value;
6541 lvl = levelOptions[lvlOpt].default_value;
6543 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6545 case 'c': lvl = UL_COOWNER; break;
6546 case 'm': lvl = UL_MASTER; break;
6547 case 'n': lvl = UL_OWNER+1; break;
6548 case 'o': lvl = UL_OP; break;
6549 case 'p': lvl = UL_PEON; break;
6550 case 'w': lvl = UL_OWNER; break;
6551 default: lvl = 0; break;
6553 cData->lvlOpts[lvlOpt] = lvl;
6555 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6556 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6559 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6561 suspended = chanserv_read_suspended(obj);
6562 cData->suspended = suspended;
6563 suspended->cData = cData;
6564 /* We could use suspended->expires and suspended->revoked to
6565 * set the CHANNEL_SUSPENDED flag, but we don't. */
6567 else if(cData->flags & CHANNEL_SUSPENDED)
6569 suspended = calloc(1, sizeof(*suspended));
6570 suspended->issued = 0;
6571 suspended->revoked = 0;
6572 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6573 suspended->expires = str ? atoi(str) : 0;
6574 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6575 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6576 suspended->reason = strdup(str ? str : "No reason");
6577 suspended->previous = NULL;
6578 cData->suspended = suspended;
6579 suspended->cData = cData;
6584 if((cData->flags & CHANNEL_SUSPENDED)
6585 && suspended->expires
6586 && (suspended->expires <= now))
6588 cData->flags &= ~CHANNEL_SUSPENDED;
6591 if(!(cData->flags & CHANNEL_SUSPENDED))
6593 struct mod_chanmode change;
6594 mod_chanmode_init(&change);
6596 change.args[0].mode = MODE_CHANOP;
6597 change.args[0].member = AddChannelUser(chanserv, cNode);
6598 mod_chanmode_announce(chanserv, cNode, &change);
6600 else if(suspended->expires > now)
6602 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6605 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6606 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6607 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6608 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6609 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6610 cData->max = str ? atoi(str) : 0;
6611 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6612 cData->greeting = str ? strdup(str) : NULL;
6613 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6614 cData->user_greeting = str ? strdup(str) : NULL;
6615 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6616 cData->topic_mask = str ? strdup(str) : NULL;
6617 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6618 cData->topic = str ? strdup(str) : NULL;
6620 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6621 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6622 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6623 cData->modes = *modes;
6624 if(cData->modes.argc > 1)
6625 cData->modes.argc = 1;
6626 if(!IsSuspended(cData))
6627 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6628 mod_chanmode_free(modes);
6631 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6632 for(it = dict_first(obj); it; it = iter_next(it))
6633 user_read_helper(iter_key(it), iter_data(it), cData);
6635 if(!cData->users && !IsProtected(cData))
6637 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6638 unregister_channel(cData, "has empty user list.");
6642 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6643 for(it = dict_first(obj); it; it = iter_next(it))
6644 ban_read_helper(iter_key(it), iter_data(it), cData);
6646 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6647 for(it = dict_first(obj); it; it = iter_next(it))
6649 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6650 struct record_data *rd = iter_data(it);
6651 const char *note, *setter;
6653 if(rd->type != RECDB_OBJECT)
6655 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6659 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6661 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6663 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6667 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6668 if(!setter) setter = "<unknown>";
6669 chanserv_add_channel_note(cData, ntype, setter, note);
6677 chanserv_dnr_read(const char *key, struct record_data *hir)
6679 const char *setter, *reason, *str;
6680 struct do_not_register *dnr;
6682 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6685 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6688 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6691 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6694 dnr = chanserv_add_dnr(key, setter, reason);
6697 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6699 dnr->set = atoi(str);
6705 chanserv_saxdb_read(struct dict *database)
6707 struct dict *section;
6710 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6711 for(it = dict_first(section); it; it = iter_next(it))
6712 chanserv_note_type_read(iter_key(it), iter_data(it));
6714 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6715 for(it = dict_first(section); it; it = iter_next(it))
6716 chanserv_channel_read(iter_key(it), iter_data(it));
6718 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6719 for(it = dict_first(section); it; it = iter_next(it))
6720 chanserv_dnr_read(iter_key(it), iter_data(it));
6726 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6728 int high_present = 0;
6729 saxdb_start_record(ctx, KEY_USERS, 1);
6730 for(; uData; uData = uData->next)
6732 if((uData->access >= UL_PRESENT) && uData->present)
6734 saxdb_start_record(ctx, uData->handle->handle, 0);
6735 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6736 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6738 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6740 saxdb_write_string(ctx, KEY_INFO, uData->info);
6741 saxdb_end_record(ctx);
6743 saxdb_end_record(ctx);
6744 return high_present;
6748 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6752 saxdb_start_record(ctx, KEY_BANS, 1);
6753 for(; bData; bData = bData->next)
6755 saxdb_start_record(ctx, bData->mask, 0);
6756 saxdb_write_int(ctx, KEY_SET, bData->set);
6757 if(bData->triggered)
6758 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6760 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6762 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6764 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6765 saxdb_end_record(ctx);
6767 saxdb_end_record(ctx);
6771 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6773 saxdb_start_record(ctx, name, 0);
6774 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6775 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6777 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6779 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6781 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6783 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6784 saxdb_end_record(ctx);
6788 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6792 enum levelOption lvlOpt;
6793 enum charOption chOpt;
6795 saxdb_start_record(ctx, channel->channel->name, 1);
6797 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6798 saxdb_write_int(ctx, KEY_MAX, channel->max);
6800 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6801 if(channel->registrar)
6802 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6803 if(channel->greeting)
6804 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6805 if(channel->user_greeting)
6806 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6807 if(channel->topic_mask)
6808 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6809 if(channel->suspended)
6810 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6812 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6813 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6814 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6815 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6816 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6818 buf[0] = channel->chOpts[chOpt];
6820 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6822 saxdb_end_record(ctx);
6824 if(channel->modes.modes_set || channel->modes.modes_clear)
6826 mod_chanmode_format(&channel->modes, buf);
6827 saxdb_write_string(ctx, KEY_MODES, buf);
6830 high_present = chanserv_write_users(ctx, channel->users);
6831 chanserv_write_bans(ctx, channel->bans);
6833 if(dict_size(channel->notes))
6837 saxdb_start_record(ctx, KEY_NOTES, 1);
6838 for(it = dict_first(channel->notes); it; it = iter_next(it))
6840 struct note *note = iter_data(it);
6841 saxdb_start_record(ctx, iter_key(it), 0);
6842 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6843 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6844 saxdb_end_record(ctx);
6846 saxdb_end_record(ctx);
6849 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6850 saxdb_end_record(ctx);
6854 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6858 saxdb_start_record(ctx, ntype->name, 0);
6859 switch(ntype->set_access_type)
6861 case NOTE_SET_CHANNEL_ACCESS:
6862 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6864 case NOTE_SET_CHANNEL_SETTER:
6865 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6867 case NOTE_SET_PRIVILEGED: default:
6868 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6871 switch(ntype->visible_type)
6873 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6874 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6875 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6877 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6878 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6879 saxdb_end_record(ctx);
6883 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6885 struct do_not_register *dnr;
6888 for(it = dict_first(dnrs); it; it = iter_next(it))
6890 dnr = iter_data(it);
6891 saxdb_start_record(ctx, dnr->chan_name, 0);
6893 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6894 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6895 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6896 saxdb_end_record(ctx);
6901 chanserv_saxdb_write(struct saxdb_context *ctx)
6904 struct chanData *channel;
6907 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6908 for(it = dict_first(note_types); it; it = iter_next(it))
6909 chanserv_write_note_type(ctx, iter_data(it));
6910 saxdb_end_record(ctx);
6913 saxdb_start_record(ctx, KEY_DNR, 1);
6914 write_dnrs_helper(ctx, handle_dnrs);
6915 write_dnrs_helper(ctx, plain_dnrs);
6916 write_dnrs_helper(ctx, mask_dnrs);
6917 saxdb_end_record(ctx);
6920 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6921 for(channel = channelList; channel; channel = channel->next)
6922 chanserv_write_channel(ctx, channel);
6923 saxdb_end_record(ctx);
6929 chanserv_db_cleanup(void) {
6931 unreg_part_func(handle_part);
6933 unregister_channel(channelList, "terminating.");
6934 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6935 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6936 free(chanserv_conf.support_channels.list);
6937 dict_delete(handle_dnrs);
6938 dict_delete(plain_dnrs);
6939 dict_delete(mask_dnrs);
6940 dict_delete(note_types);
6941 free_string_list(chanserv_conf.eightball);
6942 free_string_list(chanserv_conf.old_ban_names);
6943 free_string_list(chanserv_conf.set_shows);
6944 free(set_shows_list.list);
6945 free(uset_shows_list.list);
6948 struct userData *helper = helperList;
6949 helperList = helperList->next;
6954 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6955 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6956 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6959 init_chanserv(const char *nick)
6961 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6962 conf_register_reload(chanserv_conf_read);
6964 reg_server_link_func(handle_server_link);
6966 reg_new_channel_func(handle_new_channel);
6967 reg_join_func(handle_join);
6968 reg_part_func(handle_part);
6969 reg_kick_func(handle_kick);
6970 reg_topic_func(handle_topic);
6971 reg_mode_change_func(handle_mode);
6972 reg_nick_change_func(handle_nick_change);
6974 reg_auth_func(handle_auth);
6975 reg_handle_rename_func(handle_rename);
6976 reg_unreg_func(handle_unreg);
6978 handle_dnrs = dict_new();
6979 dict_set_free_data(handle_dnrs, free);
6980 plain_dnrs = dict_new();
6981 dict_set_free_data(plain_dnrs, free);
6982 mask_dnrs = dict_new();
6983 dict_set_free_data(mask_dnrs, free);
6985 reg_svccmd_unbind_func(handle_svccmd_unbind);
6986 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6987 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
6988 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
6989 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
6990 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
6991 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6992 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
6993 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
6994 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
6996 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
6997 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
6999 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7000 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7001 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7002 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7003 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7005 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7006 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7007 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7008 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7009 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7011 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7012 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7013 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7014 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7016 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7017 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7018 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7019 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7020 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7021 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7022 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7023 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7025 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7026 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7027 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7028 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7029 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7030 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7031 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7032 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7033 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7034 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7035 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7036 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7037 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7038 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7040 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7041 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7042 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7043 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7044 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7046 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7047 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7049 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7050 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7051 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7052 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7053 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7054 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7055 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7056 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7057 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7058 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7059 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7061 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7062 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7064 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7065 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7066 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7067 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7069 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7070 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7071 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7072 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7073 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7075 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7076 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7077 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7078 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7079 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7080 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7082 /* Channel options */
7083 DEFINE_CHANNEL_OPTION(defaulttopic);
7084 DEFINE_CHANNEL_OPTION(topicmask);
7085 DEFINE_CHANNEL_OPTION(greeting);
7086 DEFINE_CHANNEL_OPTION(usergreeting);
7087 DEFINE_CHANNEL_OPTION(modes);
7088 DEFINE_CHANNEL_OPTION(enfops);
7089 DEFINE_CHANNEL_OPTION(giveops);
7090 DEFINE_CHANNEL_OPTION(protect);
7091 DEFINE_CHANNEL_OPTION(enfmodes);
7092 DEFINE_CHANNEL_OPTION(enftopic);
7093 DEFINE_CHANNEL_OPTION(pubcmd);
7094 DEFINE_CHANNEL_OPTION(givevoice);
7095 DEFINE_CHANNEL_OPTION(userinfo);
7096 DEFINE_CHANNEL_OPTION(dynlimit);
7097 DEFINE_CHANNEL_OPTION(topicsnarf);
7098 DEFINE_CHANNEL_OPTION(nodelete);
7099 DEFINE_CHANNEL_OPTION(toys);
7100 DEFINE_CHANNEL_OPTION(setters);
7101 DEFINE_CHANNEL_OPTION(topicrefresh);
7102 DEFINE_CHANNEL_OPTION(ctcpusers);
7103 DEFINE_CHANNEL_OPTION(ctcpreaction);
7104 DEFINE_CHANNEL_OPTION(inviteme);
7105 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7107 /* Alias set topic to set defaulttopic for compatibility. */
7108 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7111 DEFINE_USER_OPTION(noautoop);
7112 DEFINE_USER_OPTION(autoinvite);
7113 DEFINE_USER_OPTION(info);
7115 /* Alias uset autovoice to uset autoop. */
7116 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7118 note_types = dict_new();
7119 dict_set_free_data(note_types, chanserv_deref_note_type);
7122 chanserv = AddService(nick, "Channel Services");
7123 service_register(chanserv, '!');
7124 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7126 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7128 if(chanserv_conf.channel_expire_frequency)
7129 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7131 if(chanserv_conf.refresh_period)
7133 time_t next_refresh;
7134 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7135 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7138 reg_exit_func(chanserv_db_cleanup);
7139 message_register_table(msgtab);