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 new %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_BAD_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 change.modes_set = change.modes_clear = 0;
1243 for(ii=0; ii<bans.used; ii++)
1245 if(!strcmp(bans.list[ii]->ban, bd->mask))
1248 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1249 change.args[0].hostmask = bd->mask;
1250 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1256 del_channel_ban(bd);
1259 static void chanserv_expire_suspension(void *data);
1262 unregister_channel(struct chanData *channel, const char *reason)
1264 char msgbuf[MAXLEN];
1266 /* After channel unregistration, the following must be cleaned
1268 - Channel information.
1271 - Channel suspension data.
1272 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1278 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1280 while(channel->users)
1281 del_channel_user(channel->users, 0);
1283 while(channel->bans)
1284 del_channel_ban(channel->bans);
1286 if(channel->topic) free(channel->topic);
1287 if(channel->registrar) free(channel->registrar);
1288 if(channel->greeting) free(channel->greeting);
1289 if(channel->user_greeting) free(channel->user_greeting);
1290 if(channel->topic_mask) free(channel->topic_mask);
1292 if(channel->prev) channel->prev->next = channel->next;
1293 else channelList = channel->next;
1295 if(channel->next) channel->next->prev = channel->prev;
1297 if(channel->suspended)
1299 struct chanNode *cNode = channel->channel;
1300 struct suspended *suspended, *next_suspended;
1302 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1304 next_suspended = suspended->previous;
1305 free(suspended->suspender);
1306 free(suspended->reason);
1307 if(suspended->expires)
1308 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1313 cNode->channel_info = NULL;
1315 channel->channel->channel_info = NULL;
1318 dict_delete(channel->notes);
1319 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1320 if(!IsSuspended(channel))
1321 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1322 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1323 UnlockChannel(channel->channel);
1325 registered_channels--;
1329 expire_channels(UNUSED_ARG(void *data))
1331 struct chanData *channel, *next;
1332 struct userData *user;
1333 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1335 intervalString(delay, chanserv_conf.channel_expire_delay);
1336 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1338 for(channel = channelList; channel; channel = next)
1340 next = channel->next;
1342 /* See if the channel can be expired. */
1343 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1344 || IsProtected(channel))
1347 /* Make sure there are no high-ranking users still in the channel. */
1348 for(user=channel->users; user; user=user->next)
1349 if(user->present && (user->access >= UL_PRESENT))
1354 /* Unregister the channel */
1355 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1356 unregister_channel(channel, "registration expired.");
1359 if(chanserv_conf.channel_expire_frequency)
1360 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1364 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1366 char protect = channel->chOpts[chProtect];
1367 struct userData *cs_victim, *cs_aggressor;
1369 /* Don't protect if no one is to be protected, someone is attacking
1370 himself, or if the aggressor is an IRC Operator. */
1371 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1374 /* Don't protect if the victim isn't authenticated (because they
1375 can't be a channel user), unless we are to protect non-users
1377 cs_victim = GetChannelAccess(channel, victim->handle_info);
1378 if(protect != 'a' && !cs_victim)
1381 /* Protect if the aggressor isn't a user because at this point,
1382 the aggressor can only be less than or equal to the victim. */
1383 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1387 /* If the aggressor was a user, then the victim can't be helped. */
1394 if(cs_victim->access > cs_aggressor->access)
1399 if(cs_victim->access >= cs_aggressor->access)
1408 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1410 struct chanData *cData = channel->channel_info;
1411 struct userData *cs_victim;
1413 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1414 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1415 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1417 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1425 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1427 if(IsService(victim))
1429 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1433 if(protect_user(victim, user, channel->channel_info))
1435 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1442 static struct do_not_register *
1443 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1445 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1446 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1447 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1448 strcpy(dnr->reason, reason);
1450 if(dnr->chan_name[0] == '*')
1451 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1452 else if(strpbrk(dnr->chan_name, "*?"))
1453 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1455 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1459 static struct dnrList
1460 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1462 struct dnrList list;
1464 struct do_not_register *dnr;
1466 dnrList_init(&list);
1467 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1468 dnrList_append(&list, dnr);
1469 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1470 dnrList_append(&list, dnr);
1472 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1473 if(match_ircglob(chan_name, iter_key(it)))
1474 dnrList_append(&list, iter_data(it));
1479 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1481 struct dnrList list;
1482 struct do_not_register *dnr;
1484 char buf[INTERVALLEN];
1486 list = chanserv_find_dnrs(chan_name, handle);
1487 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1489 dnr = list.list[ii];
1492 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1493 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1496 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1499 reply("CSMSG_MORE_DNRS", list.used - ii);
1504 struct do_not_register *
1505 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1507 struct do_not_register *dnr;
1510 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1514 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1516 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1517 if(match_ircglob(chan_name, iter_key(it)))
1518 return iter_data(it);
1523 static CHANSERV_FUNC(cmd_noregister)
1526 struct do_not_register *dnr;
1527 char buf[INTERVALLEN];
1528 unsigned int matches;
1534 reply("CSMSG_DNR_SEARCH_RESULTS");
1536 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1538 dnr = iter_data(it);
1540 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1542 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1545 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1547 dnr = iter_data(it);
1549 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1551 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1554 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1556 dnr = iter_data(it);
1558 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1560 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1565 reply("MSG_MATCH_COUNT", matches);
1567 reply("MSG_NO_MATCHES");
1573 if(!IsChannelName(target) && (*target != '*'))
1575 reply("CSMSG_NOT_DNR", target);
1581 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1582 if((*target == '*') && !get_handle_info(target + 1))
1584 reply("MSG_HANDLE_UNKNOWN", target + 1);
1587 chanserv_add_dnr(target, user->handle_info->handle, reason);
1588 reply("CSMSG_NOREGISTER_CHANNEL", target);
1592 reply("CSMSG_DNR_SEARCH_RESULTS");
1594 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1596 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1598 reply("MSG_NO_MATCHES");
1602 static CHANSERV_FUNC(cmd_allowregister)
1604 const char *chan_name = argv[1];
1606 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1608 dict_remove(handle_dnrs, chan_name+1);
1609 reply("CSMSG_DNR_REMOVED", chan_name);
1611 else if(dict_find(plain_dnrs, chan_name, NULL))
1613 dict_remove(plain_dnrs, chan_name);
1614 reply("CSMSG_DNR_REMOVED", chan_name);
1616 else if(dict_find(mask_dnrs, chan_name, NULL))
1618 dict_remove(mask_dnrs, chan_name);
1619 reply("CSMSG_DNR_REMOVED", chan_name);
1623 reply("CSMSG_NO_SUCH_DNR", chan_name);
1630 chanserv_get_owned_count(struct handle_info *hi)
1632 struct userData *cList;
1635 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1636 if(cList->access == UL_OWNER)
1641 static CHANSERV_FUNC(cmd_register)
1643 struct mod_chanmode *change;
1644 struct handle_info *handle;
1645 struct chanData *cData;
1646 struct modeNode *mn;
1647 char reason[MAXLEN];
1649 unsigned int new_channel, force=0;
1650 struct do_not_register *dnr;
1654 if(channel->channel_info)
1656 reply("CSMSG_ALREADY_REGGED", channel->name);
1660 if(channel->bad_channel)
1662 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1666 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1668 reply("CSMSG_MUST_BE_OPPED", channel->name);
1673 chan_name = channel->name;
1677 if((argc < 2) || !IsChannelName(argv[1]))
1679 reply("MSG_NOT_CHANNEL_NAME");
1683 if(opserv_bad_channel(argv[1]))
1685 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1690 chan_name = argv[1];
1693 if(argc >= (new_channel+2))
1695 if(!IsHelping(user))
1697 reply("CSMSG_PROXY_FORBIDDEN");
1701 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1703 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1704 dnr = chanserv_is_dnr(chan_name, handle);
1708 handle = user->handle_info;
1709 dnr = chanserv_is_dnr(chan_name, handle);
1713 if(!IsHelping(user))
1714 reply("CSMSG_DNR_CHANNEL", chan_name);
1716 chanserv_show_dnrs(user, cmd, chan_name, handle);
1720 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1722 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1727 channel = AddChannel(argv[1], now, NULL, NULL);
1729 cData = register_channel(channel, user->handle_info->handle);
1730 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1731 cData->modes = chanserv_conf.default_modes;
1732 change = mod_chanmode_dup(&cData->modes, 1);
1733 change->args[change->argc].mode = MODE_CHANOP;
1734 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1736 mod_chanmode_announce(chanserv, channel, change);
1737 mod_chanmode_free(change);
1739 /* Initialize the channel's max user record. */
1740 cData->max = channel->members.used;
1742 if(handle != user->handle_info)
1743 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1745 reply("CSMSG_REG_SUCCESS", channel->name);
1747 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1748 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1753 make_confirmation_string(struct userData *uData)
1755 static char strbuf[16];
1760 for(src = uData->handle->handle; *src; )
1761 accum = accum * 31 + toupper(*src++);
1763 for(src = uData->channel->channel->name; *src; )
1764 accum = accum * 31 + toupper(*src++);
1765 sprintf(strbuf, "%08x", accum);
1769 static CHANSERV_FUNC(cmd_unregister)
1772 char reason[MAXLEN];
1773 struct chanData *cData;
1774 struct userData *uData;
1776 cData = channel->channel_info;
1779 reply("CSMSG_NOT_REGISTERED", channel->name);
1783 uData = GetChannelUser(cData, user->handle_info);
1784 if(!uData || (uData->access < UL_OWNER))
1786 reply("CSMSG_NO_ACCESS");
1790 if(IsProtected(cData))
1792 reply("CSMSG_UNREG_NODELETE", channel->name);
1796 if(!IsHelping(user))
1798 const char *confirm_string;
1799 if(IsSuspended(cData))
1801 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1804 confirm_string = make_confirmation_string(uData);
1805 if((argc < 2) || strcmp(argv[1], confirm_string))
1807 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1812 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1813 name = strdup(channel->name);
1814 unregister_channel(cData, reason);
1815 reply("CSMSG_UNREG_SUCCESS", name);
1820 static CHANSERV_FUNC(cmd_move)
1822 struct chanNode *target;
1823 struct modeNode *mn;
1824 struct userData *uData;
1825 char reason[MAXLEN];
1826 struct do_not_register *dnr;
1830 if(IsProtected(channel->channel_info))
1832 reply("CSMSG_MOVE_NODELETE", channel->name);
1836 if(!IsChannelName(argv[1]))
1838 reply("MSG_NOT_CHANNEL_NAME");
1842 if(opserv_bad_channel(argv[1]))
1844 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1848 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1850 for(uData = channel->channel_info->users; uData; uData = uData->next)
1852 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1854 if(!IsHelping(user))
1855 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1857 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1863 if(!(target = GetChannel(argv[1])))
1865 target = AddChannel(argv[1], now, NULL, NULL);
1866 if(!IsSuspended(channel->channel_info))
1867 AddChannelUser(chanserv, target);
1869 else if(target->channel_info)
1871 reply("CSMSG_ALREADY_REGGED", target->name);
1874 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1875 && !IsHelping(user))
1877 reply("CSMSG_MUST_BE_OPPED", target->name);
1880 else if(!IsSuspended(channel->channel_info))
1882 struct mod_chanmode change;
1883 change.modes_set = change.modes_clear = 0;
1885 change.args[0].mode = MODE_CHANOP;
1886 change.args[0].member = AddChannelUser(chanserv, target);
1887 mod_chanmode_announce(chanserv, target, &change);
1890 /* Move the channel_info to the target channel; it
1891 shouldn't be necessary to clear timeq callbacks
1892 for the old channel. */
1893 target->channel_info = channel->channel_info;
1894 target->channel_info->channel = target;
1895 channel->channel_info = NULL;
1897 reply("CSMSG_MOVE_SUCCESS", target->name);
1899 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1900 if(!IsSuspended(target->channel_info))
1902 char reason2[MAXLEN];
1903 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1904 DelChannelUser(chanserv, channel, reason2, 0);
1906 UnlockChannel(channel);
1907 LockChannel(target);
1908 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1913 merge_users(struct chanData *source, struct chanData *target)
1915 struct userData *suData, *tuData, *next;
1921 /* Insert the source's users into the scratch area. */
1922 for(suData = source->users; suData; suData = suData->next)
1923 dict_insert(merge, suData->handle->handle, suData);
1925 /* Iterate through the target's users, looking for
1926 users common to both channels. The lower access is
1927 removed from either the scratch area or target user
1929 for(tuData = target->users; tuData; tuData = next)
1931 struct userData *choice;
1933 next = tuData->next;
1935 /* If a source user exists with the same handle as a target
1936 channel's user, resolve the conflict by removing one. */
1937 suData = dict_find(merge, tuData->handle->handle, NULL);
1941 /* Pick the data we want to keep. */
1942 /* If the access is the same, use the later seen time. */
1943 if(suData->access == tuData->access)
1944 choice = (suData->seen > tuData->seen) ? suData : tuData;
1945 else /* Otherwise, keep the higher access level. */
1946 choice = (suData->access > tuData->access) ? suData : tuData;
1948 /* Remove the user that wasn't picked. */
1949 if(choice == tuData)
1951 dict_remove(merge, suData->handle->handle);
1952 del_channel_user(suData, 0);
1955 del_channel_user(tuData, 0);
1958 /* Move the remaining users to the target channel. */
1959 for(it = dict_first(merge); it; it = iter_next(it))
1961 suData = iter_data(it);
1963 /* Insert the user into the target channel's linked list. */
1964 suData->prev = NULL;
1965 suData->next = target->users;
1966 suData->channel = target;
1969 target->users->prev = suData;
1970 target->users = suData;
1972 /* Update the user counts for the target channel; the
1973 source counts are left alone. */
1974 target->userCount++;
1977 /* Possible to assert (source->users == NULL) here. */
1978 source->users = NULL;
1983 merge_bans(struct chanData *source, struct chanData *target)
1985 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1987 /* Hold on to the original head of the target ban list
1988 to avoid comparing source bans with source bans. */
1989 tFront = target->bans;
1991 /* Perform a totally expensive O(n*m) merge, ick. */
1992 for(sbData = source->bans; sbData; sbData = sNext)
1994 /* Flag to track whether the ban's been moved
1995 to the destination yet. */
1998 /* Possible to assert (sbData->prev == NULL) here. */
1999 sNext = sbData->next;
2001 for(tbData = tFront; tbData; tbData = tNext)
2003 tNext = tbData->next;
2005 /* Perform two comparisons between each source
2006 and target ban, conflicts are resolved by
2007 keeping the broader ban and copying the later
2008 expiration and triggered time. */
2009 if(match_ircglobs(tbData->mask, sbData->mask))
2011 /* There is a broader ban in the target channel that
2012 overrides one in the source channel; remove the
2013 source ban and break. */
2014 if(sbData->expires > tbData->expires)
2015 tbData->expires = sbData->expires;
2016 if(sbData->triggered > tbData->triggered)
2017 tbData->triggered = sbData->triggered;
2018 del_channel_ban(sbData);
2021 else if(match_ircglobs(sbData->mask, tbData->mask))
2023 /* There is a broader ban in the source channel that
2024 overrides one in the target channel; remove the
2025 target ban, fall through and move the source over. */
2026 if(tbData->expires > sbData->expires)
2027 sbData->expires = tbData->expires;
2028 if(tbData->triggered > sbData->triggered)
2029 sbData->triggered = tbData->triggered;
2030 if(tbData == tFront)
2032 del_channel_ban(tbData);
2035 /* Source bans can override multiple target bans, so
2036 we allow a source to run through this loop multiple
2037 times, but we can only move it once. */
2042 /* Remove the source ban from the source ban list. */
2044 sbData->next->prev = sbData->prev;
2046 /* Modify the source ban's associated channel. */
2047 sbData->channel = target;
2049 /* Insert the ban into the target channel's linked list. */
2050 sbData->prev = NULL;
2051 sbData->next = target->bans;
2054 target->bans->prev = sbData;
2055 target->bans = sbData;
2057 /* Update the user counts for the target channel. */
2062 /* Possible to assert (source->bans == NULL) here. */
2063 source->bans = NULL;
2067 merge_data(struct chanData *source, struct chanData *target)
2069 if(source->visited > target->visited)
2070 target->visited = source->visited;
2074 merge_channel(struct chanData *source, struct chanData *target)
2076 merge_users(source, target);
2077 merge_bans(source, target);
2078 merge_data(source, target);
2081 static CHANSERV_FUNC(cmd_merge)
2083 struct userData *target_user;
2084 struct chanNode *target;
2085 char reason[MAXLEN];
2089 /* Make sure the target channel exists and is registered to the user
2090 performing the command. */
2091 if(!(target = GetChannel(argv[1])))
2093 reply("MSG_INVALID_CHANNEL");
2097 if(!target->channel_info)
2099 reply("CSMSG_NOT_REGISTERED", target->name);
2103 if(IsProtected(channel->channel_info))
2105 reply("CSMSG_MERGE_NODELETE");
2109 if(IsSuspended(target->channel_info))
2111 reply("CSMSG_MERGE_SUSPENDED");
2115 if(channel == target)
2117 reply("CSMSG_MERGE_SELF");
2121 target_user = GetChannelUser(target->channel_info, user->handle_info);
2122 if(!target_user || (target_user->access < UL_OWNER))
2124 reply("CSMSG_MERGE_NOT_OWNER");
2128 /* Merge the channel structures and associated data. */
2129 merge_channel(channel->channel_info, target->channel_info);
2130 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2131 unregister_channel(channel->channel_info, reason);
2132 reply("CSMSG_MERGE_SUCCESS", target->name);
2136 static CHANSERV_FUNC(cmd_opchan)
2138 struct mod_chanmode change;
2139 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2141 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2144 channel->channel_info->may_opchan = 0;
2145 change.modes_set = change.modes_clear = 0;
2147 change.args[0].mode = MODE_CHANOP;
2148 change.args[0].member = GetUserMode(channel, chanserv);
2149 mod_chanmode_announce(chanserv, channel, &change);
2150 reply("CSMSG_OPCHAN_DONE", channel->name);
2154 static CHANSERV_FUNC(cmd_adduser)
2156 struct userData *actee;
2157 struct userData *actor;
2158 struct handle_info *handle;
2159 unsigned short access;
2163 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2165 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2169 access = user_level_from_name(argv[2], UL_OWNER);
2172 reply("CSMSG_INVALID_ACCESS", argv[2]);
2176 actor = GetChannelUser(channel->channel_info, user->handle_info);
2177 if(actor->access <= access)
2179 reply("CSMSG_NO_BUMP_ACCESS");
2183 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2186 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2188 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2192 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2193 scan_user_presence(actee, NULL);
2194 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2198 static CHANSERV_FUNC(cmd_clvl)
2200 struct handle_info *handle;
2201 struct userData *victim;
2202 struct userData *actor;
2203 unsigned short new_access;
2204 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2208 actor = GetChannelUser(channel->channel_info, user->handle_info);
2210 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2213 if(handle == user->handle_info && !privileged)
2215 reply("CSMSG_NO_SELF_CLVL");
2219 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2221 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2225 if(actor->access <= victim->access && !privileged)
2227 reply("MSG_USER_OUTRANKED", handle->handle);
2231 new_access = user_level_from_name(argv[2], UL_OWNER);
2235 reply("CSMSG_INVALID_ACCESS", argv[2]);
2239 if(new_access >= actor->access && !privileged)
2241 reply("CSMSG_NO_BUMP_ACCESS");
2245 victim->access = new_access;
2246 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2250 static CHANSERV_FUNC(cmd_deluser)
2252 struct handle_info *handle;
2253 struct userData *victim;
2254 struct userData *actor;
2255 unsigned short access;
2260 actor = GetChannelUser(channel->channel_info, user->handle_info);
2262 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2265 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2267 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2273 access = user_level_from_name(argv[1], UL_OWNER);
2276 reply("CSMSG_INVALID_ACCESS", argv[1]);
2279 if(access != victim->access)
2281 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2287 access = victim->access;
2290 if((actor->access <= victim->access) && !IsHelping(user))
2292 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2296 chan_name = strdup(channel->name);
2297 del_channel_user(victim, 1);
2298 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2304 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2306 struct userData *actor, *uData, *next;
2308 actor = GetChannelUser(channel->channel_info, user->handle_info);
2310 if(min_access > max_access)
2312 reply("CSMSG_BAD_RANGE", min_access, max_access);
2316 if((actor->access <= max_access) && !IsHelping(user))
2318 reply("CSMSG_NO_ACCESS");
2322 for(uData = channel->channel_info->users; uData; uData = next)
2326 if((uData->access >= min_access)
2327 && (uData->access <= max_access)
2328 && match_ircglob(uData->handle->handle, mask))
2329 del_channel_user(uData, 1);
2332 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2336 static CHANSERV_FUNC(cmd_mdelowner)
2338 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2341 static CHANSERV_FUNC(cmd_mdelcoowner)
2343 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2346 static CHANSERV_FUNC(cmd_mdelmaster)
2348 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2351 static CHANSERV_FUNC(cmd_mdelop)
2353 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2356 static CHANSERV_FUNC(cmd_mdelpeon)
2358 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2362 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2364 struct banData *bData, *next;
2365 char interval[INTERVALLEN];
2370 limit = now - duration;
2371 for(bData = channel->channel_info->bans; bData; bData = next)
2375 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2378 del_channel_ban(bData);
2382 intervalString(interval, duration);
2383 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2388 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2390 struct userData *actor, *uData, *next;
2391 char interval[INTERVALLEN];
2395 actor = GetChannelUser(channel->channel_info, user->handle_info);
2396 if(min_access > max_access)
2398 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2402 if((actor->access <= max_access) && !IsHelping(user))
2404 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2409 limit = now - duration;
2410 for(uData = channel->channel_info->users; uData; uData = next)
2414 if((uData->seen > limit) || uData->present)
2417 if(((uData->access >= min_access) && (uData->access <= max_access))
2418 || (!max_access && (uData->access < actor->access)))
2420 del_channel_user(uData, 1);
2428 max_access = UL_OWNER;
2430 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2434 static CHANSERV_FUNC(cmd_trim)
2436 unsigned long duration;
2437 unsigned short min_level, max_level;
2441 duration = ParseInterval(argv[2]);
2444 reply("CSMSG_CANNOT_TRIM");
2448 if(!irccasecmp(argv[1], "bans"))
2450 cmd_trim_bans(user, channel, duration);
2453 else if(!irccasecmp(argv[1], "users"))
2455 cmd_trim_users(user, channel, 0, 0, duration);
2458 else if(parse_level_range(&min_level, &max_level, argv[1]))
2460 cmd_trim_users(user, channel, min_level, max_level, duration);
2463 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2465 cmd_trim_users(user, channel, min_level, min_level, duration);
2470 reply("CSMSG_INVALID_TRIM", argv[1]);
2475 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2476 to the user. cmd_all takes advantage of this. */
2477 static CHANSERV_FUNC(cmd_up)
2479 struct mod_chanmode change;
2480 struct userData *uData;
2483 change.modes_set = change.modes_clear = 0;
2485 change.args[0].member = GetUserMode(channel, user);
2486 if(!change.args[0].member)
2489 reply("MSG_CHANNEL_ABSENT", channel->name);
2493 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2497 reply("CSMSG_GODMODE_UP", argv[0]);
2500 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2502 change.args[0].mode = MODE_CHANOP;
2503 errmsg = "CSMSG_ALREADY_OPPED";
2507 change.args[0].mode = MODE_VOICE;
2508 errmsg = "CSMSG_ALREADY_VOICED";
2510 change.args[0].mode &= ~change.args[0].member->modes;
2511 if(!change.args[0].mode)
2514 reply(errmsg, channel->name);
2517 modcmd_chanmode_announce(&change);
2521 static CHANSERV_FUNC(cmd_down)
2523 struct mod_chanmode change;
2525 change.modes_set = change.modes_clear = 0;
2527 change.args[0].member = GetUserMode(channel, user);
2528 if(!change.args[0].member)
2531 reply("MSG_CHANNEL_ABSENT", channel->name);
2535 if(!change.args[0].member->modes)
2538 reply("CSMSG_ALREADY_DOWN", channel->name);
2542 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2543 modcmd_chanmode_announce(&change);
2547 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)
2549 struct userData *cList;
2551 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2553 if(IsSuspended(cList->channel)
2554 || IsUserSuspended(cList)
2555 || !GetUserMode(cList->channel->channel, user))
2558 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2564 static CHANSERV_FUNC(cmd_upall)
2566 return cmd_all(CSFUNC_ARGS, cmd_up);
2569 static CHANSERV_FUNC(cmd_downall)
2571 return cmd_all(CSFUNC_ARGS, cmd_down);
2574 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2575 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2578 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)
2580 unsigned int ii, valid;
2581 struct userNode *victim;
2582 struct mod_chanmode *change;
2584 change = mod_chanmode_alloc(argc - 1);
2586 for(ii=valid=0; ++ii < argc; )
2588 if(!(victim = GetUserH(argv[ii])))
2590 change->args[valid].mode = mode;
2591 change->args[valid].member = GetUserMode(channel, victim);
2592 if(!change->args[valid].member)
2594 if(validate && !validate(user, channel, victim))
2599 change->argc = valid;
2600 if(valid < (argc-1))
2601 reply("CSMSG_PROCESS_FAILED");
2604 modcmd_chanmode_announce(change);
2605 reply(action, channel->name);
2607 mod_chanmode_free(change);
2611 static CHANSERV_FUNC(cmd_op)
2613 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2616 static CHANSERV_FUNC(cmd_deop)
2618 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2621 static CHANSERV_FUNC(cmd_voice)
2623 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2626 static CHANSERV_FUNC(cmd_devoice)
2628 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2632 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2638 for(ii=0; ii<channel->members.used; ii++)
2640 struct modeNode *mn = channel->members.list[ii];
2642 if(IsService(mn->user))
2645 if(!user_matches_glob(mn->user, ban, 1))
2648 if(protect_user(mn->user, user, channel->channel_info))
2652 victims[(*victimCount)++] = mn;
2658 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2660 struct userNode *victim;
2661 struct modeNode **victims;
2662 unsigned int offset, n, victimCount, duration = 0;
2663 char *reason = "Bye.", *ban, *name;
2664 char interval[INTERVALLEN];
2666 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2667 REQUIRE_PARAMS(offset);
2670 reason = unsplit_string(argv + offset, argc - offset, NULL);
2671 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2673 /* Truncate the reason to a length of TOPICLEN, as
2674 the ircd does; however, leave room for an ellipsis
2675 and the kicker's nick. */
2676 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2680 if((victim = GetUserH(argv[1])))
2682 victims = alloca(sizeof(victims[0]));
2683 victims[0] = GetUserMode(channel, victim);
2684 /* XXX: The comparison with ACTION_KICK is just because all
2685 * other actions can work on users outside the channel, and we
2686 * want to allow those (e.g. unbans) in that case. If we add
2687 * some other ejection action for in-channel users, change
2689 victimCount = victims[0] ? 1 : 0;
2691 if(IsService(victim))
2693 reply("MSG_SERVICE_IMMUNE", victim->nick);
2697 if((action == ACTION_KICK) && !victimCount)
2699 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2703 if(protect_user(victim, user, channel->channel_info))
2705 reply("CSMSG_USER_PROTECTED", victim->nick);
2709 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2710 name = victim->nick;
2714 if(!is_ircmask(argv[1]))
2716 reply("MSG_NICK_UNKNOWN", argv[1]);
2720 victims = alloca(sizeof(victims[0]) * channel->members.used);
2722 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2724 reply("CSMSG_MASK_PROTECTED", argv[1]);
2728 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2730 reply("CSMSG_LAME_MASK", argv[1]);
2734 if((action == ACTION_KICK) && (victimCount == 0))
2736 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2740 name = ban = strdup(argv[1]);
2743 /* Truncate the ban in place if necessary; we must ensure
2744 that 'ban' is a valid ban mask before sanitizing it. */
2745 sanitize_ircmask(ban);
2747 if(action & ACTION_ADD_BAN)
2749 struct banData *bData, *next;
2751 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2753 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2758 if(action & ACTION_ADD_TIMED_BAN)
2760 duration = ParseInterval(argv[2]);
2764 reply("CSMSG_DURATION_TOO_LOW");
2768 else if(duration > (86400 * 365 * 2))
2770 reply("CSMSG_DURATION_TOO_HIGH");
2776 for(bData = channel->channel_info->bans; bData; bData = next)
2778 if(match_ircglobs(bData->mask, ban))
2780 int exact = !irccasecmp(bData->mask, ban);
2782 /* The ban is redundant; there is already a ban
2783 with the same effect in place. */
2787 free(bData->reason);
2788 bData->reason = strdup(reason);
2789 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2791 reply("CSMSG_REASON_CHANGE", ban);
2795 if(exact && bData->expires)
2799 /* If the ban matches an existing one exactly,
2800 extend the expiration time if the provided
2801 duration is longer. */
2802 if(duration && ((time_t)(now + duration) > bData->expires))
2804 bData->expires = now + duration;
2815 /* Delete the expiration timeq entry and
2816 requeue if necessary. */
2817 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2820 timeq_add(bData->expires, expire_ban, bData);
2824 /* automated kickban */
2827 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2829 reply("CSMSG_BAN_ADDED", name, channel->name);
2835 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2842 if(match_ircglobs(ban, bData->mask))
2844 /* The ban we are adding makes previously existing
2845 bans redundant; silently remove them. */
2846 del_channel_ban(bData);
2850 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);
2852 name = ban = strdup(bData->mask);
2856 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2858 extern const char *hidden_host_suffix;
2859 const char *old_name = chanserv_conf.old_ban_names->list[n];
2861 unsigned int l1, l2;
2864 l2 = strlen(old_name);
2867 if(irccasecmp(ban + l1 - l2, old_name))
2869 new_mask = malloc(MAXLEN);
2870 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2872 name = ban = new_mask;
2877 if(action & ACTION_BAN)
2879 unsigned int exists;
2880 struct mod_chanmode *change;
2882 if(channel->banlist.used >= MAXBANS)
2885 reply("CSMSG_BANLIST_FULL", channel->name);
2890 exists = ChannelBanExists(channel, ban);
2891 change = mod_chanmode_alloc(victimCount + 1);
2892 for(n = 0; n < victimCount; ++n)
2894 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2895 change->args[n].member = victims[n];
2899 change->args[n].mode = MODE_BAN;
2900 change->args[n++].hostmask = ban;
2904 modcmd_chanmode_announce(change);
2906 mod_chanmode_announce(chanserv, channel, change);
2907 mod_chanmode_free(change);
2909 if(exists && (action == ACTION_BAN))
2912 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2918 if(action & ACTION_KICK)
2920 char kick_reason[MAXLEN];
2921 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2923 for(n = 0; n < victimCount; n++)
2924 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2929 /* No response, since it was automated. */
2931 else if(action & ACTION_ADD_BAN)
2934 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2936 reply("CSMSG_BAN_ADDED", name, channel->name);
2938 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2939 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2940 else if(action & ACTION_BAN)
2941 reply("CSMSG_BAN_DONE", name, channel->name);
2942 else if(action & ACTION_KICK && victimCount)
2943 reply("CSMSG_KICK_DONE", name, channel->name);
2949 static CHANSERV_FUNC(cmd_kickban)
2951 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2954 static CHANSERV_FUNC(cmd_kick)
2956 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2959 static CHANSERV_FUNC(cmd_ban)
2961 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2964 static CHANSERV_FUNC(cmd_addban)
2966 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2969 static CHANSERV_FUNC(cmd_addtimedban)
2971 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2974 static struct mod_chanmode *
2975 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2977 struct mod_chanmode *change;
2978 unsigned char *match;
2979 unsigned int ii, count;
2981 match = alloca(bans->used);
2984 for(ii = count = 0; ii < bans->used; ++ii)
2986 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2993 for(ii = count = 0; ii < bans->used; ++ii)
2995 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3002 change = mod_chanmode_alloc(count);
3003 for(ii = count = 0; ii < bans->used; ++ii)
3007 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3008 change->args[count++].hostmask = bans->list[ii]->ban;
3014 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3016 struct userNode *actee;
3022 /* may want to allow a comma delimited list of users... */
3023 if(!(actee = GetUserH(argv[1])))
3025 if(!is_ircmask(argv[1]))
3027 reply("MSG_NICK_UNKNOWN", argv[1]);
3031 mask = strdup(argv[1]);
3034 /* We don't sanitize the mask here because ircu
3036 if(action & ACTION_UNBAN)
3038 struct mod_chanmode *change;
3039 change = find_matching_bans(&channel->banlist, actee, mask);
3042 modcmd_chanmode_announce(change);
3043 mod_chanmode_free(change);
3048 if(action & ACTION_DEL_BAN)
3050 struct banData *ban, *next;
3052 ban = channel->channel_info->bans;
3056 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3059 for( ; ban && !match_ircglobs(mask, ban->mask);
3064 del_channel_ban(ban);
3071 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3073 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3079 static CHANSERV_FUNC(cmd_unban)
3081 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3084 static CHANSERV_FUNC(cmd_delban)
3086 /* it doesn't necessarily have to remove the channel ban - may want
3087 to make that an option. */
3088 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3091 static CHANSERV_FUNC(cmd_unbanme)
3093 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3094 long flags = ACTION_UNBAN;
3096 /* remove permanent bans if the user has the proper access. */
3097 if(uData->access >= UL_MASTER)
3098 flags |= ACTION_DEL_BAN;
3100 argv[1] = user->nick;
3101 return unban_user(user, channel, 2, argv, cmd, flags);
3104 static CHANSERV_FUNC(cmd_unbanall)
3106 struct mod_chanmode *change;
3109 if(!channel->banlist.used)
3111 reply("CSMSG_NO_BANS", channel->name);
3115 change = mod_chanmode_alloc(channel->banlist.used);
3116 for(ii=0; ii<channel->banlist.used; ii++)
3118 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3119 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3121 modcmd_chanmode_announce(change);
3122 mod_chanmode_free(change);
3123 reply("CSMSG_BANS_REMOVED", channel->name);
3127 static CHANSERV_FUNC(cmd_open)
3129 struct mod_chanmode *change;
3131 change = find_matching_bans(&channel->banlist, user, NULL);
3133 change = mod_chanmode_alloc(0);
3134 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3135 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3136 && channel->channel_info->modes.modes_set)
3137 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3138 modcmd_chanmode_announce(change);
3139 reply("CSMSG_CHANNEL_OPENED", channel->name);
3140 mod_chanmode_free(change);
3144 static CHANSERV_FUNC(cmd_myaccess)
3146 struct handle_info *target_handle;
3147 struct userData *uData;
3148 const char *chanName;
3151 target_handle = user->handle_info;
3152 else if(!IsHelping(user))
3154 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3157 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3160 if(!target_handle->channels)
3162 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3166 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3167 for(uData = target_handle->channels; uData; uData = uData->u_next)
3169 struct chanData *cData = uData->channel;
3171 if(uData->access > UL_OWNER)
3173 if(IsProtected(cData)
3174 && (target_handle != user->handle_info)
3175 && !GetTrueChannelAccess(cData, user->handle_info))
3177 chanName = cData->channel->name;
3179 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3181 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3187 static CHANSERV_FUNC(cmd_access)
3189 struct userNode *target;
3190 struct handle_info *target_handle;
3191 struct userData *uData;
3193 char prefix[MAXLEN];
3198 target_handle = target->handle_info;
3200 else if((target = GetUserH(argv[1])))
3202 target_handle = target->handle_info;
3204 else if(argv[1][0] == '*')
3206 if(!(target_handle = get_handle_info(argv[1]+1)))
3208 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3214 reply("MSG_NICK_UNKNOWN", argv[1]);
3218 assert(target || target_handle);
3220 if(target == chanserv)
3222 reply("CSMSG_IS_CHANSERV");
3230 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3235 reply("MSG_USER_AUTHENTICATE", target->nick);
3238 reply("MSG_AUTHENTICATE");
3244 const char *epithet = NULL, *type = NULL;
3247 epithet = chanserv_conf.irc_operator_epithet;
3250 else if(IsNetworkHelper(target))
3252 epithet = chanserv_conf.network_helper_epithet;
3253 type = "network helper";
3255 else if(IsSupportHelper(target))
3257 epithet = chanserv_conf.support_helper_epithet;
3258 type = "support helper";
3262 if(target_handle->epithet)
3263 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3265 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3267 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3271 sprintf(prefix, "%s", target_handle->handle);
3274 if(!channel->channel_info)
3276 reply("CSMSG_NOT_REGISTERED", channel->name);
3280 helping = HANDLE_FLAGGED(target_handle, HELPING)
3281 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3282 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3284 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3285 /* To prevent possible information leaks, only show infolines
3286 * if the requestor is in the channel or it's their own
3288 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3290 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3292 /* Likewise, only say it's suspended if the user has active
3293 * access in that channel or it's their own entry. */
3294 if(IsUserSuspended(uData)
3295 && (GetChannelUser(channel->channel_info, user->handle_info)
3296 || (user->handle_info == uData->handle)))
3298 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3303 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3310 zoot_list(struct listData *list)
3312 struct userData *uData;
3313 unsigned int start, curr, highest, lowest;
3314 struct helpfile_table tmp_table;
3315 const char **temp, *msg;
3317 if(list->table.length == 1)
3320 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3322 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3323 msg = user_find_message(list->user, "MSG_NONE");
3324 send_message_type(4, list->user, list->bot, " %s", msg);
3326 tmp_table.width = list->table.width;
3327 tmp_table.flags = list->table.flags;
3328 list->table.contents[0][0] = " ";
3329 highest = list->highest;
3330 if(list->lowest != 0)
3331 lowest = list->lowest;
3332 else if(highest < 100)
3335 lowest = highest - 100;
3336 for(start = curr = 1; curr < list->table.length; )
3338 uData = list->users[curr-1];
3339 list->table.contents[curr++][0] = " ";
3340 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3343 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3345 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3346 temp = list->table.contents[--start];
3347 list->table.contents[start] = list->table.contents[0];
3348 tmp_table.contents = list->table.contents + start;
3349 tmp_table.length = curr - start;
3350 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3351 list->table.contents[start] = temp;
3353 highest = lowest - 1;
3354 lowest = (highest < 100) ? 0 : (highest - 99);
3360 def_list(struct listData *list)
3364 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3366 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3367 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3368 if(list->table.length == 1)
3370 msg = user_find_message(list->user, "MSG_NONE");
3371 send_message_type(4, list->user, list->bot, " %s", msg);
3376 userData_access_comp(const void *arg_a, const void *arg_b)
3378 const struct userData *a = *(struct userData**)arg_a;
3379 const struct userData *b = *(struct userData**)arg_b;
3381 if(a->access != b->access)
3382 res = b->access - a->access;
3384 res = irccasecmp(a->handle->handle, b->handle->handle);
3389 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3391 void (*send_list)(struct listData *);
3392 struct userData *uData;
3393 struct listData lData;
3394 unsigned int matches;
3398 lData.bot = cmd->parent->bot;
3399 lData.channel = channel;
3400 lData.lowest = lowest;
3401 lData.highest = highest;
3402 lData.search = (argc > 1) ? argv[1] : NULL;
3403 send_list = zoot_list;
3405 if(user->handle_info)
3407 switch(user->handle_info->userlist_style)
3409 case HI_STYLE_DEF: send_list = def_list; break;
3410 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3414 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3416 for(uData = channel->channel_info->users; uData; uData = uData->next)
3418 if((uData->access < lowest)
3419 || (uData->access > highest)
3420 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3422 lData.users[matches++] = uData;
3424 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3426 lData.table.length = matches+1;
3427 lData.table.width = 4;
3428 lData.table.flags = TABLE_NO_FREE;
3429 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3430 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3431 lData.table.contents[0] = ary;
3434 ary[2] = "Last Seen";
3436 for(matches = 1; matches < lData.table.length; ++matches)
3438 struct userData *uData = lData.users[matches-1];
3439 char seen[INTERVALLEN];
3441 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3442 lData.table.contents[matches] = ary;
3443 ary[0] = strtab(uData->access);
3444 ary[1] = uData->handle->handle;
3447 else if(!uData->seen)
3450 ary[2] = intervalString(seen, now - uData->seen);
3451 ary[2] = strdup(ary[2]);
3452 if(IsUserSuspended(uData))
3453 ary[3] = "Suspended";
3454 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3455 ary[3] = "Vacation";
3460 for(matches = 1; matches < lData.table.length; ++matches)
3462 free((char*)lData.table.contents[matches][2]);
3463 free(lData.table.contents[matches]);
3465 free(lData.table.contents[0]);
3466 free(lData.table.contents);
3470 static CHANSERV_FUNC(cmd_users)
3472 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3475 static CHANSERV_FUNC(cmd_wlist)
3477 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3480 static CHANSERV_FUNC(cmd_clist)
3482 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3485 static CHANSERV_FUNC(cmd_mlist)
3487 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3490 static CHANSERV_FUNC(cmd_olist)
3492 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3495 static CHANSERV_FUNC(cmd_plist)
3497 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3500 static CHANSERV_FUNC(cmd_bans)
3502 struct helpfile_table tbl;
3503 unsigned int matches = 0, timed = 0, ii;
3504 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3505 const char *msg_never, *triggered, *expires;
3506 struct banData *ban, **bans;
3513 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3515 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3517 if(search && !match_ircglobs(search, ban->mask))
3519 bans[matches++] = ban;
3524 tbl.length = matches + 1;
3525 tbl.width = 4 + timed;
3527 tbl.flags = TABLE_NO_FREE;
3528 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3529 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3530 tbl.contents[0][0] = "Mask";
3531 tbl.contents[0][1] = "Set By";
3532 tbl.contents[0][2] = "Triggered";
3535 tbl.contents[0][3] = "Expires";
3536 tbl.contents[0][4] = "Reason";
3539 tbl.contents[0][3] = "Reason";
3542 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3547 msg_never = user_find_message(user, "MSG_NEVER");
3548 for(ii = 0; ii < matches; )
3554 else if(ban->expires)
3555 expires = intervalString(e_buffer, ban->expires - now);
3557 expires = msg_never;
3560 triggered = intervalString(t_buffer, now - ban->triggered);
3562 triggered = msg_never;
3564 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3565 tbl.contents[ii][0] = ban->mask;
3566 tbl.contents[ii][1] = ban->owner;
3567 tbl.contents[ii][2] = strdup(triggered);
3570 tbl.contents[ii][3] = strdup(expires);
3571 tbl.contents[ii][4] = ban->reason;
3574 tbl.contents[ii][3] = ban->reason;
3576 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3577 reply("MSG_MATCH_COUNT", matches);
3578 for(ii = 1; ii < tbl.length; ++ii)
3580 free((char*)tbl.contents[ii][2]);
3582 free((char*)tbl.contents[ii][3]);
3583 free(tbl.contents[ii]);
3585 free(tbl.contents[0]);
3591 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3593 struct chanData *cData = channel->channel_info;
3594 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3596 if(cData->topic_mask)
3597 return !match_ircglob(new_topic, cData->topic_mask);
3598 else if(cData->topic)
3599 return irccasecmp(new_topic, cData->topic);
3604 static CHANSERV_FUNC(cmd_topic)
3606 struct chanData *cData;
3609 cData = channel->channel_info;
3614 SetChannelTopic(channel, chanserv, cData->topic, 1);
3615 reply("CSMSG_TOPIC_SET", cData->topic);
3619 reply("CSMSG_NO_TOPIC", channel->name);
3623 topic = unsplit_string(argv + 1, argc - 1, NULL);
3624 /* If they say "!topic *", use an empty topic. */
3625 if((topic[0] == '*') && (topic[1] == 0))
3627 if(bad_topic(channel, user, topic))
3629 char *topic_mask = cData->topic_mask;
3632 char new_topic[TOPICLEN+1], tchar;
3633 int pos=0, starpos=-1, dpos=0, len;
3635 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3642 len = strlen(topic);
3643 if((dpos + len) > TOPICLEN)
3644 len = TOPICLEN + 1 - dpos;
3645 memcpy(new_topic+dpos, topic, len);
3649 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3650 default: new_topic[dpos++] = tchar; break;
3653 if((dpos > TOPICLEN) || tchar)
3656 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3657 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3660 new_topic[dpos] = 0;
3661 SetChannelTopic(channel, chanserv, new_topic, 1);
3663 reply("CSMSG_TOPIC_LOCKED", channel->name);
3668 SetChannelTopic(channel, chanserv, topic, 1);
3670 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3672 /* Grab the topic and save it as the default topic. */
3674 cData->topic = strdup(channel->topic);
3680 static CHANSERV_FUNC(cmd_mode)
3682 struct mod_chanmode *change;
3686 change = &channel->channel_info->modes;
3687 if(change->modes_set || change->modes_clear) {
3688 modcmd_chanmode_announce(change);
3689 reply("CSMSG_DEFAULTED_MODES", channel->name);
3691 reply("CSMSG_NO_MODES", channel->name);
3695 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3698 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3702 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3703 && mode_lock_violated(&channel->channel_info->modes, change))
3706 mod_chanmode_format(&channel->channel_info->modes, modes);
3707 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3711 modcmd_chanmode_announce(change);
3712 mod_chanmode_free(change);
3713 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3717 static CHANSERV_FUNC(cmd_invite)
3719 struct userData *uData;
3720 struct userNode *invite;
3722 uData = GetChannelUser(channel->channel_info, user->handle_info);
3726 if(!(invite = GetUserH(argv[1])))
3728 reply("MSG_NICK_UNKNOWN", argv[1]);
3735 if(GetUserMode(channel, invite))
3737 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3743 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3744 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3746 irc_invite(chanserv, invite, channel);
3748 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3753 static CHANSERV_FUNC(cmd_inviteme)
3755 if(GetUserMode(channel, user))
3757 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3760 if(channel->channel_info
3761 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3763 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3766 irc_invite(cmd->parent->bot, user, channel);
3771 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3774 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3776 /* We display things based on two dimensions:
3777 * - Issue time: present or absent
3778 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3779 * (in order of precedence, so something both expired and revoked
3780 * only counts as revoked)
3782 combo = (suspended->issued ? 4 : 0)
3783 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3785 case 0: /* no issue time, indefinite expiration */
3786 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3788 case 1: /* no issue time, expires in future */
3789 intervalString(buf1, suspended->expires-now);
3790 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3792 case 2: /* no issue time, expired */
3793 intervalString(buf1, now-suspended->expires);
3794 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3796 case 3: /* no issue time, revoked */
3797 intervalString(buf1, now-suspended->revoked);
3798 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3800 case 4: /* issue time set, indefinite expiration */
3801 intervalString(buf1, now-suspended->issued);
3802 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3804 case 5: /* issue time set, expires in future */
3805 intervalString(buf1, now-suspended->issued);
3806 intervalString(buf2, suspended->expires-now);
3807 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3809 case 6: /* issue time set, expired */
3810 intervalString(buf1, now-suspended->issued);
3811 intervalString(buf2, now-suspended->expires);
3812 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3814 case 7: /* issue time set, revoked */
3815 intervalString(buf1, now-suspended->issued);
3816 intervalString(buf2, now-suspended->revoked);
3817 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3820 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3825 static CHANSERV_FUNC(cmd_info)
3827 char modes[MAXLEN], buffer[INTERVALLEN];
3828 struct userData *uData, *owner;
3829 struct chanData *cData;
3830 struct do_not_register *dnr;
3835 cData = channel->channel_info;
3836 reply("CSMSG_CHANNEL_INFO", channel->name);
3838 uData = GetChannelUser(cData, user->handle_info);
3839 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3841 mod_chanmode_format(&cData->modes, modes);
3842 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3843 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3846 for(it = dict_first(cData->notes); it; it = iter_next(it))
3850 note = iter_data(it);
3851 if(!note_type_visible_to_user(cData, note->type, user))
3854 padding = PADLEN - 1 - strlen(iter_key(it));
3855 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3858 reply("CSMSG_CHANNEL_MAX", cData->max);
3859 for(owner = cData->users; owner; owner = owner->next)
3860 if(owner->access == UL_OWNER)
3861 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3862 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3863 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3864 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3865 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3867 privileged = IsStaff(user);
3868 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3869 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3871 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3872 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3874 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3876 struct suspended *suspended;
3877 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3878 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3879 show_suspension_info(cmd, user, suspended);
3881 else if(IsSuspended(cData))
3883 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3884 show_suspension_info(cmd, user, cData->suspended);
3889 static CHANSERV_FUNC(cmd_netinfo)
3891 extern time_t boot_time;
3892 extern unsigned long burst_length;
3893 char interval[INTERVALLEN];
3895 reply("CSMSG_NETWORK_INFO");
3896 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3897 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3898 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3899 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3900 reply("CSMSG_NETWORK_BANS", banCount);
3901 reply("CSMSG_CHANNEL_USERS", userCount);
3902 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3903 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3908 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3910 struct helpfile_table table;
3912 struct userNode *user;
3917 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3918 table.contents = alloca(list->used*sizeof(*table.contents));
3919 for(nn=0; nn<list->used; nn++)
3921 user = list->list[nn];
3922 if(user->modes & skip_flags)
3926 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3929 nick = alloca(strlen(user->nick)+3);
3930 sprintf(nick, "(%s)", user->nick);
3934 table.contents[table.length][0] = nick;
3937 table_send(chanserv, to->nick, 0, NULL, table);
3940 static CHANSERV_FUNC(cmd_ircops)
3942 reply("CSMSG_STAFF_OPERS");
3943 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3947 static CHANSERV_FUNC(cmd_helpers)
3949 reply("CSMSG_STAFF_HELPERS");
3950 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3954 static CHANSERV_FUNC(cmd_staff)
3956 reply("CSMSG_NETWORK_STAFF");
3957 cmd_ircops(CSFUNC_ARGS);
3958 cmd_helpers(CSFUNC_ARGS);
3962 static CHANSERV_FUNC(cmd_peek)
3964 struct modeNode *mn;
3965 char modes[MODELEN];
3967 struct helpfile_table table;
3969 irc_make_chanmode(channel, modes);
3971 reply("CSMSG_PEEK_INFO", channel->name);
3972 reply("CSMSG_PEEK_TOPIC", channel->topic);
3973 reply("CSMSG_PEEK_MODES", modes);
3974 reply("CSMSG_PEEK_USERS", channel->members.used);
3978 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3979 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3980 for(n = 0; n < channel->members.used; n++)
3982 mn = channel->members.list[n];
3983 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3985 table.contents[table.length] = alloca(sizeof(**table.contents));
3986 table.contents[table.length][0] = mn->user->nick;
3991 reply("CSMSG_PEEK_OPS");
3992 table_send(chanserv, user->nick, 0, NULL, table);
3995 reply("CSMSG_PEEK_NO_OPS");
3999 static MODCMD_FUNC(cmd_wipeinfo)
4001 struct handle_info *victim;
4002 struct userData *ud, *actor;
4005 actor = GetChannelUser(channel->channel_info, user->handle_info);
4006 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4008 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4010 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4013 if((ud->access >= actor->access) && (ud != actor))
4015 reply("MSG_USER_OUTRANKED", victim->handle);
4021 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4025 static CHANSERV_FUNC(cmd_resync)
4027 struct mod_chanmode *changes;
4028 struct chanData *cData = channel->channel_info;
4029 unsigned int ii, used;
4031 changes = mod_chanmode_alloc(channel->members.used * 2);
4032 for(ii = used = 0; ii < channel->members.used; ++ii)
4034 struct modeNode *mn = channel->members.list[ii];
4035 struct userData *uData;
4036 if(IsService(mn->user))
4038 /* must not change modes for this user */
4040 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4044 changes->args[used].mode = MODE_REMOVE | mn->modes;
4045 changes->args[used++].member = mn;
4048 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4050 if(mn->modes & MODE_CHANOP)
4052 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4053 changes->args[used++].member = mn;
4055 if(!(mn->modes & MODE_VOICE))
4057 changes->args[used].mode = MODE_VOICE;
4058 changes->args[used++].member = mn;
4063 if(!(mn->modes & MODE_CHANOP))
4065 changes->args[used].mode = MODE_CHANOP;
4066 changes->args[used++].member = mn;
4070 changes->argc = used;
4071 modcmd_chanmode_announce(changes);
4072 mod_chanmode_free(changes);
4073 reply("CSMSG_RESYNCED_USERS", channel->name);
4077 static CHANSERV_FUNC(cmd_seen)
4079 struct userData *uData;
4080 struct handle_info *handle;
4081 char seen[INTERVALLEN];
4085 if(!irccasecmp(argv[1], chanserv->nick))
4087 reply("CSMSG_IS_CHANSERV");
4091 if(!(handle = get_handle_info(argv[1])))
4093 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4097 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4099 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4104 reply("CSMSG_USER_PRESENT", handle->handle);
4105 else if(uData->seen)
4106 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4108 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4110 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4111 reply("CSMSG_USER_VACATION", handle->handle);
4116 static MODCMD_FUNC(cmd_names)
4118 struct userNode *targ;
4119 struct userData *targData;
4120 unsigned int ii, pos;
4123 for(ii=pos=0; ii<channel->members.used; ++ii)
4125 targ = channel->members.list[ii]->user;
4126 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4129 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4132 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4136 if(IsUserSuspended(targData))
4138 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4141 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4142 reply("CSMSG_END_NAMES", channel->name);
4147 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4149 switch(ntype->visible_type)
4151 case NOTE_VIS_ALL: return 1;
4152 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4153 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4158 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4160 struct userData *uData;
4162 switch(ntype->set_access_type)
4164 case NOTE_SET_CHANNEL_ACCESS:
4165 if(!user->handle_info)
4167 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4169 return uData->access >= ntype->set_access.min_ulevel;
4170 case NOTE_SET_CHANNEL_SETTER:
4171 return check_user_level(channel, user, lvlSetters, 1, 0);
4172 case NOTE_SET_PRIVILEGED: default:
4173 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4177 static CHANSERV_FUNC(cmd_note)
4179 struct chanData *cData;
4181 struct note_type *ntype;
4183 cData = channel->channel_info;
4186 reply("CSMSG_NOT_REGISTERED", channel->name);
4190 /* If no arguments, show all visible notes for the channel. */
4196 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4198 note = iter_data(it);
4199 if(!note_type_visible_to_user(cData, note->type, user))
4202 reply("CSMSG_NOTELIST_HEADER", channel->name);
4203 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4206 reply("CSMSG_NOTELIST_END", channel->name);
4208 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4210 /* If one argument, show the named note. */
4213 if((note = dict_find(cData->notes, argv[1], NULL))
4214 && note_type_visible_to_user(cData, note->type, user))
4216 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4218 else if((ntype = dict_find(note_types, argv[1], NULL))
4219 && note_type_visible_to_user(NULL, ntype, user))
4221 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4226 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4230 /* Assume they're trying to set a note. */
4234 ntype = dict_find(note_types, argv[1], NULL);
4237 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4240 else if(note_type_settable_by_user(channel, ntype, user))
4242 note_text = unsplit_string(argv+2, argc-2, NULL);
4243 if((note = dict_find(cData->notes, argv[1], NULL)))
4244 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4245 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4246 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4248 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4250 /* The note is viewable to staff only, so return 0
4251 to keep the invocation from getting logged (or
4252 regular users can see it in !events). */
4258 reply("CSMSG_NO_ACCESS");
4265 static CHANSERV_FUNC(cmd_delnote)
4270 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4271 || !note_type_settable_by_user(channel, note->type, user))
4273 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4276 dict_remove(channel->channel_info->notes, note->type->name);
4277 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4281 static CHANSERV_FUNC(cmd_events)
4283 struct logSearch discrim;
4284 struct logReport report;
4285 unsigned int matches, limit;
4287 limit = (argc > 1) ? atoi(argv[1]) : 10;
4288 if(limit < 1 || limit > 200) limit = 10;
4290 memset(&discrim, 0, sizeof(discrim));
4291 discrim.masks.bot = chanserv;
4292 discrim.masks.channel_name = channel->name;
4293 if(argc > 2) discrim.masks.command = argv[2];
4294 discrim.limit = limit;
4295 discrim.max_time = INT_MAX;
4296 discrim.severities = 1 << LOG_COMMAND;
4297 report.reporter = chanserv;
4299 reply("CSMSG_EVENT_SEARCH_RESULTS");
4300 matches = log_entry_search(&discrim, log_report_entry, &report);
4302 reply("MSG_MATCH_COUNT", matches);
4304 reply("MSG_NO_MATCHES");
4308 static CHANSERV_FUNC(cmd_say)
4314 msg = unsplit_string(argv + 1, argc - 1, NULL);
4315 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4317 else if(GetUserH(argv[1]))
4320 msg = unsplit_string(argv + 2, argc - 2, NULL);
4321 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4325 reply("You must specify the name of a channel or user.");
4331 static CHANSERV_FUNC(cmd_emote)
4337 /* CTCP is so annoying. */
4338 msg = unsplit_string(argv + 1, argc - 1, NULL);
4339 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4341 else if(GetUserH(argv[1]))
4343 msg = unsplit_string(argv + 2, argc - 2, NULL);
4344 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4348 reply("You must specify the name of a channel or user.");
4354 struct channelList *
4355 chanserv_support_channels(void)
4357 return &chanserv_conf.support_channels;
4360 static CHANSERV_FUNC(cmd_expire)
4362 int channel_count = registered_channels;
4363 expire_channels(NULL);
4364 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4369 chanserv_expire_suspension(void *data)
4371 struct suspended *suspended = data;
4372 struct chanNode *channel;
4373 struct mod_chanmode change;
4375 if(!suspended->expires || (now < suspended->expires))
4376 suspended->revoked = now;
4377 channel = suspended->cData->channel;
4378 suspended->cData->channel = channel;
4379 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4380 change.modes_set = change.modes_clear = 0;
4382 change.args[0].mode = MODE_CHANOP;
4383 change.args[0].member = AddChannelUser(chanserv, channel);
4384 mod_chanmode_announce(chanserv, channel, &change);
4387 static CHANSERV_FUNC(cmd_csuspend)
4389 struct suspended *suspended;
4390 char reason[MAXLEN];
4391 time_t expiry, duration;
4392 struct userData *uData;
4396 if(IsProtected(channel->channel_info))
4398 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4402 if(argv[1][0] == '!')
4404 else if(IsSuspended(channel->channel_info))
4406 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4407 show_suspension_info(cmd, user, channel->channel_info->suspended);
4411 if(!strcmp(argv[1], "0"))
4413 else if((duration = ParseInterval(argv[1])))
4414 expiry = now + duration;
4417 reply("MSG_INVALID_DURATION", argv[1]);
4421 unsplit_string(argv + 2, argc - 2, reason);
4423 suspended = calloc(1, sizeof(*suspended));
4424 suspended->revoked = 0;
4425 suspended->issued = now;
4426 suspended->suspender = strdup(user->handle_info->handle);
4427 suspended->expires = expiry;
4428 suspended->reason = strdup(reason);
4429 suspended->cData = channel->channel_info;
4430 suspended->previous = suspended->cData->suspended;
4431 suspended->cData->suspended = suspended;
4433 if(suspended->expires)
4434 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4436 if(IsSuspended(channel->channel_info))
4438 suspended->previous->revoked = now;
4439 if(suspended->previous->expires)
4440 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4441 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4442 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4446 /* Mark all users in channel as absent. */
4447 for(uData = channel->channel_info->users; uData; uData = uData->next)
4456 /* Mark the channel as suspended, then part. */
4457 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4458 DelChannelUser(chanserv, channel, suspended->reason, 0);
4459 reply("CSMSG_SUSPENDED", channel->name);
4460 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4461 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4466 static CHANSERV_FUNC(cmd_cunsuspend)
4468 struct suspended *suspended;
4469 char message[MAXLEN];
4471 if(!IsSuspended(channel->channel_info))
4473 reply("CSMSG_NOT_SUSPENDED", channel->name);
4477 suspended = channel->channel_info->suspended;
4479 /* Expire the suspension and join ChanServ to the channel. */
4480 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4481 chanserv_expire_suspension(suspended);
4482 reply("CSMSG_UNSUSPENDED", channel->name);
4483 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4484 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4488 typedef struct chanservSearch
4496 unsigned long flags;
4500 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4503 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4508 search = malloc(sizeof(struct chanservSearch));
4509 memset(search, 0, sizeof(*search));
4512 for(i = 0; i < argc; i++)
4514 /* Assume all criteria require arguments. */
4517 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4521 if(!irccasecmp(argv[i], "name"))
4522 search->name = argv[++i];
4523 else if(!irccasecmp(argv[i], "registrar"))
4524 search->registrar = argv[++i];
4525 else if(!irccasecmp(argv[i], "unvisited"))
4526 search->unvisited = ParseInterval(argv[++i]);
4527 else if(!irccasecmp(argv[i], "registered"))
4528 search->registered = ParseInterval(argv[++i]);
4529 else if(!irccasecmp(argv[i], "flags"))
4532 if(!irccasecmp(argv[i], "nodelete"))
4533 search->flags |= CHANNEL_NODELETE;
4534 else if(!irccasecmp(argv[i], "suspended"))
4535 search->flags |= CHANNEL_SUSPENDED;
4538 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4542 else if(!irccasecmp(argv[i], "limit"))
4543 search->limit = strtoul(argv[++i], NULL, 10);
4546 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4551 if(search->name && !strcmp(search->name, "*"))
4553 if(search->registrar && !strcmp(search->registrar, "*"))
4554 search->registrar = 0;
4563 chanserv_channel_match(struct chanData *channel, search_t search)
4565 const char *name = channel->channel->name;
4566 if((search->name && !match_ircglob(name, search->name)) ||
4567 (search->registrar && !channel->registrar) ||
4568 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4569 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4570 (search->registered && (now - channel->registered) > search->registered) ||
4571 (search->flags && ((search->flags & channel->flags) != search->flags)))
4578 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4580 struct chanData *channel;
4581 unsigned int matches = 0;
4583 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4585 if(!chanserv_channel_match(channel, search))
4595 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4600 search_print(struct chanData *channel, void *data)
4602 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4605 static CHANSERV_FUNC(cmd_search)
4608 unsigned int matches;
4609 channel_search_func action;
4613 if(!irccasecmp(argv[1], "count"))
4614 action = search_count;
4615 else if(!irccasecmp(argv[1], "print"))
4616 action = search_print;
4619 reply("CSMSG_ACTION_INVALID", argv[1]);
4623 search = chanserv_search_create(user, argc - 2, argv + 2);
4627 if(action == search_count)
4628 search->limit = INT_MAX;
4630 if(action == search_print)
4631 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4633 matches = chanserv_channel_search(search, action, user);
4636 reply("MSG_MATCH_COUNT", matches);
4638 reply("MSG_NO_MATCHES");
4644 static CHANSERV_FUNC(cmd_unvisited)
4646 struct chanData *cData;
4647 time_t interval = chanserv_conf.channel_expire_delay;
4648 char buffer[INTERVALLEN];
4649 unsigned int limit = 25, matches = 0;
4653 interval = ParseInterval(argv[1]);
4655 limit = atoi(argv[2]);
4658 intervalString(buffer, interval);
4659 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4661 for(cData = channelList; cData && matches < limit; cData = cData->next)
4663 if((now - cData->visited) < interval)
4666 intervalString(buffer, now - cData->visited);
4667 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4674 static MODCMD_FUNC(chan_opt_defaulttopic)
4680 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4682 reply("CSMSG_TOPIC_LOCKED", channel->name);
4686 topic = unsplit_string(argv+1, argc-1, NULL);
4688 free(channel->channel_info->topic);
4689 if(topic[0] == '*' && topic[1] == 0)
4691 topic = channel->channel_info->topic = NULL;
4695 topic = channel->channel_info->topic = strdup(topic);
4696 if(channel->channel_info->topic_mask
4697 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4698 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4700 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4703 if(channel->channel_info->topic)
4704 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4706 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4710 static MODCMD_FUNC(chan_opt_topicmask)
4714 struct chanData *cData = channel->channel_info;
4717 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4719 reply("CSMSG_TOPIC_LOCKED", channel->name);
4723 mask = unsplit_string(argv+1, argc-1, NULL);
4725 if(cData->topic_mask)
4726 free(cData->topic_mask);
4727 if(mask[0] == '*' && mask[1] == 0)
4729 cData->topic_mask = 0;
4733 cData->topic_mask = strdup(mask);
4735 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4736 else if(!match_ircglob(cData->topic, cData->topic_mask))
4737 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4741 if(channel->channel_info->topic_mask)
4742 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4744 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4748 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4752 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4756 if(greeting[0] == '*' && greeting[1] == 0)
4760 unsigned int length = strlen(greeting);
4761 if(length > chanserv_conf.greeting_length)
4763 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4766 *data = strdup(greeting);
4775 reply(name, user_find_message(user, "MSG_NONE"));
4779 static MODCMD_FUNC(chan_opt_greeting)
4781 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4784 static MODCMD_FUNC(chan_opt_usergreeting)
4786 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4789 static MODCMD_FUNC(chan_opt_modes)
4791 struct mod_chanmode *new_modes;
4792 char modes[MODELEN];
4796 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4798 reply("CSMSG_NO_ACCESS");
4801 if(argv[1][0] == '*' && argv[1][1] == 0)
4803 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4805 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4807 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4810 else if(new_modes->argc > 1)
4812 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4813 mod_chanmode_free(new_modes);
4818 channel->channel_info->modes = *new_modes;
4819 modcmd_chanmode_announce(new_modes);
4820 mod_chanmode_free(new_modes);
4824 mod_chanmode_format(&channel->channel_info->modes, modes);
4826 reply("CSMSG_SET_MODES", modes);
4828 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4832 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4834 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4836 struct chanData *cData = channel->channel_info;
4841 /* Set flag according to value. */
4842 if(enabled_string(argv[1]))
4844 cData->flags |= mask;
4847 else if(disabled_string(argv[1]))
4849 cData->flags &= ~mask;
4854 reply("MSG_INVALID_BINARY", argv[1]);
4860 /* Find current option value. */
4861 value = (cData->flags & mask) ? 1 : 0;
4865 reply(name, user_find_message(user, "MSG_ON"));
4867 reply(name, user_find_message(user, "MSG_OFF"));
4871 static MODCMD_FUNC(chan_opt_nodelete)
4873 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4875 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4879 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4882 static MODCMD_FUNC(chan_opt_dynlimit)
4884 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4887 /* TODO: reimplement
4889 static MODCMD_FUNC(chan_opt_userinfo)
4891 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4894 static MODCMD_FUNC(chan_opt_voice)
4896 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4899 static MODCMD_FUNC(chan_opt_topicsnarf)
4901 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4903 reply("CSMSG_TOPIC_LOCKED", channel->name);
4906 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4909 static MODCMD_FUNC(chan_opt_peoninvite)
4911 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4916 static MODCMD_FUNC(chan_opt_defaults)
4918 struct userData *uData;
4919 struct chanData *cData;
4920 const char *confirm;
4921 enum levelOption lvlOpt;
4922 enum charOption chOpt;
4924 cData = channel->channel_info;
4925 uData = GetChannelUser(cData, user->handle_info);
4926 if(!uData || (uData->access < UL_OWNER))
4928 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4931 confirm = make_confirmation_string(uData);
4932 if((argc < 2) || strcmp(argv[1], confirm))
4934 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4937 cData->flags = CHANNEL_DEFAULT_FLAGS;
4938 cData->modes = chanserv_conf.default_modes;
4939 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4940 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4941 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4942 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4943 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4948 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4950 struct chanData *cData = channel->channel_info;
4951 struct userData *uData;
4952 unsigned short value;
4956 if(!check_user_level(channel, user, option, 1, 1))
4958 reply("CSMSG_CANNOT_SET");
4961 value = user_level_from_name(argv[1], UL_OWNER+1);
4962 if(!value && strcmp(argv[1], "0"))
4964 reply("CSMSG_INVALID_ACCESS", argv[1]);
4967 uData = GetChannelUser(cData, user->handle_info);
4968 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4970 reply("CSMSG_BAD_SETLEVEL");
4976 if(value > cData->lvlOpts[lvlGiveOps])
4978 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4983 if(value < cData->lvlOpts[lvlGiveVoice])
4985 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4990 /* This test only applies to owners, since non-owners
4991 * trying to set an option to above their level get caught
4992 * by the CSMSG_BAD_SETLEVEL test above.
4994 if(value > uData->access)
4996 reply("CSMSG_BAD_SETTERS");
5003 cData->lvlOpts[option] = value;
5005 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5009 static MODCMD_FUNC(chan_opt_enfops)
5011 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5014 static MODCMD_FUNC(chan_opt_giveops)
5016 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5019 static MODCMD_FUNC(chan_opt_enfmodes)
5021 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5024 static MODCMD_FUNC(chan_opt_enftopic)
5026 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5029 static MODCMD_FUNC(chan_opt_pubcmd)
5031 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5034 static MODCMD_FUNC(chan_opt_setters)
5036 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5039 static MODCMD_FUNC(chan_opt_ctcpusers)
5041 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5044 static MODCMD_FUNC(chan_opt_userinfo)
5046 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5049 static MODCMD_FUNC(chan_opt_givevoice)
5051 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5054 static MODCMD_FUNC(chan_opt_topicsnarf)
5056 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5059 static MODCMD_FUNC(chan_opt_inviteme)
5061 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5065 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5067 struct chanData *cData = channel->channel_info;
5068 int count = charOptions[option].count, index;
5072 index = atoi(argv[1]);
5074 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5076 reply("CSMSG_INVALID_NUMERIC", index);
5077 /* Show possible values. */
5078 for(index = 0; index < count; index++)
5079 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5083 cData->chOpts[option] = charOptions[option].values[index].value;
5087 /* Find current option value. */
5090 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5094 /* Somehow, the option value is corrupt; reset it to the default. */
5095 cData->chOpts[option] = charOptions[option].default_value;
5100 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5104 static MODCMD_FUNC(chan_opt_protect)
5106 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5109 static MODCMD_FUNC(chan_opt_toys)
5111 return channel_multiple_option(chToys, CSFUNC_ARGS);
5114 static MODCMD_FUNC(chan_opt_ctcpreaction)
5116 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5119 static MODCMD_FUNC(chan_opt_topicrefresh)
5121 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5124 static struct svccmd_list set_shows_list;
5127 handle_svccmd_unbind(struct svccmd *target) {
5129 for(ii=0; ii<set_shows_list.used; ++ii)
5130 if(target == set_shows_list.list[ii])
5131 set_shows_list.used = 0;
5134 static CHANSERV_FUNC(cmd_set)
5136 struct svccmd *subcmd;
5140 /* Check if we need to (re-)initialize set_shows_list. */
5141 if(!set_shows_list.used)
5143 if(!set_shows_list.size)
5145 set_shows_list.size = chanserv_conf.set_shows->used;
5146 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5148 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5150 const char *name = chanserv_conf.set_shows->list[ii];
5151 sprintf(buf, "%s %s", argv[0], name);
5152 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5155 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5158 svccmd_list_append(&set_shows_list, subcmd);
5164 reply("CSMSG_CHANNEL_OPTIONS");
5165 for(ii = 0; ii < set_shows_list.used; ii++)
5167 subcmd = set_shows_list.list[ii];
5168 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5173 sprintf(buf, "%s %s", argv[0], argv[1]);
5174 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5177 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5180 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5182 reply("CSMSG_NO_ACCESS");
5186 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5190 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5192 struct userData *uData;
5194 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5197 reply("CSMSG_NOT_USER", channel->name);
5203 /* Just show current option value. */
5205 else if(enabled_string(argv[1]))
5207 uData->flags |= mask;
5209 else if(disabled_string(argv[1]))
5211 uData->flags &= ~mask;
5215 reply("MSG_INVALID_BINARY", argv[1]);
5219 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5223 static MODCMD_FUNC(user_opt_noautoop)
5225 struct userData *uData;
5227 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5230 reply("CSMSG_NOT_USER", channel->name);
5233 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5234 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5236 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5239 static MODCMD_FUNC(user_opt_autoinvite)
5241 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5244 static MODCMD_FUNC(user_opt_info)
5246 struct userData *uData;
5249 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5253 /* If they got past the command restrictions (which require access)
5254 * but fail this test, we have some fool with security override on.
5256 reply("CSMSG_NOT_USER", channel->name);
5262 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5265 if(infoline[0] == '*' && infoline[1] == 0)
5268 uData->info = strdup(infoline);
5271 reply("CSMSG_USET_INFO", uData->info);
5273 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5277 struct svccmd_list uset_shows_list;
5279 static CHANSERV_FUNC(cmd_uset)
5281 struct svccmd *subcmd;
5285 /* Check if we need to (re-)initialize uset_shows_list. */
5286 if(!uset_shows_list.used)
5290 "NoAutoOp", "AutoInvite", "Info"
5293 if(!uset_shows_list.size)
5295 uset_shows_list.size = ArrayLength(options);
5296 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5298 for(ii = 0; ii < ArrayLength(options); ii++)
5300 const char *name = options[ii];
5301 sprintf(buf, "%s %s", argv[0], name);
5302 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5305 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5308 svccmd_list_append(&uset_shows_list, subcmd);
5314 /* Do this so options are presented in a consistent order. */
5315 reply("CSMSG_USER_OPTIONS");
5316 for(ii = 0; ii < uset_shows_list.used; ii++)
5317 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5321 sprintf(buf, "%s %s", argv[0], argv[1]);
5322 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5325 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5329 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5332 static CHANSERV_FUNC(cmd_giveownership)
5334 struct handle_info *new_owner_hi;
5335 struct userData *new_owner, *curr_user;
5336 struct chanData *cData = channel->channel_info;
5337 struct do_not_register *dnr;
5339 unsigned short co_access;
5340 char reason[MAXLEN];
5343 curr_user = GetChannelAccess(cData, user->handle_info);
5344 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5345 if(!curr_user || (curr_user->access != UL_OWNER))
5347 struct userData *owner = NULL;
5348 for(curr_user = channel->channel_info->users;
5350 curr_user = curr_user->next)
5352 if(curr_user->access != UL_OWNER)
5356 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5363 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5365 if(new_owner_hi == user->handle_info)
5367 reply("CSMSG_NO_TRANSFER_SELF");
5370 new_owner = GetChannelAccess(cData, new_owner_hi);
5373 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5376 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5378 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5381 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5382 if(!IsHelping(user))
5383 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5385 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5388 if(new_owner->access >= UL_COOWNER)
5389 co_access = new_owner->access;
5391 co_access = UL_COOWNER;
5392 new_owner->access = UL_OWNER;
5394 curr_user->access = co_access;
5395 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5396 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5397 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5401 static CHANSERV_FUNC(cmd_suspend)
5403 struct handle_info *hi;
5404 struct userData *self, *target;
5407 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5408 self = GetChannelUser(channel->channel_info, user->handle_info);
5409 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5411 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5414 if(target->access >= self->access)
5416 reply("MSG_USER_OUTRANKED", hi->handle);
5419 if(target->flags & USER_SUSPENDED)
5421 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5426 target->present = 0;
5429 target->flags |= USER_SUSPENDED;
5430 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5434 static CHANSERV_FUNC(cmd_unsuspend)
5436 struct handle_info *hi;
5437 struct userData *self, *target;
5440 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5441 self = GetChannelUser(channel->channel_info, user->handle_info);
5442 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5444 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5447 if(target->access >= self->access)
5449 reply("MSG_USER_OUTRANKED", hi->handle);
5452 if(!(target->flags & USER_SUSPENDED))
5454 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5457 target->flags &= ~USER_SUSPENDED;
5458 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5462 static MODCMD_FUNC(cmd_deleteme)
5464 struct handle_info *hi;
5465 struct userData *target;
5466 const char *confirm_string;
5467 unsigned short access;
5470 hi = user->handle_info;
5471 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5473 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5476 if(target->access == UL_OWNER)
5478 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5481 confirm_string = make_confirmation_string(target);
5482 if((argc < 2) || strcmp(argv[1], confirm_string))
5484 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5487 access = target->access;
5488 channel_name = strdup(channel->name);
5489 del_channel_user(target, 1);
5490 reply("CSMSG_DELETED_YOU", access, channel_name);
5496 chanserv_refresh_topics(UNUSED_ARG(void *data))
5498 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5499 struct chanData *cData;
5502 for(cData = channelList; cData; cData = cData->next)
5504 if(IsSuspended(cData))
5506 opt = cData->chOpts[chTopicRefresh];
5509 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5512 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5513 cData->last_refresh = refresh_num;
5515 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5518 static CHANSERV_FUNC(cmd_unf)
5522 char response[MAXLEN];
5523 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5524 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5525 irc_privmsg(cmd->parent->bot, channel->name, response);
5528 reply("CSMSG_UNF_RESPONSE");
5532 static CHANSERV_FUNC(cmd_ping)
5536 char response[MAXLEN];
5537 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5538 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5539 irc_privmsg(cmd->parent->bot, channel->name, response);
5542 reply("CSMSG_PING_RESPONSE");
5546 static CHANSERV_FUNC(cmd_wut)
5550 char response[MAXLEN];
5551 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5552 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5553 irc_privmsg(cmd->parent->bot, channel->name, response);
5556 reply("CSMSG_WUT_RESPONSE");
5560 static CHANSERV_FUNC(cmd_8ball)
5562 unsigned int i, j, accum;
5567 for(i=1; i<argc; i++)
5568 for(j=0; argv[i][j]; j++)
5569 accum = (accum << 5) - accum + toupper(argv[i][j]);
5570 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5573 char response[MAXLEN];
5574 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5575 irc_privmsg(cmd->parent->bot, channel->name, response);
5578 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5582 static CHANSERV_FUNC(cmd_d)
5584 unsigned long sides, count, modifier, ii, total;
5585 char response[MAXLEN], *sep;
5589 if((count = strtoul(argv[1], &sep, 10)) < 1)
5599 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5600 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5604 else if((sep[0] == '-') && isdigit(sep[1]))
5605 modifier = strtoul(sep, NULL, 10);
5606 else if((sep[0] == '+') && isdigit(sep[1]))
5607 modifier = strtoul(sep+1, NULL, 10);
5614 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5619 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5622 for(total = ii = 0; ii < count; ++ii)
5623 total += (rand() % sides) + 1;
5626 if((count > 1) || modifier)
5628 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5629 sprintf(response, fmt, total, count, sides, modifier);
5633 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5634 sprintf(response, fmt, total, sides);
5637 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5639 send_message_type(4, user, cmd->parent->bot, "%s", response);
5643 static CHANSERV_FUNC(cmd_huggle)
5645 char response[MAXLEN];
5647 /* CTCP must be via PRIVMSG, never notice */
5650 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5651 sprintf(response, fmt, user->nick);
5652 irc_privmsg(cmd->parent->bot, channel->name, response);
5656 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5657 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5663 chanserv_adjust_limit(void *data)
5665 struct mod_chanmode change;
5666 struct chanData *cData = data;
5667 struct chanNode *channel = cData->channel;
5670 if(IsSuspended(cData))
5673 cData->limitAdjusted = now;
5674 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5675 if(cData->modes.modes_set & MODE_LIMIT)
5677 if(limit > cData->modes.new_limit)
5678 limit = cData->modes.new_limit;
5679 else if(limit == cData->modes.new_limit)
5683 change.modes_set = MODE_LIMIT;
5684 change.modes_clear = 0;
5685 change.new_limit = limit;
5687 mod_chanmode_announce(chanserv, channel, &change);
5691 handle_new_channel(struct chanNode *channel)
5693 struct chanData *cData;
5695 if(!(cData = channel->channel_info))
5698 if(cData->modes.modes_set || cData->modes.modes_clear)
5699 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5701 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5702 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5705 /* Welcome to my worst nightmare. Warning: Read (or modify)
5706 the code below at your own risk. */
5708 handle_join(struct modeNode *mNode)
5710 struct mod_chanmode change;
5711 struct userNode *user = mNode->user;
5712 struct chanNode *channel = mNode->channel;
5713 struct chanData *cData;
5714 struct userData *uData = NULL;
5715 struct banData *bData;
5716 struct handle_info *handle;
5717 unsigned int modes = 0, info = 0;
5720 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5723 cData = channel->channel_info;
5724 if(channel->members.used > cData->max)
5725 cData->max = channel->members.used;
5727 /* Check for bans. If they're joining through a ban, one of two
5729 * 1: Join during a netburst, by riding the break. Kick them
5730 * unless they have ops or voice in the channel.
5731 * 2: They're allowed to join through the ban (an invite in
5732 * ircu2.10, or a +e on Hybrid, or something).
5733 * If they're not joining through a ban, and the banlist is not
5734 * full, see if they're on the banlist for the channel. If so,
5737 if(user->uplink->burst && !mNode->modes)
5740 for(ii = 0; ii < channel->banlist.used; ii++)
5742 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5744 /* Riding a netburst. Naughty. */
5745 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5751 change.modes_set = change.modes_clear = 0;
5753 if(channel->banlist.used < MAXBANS)
5755 /* Not joining through a ban. */
5756 for(bData = cData->bans;
5757 bData && !user_matches_glob(user, bData->mask, 1);
5758 bData = bData->next);
5762 char kick_reason[MAXLEN];
5763 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5765 bData->triggered = now;
5766 if(bData != cData->bans)
5768 /* Shuffle the ban to the head of the list. */
5769 if(bData->next) bData->next->prev = bData->prev;
5770 if(bData->prev) bData->prev->next = bData->next;
5773 bData->next = cData->bans;
5776 cData->bans->prev = bData;
5777 cData->bans = bData;
5780 change.args[0].mode = MODE_BAN;
5781 change.args[0].hostmask = bData->mask;
5782 mod_chanmode_announce(chanserv, channel, &change);
5783 KickChannelUser(user, channel, chanserv, kick_reason);
5788 /* ChanServ will not modify the limits in join-flooded channels.
5789 It will also skip DynLimit processing when the user (or srvx)
5790 is bursting in, because there are likely more incoming. */
5791 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5792 && !user->uplink->burst
5793 && !channel->join_flooded
5794 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5796 /* The user count has begun "bumping" into the channel limit,
5797 so set a timer to raise the limit a bit. Any previous
5798 timers are removed so three incoming users within the delay
5799 results in one limit change, not three. */
5801 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5802 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5805 if(channel->join_flooded)
5807 /* don't automatically give ops or voice during a join flood */
5809 else if(cData->lvlOpts[lvlGiveOps] == 0)
5810 modes |= MODE_CHANOP;
5811 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5812 modes |= MODE_VOICE;
5814 greeting = cData->greeting;
5815 if(user->handle_info)
5817 handle = user->handle_info;
5819 if(IsHelper(user) && !IsHelping(user))
5822 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5824 if(channel == chanserv_conf.support_channels.list[ii])
5826 HANDLE_SET_FLAG(user->handle_info, HELPING);
5832 uData = GetTrueChannelAccess(cData, handle);
5833 if(uData && !IsUserSuspended(uData))
5835 /* Ops and above were handled by the above case. */
5836 if(IsUserAutoOp(uData))
5838 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5839 modes |= MODE_CHANOP;
5840 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5841 modes |= MODE_VOICE;
5843 if(uData->access >= UL_PRESENT)
5844 cData->visited = now;
5845 if(cData->user_greeting)
5846 greeting = cData->user_greeting;
5848 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5849 && ((now - uData->seen) >= chanserv_conf.info_delay)
5856 if(!user->uplink->burst)
5860 if(modes & MODE_CHANOP)
5861 modes &= ~MODE_VOICE;
5862 change.args[0].mode = modes;
5863 change.args[0].member = mNode;
5864 mod_chanmode_announce(chanserv, channel, &change);
5866 if(greeting && !user->uplink->burst)
5867 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5869 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5875 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5877 struct mod_chanmode change;
5878 struct userData *channel;
5879 unsigned int ii, jj;
5881 if(!user->handle_info)
5884 change.modes_set = change.modes_clear = 0;
5886 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5888 struct chanNode *cn;
5889 struct modeNode *mn;
5890 if(IsUserSuspended(channel)
5891 || IsSuspended(channel->channel)
5892 || !(cn = channel->channel->channel))
5895 mn = GetUserMode(cn, user);
5898 if(!IsUserSuspended(channel)
5899 && IsUserAutoInvite(channel)
5900 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5901 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5903 irc_invite(chanserv, user, cn);
5907 if(channel->access >= UL_PRESENT)
5908 channel->channel->visited = now;
5910 if(IsUserAutoOp(channel))
5912 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5913 change.args[0].mode = MODE_CHANOP;
5914 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5915 change.args[0].mode = MODE_VOICE;
5917 change.args[0].mode = 0;
5918 change.args[0].member = mn;
5919 if(change.args[0].mode)
5920 mod_chanmode_announce(chanserv, cn, &change);
5923 channel->seen = now;
5924 channel->present = 1;
5927 for(ii = 0; ii < user->channels.used; ++ii)
5929 struct chanNode *channel = user->channels.list[ii]->channel;
5930 struct banData *ban;
5932 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5933 || !channel->channel_info)
5935 for(jj = 0; jj < channel->banlist.used; ++jj)
5936 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5938 if(jj < channel->banlist.used)
5940 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5942 char kick_reason[MAXLEN];
5943 if(!user_matches_glob(user, ban->mask, 1))
5945 change.args[0].mode = MODE_BAN;
5946 change.args[0].hostmask = ban->mask;
5947 mod_chanmode_announce(chanserv, channel, &change);
5948 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5949 KickChannelUser(user, channel, chanserv, kick_reason);
5950 ban->triggered = now;
5955 if(IsSupportHelper(user))
5957 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5959 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5961 HANDLE_SET_FLAG(user->handle_info, HELPING);
5969 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5971 struct chanData *cData;
5972 struct userData *uData;
5974 cData = channel->channel_info;
5975 if(!cData || IsSuspended(cData) || IsLocal(user))
5978 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5980 /* Allow for a bit of padding so that the limit doesn't
5981 track the user count exactly, which could get annoying. */
5982 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5984 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5985 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5989 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5990 scan_user_presence(uData, user);
5992 if(IsHelping(user) && IsSupportHelper(user))
5994 unsigned int ii, jj;
5995 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5997 for(jj = 0; jj < user->channels.used; ++jj)
5998 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6000 if(jj < user->channels.used)
6003 if(ii == chanserv_conf.support_channels.used)
6004 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6009 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6011 if(!channel->channel_info || !kicker || IsService(kicker)
6012 || (kicker == victim) || IsSuspended(channel->channel_info)
6013 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6016 if(protect_user(victim, kicker, channel->channel_info))
6018 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6019 KickChannelUser(kicker, channel, chanserv, reason);
6024 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6026 struct chanData *cData;
6028 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6031 cData = channel->channel_info;
6032 if(bad_topic(channel, user, channel->topic))
6034 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6035 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6036 SetChannelTopic(channel, chanserv, old_topic, 1);
6037 else if(cData->topic)
6038 SetChannelTopic(channel, chanserv, cData->topic, 1);
6041 /* With topicsnarf, grab the topic and save it as the default topic. */
6042 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6045 cData->topic = strdup(channel->topic);
6051 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6053 struct mod_chanmode *bounce = NULL;
6054 unsigned int bnc, ii;
6057 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6060 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6061 && mode_lock_violated(&channel->channel_info->modes, change))
6063 char correct[MAXLEN];
6064 bounce = mod_chanmode_alloc(change->argc + 1);
6065 *bounce = channel->channel_info->modes;
6066 mod_chanmode_format(&channel->channel_info->modes, correct);
6067 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6069 for(ii = bnc = 0; ii < change->argc; ++ii)
6071 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6073 const struct userNode *victim = change->args[ii].member->user;
6074 if(!protect_user(victim, user, channel->channel_info))
6077 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6080 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6081 bounce->args[bnc].member = GetUserMode(channel, user);
6082 if(bounce->args[bnc].member)
6086 bounce->args[bnc].mode = MODE_CHANOP;
6087 bounce->args[bnc].member = change->args[ii].member;
6089 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6091 else if(change->args[ii].mode & MODE_CHANOP)
6093 const struct userNode *victim = change->args[ii].member->user;
6094 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6097 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6098 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6099 bounce->args[bnc].member = change->args[ii].member;
6102 else if(change->args[ii].mode & MODE_BAN)
6104 const char *ban = change->args[ii].hostmask;
6105 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6108 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6109 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6110 bounce->args[bnc].hostmask = ban;
6112 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6117 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6118 mod_chanmode_announce(chanserv, channel, bounce);
6119 mod_chanmode_free(bounce);
6124 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6126 struct chanNode *channel;
6127 struct banData *bData;
6128 struct mod_chanmode change;
6129 unsigned int ii, jj;
6130 char kick_reason[MAXLEN];
6132 change.modes_set = change.modes_clear = 0;
6134 change.args[0].mode = MODE_BAN;
6135 for(ii = 0; ii < user->channels.used; ++ii)
6137 channel = user->channels.list[ii]->channel;
6138 /* Need not check for bans if they're opped or voiced. */
6139 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6141 /* Need not check for bans unless channel registration is active. */
6142 if(!channel->channel_info || IsSuspended(channel->channel_info))
6144 /* Look for a matching ban already on the channel. */
6145 for(jj = 0; jj < channel->banlist.used; ++jj)
6146 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6148 /* Need not act if we found one. */
6149 if(jj < channel->banlist.used)
6151 /* Look for a matching ban in this channel. */
6152 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6154 if(!user_matches_glob(user, bData->mask, 1))
6156 change.args[0].hostmask = bData->mask;
6157 mod_chanmode_announce(chanserv, channel, &change);
6158 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6159 KickChannelUser(user, channel, chanserv, kick_reason);
6160 bData->triggered = now;
6161 break; /* we don't need to check any more bans in the channel */
6166 static void handle_rename(struct handle_info *handle, const char *old_handle)
6168 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6172 dict_remove2(handle_dnrs, old_handle, 1);
6173 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6174 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6179 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6181 struct userNode *h_user;
6183 if(handle->channels)
6185 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6186 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6188 while(handle->channels)
6189 del_channel_user(handle->channels, 1);
6194 handle_server_link(UNUSED_ARG(struct server *server))
6196 struct chanData *cData;
6198 for(cData = channelList; cData; cData = cData->next)
6200 if(!IsSuspended(cData))
6201 cData->may_opchan = 1;
6202 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6203 && !cData->channel->join_flooded
6204 && ((cData->channel->limit - cData->channel->members.used)
6205 < chanserv_conf.adjust_threshold))
6207 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6208 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6214 chanserv_conf_read(void)
6218 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6219 struct mod_chanmode *change;
6220 struct string_list *strlist;
6221 struct chanNode *chan;
6224 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6226 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6229 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6230 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6231 chanserv_conf.support_channels.used = 0;
6232 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6234 for(ii = 0; ii < strlist->used; ++ii)
6236 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6239 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6241 channelList_append(&chanserv_conf.support_channels, chan);
6244 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6247 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6250 chan = AddChannel(str, now, str2, NULL);
6252 channelList_append(&chanserv_conf.support_channels, chan);
6254 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6255 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6256 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6257 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6258 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6259 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6260 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6261 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6262 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6263 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6264 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6265 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6266 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6267 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6268 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6269 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6270 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6271 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6272 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6273 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6274 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6276 NickChange(chanserv, str, 0);
6277 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6278 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6279 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6280 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6281 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6282 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6283 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6284 chanserv_conf.max_owned = str ? atoi(str) : 5;
6285 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6286 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6287 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6288 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6289 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6290 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6291 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6294 safestrncpy(mode_line, str, sizeof(mode_line));
6295 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6296 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6298 chanserv_conf.default_modes = *change;
6299 mod_chanmode_free(change);
6301 free_string_list(chanserv_conf.set_shows);
6302 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6304 strlist = string_list_copy(strlist);
6307 static const char *list[] = {
6308 /* free form text */
6309 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6310 /* options based on user level */
6311 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6312 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6313 /* multiple choice options */
6314 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6315 /* binary options */
6316 "DynLimit", "NoDelete",
6321 strlist = alloc_string_list(ArrayLength(list)-1);
6322 for(ii=0; list[ii]; ii++)
6323 string_list_append(strlist, strdup(list[ii]));
6325 chanserv_conf.set_shows = strlist;
6326 /* We don't look things up now, in case the list refers to options
6327 * defined by modules initialized after this point. Just mark the
6328 * function list as invalid, so it will be initialized.
6330 set_shows_list.used = 0;
6331 free_string_list(chanserv_conf.eightball);
6332 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6335 strlist = string_list_copy(strlist);
6339 strlist = alloc_string_list(4);
6340 string_list_append(strlist, strdup("Yes."));
6341 string_list_append(strlist, strdup("No."));
6342 string_list_append(strlist, strdup("Maybe so."));
6344 chanserv_conf.eightball = strlist;
6345 free_string_list(chanserv_conf.old_ban_names);
6346 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6348 strlist = string_list_copy(strlist);
6350 strlist = alloc_string_list(2);
6351 chanserv_conf.old_ban_names = strlist;
6355 chanserv_note_type_read(const char *key, struct record_data *rd)
6358 struct note_type *ntype;
6361 if(!(obj = GET_RECORD_OBJECT(rd)))
6363 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6366 if(!(ntype = chanserv_create_note_type(key)))
6368 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6372 /* Figure out set access */
6373 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6375 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6376 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6378 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6380 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6381 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6383 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6385 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6389 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6390 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6391 ntype->set_access.min_opserv = 0;
6394 /* Figure out visibility */
6395 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6396 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6397 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6398 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6399 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6400 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6401 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6402 ntype->visible_type = NOTE_VIS_ALL;
6404 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6406 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6407 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6411 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6413 struct handle_info *handle;
6414 struct userData *uData;
6415 char *seen, *inf, *flags;
6417 unsigned short access;
6419 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6421 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6425 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6426 if(access > UL_OWNER)
6428 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6432 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6433 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6434 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6435 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6436 handle = get_handle_info(key);
6439 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6443 uData = add_channel_user(chan, handle, access, last_seen, inf);
6444 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6448 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6450 struct banData *bData;
6451 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6452 time_t set_time, triggered_time, expires_time;
6454 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6456 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6460 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6461 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6462 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6463 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6464 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6465 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6467 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6468 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6470 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6472 expires_time = set_time + atoi(s_duration);
6476 if(expires_time && (expires_time < now))
6479 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6482 static struct suspended *
6483 chanserv_read_suspended(dict_t obj)
6485 struct suspended *suspended = calloc(1, sizeof(*suspended));
6489 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6490 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6491 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6492 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6493 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6494 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6495 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6496 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6497 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6498 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6503 chanserv_channel_read(const char *key, struct record_data *hir)
6505 struct suspended *suspended;
6506 struct mod_chanmode *modes;
6507 struct chanNode *cNode;
6508 struct chanData *cData;
6509 struct dict *channel, *obj;
6510 char *str, *argv[10];
6514 channel = hir->d.object;
6516 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6519 cNode = AddChannel(key, now, NULL, NULL);
6522 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6525 cData = register_channel(cNode, str);
6528 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6532 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6534 enum levelOption lvlOpt;
6535 enum charOption chOpt;
6537 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6538 cData->flags = atoi(str);
6540 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6542 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6544 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6545 else if(levelOptions[lvlOpt].old_flag)
6547 if(cData->flags & levelOptions[lvlOpt].old_flag)
6548 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6550 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6554 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6556 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6558 cData->chOpts[chOpt] = str[0];
6561 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6563 enum levelOption lvlOpt;
6564 enum charOption chOpt;
6567 cData->flags = base64toint(str, 5);
6568 count = strlen(str += 5);
6569 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6572 if(levelOptions[lvlOpt].old_flag)
6574 if(cData->flags & levelOptions[lvlOpt].old_flag)
6575 lvl = levelOptions[lvlOpt].flag_value;
6577 lvl = levelOptions[lvlOpt].default_value;
6579 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6581 case 'c': lvl = UL_COOWNER; break;
6582 case 'm': lvl = UL_MASTER; break;
6583 case 'n': lvl = UL_OWNER+1; break;
6584 case 'o': lvl = UL_OP; break;
6585 case 'p': lvl = UL_PEON; break;
6586 case 'w': lvl = UL_OWNER; break;
6587 default: lvl = 0; break;
6589 cData->lvlOpts[lvlOpt] = lvl;
6591 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6592 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6595 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6597 suspended = chanserv_read_suspended(obj);
6598 cData->suspended = suspended;
6599 suspended->cData = cData;
6600 /* We could use suspended->expires and suspended->revoked to
6601 * set the CHANNEL_SUSPENDED flag, but we don't. */
6603 else if(cData->flags & CHANNEL_SUSPENDED)
6605 suspended = calloc(1, sizeof(*suspended));
6606 suspended->issued = 0;
6607 suspended->revoked = 0;
6608 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6609 suspended->expires = str ? atoi(str) : 0;
6610 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6611 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6612 suspended->reason = strdup(str ? str : "No reason");
6613 suspended->previous = NULL;
6614 cData->suspended = suspended;
6615 suspended->cData = cData;
6620 if((cData->flags & CHANNEL_SUSPENDED)
6621 && suspended->expires
6622 && (suspended->expires <= now))
6624 cData->flags &= ~CHANNEL_SUSPENDED;
6627 if(!(cData->flags & CHANNEL_SUSPENDED))
6629 struct mod_chanmode change;
6630 change.modes_set = change.modes_clear = 0;
6632 change.args[0].mode = MODE_CHANOP;
6633 change.args[0].member = AddChannelUser(chanserv, cNode);
6634 mod_chanmode_announce(chanserv, cNode, &change);
6636 else if(suspended->expires > now)
6638 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6641 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6642 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6643 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6644 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6645 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6646 cData->max = str ? atoi(str) : 0;
6647 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6648 cData->greeting = str ? strdup(str) : NULL;
6649 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6650 cData->user_greeting = str ? strdup(str) : NULL;
6651 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6652 cData->topic_mask = str ? strdup(str) : NULL;
6653 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6654 cData->topic = str ? strdup(str) : NULL;
6656 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6657 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6658 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6659 cData->modes = *modes;
6660 if(cData->modes.argc > 1)
6661 cData->modes.argc = 1;
6662 if(!IsSuspended(cData))
6663 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6664 mod_chanmode_free(modes);
6667 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6668 for(it = dict_first(obj); it; it = iter_next(it))
6669 user_read_helper(iter_key(it), iter_data(it), cData);
6671 if(!cData->users && !IsProtected(cData))
6673 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6674 unregister_channel(cData, "has empty user list.");
6678 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6679 for(it = dict_first(obj); it; it = iter_next(it))
6680 ban_read_helper(iter_key(it), iter_data(it), cData);
6682 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6683 for(it = dict_first(obj); it; it = iter_next(it))
6685 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6686 struct record_data *rd = iter_data(it);
6687 const char *note, *setter;
6689 if(rd->type != RECDB_OBJECT)
6691 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6695 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6697 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6699 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6703 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6704 if(!setter) setter = "<unknown>";
6705 chanserv_add_channel_note(cData, ntype, setter, note);
6713 chanserv_dnr_read(const char *key, struct record_data *hir)
6715 const char *setter, *reason, *str;
6716 struct do_not_register *dnr;
6718 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6721 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6724 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6727 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6730 dnr = chanserv_add_dnr(key, setter, reason);
6733 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6735 dnr->set = atoi(str);
6741 chanserv_saxdb_read(struct dict *database)
6743 struct dict *section;
6746 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6747 for(it = dict_first(section); it; it = iter_next(it))
6748 chanserv_note_type_read(iter_key(it), iter_data(it));
6750 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6751 for(it = dict_first(section); it; it = iter_next(it))
6752 chanserv_channel_read(iter_key(it), iter_data(it));
6754 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6755 for(it = dict_first(section); it; it = iter_next(it))
6756 chanserv_dnr_read(iter_key(it), iter_data(it));
6762 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6764 int high_present = 0;
6765 saxdb_start_record(ctx, KEY_USERS, 1);
6766 for(; uData; uData = uData->next)
6768 if((uData->access >= UL_PRESENT) && uData->present)
6770 saxdb_start_record(ctx, uData->handle->handle, 0);
6771 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6772 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6774 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6776 saxdb_write_string(ctx, KEY_INFO, uData->info);
6777 saxdb_end_record(ctx);
6779 saxdb_end_record(ctx);
6780 return high_present;
6784 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6788 saxdb_start_record(ctx, KEY_BANS, 1);
6789 for(; bData; bData = bData->next)
6791 saxdb_start_record(ctx, bData->mask, 0);
6792 saxdb_write_int(ctx, KEY_SET, bData->set);
6793 if(bData->triggered)
6794 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6796 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6798 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6800 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6801 saxdb_end_record(ctx);
6803 saxdb_end_record(ctx);
6807 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6809 saxdb_start_record(ctx, name, 0);
6810 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6811 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6813 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6815 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6817 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6819 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6820 saxdb_end_record(ctx);
6824 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6828 enum levelOption lvlOpt;
6829 enum charOption chOpt;
6831 saxdb_start_record(ctx, channel->channel->name, 1);
6833 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6834 saxdb_write_int(ctx, KEY_MAX, channel->max);
6836 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6837 if(channel->registrar)
6838 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6839 if(channel->greeting)
6840 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6841 if(channel->user_greeting)
6842 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6843 if(channel->topic_mask)
6844 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6845 if(channel->suspended)
6846 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6848 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6849 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6850 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6851 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6852 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6854 buf[0] = channel->chOpts[chOpt];
6856 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6858 saxdb_end_record(ctx);
6860 if(channel->modes.modes_set || channel->modes.modes_clear)
6862 mod_chanmode_format(&channel->modes, buf);
6863 saxdb_write_string(ctx, KEY_MODES, buf);
6866 high_present = chanserv_write_users(ctx, channel->users);
6867 chanserv_write_bans(ctx, channel->bans);
6869 if(dict_size(channel->notes))
6873 saxdb_start_record(ctx, KEY_NOTES, 1);
6874 for(it = dict_first(channel->notes); it; it = iter_next(it))
6876 struct note *note = iter_data(it);
6877 saxdb_start_record(ctx, iter_key(it), 0);
6878 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6879 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6880 saxdb_end_record(ctx);
6882 saxdb_end_record(ctx);
6885 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6886 saxdb_end_record(ctx);
6890 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6894 saxdb_start_record(ctx, ntype->name, 0);
6895 switch(ntype->set_access_type)
6897 case NOTE_SET_CHANNEL_ACCESS:
6898 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6900 case NOTE_SET_CHANNEL_SETTER:
6901 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6903 case NOTE_SET_PRIVILEGED: default:
6904 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6907 switch(ntype->visible_type)
6909 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6910 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6911 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6913 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6914 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6915 saxdb_end_record(ctx);
6919 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6921 struct do_not_register *dnr;
6924 for(it = dict_first(dnrs); it; it = iter_next(it))
6926 dnr = iter_data(it);
6927 saxdb_start_record(ctx, dnr->chan_name, 0);
6929 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6930 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6931 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6932 saxdb_end_record(ctx);
6937 chanserv_saxdb_write(struct saxdb_context *ctx)
6940 struct chanData *channel;
6943 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6944 for(it = dict_first(note_types); it; it = iter_next(it))
6945 chanserv_write_note_type(ctx, iter_data(it));
6946 saxdb_end_record(ctx);
6949 saxdb_start_record(ctx, KEY_DNR, 1);
6950 write_dnrs_helper(ctx, handle_dnrs);
6951 write_dnrs_helper(ctx, plain_dnrs);
6952 write_dnrs_helper(ctx, mask_dnrs);
6953 saxdb_end_record(ctx);
6956 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6957 for(channel = channelList; channel; channel = channel->next)
6958 chanserv_write_channel(ctx, channel);
6959 saxdb_end_record(ctx);
6965 chanserv_db_cleanup(void) {
6967 unreg_part_func(handle_part);
6969 unregister_channel(channelList, "terminating.");
6970 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6971 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6972 free(chanserv_conf.support_channels.list);
6973 dict_delete(handle_dnrs);
6974 dict_delete(plain_dnrs);
6975 dict_delete(mask_dnrs);
6976 dict_delete(note_types);
6977 free_string_list(chanserv_conf.eightball);
6978 free_string_list(chanserv_conf.old_ban_names);
6979 free_string_list(chanserv_conf.set_shows);
6980 free(set_shows_list.list);
6981 free(uset_shows_list.list);
6984 struct userData *helper = helperList;
6985 helperList = helperList->next;
6990 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6991 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6992 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6995 init_chanserv(const char *nick)
6997 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6998 conf_register_reload(chanserv_conf_read);
7000 reg_server_link_func(handle_server_link);
7002 reg_new_channel_func(handle_new_channel);
7003 reg_join_func(handle_join);
7004 reg_part_func(handle_part);
7005 reg_kick_func(handle_kick);
7006 reg_topic_func(handle_topic);
7007 reg_mode_change_func(handle_mode);
7008 reg_nick_change_func(handle_nick_change);
7010 reg_auth_func(handle_auth);
7011 reg_handle_rename_func(handle_rename);
7012 reg_unreg_func(handle_unreg);
7014 handle_dnrs = dict_new();
7015 dict_set_free_data(handle_dnrs, free);
7016 plain_dnrs = dict_new();
7017 dict_set_free_data(plain_dnrs, free);
7018 mask_dnrs = dict_new();
7019 dict_set_free_data(mask_dnrs, free);
7021 reg_svccmd_unbind_func(handle_svccmd_unbind);
7022 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7023 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7024 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7025 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7026 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7027 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7028 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7029 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7030 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7032 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7033 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7035 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7036 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7037 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7038 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7039 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7041 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7042 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7043 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7044 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7045 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7047 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7048 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", NULL);
7049 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7050 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7052 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7053 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7054 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7055 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7056 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7057 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7058 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7059 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7061 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7062 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7063 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7064 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7065 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7066 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7067 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7068 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7069 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7070 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7071 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7072 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7073 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7074 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7076 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7077 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7078 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7079 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7080 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7082 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7083 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7085 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7086 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7087 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7088 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7089 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7090 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7091 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7092 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7093 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7094 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7095 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7097 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7098 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7100 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7101 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7102 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7103 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7105 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7106 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7107 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7108 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7109 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7111 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7112 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7113 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7114 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7115 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7116 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7118 /* Channel options */
7119 DEFINE_CHANNEL_OPTION(defaulttopic);
7120 DEFINE_CHANNEL_OPTION(topicmask);
7121 DEFINE_CHANNEL_OPTION(greeting);
7122 DEFINE_CHANNEL_OPTION(usergreeting);
7123 DEFINE_CHANNEL_OPTION(modes);
7124 DEFINE_CHANNEL_OPTION(enfops);
7125 DEFINE_CHANNEL_OPTION(giveops);
7126 DEFINE_CHANNEL_OPTION(protect);
7127 DEFINE_CHANNEL_OPTION(enfmodes);
7128 DEFINE_CHANNEL_OPTION(enftopic);
7129 DEFINE_CHANNEL_OPTION(pubcmd);
7130 DEFINE_CHANNEL_OPTION(givevoice);
7131 DEFINE_CHANNEL_OPTION(userinfo);
7132 DEFINE_CHANNEL_OPTION(dynlimit);
7133 DEFINE_CHANNEL_OPTION(topicsnarf);
7134 DEFINE_CHANNEL_OPTION(nodelete);
7135 DEFINE_CHANNEL_OPTION(toys);
7136 DEFINE_CHANNEL_OPTION(setters);
7137 DEFINE_CHANNEL_OPTION(topicrefresh);
7138 DEFINE_CHANNEL_OPTION(ctcpusers);
7139 DEFINE_CHANNEL_OPTION(ctcpreaction);
7140 DEFINE_CHANNEL_OPTION(inviteme);
7141 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7143 /* Alias set topic to set defaulttopic for compatibility. */
7144 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7147 DEFINE_USER_OPTION(noautoop);
7148 DEFINE_USER_OPTION(autoinvite);
7149 DEFINE_USER_OPTION(info);
7151 /* Alias uset autovoice to uset autoop. */
7152 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7154 note_types = dict_new();
7155 dict_set_free_data(note_types, chanserv_deref_note_type);
7158 chanserv = AddService(nick, "Channel Services");
7159 service_register(chanserv, '!');
7160 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7162 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7164 if(chanserv_conf.channel_expire_frequency)
7165 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7167 if(chanserv_conf.refresh_period)
7169 time_t next_refresh;
7170 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7171 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7174 reg_exit_func(chanserv_db_cleanup);
7175 message_register_table(msgtab);