1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for opserv_bad_channel() */
27 #define CHANSERV_CONF_NAME "services/chanserv"
29 /* ChanServ options */
30 #define KEY_SUPPORT_CHANNEL "support_channel"
31 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
32 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
33 #define KEY_INFO_DELAY "info_delay"
34 #define KEY_MAX_GREETLEN "max_greetlen"
35 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
36 #define KEY_ADJUST_DELAY "adjust_delay"
37 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
38 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
39 #define KEY_MAX_CHAN_USERS "max_chan_users"
40 #define KEY_MAX_CHAN_BANS "max_chan_bans"
41 #define KEY_NICK "nick"
42 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
43 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
44 #define KEY_SWITCH_TIMEOUT "switch_timeout"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
56 /* ChanServ database */
57 #define KEY_CHANNELS "channels"
58 #define KEY_NOTE_TYPES "note_types"
60 /* Note type parameters */
61 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
62 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
63 #define KEY_NOTE_SETTER_ACCESS "setter_access"
64 #define KEY_NOTE_VISIBILITY "visibility"
65 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
66 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
67 #define KEY_NOTE_VIS_ALL "all"
68 #define KEY_NOTE_MAX_LENGTH "max_length"
69 #define KEY_NOTE_SETTER "setter"
70 #define KEY_NOTE_NOTE "note"
72 /* Do-not-register channels */
74 #define KEY_DNR_SET "set"
75 #define KEY_DNR_SETTER "setter"
76 #define KEY_DNR_REASON "reason"
79 #define KEY_REGISTERED "registered"
80 #define KEY_REGISTRAR "registrar"
81 #define KEY_SUSPENDED "suspended"
82 #define KEY_PREVIOUS "previous"
83 #define KEY_SUSPENDER "suspender"
84 #define KEY_ISSUED "issued"
85 #define KEY_REVOKED "revoked"
86 #define KEY_SUSPEND_EXPIRES "suspend_expires"
87 #define KEY_SUSPEND_REASON "suspend_reason"
88 #define KEY_VISITED "visited"
89 #define KEY_TOPIC "topic"
90 #define KEY_GREETING "greeting"
91 #define KEY_USER_GREETING "user_greeting"
92 #define KEY_MODES "modes"
93 #define KEY_FLAGS "flags"
94 #define KEY_OPTIONS "options"
95 #define KEY_USERS "users"
96 #define KEY_BANS "bans"
98 #define KEY_NOTES "notes"
99 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_LEVEL "level"
103 #define KEY_INFO "info"
104 #define KEY_SEEN "seen"
107 #define KEY_OWNER "owner"
108 #define KEY_REASON "reason"
109 #define KEY_SET "set"
110 #define KEY_DURATION "duration"
111 #define KEY_EXPIRES "expires"
112 #define KEY_TRIGGERED "triggered"
114 #define CHANNEL_DEFAULT_FLAGS (0)
115 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
117 /* Administrative messages */
118 static const struct message_entry msgtab[] = {
119 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
121 /* Channel registration */
122 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
123 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
124 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
125 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
126 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
127 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
129 /* Do-not-register channels */
130 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
131 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
132 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
133 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
134 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
135 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
136 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
137 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
138 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
139 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
140 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
142 /* Channel unregistration */
143 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
144 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
145 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
146 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
149 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
150 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
152 /* Channel merging */
153 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
154 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
155 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
156 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
157 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
159 /* Handle unregistration */
160 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
163 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
164 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
165 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
166 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
167 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
168 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
169 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
170 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
171 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
172 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
173 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
174 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
175 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
177 /* Removing yourself from a channel. */
178 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
179 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
180 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
182 /* User management */
183 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
241 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
242 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
243 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
244 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
245 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
246 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
247 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
248 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
249 { "CSMSG_SET_MODES", "$bModes $b %s" },
250 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
251 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
252 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
253 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
254 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
255 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
256 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
257 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
258 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
259 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
260 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
261 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
262 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
263 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
264 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
265 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
266 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
267 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
268 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
269 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
270 { "CSMSG_USET_INFO", "$bInfo $b %s" },
272 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
273 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
274 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
275 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
276 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
277 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
278 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
279 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
280 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
281 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
282 { "CSMSG_PROTECT_NONE", "No users will be protected." },
283 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
284 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
285 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
286 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
287 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
288 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
289 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
290 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
291 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
292 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
293 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
294 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
296 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
297 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
298 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
299 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
300 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
302 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
303 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
304 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
306 /* Channel userlist */
307 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
308 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
309 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
310 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
312 /* Channel note list */
313 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
314 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
315 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
316 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
317 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
318 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
319 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
320 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
321 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
322 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
323 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
324 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
325 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
326 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
327 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
329 /* Channel [un]suspension */
330 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
331 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
332 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
333 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
334 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
335 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
336 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
338 /* Access information */
339 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
340 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
341 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
342 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
343 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
344 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
345 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
346 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
347 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
348 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
349 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
351 /* Seen information */
352 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
353 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
354 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
355 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
357 /* Names information */
358 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
359 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
361 /* Channel information */
362 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
363 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
364 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
365 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
366 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
367 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
368 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
369 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
370 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
371 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
372 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
373 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
374 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
375 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
378 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
379 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
380 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
381 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
382 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
384 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
385 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
386 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
387 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
388 { "CSMSG_PEEK_OPS", "$bOps:$b" },
389 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
391 /* Network information */
392 { "CSMSG_NETWORK_INFO", "Network Information:" },
393 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
394 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
395 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
396 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
397 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
398 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
399 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
402 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
403 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
404 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
406 /* Channel searches */
407 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
408 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
409 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
410 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
412 /* Channel configuration */
413 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
414 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
415 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
418 { "CSMSG_USER_OPTIONS", "User Options:" },
419 { "CSMSG_USER_PROTECTED", "That user is protected." },
422 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
423 { "CSMSG_PING_RESPONSE", "Pong!" },
424 { "CSMSG_WUT_RESPONSE", "wut" },
425 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
426 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
427 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
428 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
429 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
430 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
431 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
434 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
438 /* eject_user and unban_user flags */
439 #define ACTION_KICK 0x0001
440 #define ACTION_BAN 0x0002
441 #define ACTION_ADD_BAN 0x0004
442 #define ACTION_ADD_TIMED_BAN 0x0008
443 #define ACTION_UNBAN 0x0010
444 #define ACTION_DEL_BAN 0x0020
446 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
447 #define MODELEN 40 + KEYLEN
451 #define CSFUNC_ARGS user, channel, argc, argv, cmd
453 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
454 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
455 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
456 reply("MSG_MISSING_PARAMS", argv[0]); \
460 DECLARE_LIST(dnrList, struct do_not_register *);
461 DEFINE_LIST(dnrList, struct do_not_register *);
463 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
465 struct userNode *chanserv;
467 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
468 static struct log_type *CS_LOG;
472 struct channelList support_channels;
473 struct mod_chanmode default_modes;
475 unsigned long db_backup_frequency;
476 unsigned long channel_expire_frequency;
479 unsigned int adjust_delay;
480 long channel_expire_delay;
481 unsigned int nodelete_level;
483 unsigned int adjust_threshold;
484 int join_flood_threshold;
486 unsigned int greeting_length;
487 unsigned int refresh_period;
489 unsigned int max_owned;
490 unsigned int max_chan_users;
491 unsigned int max_chan_bans;
493 struct string_list *set_shows;
494 struct string_list *eightball;
495 struct string_list *old_ban_names;
497 const char *ctcp_short_ban_duration;
498 const char *ctcp_long_ban_duration;
500 const char *irc_operator_epithet;
501 const char *network_helper_epithet;
502 const char *support_helper_epithet;
507 struct userNode *user;
508 struct userNode *bot;
509 struct chanNode *channel;
511 unsigned short lowest;
512 unsigned short highest;
513 struct userData **users;
514 struct helpfile_table table;
517 enum note_access_type
519 NOTE_SET_CHANNEL_ACCESS,
520 NOTE_SET_CHANNEL_SETTER,
524 enum note_visible_type
527 NOTE_VIS_CHANNEL_USERS,
533 enum note_access_type set_access_type;
535 unsigned int min_opserv;
536 unsigned short min_ulevel;
538 enum note_visible_type visible_type;
539 unsigned int max_length;
546 struct note_type *type;
547 char setter[NICKSERV_HANDLE_LEN+1];
551 static unsigned int registered_channels;
552 static unsigned int banCount;
554 static const struct {
557 unsigned short level;
560 { "peon", "Peon", UL_PEON, '+' },
561 { "op", "Op", UL_OP, '@' },
562 { "master", "Master", UL_MASTER, '%' },
563 { "coowner", "Coowner", UL_COOWNER, '*' },
564 { "owner", "Owner", UL_OWNER, '!' },
565 { "helper", "BUG:", UL_HELPER, 'X' }
568 static const struct {
571 unsigned short default_value;
572 unsigned int old_idx;
573 unsigned int old_flag;
574 unsigned short flag_value;
576 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
577 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
578 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
579 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
580 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
581 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
582 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
583 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
584 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
585 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
586 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
589 struct charOptionValues {
592 } protectValues[] = {
593 { 'a', "CSMSG_PROTECT_ALL" },
594 { 'e', "CSMSG_PROTECT_EQUAL" },
595 { 'l', "CSMSG_PROTECT_LOWER" },
596 { 'n', "CSMSG_PROTECT_NONE" }
598 { 'd', "CSMSG_TOYS_DISABLED" },
599 { 'n', "CSMSG_TOYS_PRIVATE" },
600 { 'p', "CSMSG_TOYS_PUBLIC" }
601 }, topicRefreshValues[] = {
602 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
603 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
604 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
605 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
606 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
607 }, ctcpReactionValues[] = {
608 { 'k', "CSMSG_CTCPREACTION_KICK" },
609 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
610 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
611 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
614 static const struct {
618 unsigned int old_idx;
620 struct charOptionValues *values;
622 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
623 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
624 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
625 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
628 struct userData *helperList;
629 struct chanData *channelList;
630 static struct module *chanserv_module;
631 static unsigned int userCount;
633 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
634 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
635 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
638 user_level_from_name(const char *name, unsigned short clamp_level)
640 unsigned int level = 0, ii;
643 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
644 if(!irccasecmp(name, accessLevels[ii].name))
645 level = accessLevels[ii].level;
646 if(level > clamp_level)
652 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
655 *minl = strtoul(arg, &sep, 10);
663 *maxl = strtoul(sep+1, &sep, 10);
671 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
673 struct userData *uData, **head;
675 if(!channel || !handle)
678 if(override && HANDLE_FLAGGED(handle, HELPING)
679 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
681 for(uData = helperList;
682 uData && uData->handle != handle;
683 uData = uData->next);
687 uData = calloc(1, sizeof(struct userData));
688 uData->handle = handle;
690 uData->access = UL_HELPER;
696 uData->next = helperList;
698 helperList->prev = uData;
706 for(uData = channel->users; uData; uData = uData->next)
707 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
710 head = &(channel->users);
713 if(uData && (uData != *head))
715 /* Shuffle the user to the head of whatever list he was in. */
717 uData->next->prev = uData->prev;
719 uData->prev->next = uData->next;
725 (**head).prev = uData;
732 /* Returns non-zero if user has at least the minimum access.
733 * exempt_owner is set when handling !set, so the owner can set things
736 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
738 struct userData *uData;
739 struct chanData *cData = channel->channel_info;
740 unsigned short minimum = cData->lvlOpts[opt];
743 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
746 if(minimum <= uData->access)
748 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
753 /* Scan for other users authenticated to the same handle
754 still in the channel. If so, keep them listed as present.
756 user is optional, if not null, it skips checking that userNode
757 (for the handle_part function) */
759 scan_user_presence(struct userData *uData, struct userNode *user)
763 if(IsSuspended(uData->channel)
764 || IsUserSuspended(uData)
765 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
777 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
779 unsigned int eflags, argc;
781 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
783 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
784 if(!channel->channel_info
785 || IsSuspended(channel->channel_info)
787 || !ircncasecmp(text, "ACTION ", 7))
789 /* Figure out the minimum level needed to CTCP the channel */
790 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
792 /* We need to enforce against them; do so. */
795 argv[1] = user->nick;
797 if(GetUserMode(channel, user))
798 eflags |= ACTION_KICK;
799 switch(channel->channel_info->chOpts[chCTCPReaction]) {
800 default: case 'k': /* just do the kick */ break;
802 eflags |= ACTION_BAN;
805 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
806 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
809 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
810 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
813 argv[argc++] = bad_ctcp_reason;
814 eject_user(chanserv, channel, argc, argv, NULL, eflags);
818 chanserv_create_note_type(const char *name)
820 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
821 strcpy(ntype->name, name);
823 dict_insert(note_types, ntype->name, ntype);
828 chanserv_deref_note_type(void *data)
830 struct note_type *ntype = data;
832 if(--ntype->refs > 0)
838 chanserv_flush_note_type(struct note_type *ntype)
840 struct chanData *cData;
841 for(cData = channelList; cData; cData = cData->next)
842 dict_remove(cData->notes, ntype->name);
846 chanserv_truncate_notes(struct note_type *ntype)
848 struct chanData *cData;
850 unsigned int size = sizeof(*note) + ntype->max_length;
852 for(cData = channelList; cData; cData = cData->next) {
853 note = dict_find(cData->notes, ntype->name, NULL);
856 if(strlen(note->note) <= ntype->max_length)
858 dict_remove2(cData->notes, ntype->name, 1);
859 note = realloc(note, size);
860 note->note[ntype->max_length] = 0;
861 dict_insert(cData->notes, ntype->name, note);
865 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
868 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
871 unsigned int len = strlen(text);
873 if(len > type->max_length) len = type->max_length;
874 note = calloc(1, sizeof(*note) + len);
876 strncpy(note->setter, setter, sizeof(note->setter)-1);
877 memcpy(note->note, text, len);
879 dict_insert(channel->notes, type->name, note);
885 chanserv_free_note(void *data)
887 struct note *note = data;
889 chanserv_deref_note_type(note->type);
890 assert(note->type->refs > 0); /* must use delnote to remove the type */
894 static MODCMD_FUNC(cmd_createnote) {
895 struct note_type *ntype;
896 unsigned int arg = 1, existed = 0, max_length;
898 if((ntype = dict_find(note_types, argv[1], NULL)))
901 ntype = chanserv_create_note_type(argv[arg]);
902 if(!irccasecmp(argv[++arg], "privileged"))
905 ntype->set_access_type = NOTE_SET_PRIVILEGED;
906 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
908 else if(!irccasecmp(argv[arg], "channel"))
910 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
913 reply("CSMSG_INVALID_ACCESS", argv[arg]);
916 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
917 ntype->set_access.min_ulevel = ulvl;
919 else if(!irccasecmp(argv[arg], "setter"))
921 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
925 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
929 if(!irccasecmp(argv[++arg], "privileged"))
930 ntype->visible_type = NOTE_VIS_PRIVILEGED;
931 else if(!irccasecmp(argv[arg], "channel_users"))
932 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
933 else if(!irccasecmp(argv[arg], "all"))
934 ntype->visible_type = NOTE_VIS_ALL;
936 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
940 if((arg+1) >= argc) {
941 reply("MSG_MISSING_PARAMS", argv[0]);
944 max_length = strtoul(argv[++arg], NULL, 0);
945 if(max_length < 20 || max_length > 450)
947 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
950 if(existed && (max_length < ntype->max_length))
952 ntype->max_length = max_length;
953 chanserv_truncate_notes(ntype);
955 ntype->max_length = max_length;
958 reply("CSMSG_NOTE_MODIFIED", ntype->name);
960 reply("CSMSG_NOTE_CREATED", ntype->name);
965 dict_remove(note_types, ntype->name);
969 static MODCMD_FUNC(cmd_removenote) {
970 struct note_type *ntype;
973 ntype = dict_find(note_types, argv[1], NULL);
974 force = (argc > 2) && !irccasecmp(argv[2], "force");
977 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
984 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
987 chanserv_flush_note_type(ntype);
989 dict_remove(note_types, argv[1]);
990 reply("CSMSG_NOTE_DELETED", argv[1]);
995 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
999 if(orig->modes_set & change->modes_clear)
1001 if(orig->modes_clear & change->modes_set)
1003 if((orig->modes_set & MODE_KEY)
1004 && strcmp(orig->new_key, change->new_key))
1006 if((orig->modes_set & MODE_LIMIT)
1007 && (orig->new_limit != change->new_limit))
1012 static char max_length_text[MAXLEN+1][16];
1014 static struct helpfile_expansion
1015 chanserv_expand_variable(const char *variable)
1017 struct helpfile_expansion exp;
1019 if(!irccasecmp(variable, "notes"))
1022 exp.type = HF_TABLE;
1023 exp.value.table.length = 1;
1024 exp.value.table.width = 3;
1025 exp.value.table.flags = 0;
1026 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1027 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1028 exp.value.table.contents[0][0] = "Note Type";
1029 exp.value.table.contents[0][1] = "Visibility";
1030 exp.value.table.contents[0][2] = "Max Length";
1031 for(it=dict_first(note_types); it; it=iter_next(it))
1033 struct note_type *ntype = iter_data(it);
1036 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1037 row = exp.value.table.length++;
1038 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1039 exp.value.table.contents[row][0] = ntype->name;
1040 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1041 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1043 if(!max_length_text[ntype->max_length][0])
1044 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1045 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1050 exp.type = HF_STRING;
1051 exp.value.str = NULL;
1055 static struct chanData*
1056 register_channel(struct chanNode *cNode, char *registrar)
1058 struct chanData *channel;
1059 enum levelOption lvlOpt;
1060 enum charOption chOpt;
1062 channel = calloc(1, sizeof(struct chanData));
1064 channel->notes = dict_new();
1065 dict_set_free_data(channel->notes, chanserv_free_note);
1067 channel->registrar = strdup(registrar);
1068 channel->registered = now;
1069 channel->visited = now;
1070 channel->limitAdjusted = now;
1071 channel->flags = CHANNEL_DEFAULT_FLAGS;
1072 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1073 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1074 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1075 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1077 channel->prev = NULL;
1078 channel->next = channelList;
1081 channelList->prev = channel;
1082 channelList = channel;
1083 registered_channels++;
1085 channel->channel = cNode;
1087 cNode->channel_info = channel;
1092 static struct userData*
1093 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1095 struct userData *ud;
1097 if(access > UL_OWNER)
1100 ud = calloc(1, sizeof(*ud));
1101 ud->channel = channel;
1102 ud->handle = handle;
1104 ud->access = access;
1105 ud->info = info ? strdup(info) : NULL;
1108 ud->next = channel->users;
1110 channel->users->prev = ud;
1111 channel->users = ud;
1113 channel->userCount++;
1117 ud->u_next = ud->handle->channels;
1119 ud->u_next->u_prev = ud;
1120 ud->handle->channels = ud;
1125 static void unregister_channel(struct chanData *channel, const char *reason);
1128 del_channel_user(struct userData *user, int do_gc)
1130 struct chanData *channel = user->channel;
1132 channel->userCount--;
1136 user->prev->next = user->next;
1138 channel->users = user->next;
1140 user->next->prev = user->prev;
1143 user->u_prev->u_next = user->u_next;
1145 user->handle->channels = user->u_next;
1147 user->u_next->u_prev = user->u_prev;
1151 if(do_gc && !channel->users && !IsProtected(channel))
1152 unregister_channel(channel, "lost all users.");
1155 static void expire_ban(void *data);
1157 static struct banData*
1158 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1161 unsigned int ii, l1, l2;
1166 bd = malloc(sizeof(struct banData));
1168 bd->channel = channel;
1170 bd->triggered = triggered;
1171 bd->expires = expires;
1173 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1175 extern const char *hidden_host_suffix;
1176 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1180 l2 = strlen(old_name);
1183 if(irccasecmp(mask + l1 - l2, old_name))
1185 new_mask = alloca(MAXLEN);
1186 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1189 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1191 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1192 bd->reason = reason ? strdup(reason) : NULL;
1195 timeq_add(expires, expire_ban, bd);
1198 bd->next = channel->bans;
1200 channel->bans->prev = bd;
1202 channel->banCount++;
1209 del_channel_ban(struct banData *ban)
1211 ban->channel->banCount--;
1215 ban->prev->next = ban->next;
1217 ban->channel->bans = ban->next;
1220 ban->next->prev = ban->prev;
1223 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1232 expire_ban(void *data)
1234 struct banData *bd = data;
1235 if(!IsSuspended(bd->channel))
1237 struct banList bans;
1238 struct mod_chanmode change;
1240 bans = bd->channel->channel->banlist;
1241 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);
3544 free(tbl.contents[0]);
3549 msg_never = user_find_message(user, "MSG_NEVER");
3550 for(ii = 0; ii < matches; )
3556 else if(ban->expires)
3557 expires = intervalString(e_buffer, ban->expires - now);
3559 expires = msg_never;
3562 triggered = intervalString(t_buffer, now - ban->triggered);
3564 triggered = msg_never;
3566 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3567 tbl.contents[ii][0] = ban->mask;
3568 tbl.contents[ii][1] = ban->owner;
3569 tbl.contents[ii][2] = strdup(triggered);
3572 tbl.contents[ii][3] = strdup(expires);
3573 tbl.contents[ii][4] = ban->reason;
3576 tbl.contents[ii][3] = ban->reason;
3578 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3579 reply("MSG_MATCH_COUNT", matches);
3580 for(ii = 1; ii < tbl.length; ++ii)
3582 free((char*)tbl.contents[ii][2]);
3584 free((char*)tbl.contents[ii][3]);
3585 free(tbl.contents[ii]);
3587 free(tbl.contents[0]);
3593 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3595 struct chanData *cData = channel->channel_info;
3596 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3598 if(cData->topic_mask)
3599 return !match_ircglob(new_topic, cData->topic_mask);
3600 else if(cData->topic)
3601 return irccasecmp(new_topic, cData->topic);
3606 static CHANSERV_FUNC(cmd_topic)
3608 struct chanData *cData;
3611 cData = channel->channel_info;
3616 SetChannelTopic(channel, chanserv, cData->topic, 1);
3617 reply("CSMSG_TOPIC_SET", cData->topic);
3621 reply("CSMSG_NO_TOPIC", channel->name);
3625 topic = unsplit_string(argv + 1, argc - 1, NULL);
3626 /* If they say "!topic *", use an empty topic. */
3627 if((topic[0] == '*') && (topic[1] == 0))
3629 if(bad_topic(channel, user, topic))
3631 char *topic_mask = cData->topic_mask;
3634 char new_topic[TOPICLEN+1], tchar;
3635 int pos=0, starpos=-1, dpos=0, len;
3637 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3644 len = strlen(topic);
3645 if((dpos + len) > TOPICLEN)
3646 len = TOPICLEN + 1 - dpos;
3647 memcpy(new_topic+dpos, topic, len);
3651 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3652 default: new_topic[dpos++] = tchar; break;
3655 if((dpos > TOPICLEN) || tchar)
3658 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3659 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3662 new_topic[dpos] = 0;
3663 SetChannelTopic(channel, chanserv, new_topic, 1);
3665 reply("CSMSG_TOPIC_LOCKED", channel->name);
3670 SetChannelTopic(channel, chanserv, topic, 1);
3672 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3674 /* Grab the topic and save it as the default topic. */
3676 cData->topic = strdup(channel->topic);
3682 static CHANSERV_FUNC(cmd_mode)
3684 struct mod_chanmode *change;
3688 change = &channel->channel_info->modes;
3689 if(change->modes_set || change->modes_clear) {
3690 modcmd_chanmode_announce(change);
3691 reply("CSMSG_DEFAULTED_MODES", channel->name);
3693 reply("CSMSG_NO_MODES", channel->name);
3697 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3700 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3704 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3705 && mode_lock_violated(&channel->channel_info->modes, change))
3708 mod_chanmode_format(&channel->channel_info->modes, modes);
3709 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3713 modcmd_chanmode_announce(change);
3714 mod_chanmode_free(change);
3715 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3719 static CHANSERV_FUNC(cmd_invite)
3721 struct userData *uData;
3722 struct userNode *invite;
3724 uData = GetChannelUser(channel->channel_info, user->handle_info);
3728 if(!(invite = GetUserH(argv[1])))
3730 reply("MSG_NICK_UNKNOWN", argv[1]);
3737 if(GetUserMode(channel, invite))
3739 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3745 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3746 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3748 irc_invite(chanserv, invite, channel);
3750 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3755 static CHANSERV_FUNC(cmd_inviteme)
3757 if(GetUserMode(channel, user))
3759 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3762 if(channel->channel_info
3763 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3765 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3768 irc_invite(cmd->parent->bot, user, channel);
3773 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3776 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3778 /* We display things based on two dimensions:
3779 * - Issue time: present or absent
3780 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3781 * (in order of precedence, so something both expired and revoked
3782 * only counts as revoked)
3784 combo = (suspended->issued ? 4 : 0)
3785 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3787 case 0: /* no issue time, indefinite expiration */
3788 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3790 case 1: /* no issue time, expires in future */
3791 intervalString(buf1, suspended->expires-now);
3792 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3794 case 2: /* no issue time, expired */
3795 intervalString(buf1, now-suspended->expires);
3796 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3798 case 3: /* no issue time, revoked */
3799 intervalString(buf1, now-suspended->revoked);
3800 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3802 case 4: /* issue time set, indefinite expiration */
3803 intervalString(buf1, now-suspended->issued);
3804 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3806 case 5: /* issue time set, expires in future */
3807 intervalString(buf1, now-suspended->issued);
3808 intervalString(buf2, suspended->expires-now);
3809 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3811 case 6: /* issue time set, expired */
3812 intervalString(buf1, now-suspended->issued);
3813 intervalString(buf2, now-suspended->expires);
3814 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3816 case 7: /* issue time set, revoked */
3817 intervalString(buf1, now-suspended->issued);
3818 intervalString(buf2, now-suspended->revoked);
3819 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3822 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3827 static CHANSERV_FUNC(cmd_info)
3829 char modes[MAXLEN], buffer[INTERVALLEN];
3830 struct userData *uData, *owner;
3831 struct chanData *cData;
3832 struct do_not_register *dnr;
3837 cData = channel->channel_info;
3838 reply("CSMSG_CHANNEL_INFO", channel->name);
3840 uData = GetChannelUser(cData, user->handle_info);
3841 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3843 mod_chanmode_format(&cData->modes, modes);
3844 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3845 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3848 for(it = dict_first(cData->notes); it; it = iter_next(it))
3852 note = iter_data(it);
3853 if(!note_type_visible_to_user(cData, note->type, user))
3856 padding = PADLEN - 1 - strlen(iter_key(it));
3857 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3860 reply("CSMSG_CHANNEL_MAX", cData->max);
3861 for(owner = cData->users; owner; owner = owner->next)
3862 if(owner->access == UL_OWNER)
3863 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3864 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3865 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3866 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3867 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3869 privileged = IsStaff(user);
3870 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3871 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3873 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3874 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3876 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3878 struct suspended *suspended;
3879 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3880 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3881 show_suspension_info(cmd, user, suspended);
3883 else if(IsSuspended(cData))
3885 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3886 show_suspension_info(cmd, user, cData->suspended);
3891 static CHANSERV_FUNC(cmd_netinfo)
3893 extern time_t boot_time;
3894 extern unsigned long burst_length;
3895 char interval[INTERVALLEN];
3897 reply("CSMSG_NETWORK_INFO");
3898 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3899 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3900 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3901 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3902 reply("CSMSG_NETWORK_BANS", banCount);
3903 reply("CSMSG_CHANNEL_USERS", userCount);
3904 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3905 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3910 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3912 struct helpfile_table table;
3914 struct userNode *user;
3919 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3920 table.contents = alloca(list->used*sizeof(*table.contents));
3921 for(nn=0; nn<list->used; nn++)
3923 user = list->list[nn];
3924 if(user->modes & skip_flags)
3928 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3931 nick = alloca(strlen(user->nick)+3);
3932 sprintf(nick, "(%s)", user->nick);
3936 table.contents[table.length][0] = nick;
3939 table_send(chanserv, to->nick, 0, NULL, table);
3942 static CHANSERV_FUNC(cmd_ircops)
3944 reply("CSMSG_STAFF_OPERS");
3945 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3949 static CHANSERV_FUNC(cmd_helpers)
3951 reply("CSMSG_STAFF_HELPERS");
3952 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3956 static CHANSERV_FUNC(cmd_staff)
3958 reply("CSMSG_NETWORK_STAFF");
3959 cmd_ircops(CSFUNC_ARGS);
3960 cmd_helpers(CSFUNC_ARGS);
3964 static CHANSERV_FUNC(cmd_peek)
3966 struct modeNode *mn;
3967 char modes[MODELEN];
3969 struct helpfile_table table;
3971 irc_make_chanmode(channel, modes);
3973 reply("CSMSG_PEEK_INFO", channel->name);
3974 reply("CSMSG_PEEK_TOPIC", channel->topic);
3975 reply("CSMSG_PEEK_MODES", modes);
3976 reply("CSMSG_PEEK_USERS", channel->members.used);
3980 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3981 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3982 for(n = 0; n < channel->members.used; n++)
3984 mn = channel->members.list[n];
3985 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3987 table.contents[table.length] = alloca(sizeof(**table.contents));
3988 table.contents[table.length][0] = mn->user->nick;
3993 reply("CSMSG_PEEK_OPS");
3994 table_send(chanserv, user->nick, 0, NULL, table);
3997 reply("CSMSG_PEEK_NO_OPS");
4001 static MODCMD_FUNC(cmd_wipeinfo)
4003 struct handle_info *victim;
4004 struct userData *ud, *actor;
4007 actor = GetChannelUser(channel->channel_info, user->handle_info);
4008 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4010 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4012 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4015 if((ud->access >= actor->access) && (ud != actor))
4017 reply("MSG_USER_OUTRANKED", victim->handle);
4023 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4027 static CHANSERV_FUNC(cmd_resync)
4029 struct mod_chanmode *changes;
4030 struct chanData *cData = channel->channel_info;
4031 unsigned int ii, used;
4033 changes = mod_chanmode_alloc(channel->members.used * 2);
4034 for(ii = used = 0; ii < channel->members.used; ++ii)
4036 struct modeNode *mn = channel->members.list[ii];
4037 struct userData *uData;
4038 if(IsService(mn->user))
4040 /* must not change modes for this user */
4042 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4046 changes->args[used].mode = MODE_REMOVE | mn->modes;
4047 changes->args[used++].member = mn;
4050 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4052 if(mn->modes & MODE_CHANOP)
4054 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4055 changes->args[used++].member = mn;
4057 if(!(mn->modes & MODE_VOICE))
4059 changes->args[used].mode = MODE_VOICE;
4060 changes->args[used++].member = mn;
4065 if(!(mn->modes & MODE_CHANOP))
4067 changes->args[used].mode = MODE_CHANOP;
4068 changes->args[used++].member = mn;
4072 changes->argc = used;
4073 modcmd_chanmode_announce(changes);
4074 mod_chanmode_free(changes);
4075 reply("CSMSG_RESYNCED_USERS", channel->name);
4079 static CHANSERV_FUNC(cmd_seen)
4081 struct userData *uData;
4082 struct handle_info *handle;
4083 char seen[INTERVALLEN];
4087 if(!irccasecmp(argv[1], chanserv->nick))
4089 reply("CSMSG_IS_CHANSERV");
4093 if(!(handle = get_handle_info(argv[1])))
4095 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4099 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4101 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4106 reply("CSMSG_USER_PRESENT", handle->handle);
4107 else if(uData->seen)
4108 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4110 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4112 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4113 reply("CSMSG_USER_VACATION", handle->handle);
4118 static MODCMD_FUNC(cmd_names)
4120 struct userNode *targ;
4121 struct userData *targData;
4122 unsigned int ii, pos;
4125 for(ii=pos=0; ii<channel->members.used; ++ii)
4127 targ = channel->members.list[ii]->user;
4128 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4131 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4134 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4138 if(IsUserSuspended(targData))
4140 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4143 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4144 reply("CSMSG_END_NAMES", channel->name);
4149 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4151 switch(ntype->visible_type)
4153 case NOTE_VIS_ALL: return 1;
4154 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4155 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4160 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4162 struct userData *uData;
4164 switch(ntype->set_access_type)
4166 case NOTE_SET_CHANNEL_ACCESS:
4167 if(!user->handle_info)
4169 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4171 return uData->access >= ntype->set_access.min_ulevel;
4172 case NOTE_SET_CHANNEL_SETTER:
4173 return check_user_level(channel, user, lvlSetters, 1, 0);
4174 case NOTE_SET_PRIVILEGED: default:
4175 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4179 static CHANSERV_FUNC(cmd_note)
4181 struct chanData *cData;
4183 struct note_type *ntype;
4185 cData = channel->channel_info;
4188 reply("CSMSG_NOT_REGISTERED", channel->name);
4192 /* If no arguments, show all visible notes for the channel. */
4198 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4200 note = iter_data(it);
4201 if(!note_type_visible_to_user(cData, note->type, user))
4204 reply("CSMSG_NOTELIST_HEADER", channel->name);
4205 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4208 reply("CSMSG_NOTELIST_END", channel->name);
4210 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4212 /* If one argument, show the named note. */
4215 if((note = dict_find(cData->notes, argv[1], NULL))
4216 && note_type_visible_to_user(cData, note->type, user))
4218 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4220 else if((ntype = dict_find(note_types, argv[1], NULL))
4221 && note_type_visible_to_user(NULL, ntype, user))
4223 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4228 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4232 /* Assume they're trying to set a note. */
4236 ntype = dict_find(note_types, argv[1], NULL);
4239 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4242 else if(note_type_settable_by_user(channel, ntype, user))
4244 note_text = unsplit_string(argv+2, argc-2, NULL);
4245 if((note = dict_find(cData->notes, argv[1], NULL)))
4246 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4247 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4248 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4250 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4252 /* The note is viewable to staff only, so return 0
4253 to keep the invocation from getting logged (or
4254 regular users can see it in !events). */
4260 reply("CSMSG_NO_ACCESS");
4267 static CHANSERV_FUNC(cmd_delnote)
4272 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4273 || !note_type_settable_by_user(channel, note->type, user))
4275 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4278 dict_remove(channel->channel_info->notes, note->type->name);
4279 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4283 static CHANSERV_FUNC(cmd_events)
4285 struct logSearch discrim;
4286 struct logReport report;
4287 unsigned int matches, limit;
4289 limit = (argc > 1) ? atoi(argv[1]) : 10;
4290 if(limit < 1 || limit > 200) limit = 10;
4292 memset(&discrim, 0, sizeof(discrim));
4293 discrim.masks.bot = chanserv;
4294 discrim.masks.channel_name = channel->name;
4295 if(argc > 2) discrim.masks.command = argv[2];
4296 discrim.limit = limit;
4297 discrim.max_time = INT_MAX;
4298 discrim.severities = 1 << LOG_COMMAND;
4299 report.reporter = chanserv;
4301 reply("CSMSG_EVENT_SEARCH_RESULTS");
4302 matches = log_entry_search(&discrim, log_report_entry, &report);
4304 reply("MSG_MATCH_COUNT", matches);
4306 reply("MSG_NO_MATCHES");
4310 static CHANSERV_FUNC(cmd_say)
4316 msg = unsplit_string(argv + 1, argc - 1, NULL);
4317 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4319 else if(GetUserH(argv[1]))
4322 msg = unsplit_string(argv + 2, argc - 2, NULL);
4323 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4327 reply("You must specify the name of a channel or user.");
4333 static CHANSERV_FUNC(cmd_emote)
4339 /* CTCP is so annoying. */
4340 msg = unsplit_string(argv + 1, argc - 1, NULL);
4341 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4343 else if(GetUserH(argv[1]))
4345 msg = unsplit_string(argv + 2, argc - 2, NULL);
4346 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4350 reply("You must specify the name of a channel or user.");
4356 struct channelList *
4357 chanserv_support_channels(void)
4359 return &chanserv_conf.support_channels;
4362 static CHANSERV_FUNC(cmd_expire)
4364 int channel_count = registered_channels;
4365 expire_channels(NULL);
4366 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4371 chanserv_expire_suspension(void *data)
4373 struct suspended *suspended = data;
4374 struct chanNode *channel;
4375 struct mod_chanmode change;
4377 if(!suspended->expires || (now < suspended->expires))
4378 suspended->revoked = now;
4379 channel = suspended->cData->channel;
4380 suspended->cData->channel = channel;
4381 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4382 change.modes_set = change.modes_clear = 0;
4384 change.args[0].mode = MODE_CHANOP;
4385 change.args[0].member = AddChannelUser(chanserv, channel);
4386 mod_chanmode_announce(chanserv, channel, &change);
4389 static CHANSERV_FUNC(cmd_csuspend)
4391 struct suspended *suspended;
4392 char reason[MAXLEN];
4393 time_t expiry, duration;
4394 struct userData *uData;
4398 if(IsProtected(channel->channel_info))
4400 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4404 if(argv[1][0] == '!')
4406 else if(IsSuspended(channel->channel_info))
4408 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4409 show_suspension_info(cmd, user, channel->channel_info->suspended);
4413 if(!strcmp(argv[1], "0"))
4415 else if((duration = ParseInterval(argv[1])))
4416 expiry = now + duration;
4419 reply("MSG_INVALID_DURATION", argv[1]);
4423 unsplit_string(argv + 2, argc - 2, reason);
4425 suspended = calloc(1, sizeof(*suspended));
4426 suspended->revoked = 0;
4427 suspended->issued = now;
4428 suspended->suspender = strdup(user->handle_info->handle);
4429 suspended->expires = expiry;
4430 suspended->reason = strdup(reason);
4431 suspended->cData = channel->channel_info;
4432 suspended->previous = suspended->cData->suspended;
4433 suspended->cData->suspended = suspended;
4435 if(suspended->expires)
4436 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4438 if(IsSuspended(channel->channel_info))
4440 suspended->previous->revoked = now;
4441 if(suspended->previous->expires)
4442 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4443 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4444 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4448 /* Mark all users in channel as absent. */
4449 for(uData = channel->channel_info->users; uData; uData = uData->next)
4458 /* Mark the channel as suspended, then part. */
4459 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4460 DelChannelUser(chanserv, channel, suspended->reason, 0);
4461 reply("CSMSG_SUSPENDED", channel->name);
4462 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4463 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4468 static CHANSERV_FUNC(cmd_cunsuspend)
4470 struct suspended *suspended;
4471 char message[MAXLEN];
4473 if(!IsSuspended(channel->channel_info))
4475 reply("CSMSG_NOT_SUSPENDED", channel->name);
4479 suspended = channel->channel_info->suspended;
4481 /* Expire the suspension and join ChanServ to the channel. */
4482 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4483 chanserv_expire_suspension(suspended);
4484 reply("CSMSG_UNSUSPENDED", channel->name);
4485 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4486 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4490 typedef struct chanservSearch
4498 unsigned long flags;
4502 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4505 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4510 search = malloc(sizeof(struct chanservSearch));
4511 memset(search, 0, sizeof(*search));
4514 for(i = 0; i < argc; i++)
4516 /* Assume all criteria require arguments. */
4519 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4523 if(!irccasecmp(argv[i], "name"))
4524 search->name = argv[++i];
4525 else if(!irccasecmp(argv[i], "registrar"))
4526 search->registrar = argv[++i];
4527 else if(!irccasecmp(argv[i], "unvisited"))
4528 search->unvisited = ParseInterval(argv[++i]);
4529 else if(!irccasecmp(argv[i], "registered"))
4530 search->registered = ParseInterval(argv[++i]);
4531 else if(!irccasecmp(argv[i], "flags"))
4534 if(!irccasecmp(argv[i], "nodelete"))
4535 search->flags |= CHANNEL_NODELETE;
4536 else if(!irccasecmp(argv[i], "suspended"))
4537 search->flags |= CHANNEL_SUSPENDED;
4540 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4544 else if(!irccasecmp(argv[i], "limit"))
4545 search->limit = strtoul(argv[++i], NULL, 10);
4548 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4553 if(search->name && !strcmp(search->name, "*"))
4555 if(search->registrar && !strcmp(search->registrar, "*"))
4556 search->registrar = 0;
4565 chanserv_channel_match(struct chanData *channel, search_t search)
4567 const char *name = channel->channel->name;
4568 if((search->name && !match_ircglob(name, search->name)) ||
4569 (search->registrar && !channel->registrar) ||
4570 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4571 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4572 (search->registered && (now - channel->registered) > search->registered) ||
4573 (search->flags && ((search->flags & channel->flags) != search->flags)))
4580 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4582 struct chanData *channel;
4583 unsigned int matches = 0;
4585 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4587 if(!chanserv_channel_match(channel, search))
4597 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4602 search_print(struct chanData *channel, void *data)
4604 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4607 static CHANSERV_FUNC(cmd_search)
4610 unsigned int matches;
4611 channel_search_func action;
4615 if(!irccasecmp(argv[1], "count"))
4616 action = search_count;
4617 else if(!irccasecmp(argv[1], "print"))
4618 action = search_print;
4621 reply("CSMSG_ACTION_INVALID", argv[1]);
4625 search = chanserv_search_create(user, argc - 2, argv + 2);
4629 if(action == search_count)
4630 search->limit = INT_MAX;
4632 if(action == search_print)
4633 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4635 matches = chanserv_channel_search(search, action, user);
4638 reply("MSG_MATCH_COUNT", matches);
4640 reply("MSG_NO_MATCHES");
4646 static CHANSERV_FUNC(cmd_unvisited)
4648 struct chanData *cData;
4649 time_t interval = chanserv_conf.channel_expire_delay;
4650 char buffer[INTERVALLEN];
4651 unsigned int limit = 25, matches = 0;
4655 interval = ParseInterval(argv[1]);
4657 limit = atoi(argv[2]);
4660 intervalString(buffer, interval);
4661 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4663 for(cData = channelList; cData && matches < limit; cData = cData->next)
4665 if((now - cData->visited) < interval)
4668 intervalString(buffer, now - cData->visited);
4669 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4676 static MODCMD_FUNC(chan_opt_defaulttopic)
4682 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4684 reply("CSMSG_TOPIC_LOCKED", channel->name);
4688 topic = unsplit_string(argv+1, argc-1, NULL);
4690 free(channel->channel_info->topic);
4691 if(topic[0] == '*' && topic[1] == 0)
4693 topic = channel->channel_info->topic = NULL;
4697 topic = channel->channel_info->topic = strdup(topic);
4698 if(channel->channel_info->topic_mask
4699 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4700 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4702 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4705 if(channel->channel_info->topic)
4706 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4708 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4712 static MODCMD_FUNC(chan_opt_topicmask)
4716 struct chanData *cData = channel->channel_info;
4719 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4721 reply("CSMSG_TOPIC_LOCKED", channel->name);
4725 mask = unsplit_string(argv+1, argc-1, NULL);
4727 if(cData->topic_mask)
4728 free(cData->topic_mask);
4729 if(mask[0] == '*' && mask[1] == 0)
4731 cData->topic_mask = 0;
4735 cData->topic_mask = strdup(mask);
4737 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4738 else if(!match_ircglob(cData->topic, cData->topic_mask))
4739 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4743 if(channel->channel_info->topic_mask)
4744 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4746 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4750 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4754 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4758 if(greeting[0] == '*' && greeting[1] == 0)
4762 unsigned int length = strlen(greeting);
4763 if(length > chanserv_conf.greeting_length)
4765 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4768 *data = strdup(greeting);
4777 reply(name, user_find_message(user, "MSG_NONE"));
4781 static MODCMD_FUNC(chan_opt_greeting)
4783 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4786 static MODCMD_FUNC(chan_opt_usergreeting)
4788 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4791 static MODCMD_FUNC(chan_opt_modes)
4793 struct mod_chanmode *new_modes;
4794 char modes[MODELEN];
4798 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4800 reply("CSMSG_NO_ACCESS");
4803 if(argv[1][0] == '*' && argv[1][1] == 0)
4805 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4807 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4809 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4812 else if(new_modes->argc > 1)
4814 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4815 mod_chanmode_free(new_modes);
4820 channel->channel_info->modes = *new_modes;
4821 modcmd_chanmode_announce(new_modes);
4822 mod_chanmode_free(new_modes);
4826 mod_chanmode_format(&channel->channel_info->modes, modes);
4828 reply("CSMSG_SET_MODES", modes);
4830 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4834 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4836 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4838 struct chanData *cData = channel->channel_info;
4843 /* Set flag according to value. */
4844 if(enabled_string(argv[1]))
4846 cData->flags |= mask;
4849 else if(disabled_string(argv[1]))
4851 cData->flags &= ~mask;
4856 reply("MSG_INVALID_BINARY", argv[1]);
4862 /* Find current option value. */
4863 value = (cData->flags & mask) ? 1 : 0;
4867 reply(name, user_find_message(user, "MSG_ON"));
4869 reply(name, user_find_message(user, "MSG_OFF"));
4873 static MODCMD_FUNC(chan_opt_nodelete)
4875 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4877 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4881 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4884 static MODCMD_FUNC(chan_opt_dynlimit)
4886 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4889 /* TODO: reimplement
4891 static MODCMD_FUNC(chan_opt_userinfo)
4893 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4896 static MODCMD_FUNC(chan_opt_voice)
4898 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4901 static MODCMD_FUNC(chan_opt_topicsnarf)
4903 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4905 reply("CSMSG_TOPIC_LOCKED", channel->name);
4908 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4911 static MODCMD_FUNC(chan_opt_peoninvite)
4913 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4918 static MODCMD_FUNC(chan_opt_defaults)
4920 struct userData *uData;
4921 struct chanData *cData;
4922 const char *confirm;
4923 enum levelOption lvlOpt;
4924 enum charOption chOpt;
4926 cData = channel->channel_info;
4927 uData = GetChannelUser(cData, user->handle_info);
4928 if(!uData || (uData->access < UL_OWNER))
4930 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4933 confirm = make_confirmation_string(uData);
4934 if((argc < 2) || strcmp(argv[1], confirm))
4936 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4939 cData->flags = CHANNEL_DEFAULT_FLAGS;
4940 cData->modes = chanserv_conf.default_modes;
4941 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4942 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4943 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4944 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4945 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4950 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4952 struct chanData *cData = channel->channel_info;
4953 struct userData *uData;
4954 unsigned short value;
4958 if(!check_user_level(channel, user, option, 1, 1))
4960 reply("CSMSG_CANNOT_SET");
4963 value = user_level_from_name(argv[1], UL_OWNER+1);
4964 if(!value && strcmp(argv[1], "0"))
4966 reply("CSMSG_INVALID_ACCESS", argv[1]);
4969 uData = GetChannelUser(cData, user->handle_info);
4970 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4972 reply("CSMSG_BAD_SETLEVEL");
4978 if(value > cData->lvlOpts[lvlGiveOps])
4980 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4985 if(value < cData->lvlOpts[lvlGiveVoice])
4987 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4992 /* This test only applies to owners, since non-owners
4993 * trying to set an option to above their level get caught
4994 * by the CSMSG_BAD_SETLEVEL test above.
4996 if(value > uData->access)
4998 reply("CSMSG_BAD_SETTERS");
5005 cData->lvlOpts[option] = value;
5007 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5011 static MODCMD_FUNC(chan_opt_enfops)
5013 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5016 static MODCMD_FUNC(chan_opt_giveops)
5018 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5021 static MODCMD_FUNC(chan_opt_enfmodes)
5023 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5026 static MODCMD_FUNC(chan_opt_enftopic)
5028 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5031 static MODCMD_FUNC(chan_opt_pubcmd)
5033 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5036 static MODCMD_FUNC(chan_opt_setters)
5038 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5041 static MODCMD_FUNC(chan_opt_ctcpusers)
5043 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5046 static MODCMD_FUNC(chan_opt_userinfo)
5048 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5051 static MODCMD_FUNC(chan_opt_givevoice)
5053 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5056 static MODCMD_FUNC(chan_opt_topicsnarf)
5058 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5061 static MODCMD_FUNC(chan_opt_inviteme)
5063 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5067 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5069 struct chanData *cData = channel->channel_info;
5070 int count = charOptions[option].count, index;
5074 index = atoi(argv[1]);
5076 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5078 reply("CSMSG_INVALID_NUMERIC", index);
5079 /* Show possible values. */
5080 for(index = 0; index < count; index++)
5081 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5085 cData->chOpts[option] = charOptions[option].values[index].value;
5089 /* Find current option value. */
5092 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5096 /* Somehow, the option value is corrupt; reset it to the default. */
5097 cData->chOpts[option] = charOptions[option].default_value;
5102 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5106 static MODCMD_FUNC(chan_opt_protect)
5108 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5111 static MODCMD_FUNC(chan_opt_toys)
5113 return channel_multiple_option(chToys, CSFUNC_ARGS);
5116 static MODCMD_FUNC(chan_opt_ctcpreaction)
5118 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5121 static MODCMD_FUNC(chan_opt_topicrefresh)
5123 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5126 static struct svccmd_list set_shows_list;
5129 handle_svccmd_unbind(struct svccmd *target) {
5131 for(ii=0; ii<set_shows_list.used; ++ii)
5132 if(target == set_shows_list.list[ii])
5133 set_shows_list.used = 0;
5136 static CHANSERV_FUNC(cmd_set)
5138 struct svccmd *subcmd;
5142 /* Check if we need to (re-)initialize set_shows_list. */
5143 if(!set_shows_list.used)
5145 if(!set_shows_list.size)
5147 set_shows_list.size = chanserv_conf.set_shows->used;
5148 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5150 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5152 const char *name = chanserv_conf.set_shows->list[ii];
5153 sprintf(buf, "%s %s", argv[0], name);
5154 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5157 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5160 svccmd_list_append(&set_shows_list, subcmd);
5166 reply("CSMSG_CHANNEL_OPTIONS");
5167 for(ii = 0; ii < set_shows_list.used; ii++)
5169 subcmd = set_shows_list.list[ii];
5170 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5175 sprintf(buf, "%s %s", argv[0], argv[1]);
5176 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5179 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5182 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5184 reply("CSMSG_NO_ACCESS");
5188 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5192 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5194 struct userData *uData;
5196 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5199 reply("CSMSG_NOT_USER", channel->name);
5205 /* Just show current option value. */
5207 else if(enabled_string(argv[1]))
5209 uData->flags |= mask;
5211 else if(disabled_string(argv[1]))
5213 uData->flags &= ~mask;
5217 reply("MSG_INVALID_BINARY", argv[1]);
5221 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5225 static MODCMD_FUNC(user_opt_noautoop)
5227 struct userData *uData;
5229 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5232 reply("CSMSG_NOT_USER", channel->name);
5235 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5236 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5238 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5241 static MODCMD_FUNC(user_opt_autoinvite)
5243 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5246 static MODCMD_FUNC(user_opt_info)
5248 struct userData *uData;
5251 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5255 /* If they got past the command restrictions (which require access)
5256 * but fail this test, we have some fool with security override on.
5258 reply("CSMSG_NOT_USER", channel->name);
5264 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5267 if(infoline[0] == '*' && infoline[1] == 0)
5270 uData->info = strdup(infoline);
5273 reply("CSMSG_USET_INFO", uData->info);
5275 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5279 struct svccmd_list uset_shows_list;
5281 static CHANSERV_FUNC(cmd_uset)
5283 struct svccmd *subcmd;
5287 /* Check if we need to (re-)initialize uset_shows_list. */
5288 if(!uset_shows_list.used)
5292 "NoAutoOp", "AutoInvite", "Info"
5295 if(!uset_shows_list.size)
5297 uset_shows_list.size = ArrayLength(options);
5298 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5300 for(ii = 0; ii < ArrayLength(options); ii++)
5302 const char *name = options[ii];
5303 sprintf(buf, "%s %s", argv[0], name);
5304 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5307 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5310 svccmd_list_append(&uset_shows_list, subcmd);
5316 /* Do this so options are presented in a consistent order. */
5317 reply("CSMSG_USER_OPTIONS");
5318 for(ii = 0; ii < uset_shows_list.used; ii++)
5319 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5323 sprintf(buf, "%s %s", argv[0], argv[1]);
5324 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5327 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5331 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5334 static CHANSERV_FUNC(cmd_giveownership)
5336 struct handle_info *new_owner_hi;
5337 struct userData *new_owner, *curr_user;
5338 struct chanData *cData = channel->channel_info;
5339 struct do_not_register *dnr;
5341 unsigned short co_access;
5342 char reason[MAXLEN];
5345 curr_user = GetChannelAccess(cData, user->handle_info);
5346 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5347 if(!curr_user || (curr_user->access != UL_OWNER))
5349 struct userData *owner = NULL;
5350 for(curr_user = channel->channel_info->users;
5352 curr_user = curr_user->next)
5354 if(curr_user->access != UL_OWNER)
5358 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5365 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5367 if(new_owner_hi == user->handle_info)
5369 reply("CSMSG_NO_TRANSFER_SELF");
5372 new_owner = GetChannelAccess(cData, new_owner_hi);
5375 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5378 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5380 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5383 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5384 if(!IsHelping(user))
5385 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5387 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5390 if(new_owner->access >= UL_COOWNER)
5391 co_access = new_owner->access;
5393 co_access = UL_COOWNER;
5394 new_owner->access = UL_OWNER;
5396 curr_user->access = co_access;
5397 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5398 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5399 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5403 static CHANSERV_FUNC(cmd_suspend)
5405 struct handle_info *hi;
5406 struct userData *self, *target;
5409 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5410 self = GetChannelUser(channel->channel_info, user->handle_info);
5411 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5413 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5416 if(target->access >= self->access)
5418 reply("MSG_USER_OUTRANKED", hi->handle);
5421 if(target->flags & USER_SUSPENDED)
5423 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5428 target->present = 0;
5431 target->flags |= USER_SUSPENDED;
5432 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5436 static CHANSERV_FUNC(cmd_unsuspend)
5438 struct handle_info *hi;
5439 struct userData *self, *target;
5442 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5443 self = GetChannelUser(channel->channel_info, user->handle_info);
5444 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5446 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5449 if(target->access >= self->access)
5451 reply("MSG_USER_OUTRANKED", hi->handle);
5454 if(!(target->flags & USER_SUSPENDED))
5456 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5459 target->flags &= ~USER_SUSPENDED;
5460 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5464 static MODCMD_FUNC(cmd_deleteme)
5466 struct handle_info *hi;
5467 struct userData *target;
5468 const char *confirm_string;
5469 unsigned short access;
5472 hi = user->handle_info;
5473 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5475 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5478 if(target->access == UL_OWNER)
5480 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5483 confirm_string = make_confirmation_string(target);
5484 if((argc < 2) || strcmp(argv[1], confirm_string))
5486 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5489 access = target->access;
5490 channel_name = strdup(channel->name);
5491 del_channel_user(target, 1);
5492 reply("CSMSG_DELETED_YOU", access, channel_name);
5498 chanserv_refresh_topics(UNUSED_ARG(void *data))
5500 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5501 struct chanData *cData;
5504 for(cData = channelList; cData; cData = cData->next)
5506 if(IsSuspended(cData))
5508 opt = cData->chOpts[chTopicRefresh];
5511 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5514 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5515 cData->last_refresh = refresh_num;
5517 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5520 static CHANSERV_FUNC(cmd_unf)
5524 char response[MAXLEN];
5525 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5526 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5527 irc_privmsg(cmd->parent->bot, channel->name, response);
5530 reply("CSMSG_UNF_RESPONSE");
5534 static CHANSERV_FUNC(cmd_ping)
5538 char response[MAXLEN];
5539 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5540 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5541 irc_privmsg(cmd->parent->bot, channel->name, response);
5544 reply("CSMSG_PING_RESPONSE");
5548 static CHANSERV_FUNC(cmd_wut)
5552 char response[MAXLEN];
5553 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5554 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5555 irc_privmsg(cmd->parent->bot, channel->name, response);
5558 reply("CSMSG_WUT_RESPONSE");
5562 static CHANSERV_FUNC(cmd_8ball)
5564 unsigned int i, j, accum;
5569 for(i=1; i<argc; i++)
5570 for(j=0; argv[i][j]; j++)
5571 accum = (accum << 5) - accum + toupper(argv[i][j]);
5572 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5575 char response[MAXLEN];
5576 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5577 irc_privmsg(cmd->parent->bot, channel->name, response);
5580 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5584 static CHANSERV_FUNC(cmd_d)
5586 unsigned long sides, count, modifier, ii, total;
5587 char response[MAXLEN], *sep;
5591 if((count = strtoul(argv[1], &sep, 10)) < 1)
5601 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5602 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5606 else if((sep[0] == '-') && isdigit(sep[1]))
5607 modifier = strtoul(sep, NULL, 10);
5608 else if((sep[0] == '+') && isdigit(sep[1]))
5609 modifier = strtoul(sep+1, NULL, 10);
5616 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5621 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5624 for(total = ii = 0; ii < count; ++ii)
5625 total += (rand() % sides) + 1;
5628 if((count > 1) || modifier)
5630 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5631 sprintf(response, fmt, total, count, sides, modifier);
5635 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5636 sprintf(response, fmt, total, sides);
5639 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5641 send_message_type(4, user, cmd->parent->bot, "%s", response);
5645 static CHANSERV_FUNC(cmd_huggle)
5647 char response[MAXLEN];
5649 /* CTCP must be via PRIVMSG, never notice */
5652 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5653 sprintf(response, fmt, user->nick);
5654 irc_privmsg(cmd->parent->bot, channel->name, response);
5658 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5659 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5665 chanserv_adjust_limit(void *data)
5667 struct mod_chanmode change;
5668 struct chanData *cData = data;
5669 struct chanNode *channel = cData->channel;
5672 if(IsSuspended(cData))
5675 cData->limitAdjusted = now;
5676 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5677 if(cData->modes.modes_set & MODE_LIMIT)
5679 if(limit > cData->modes.new_limit)
5680 limit = cData->modes.new_limit;
5681 else if(limit == cData->modes.new_limit)
5685 change.modes_set = MODE_LIMIT;
5686 change.modes_clear = 0;
5687 change.new_limit = limit;
5689 mod_chanmode_announce(chanserv, channel, &change);
5693 handle_new_channel(struct chanNode *channel)
5695 struct chanData *cData;
5697 if(!(cData = channel->channel_info))
5700 if(cData->modes.modes_set || cData->modes.modes_clear)
5701 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5703 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5704 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5707 /* Welcome to my worst nightmare. Warning: Read (or modify)
5708 the code below at your own risk. */
5710 handle_join(struct modeNode *mNode)
5712 struct mod_chanmode change;
5713 struct userNode *user = mNode->user;
5714 struct chanNode *channel = mNode->channel;
5715 struct chanData *cData;
5716 struct userData *uData = NULL;
5717 struct banData *bData;
5718 struct handle_info *handle;
5719 unsigned int modes = 0, info = 0;
5722 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5725 cData = channel->channel_info;
5726 if(channel->members.used > cData->max)
5727 cData->max = channel->members.used;
5729 /* Check for bans. If they're joining through a ban, one of two
5731 * 1: Join during a netburst, by riding the break. Kick them
5732 * unless they have ops or voice in the channel.
5733 * 2: They're allowed to join through the ban (an invite in
5734 * ircu2.10, or a +e on Hybrid, or something).
5735 * If they're not joining through a ban, and the banlist is not
5736 * full, see if they're on the banlist for the channel. If so,
5739 if(user->uplink->burst && !mNode->modes)
5742 for(ii = 0; ii < channel->banlist.used; ii++)
5744 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5746 /* Riding a netburst. Naughty. */
5747 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5753 change.modes_set = change.modes_clear = 0;
5755 if(channel->banlist.used < MAXBANS)
5757 /* Not joining through a ban. */
5758 for(bData = cData->bans;
5759 bData && !user_matches_glob(user, bData->mask, 1);
5760 bData = bData->next);
5764 char kick_reason[MAXLEN];
5765 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5767 bData->triggered = now;
5768 if(bData != cData->bans)
5770 /* Shuffle the ban to the head of the list. */
5772 bData->next->prev = bData->prev;
5774 bData->prev->next = bData->next;
5777 bData->next = cData->bans;
5780 cData->bans->prev = bData;
5781 cData->bans = bData;
5784 change.args[0].mode = MODE_BAN;
5785 change.args[0].hostmask = bData->mask;
5786 mod_chanmode_announce(chanserv, channel, &change);
5787 KickChannelUser(user, channel, chanserv, kick_reason);
5792 /* ChanServ will not modify the limits in join-flooded channels.
5793 It will also skip DynLimit processing when the user (or srvx)
5794 is bursting in, because there are likely more incoming. */
5795 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5796 && !user->uplink->burst
5797 && !channel->join_flooded
5798 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5800 /* The user count has begun "bumping" into the channel limit,
5801 so set a timer to raise the limit a bit. Any previous
5802 timers are removed so three incoming users within the delay
5803 results in one limit change, not three. */
5805 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5806 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5809 if(channel->join_flooded)
5811 /* don't automatically give ops or voice during a join flood */
5813 else if(cData->lvlOpts[lvlGiveOps] == 0)
5814 modes |= MODE_CHANOP;
5815 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5816 modes |= MODE_VOICE;
5818 greeting = cData->greeting;
5819 if(user->handle_info)
5821 handle = user->handle_info;
5823 if(IsHelper(user) && !IsHelping(user))
5826 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5828 if(channel == chanserv_conf.support_channels.list[ii])
5830 HANDLE_SET_FLAG(user->handle_info, HELPING);
5836 uData = GetTrueChannelAccess(cData, handle);
5837 if(uData && !IsUserSuspended(uData))
5839 /* Ops and above were handled by the above case. */
5840 if(IsUserAutoOp(uData))
5842 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5843 modes |= MODE_CHANOP;
5844 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5845 modes |= MODE_VOICE;
5847 if(uData->access >= UL_PRESENT)
5848 cData->visited = now;
5849 if(cData->user_greeting)
5850 greeting = cData->user_greeting;
5852 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5853 && ((now - uData->seen) >= chanserv_conf.info_delay)
5860 if(!user->uplink->burst)
5864 if(modes & MODE_CHANOP)
5865 modes &= ~MODE_VOICE;
5866 change.args[0].mode = modes;
5867 change.args[0].member = mNode;
5868 mod_chanmode_announce(chanserv, channel, &change);
5870 if(greeting && !user->uplink->burst)
5871 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5873 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5879 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5881 struct mod_chanmode change;
5882 struct userData *channel;
5883 unsigned int ii, jj;
5885 if(!user->handle_info)
5888 change.modes_set = change.modes_clear = 0;
5890 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5892 struct chanNode *cn;
5893 struct modeNode *mn;
5894 if(IsUserSuspended(channel)
5895 || IsSuspended(channel->channel)
5896 || !(cn = channel->channel->channel))
5899 mn = GetUserMode(cn, user);
5902 if(!IsUserSuspended(channel)
5903 && IsUserAutoInvite(channel)
5904 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5905 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5907 irc_invite(chanserv, user, cn);
5911 if(channel->access >= UL_PRESENT)
5912 channel->channel->visited = now;
5914 if(IsUserAutoOp(channel))
5916 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5917 change.args[0].mode = MODE_CHANOP;
5918 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5919 change.args[0].mode = MODE_VOICE;
5921 change.args[0].mode = 0;
5922 change.args[0].member = mn;
5923 if(change.args[0].mode)
5924 mod_chanmode_announce(chanserv, cn, &change);
5927 channel->seen = now;
5928 channel->present = 1;
5931 for(ii = 0; ii < user->channels.used; ++ii)
5933 struct chanNode *channel = user->channels.list[ii]->channel;
5934 struct banData *ban;
5936 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5937 || !channel->channel_info)
5939 for(jj = 0; jj < channel->banlist.used; ++jj)
5940 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5942 if(jj < channel->banlist.used)
5944 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5946 char kick_reason[MAXLEN];
5947 if(!user_matches_glob(user, ban->mask, 1))
5949 change.args[0].mode = MODE_BAN;
5950 change.args[0].hostmask = ban->mask;
5951 mod_chanmode_announce(chanserv, channel, &change);
5952 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5953 KickChannelUser(user, channel, chanserv, kick_reason);
5954 ban->triggered = now;
5959 if(IsSupportHelper(user))
5961 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5963 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5965 HANDLE_SET_FLAG(user->handle_info, HELPING);
5973 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5975 struct chanData *cData;
5976 struct userData *uData;
5978 cData = channel->channel_info;
5979 if(!cData || IsSuspended(cData) || IsLocal(user))
5982 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5984 /* Allow for a bit of padding so that the limit doesn't
5985 track the user count exactly, which could get annoying. */
5986 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5988 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5989 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5993 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5994 scan_user_presence(uData, user);
5996 if(IsHelping(user) && IsSupportHelper(user))
5998 unsigned int ii, jj;
5999 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6001 for(jj = 0; jj < user->channels.used; ++jj)
6002 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6004 if(jj < user->channels.used)
6007 if(ii == chanserv_conf.support_channels.used)
6008 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6013 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6015 if(!channel->channel_info || !kicker || IsService(kicker)
6016 || (kicker == victim) || IsSuspended(channel->channel_info)
6017 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6020 if(protect_user(victim, kicker, channel->channel_info))
6022 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6023 KickChannelUser(kicker, channel, chanserv, reason);
6028 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6030 struct chanData *cData;
6032 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6035 cData = channel->channel_info;
6036 if(bad_topic(channel, user, channel->topic))
6038 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6039 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6040 SetChannelTopic(channel, chanserv, old_topic, 1);
6041 else if(cData->topic)
6042 SetChannelTopic(channel, chanserv, cData->topic, 1);
6045 /* With topicsnarf, grab the topic and save it as the default topic. */
6046 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6049 cData->topic = strdup(channel->topic);
6055 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6057 struct mod_chanmode *bounce = NULL;
6058 unsigned int bnc, ii;
6061 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6064 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6065 && mode_lock_violated(&channel->channel_info->modes, change))
6067 char correct[MAXLEN];
6068 bounce = mod_chanmode_alloc(change->argc + 1);
6069 *bounce = channel->channel_info->modes;
6070 mod_chanmode_format(&channel->channel_info->modes, correct);
6071 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6073 for(ii = bnc = 0; ii < change->argc; ++ii)
6075 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6077 const struct userNode *victim = change->args[ii].member->user;
6078 if(!protect_user(victim, user, channel->channel_info))
6081 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6084 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6085 bounce->args[bnc].member = GetUserMode(channel, user);
6086 if(bounce->args[bnc].member)
6090 bounce->args[bnc].mode = MODE_CHANOP;
6091 bounce->args[bnc].member = change->args[ii].member;
6093 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6095 else if(change->args[ii].mode & MODE_CHANOP)
6097 const struct userNode *victim = change->args[ii].member->user;
6098 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6101 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6102 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6103 bounce->args[bnc].member = change->args[ii].member;
6106 else if(change->args[ii].mode & MODE_BAN)
6108 const char *ban = change->args[ii].hostmask;
6109 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6112 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6113 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6114 bounce->args[bnc].hostmask = ban;
6116 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6121 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6122 mod_chanmode_announce(chanserv, channel, bounce);
6123 mod_chanmode_free(bounce);
6128 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6130 struct chanNode *channel;
6131 struct banData *bData;
6132 struct mod_chanmode change;
6133 unsigned int ii, jj;
6134 char kick_reason[MAXLEN];
6136 change.modes_set = change.modes_clear = 0;
6138 change.args[0].mode = MODE_BAN;
6139 for(ii = 0; ii < user->channels.used; ++ii)
6141 channel = user->channels.list[ii]->channel;
6142 /* Need not check for bans if they're opped or voiced. */
6143 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6145 /* Need not check for bans unless channel registration is active. */
6146 if(!channel->channel_info || IsSuspended(channel->channel_info))
6148 /* Look for a matching ban already on the channel. */
6149 for(jj = 0; jj < channel->banlist.used; ++jj)
6150 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6152 /* Need not act if we found one. */
6153 if(jj < channel->banlist.used)
6155 /* Look for a matching ban in this channel. */
6156 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6158 if(!user_matches_glob(user, bData->mask, 1))
6160 change.args[0].hostmask = bData->mask;
6161 mod_chanmode_announce(chanserv, channel, &change);
6162 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6163 KickChannelUser(user, channel, chanserv, kick_reason);
6164 bData->triggered = now;
6165 break; /* we don't need to check any more bans in the channel */
6170 static void handle_rename(struct handle_info *handle, const char *old_handle)
6172 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6176 dict_remove2(handle_dnrs, old_handle, 1);
6177 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6178 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6183 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6185 struct userNode *h_user;
6187 if(handle->channels)
6189 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6190 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6192 while(handle->channels)
6193 del_channel_user(handle->channels, 1);
6198 handle_server_link(UNUSED_ARG(struct server *server))
6200 struct chanData *cData;
6202 for(cData = channelList; cData; cData = cData->next)
6204 if(!IsSuspended(cData))
6205 cData->may_opchan = 1;
6206 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6207 && !cData->channel->join_flooded
6208 && ((cData->channel->limit - cData->channel->members.used)
6209 < chanserv_conf.adjust_threshold))
6211 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6212 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6218 chanserv_conf_read(void)
6222 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6223 struct mod_chanmode *change;
6224 struct string_list *strlist;
6225 struct chanNode *chan;
6228 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6230 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6233 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6234 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6235 chanserv_conf.support_channels.used = 0;
6236 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6238 for(ii = 0; ii < strlist->used; ++ii)
6240 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6243 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6245 channelList_append(&chanserv_conf.support_channels, chan);
6248 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6251 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6254 chan = AddChannel(str, now, str2, NULL);
6256 channelList_append(&chanserv_conf.support_channels, chan);
6258 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6259 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6260 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6261 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6262 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6263 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6264 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6265 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6266 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6267 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6268 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6269 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6270 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6271 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6272 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6273 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6274 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6275 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6276 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6277 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6278 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6280 NickChange(chanserv, str, 0);
6281 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6282 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6283 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6284 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6285 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6286 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6287 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6288 chanserv_conf.max_owned = str ? atoi(str) : 5;
6289 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6290 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6291 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6292 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6293 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6294 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6295 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6298 safestrncpy(mode_line, str, sizeof(mode_line));
6299 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6300 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6302 chanserv_conf.default_modes = *change;
6303 mod_chanmode_free(change);
6305 free_string_list(chanserv_conf.set_shows);
6306 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6308 strlist = string_list_copy(strlist);
6311 static const char *list[] = {
6312 /* free form text */
6313 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6314 /* options based on user level */
6315 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6316 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6317 /* multiple choice options */
6318 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6319 /* binary options */
6320 "DynLimit", "NoDelete",
6325 strlist = alloc_string_list(ArrayLength(list)-1);
6326 for(ii=0; list[ii]; ii++)
6327 string_list_append(strlist, strdup(list[ii]));
6329 chanserv_conf.set_shows = strlist;
6330 /* We don't look things up now, in case the list refers to options
6331 * defined by modules initialized after this point. Just mark the
6332 * function list as invalid, so it will be initialized.
6334 set_shows_list.used = 0;
6335 free_string_list(chanserv_conf.eightball);
6336 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6339 strlist = string_list_copy(strlist);
6343 strlist = alloc_string_list(4);
6344 string_list_append(strlist, strdup("Yes."));
6345 string_list_append(strlist, strdup("No."));
6346 string_list_append(strlist, strdup("Maybe so."));
6348 chanserv_conf.eightball = strlist;
6349 free_string_list(chanserv_conf.old_ban_names);
6350 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6352 strlist = string_list_copy(strlist);
6354 strlist = alloc_string_list(2);
6355 chanserv_conf.old_ban_names = strlist;
6359 chanserv_note_type_read(const char *key, struct record_data *rd)
6362 struct note_type *ntype;
6365 if(!(obj = GET_RECORD_OBJECT(rd)))
6367 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6370 if(!(ntype = chanserv_create_note_type(key)))
6372 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6376 /* Figure out set access */
6377 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6379 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6380 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6382 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6384 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6385 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6387 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6389 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6393 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6394 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6395 ntype->set_access.min_opserv = 0;
6398 /* Figure out visibility */
6399 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6400 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6401 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6402 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6403 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6404 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6405 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6406 ntype->visible_type = NOTE_VIS_ALL;
6408 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6410 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6411 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6415 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6417 struct handle_info *handle;
6418 struct userData *uData;
6419 char *seen, *inf, *flags;
6421 unsigned short access;
6423 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6425 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6429 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6430 if(access > UL_OWNER)
6432 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6436 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6437 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6438 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6439 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6440 handle = get_handle_info(key);
6443 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6447 uData = add_channel_user(chan, handle, access, last_seen, inf);
6448 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6452 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6454 struct banData *bData;
6455 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6456 time_t set_time, triggered_time, expires_time;
6458 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6460 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6464 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6465 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6466 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6467 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6468 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6469 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6471 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6472 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6474 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6476 expires_time = set_time + atoi(s_duration);
6480 if(expires_time && (expires_time < now))
6483 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6486 static struct suspended *
6487 chanserv_read_suspended(dict_t obj)
6489 struct suspended *suspended = calloc(1, sizeof(*suspended));
6493 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6494 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6495 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6496 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6497 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6498 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6499 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6500 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6501 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6502 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6507 chanserv_channel_read(const char *key, struct record_data *hir)
6509 struct suspended *suspended;
6510 struct mod_chanmode *modes;
6511 struct chanNode *cNode;
6512 struct chanData *cData;
6513 struct dict *channel, *obj;
6514 char *str, *argv[10];
6518 channel = hir->d.object;
6520 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6523 cNode = AddChannel(key, now, NULL, NULL);
6526 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6529 cData = register_channel(cNode, str);
6532 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6536 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6538 enum levelOption lvlOpt;
6539 enum charOption chOpt;
6541 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6542 cData->flags = atoi(str);
6544 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6546 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6548 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6549 else if(levelOptions[lvlOpt].old_flag)
6551 if(cData->flags & levelOptions[lvlOpt].old_flag)
6552 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6554 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6558 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6560 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6562 cData->chOpts[chOpt] = str[0];
6565 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6567 enum levelOption lvlOpt;
6568 enum charOption chOpt;
6571 cData->flags = base64toint(str, 5);
6572 count = strlen(str += 5);
6573 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6576 if(levelOptions[lvlOpt].old_flag)
6578 if(cData->flags & levelOptions[lvlOpt].old_flag)
6579 lvl = levelOptions[lvlOpt].flag_value;
6581 lvl = levelOptions[lvlOpt].default_value;
6583 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6585 case 'c': lvl = UL_COOWNER; break;
6586 case 'm': lvl = UL_MASTER; break;
6587 case 'n': lvl = UL_OWNER+1; break;
6588 case 'o': lvl = UL_OP; break;
6589 case 'p': lvl = UL_PEON; break;
6590 case 'w': lvl = UL_OWNER; break;
6591 default: lvl = 0; break;
6593 cData->lvlOpts[lvlOpt] = lvl;
6595 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6596 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6599 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6601 suspended = chanserv_read_suspended(obj);
6602 cData->suspended = suspended;
6603 suspended->cData = cData;
6604 /* We could use suspended->expires and suspended->revoked to
6605 * set the CHANNEL_SUSPENDED flag, but we don't. */
6607 else if(cData->flags & CHANNEL_SUSPENDED)
6609 suspended = calloc(1, sizeof(*suspended));
6610 suspended->issued = 0;
6611 suspended->revoked = 0;
6612 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6613 suspended->expires = str ? atoi(str) : 0;
6614 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6615 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6616 suspended->reason = strdup(str ? str : "No reason");
6617 suspended->previous = NULL;
6618 cData->suspended = suspended;
6619 suspended->cData = cData;
6624 if((cData->flags & CHANNEL_SUSPENDED)
6625 && suspended->expires
6626 && (suspended->expires <= now))
6628 cData->flags &= ~CHANNEL_SUSPENDED;
6631 if(!(cData->flags & CHANNEL_SUSPENDED))
6633 struct mod_chanmode change;
6634 change.modes_set = change.modes_clear = 0;
6636 change.args[0].mode = MODE_CHANOP;
6637 change.args[0].member = AddChannelUser(chanserv, cNode);
6638 mod_chanmode_announce(chanserv, cNode, &change);
6640 else if(suspended->expires > now)
6642 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6645 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6646 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6647 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6648 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6649 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6650 cData->max = str ? atoi(str) : 0;
6651 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6652 cData->greeting = str ? strdup(str) : NULL;
6653 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6654 cData->user_greeting = str ? strdup(str) : NULL;
6655 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6656 cData->topic_mask = str ? strdup(str) : NULL;
6657 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6658 cData->topic = str ? strdup(str) : NULL;
6660 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6661 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6662 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6663 cData->modes = *modes;
6664 if(cData->modes.argc > 1)
6665 cData->modes.argc = 1;
6666 if(!IsSuspended(cData))
6667 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6668 mod_chanmode_free(modes);
6671 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6672 for(it = dict_first(obj); it; it = iter_next(it))
6673 user_read_helper(iter_key(it), iter_data(it), cData);
6675 if(!cData->users && !IsProtected(cData))
6677 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6678 unregister_channel(cData, "has empty user list.");
6682 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6683 for(it = dict_first(obj); it; it = iter_next(it))
6684 ban_read_helper(iter_key(it), iter_data(it), cData);
6686 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6687 for(it = dict_first(obj); it; it = iter_next(it))
6689 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6690 struct record_data *rd = iter_data(it);
6691 const char *note, *setter;
6693 if(rd->type != RECDB_OBJECT)
6695 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6699 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6701 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6703 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6707 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6708 if(!setter) setter = "<unknown>";
6709 chanserv_add_channel_note(cData, ntype, setter, note);
6717 chanserv_dnr_read(const char *key, struct record_data *hir)
6719 const char *setter, *reason, *str;
6720 struct do_not_register *dnr;
6722 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6725 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6728 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6731 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6734 dnr = chanserv_add_dnr(key, setter, reason);
6737 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6739 dnr->set = atoi(str);
6745 chanserv_saxdb_read(struct dict *database)
6747 struct dict *section;
6750 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6751 for(it = dict_first(section); it; it = iter_next(it))
6752 chanserv_note_type_read(iter_key(it), iter_data(it));
6754 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6755 for(it = dict_first(section); it; it = iter_next(it))
6756 chanserv_channel_read(iter_key(it), iter_data(it));
6758 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6759 for(it = dict_first(section); it; it = iter_next(it))
6760 chanserv_dnr_read(iter_key(it), iter_data(it));
6766 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6768 int high_present = 0;
6769 saxdb_start_record(ctx, KEY_USERS, 1);
6770 for(; uData; uData = uData->next)
6772 if((uData->access >= UL_PRESENT) && uData->present)
6774 saxdb_start_record(ctx, uData->handle->handle, 0);
6775 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6776 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6778 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6780 saxdb_write_string(ctx, KEY_INFO, uData->info);
6781 saxdb_end_record(ctx);
6783 saxdb_end_record(ctx);
6784 return high_present;
6788 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6792 saxdb_start_record(ctx, KEY_BANS, 1);
6793 for(; bData; bData = bData->next)
6795 saxdb_start_record(ctx, bData->mask, 0);
6796 saxdb_write_int(ctx, KEY_SET, bData->set);
6797 if(bData->triggered)
6798 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6800 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6802 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6804 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6805 saxdb_end_record(ctx);
6807 saxdb_end_record(ctx);
6811 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6813 saxdb_start_record(ctx, name, 0);
6814 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6815 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6817 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6819 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6821 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6823 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6824 saxdb_end_record(ctx);
6828 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6832 enum levelOption lvlOpt;
6833 enum charOption chOpt;
6835 saxdb_start_record(ctx, channel->channel->name, 1);
6837 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6838 saxdb_write_int(ctx, KEY_MAX, channel->max);
6840 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6841 if(channel->registrar)
6842 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6843 if(channel->greeting)
6844 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6845 if(channel->user_greeting)
6846 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6847 if(channel->topic_mask)
6848 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6849 if(channel->suspended)
6850 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6852 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6853 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6854 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6855 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6856 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6858 buf[0] = channel->chOpts[chOpt];
6860 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6862 saxdb_end_record(ctx);
6864 if(channel->modes.modes_set || channel->modes.modes_clear)
6866 mod_chanmode_format(&channel->modes, buf);
6867 saxdb_write_string(ctx, KEY_MODES, buf);
6870 high_present = chanserv_write_users(ctx, channel->users);
6871 chanserv_write_bans(ctx, channel->bans);
6873 if(dict_size(channel->notes))
6877 saxdb_start_record(ctx, KEY_NOTES, 1);
6878 for(it = dict_first(channel->notes); it; it = iter_next(it))
6880 struct note *note = iter_data(it);
6881 saxdb_start_record(ctx, iter_key(it), 0);
6882 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6883 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6884 saxdb_end_record(ctx);
6886 saxdb_end_record(ctx);
6889 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6890 saxdb_end_record(ctx);
6894 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6898 saxdb_start_record(ctx, ntype->name, 0);
6899 switch(ntype->set_access_type)
6901 case NOTE_SET_CHANNEL_ACCESS:
6902 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6904 case NOTE_SET_CHANNEL_SETTER:
6905 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6907 case NOTE_SET_PRIVILEGED: default:
6908 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6911 switch(ntype->visible_type)
6913 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6914 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6915 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6917 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6918 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6919 saxdb_end_record(ctx);
6923 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6925 struct do_not_register *dnr;
6928 for(it = dict_first(dnrs); it; it = iter_next(it))
6930 dnr = iter_data(it);
6931 saxdb_start_record(ctx, dnr->chan_name, 0);
6933 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6934 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6935 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6936 saxdb_end_record(ctx);
6941 chanserv_saxdb_write(struct saxdb_context *ctx)
6944 struct chanData *channel;
6947 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6948 for(it = dict_first(note_types); it; it = iter_next(it))
6949 chanserv_write_note_type(ctx, iter_data(it));
6950 saxdb_end_record(ctx);
6953 saxdb_start_record(ctx, KEY_DNR, 1);
6954 write_dnrs_helper(ctx, handle_dnrs);
6955 write_dnrs_helper(ctx, plain_dnrs);
6956 write_dnrs_helper(ctx, mask_dnrs);
6957 saxdb_end_record(ctx);
6960 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6961 for(channel = channelList; channel; channel = channel->next)
6962 chanserv_write_channel(ctx, channel);
6963 saxdb_end_record(ctx);
6969 chanserv_db_cleanup(void) {
6971 unreg_part_func(handle_part);
6973 unregister_channel(channelList, "terminating.");
6974 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6975 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6976 free(chanserv_conf.support_channels.list);
6977 dict_delete(handle_dnrs);
6978 dict_delete(plain_dnrs);
6979 dict_delete(mask_dnrs);
6980 dict_delete(note_types);
6981 free_string_list(chanserv_conf.eightball);
6982 free_string_list(chanserv_conf.old_ban_names);
6983 free_string_list(chanserv_conf.set_shows);
6984 free(set_shows_list.list);
6985 free(uset_shows_list.list);
6988 struct userData *helper = helperList;
6989 helperList = helperList->next;
6994 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6995 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6996 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6999 init_chanserv(const char *nick)
7001 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7002 conf_register_reload(chanserv_conf_read);
7004 reg_server_link_func(handle_server_link);
7006 reg_new_channel_func(handle_new_channel);
7007 reg_join_func(handle_join);
7008 reg_part_func(handle_part);
7009 reg_kick_func(handle_kick);
7010 reg_topic_func(handle_topic);
7011 reg_mode_change_func(handle_mode);
7012 reg_nick_change_func(handle_nick_change);
7014 reg_auth_func(handle_auth);
7015 reg_handle_rename_func(handle_rename);
7016 reg_unreg_func(handle_unreg);
7018 handle_dnrs = dict_new();
7019 dict_set_free_data(handle_dnrs, free);
7020 plain_dnrs = dict_new();
7021 dict_set_free_data(plain_dnrs, free);
7022 mask_dnrs = dict_new();
7023 dict_set_free_data(mask_dnrs, free);
7025 reg_svccmd_unbind_func(handle_svccmd_unbind);
7026 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7027 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7028 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7029 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7030 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7031 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7032 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7033 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7034 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7036 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7037 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7039 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7040 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7041 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7042 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7043 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7045 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7046 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7047 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7048 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7049 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7051 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7052 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7053 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7054 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7056 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7057 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7058 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7059 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7060 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7061 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7062 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7063 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7065 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7066 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7067 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7068 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7069 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7070 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7071 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7072 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7073 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7074 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7075 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7076 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7077 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7078 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7080 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7081 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7082 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7083 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7084 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7086 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7087 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7089 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7090 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7091 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7092 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7093 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7094 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7095 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7096 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7097 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7098 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7099 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7101 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7102 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7104 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7105 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7106 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7107 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7109 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7110 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7111 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7112 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7113 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7115 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7116 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7117 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7118 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7119 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7120 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7122 /* Channel options */
7123 DEFINE_CHANNEL_OPTION(defaulttopic);
7124 DEFINE_CHANNEL_OPTION(topicmask);
7125 DEFINE_CHANNEL_OPTION(greeting);
7126 DEFINE_CHANNEL_OPTION(usergreeting);
7127 DEFINE_CHANNEL_OPTION(modes);
7128 DEFINE_CHANNEL_OPTION(enfops);
7129 DEFINE_CHANNEL_OPTION(giveops);
7130 DEFINE_CHANNEL_OPTION(protect);
7131 DEFINE_CHANNEL_OPTION(enfmodes);
7132 DEFINE_CHANNEL_OPTION(enftopic);
7133 DEFINE_CHANNEL_OPTION(pubcmd);
7134 DEFINE_CHANNEL_OPTION(givevoice);
7135 DEFINE_CHANNEL_OPTION(userinfo);
7136 DEFINE_CHANNEL_OPTION(dynlimit);
7137 DEFINE_CHANNEL_OPTION(topicsnarf);
7138 DEFINE_CHANNEL_OPTION(nodelete);
7139 DEFINE_CHANNEL_OPTION(toys);
7140 DEFINE_CHANNEL_OPTION(setters);
7141 DEFINE_CHANNEL_OPTION(topicrefresh);
7142 DEFINE_CHANNEL_OPTION(ctcpusers);
7143 DEFINE_CHANNEL_OPTION(ctcpreaction);
7144 DEFINE_CHANNEL_OPTION(inviteme);
7145 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7147 /* Alias set topic to set defaulttopic for compatibility. */
7148 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7151 DEFINE_USER_OPTION(noautoop);
7152 DEFINE_USER_OPTION(autoinvite);
7153 DEFINE_USER_OPTION(info);
7155 /* Alias uset autovoice to uset autoop. */
7156 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7158 note_types = dict_new();
7159 dict_set_free_data(note_types, chanserv_deref_note_type);
7162 chanserv = AddService(nick, "Channel Services");
7163 service_register(chanserv, '!');
7164 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7166 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7168 if(chanserv_conf.channel_expire_frequency)
7169 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7171 if(chanserv_conf.refresh_period)
7173 time_t next_refresh;
7174 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7175 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7178 reg_exit_func(chanserv_db_cleanup);
7179 message_register_table(msgtab);