1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
23 #include "opserv.h" /* for opserv_bad_channel() */
27 #define CHANSERV_CONF_NAME "services/chanserv"
29 /* ChanServ options */
30 #define KEY_SUPPORT_CHANNEL "support_channel"
31 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
32 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
33 #define KEY_INFO_DELAY "info_delay"
34 #define KEY_MAX_GREETLEN "max_greetlen"
35 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
36 #define KEY_ADJUST_DELAY "adjust_delay"
37 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
38 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
39 #define KEY_MAX_CHAN_USERS "max_chan_users"
40 #define KEY_MAX_CHAN_BANS "max_chan_bans"
41 #define KEY_NICK "nick"
42 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
43 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
44 #define KEY_SWITCH_TIMEOUT "switch_timeout"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
56 /* ChanServ database */
57 #define KEY_CHANNELS "channels"
58 #define KEY_NOTE_TYPES "note_types"
60 /* Note type parameters */
61 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
62 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
63 #define KEY_NOTE_SETTER_ACCESS "setter_access"
64 #define KEY_NOTE_VISIBILITY "visibility"
65 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
66 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
67 #define KEY_NOTE_VIS_ALL "all"
68 #define KEY_NOTE_MAX_LENGTH "max_length"
69 #define KEY_NOTE_SETTER "setter"
70 #define KEY_NOTE_NOTE "note"
72 /* Do-not-register channels */
74 #define KEY_DNR_SET "set"
75 #define KEY_DNR_SETTER "setter"
76 #define KEY_DNR_REASON "reason"
79 #define KEY_REGISTERED "registered"
80 #define KEY_REGISTRAR "registrar"
81 #define KEY_SUSPENDED "suspended"
82 #define KEY_PREVIOUS "previous"
83 #define KEY_SUSPENDER "suspender"
84 #define KEY_ISSUED "issued"
85 #define KEY_REVOKED "revoked"
86 #define KEY_SUSPEND_EXPIRES "suspend_expires"
87 #define KEY_SUSPEND_REASON "suspend_reason"
88 #define KEY_VISITED "visited"
89 #define KEY_TOPIC "topic"
90 #define KEY_GREETING "greeting"
91 #define KEY_USER_GREETING "user_greeting"
92 #define KEY_MODES "modes"
93 #define KEY_FLAGS "flags"
94 #define KEY_OPTIONS "options"
95 #define KEY_USERS "users"
96 #define KEY_BANS "bans"
98 #define KEY_NOTES "notes"
99 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_LEVEL "level"
103 #define KEY_INFO "info"
104 #define KEY_SEEN "seen"
107 #define KEY_OWNER "owner"
108 #define KEY_REASON "reason"
109 #define KEY_SET "set"
110 #define KEY_DURATION "duration"
111 #define KEY_EXPIRES "expires"
112 #define KEY_TRIGGERED "triggered"
114 #define CHANNEL_DEFAULT_FLAGS (0)
115 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
117 /* Administrative messages */
118 static const struct message_entry msgtab[] = {
119 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
121 /* Channel registration */
122 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
123 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
124 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
125 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
126 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
127 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
129 /* Do-not-register channels */
130 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
131 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
132 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
133 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
134 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
135 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
136 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
137 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
138 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
139 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
140 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
142 /* Channel unregistration */
143 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
144 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
145 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
146 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
149 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
150 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
152 /* Channel merging */
153 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
154 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
155 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
156 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
157 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
159 /* Handle unregistration */
160 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
163 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
164 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
165 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
166 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
167 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
168 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
169 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
170 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
171 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
172 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
173 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
174 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
175 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
177 /* Removing yourself from a channel. */
178 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
179 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
180 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
182 /* User management */
183 { "CSMSG_ADDED_USER", "Added new %s to the %s user list with access %d." },
184 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
185 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
186 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
187 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
188 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
189 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
190 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
192 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
193 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
194 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
195 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
196 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
199 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
200 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
201 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
202 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
203 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
204 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
205 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
206 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
207 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
208 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
209 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
210 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
211 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
212 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
213 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
214 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
216 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
218 /* Channel management */
219 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
220 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
221 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
223 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
224 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
225 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
226 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
227 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
228 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
229 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
231 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
232 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
233 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
234 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
235 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
236 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
237 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
238 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
239 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
240 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
241 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
242 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
243 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
244 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
245 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
246 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
247 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
248 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
249 { "CSMSG_SET_MODES", "$bModes $b %s" },
250 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
251 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
252 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
253 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
254 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
255 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
256 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
257 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
258 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
259 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
260 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
261 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
262 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
263 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
264 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
265 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
266 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
267 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
268 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
269 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
270 { "CSMSG_USET_INFO", "$bInfo $b %s" },
272 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
273 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
274 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
275 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
276 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
277 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
278 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
279 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
280 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
281 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
282 { "CSMSG_PROTECT_NONE", "No users will be protected." },
283 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
284 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
285 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
286 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
287 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
288 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
289 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
290 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
291 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
292 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
293 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
294 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
296 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
297 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
298 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
299 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
300 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
302 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
303 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
304 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
306 /* Channel userlist */
307 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
308 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
309 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
310 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
312 /* Channel note list */
313 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
314 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
315 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
316 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
317 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
318 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
319 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
320 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
321 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
322 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
323 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
324 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
325 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
326 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
327 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
329 /* Channel [un]suspension */
330 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
331 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
332 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
333 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
334 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
335 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
336 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
338 /* Access information */
339 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
340 { "CSMSG_ACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
341 { "CSMSG_SQUAT_ACCESS", "You do 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 LockChannel(target);
1867 if(!IsSuspended(channel->channel_info))
1868 AddChannelUser(chanserv, target);
1870 else if(target->channel_info)
1872 reply("CSMSG_ALREADY_REGGED", target->name);
1875 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1876 && !IsHelping(user))
1878 reply("CSMSG_MUST_BE_OPPED", target->name);
1881 else if(!IsSuspended(channel->channel_info))
1883 struct mod_chanmode change;
1884 change.modes_set = change.modes_clear = 0;
1886 change.args[0].mode = MODE_CHANOP;
1887 change.args[0].member = AddChannelUser(chanserv, target);
1888 mod_chanmode_announce(chanserv, target, &change);
1891 /* Move the channel_info to the target channel; it
1892 shouldn't be necessary to clear timeq callbacks
1893 for the old channel. */
1894 target->channel_info = channel->channel_info;
1895 target->channel_info->channel = target;
1896 channel->channel_info = NULL;
1898 reply("CSMSG_MOVE_SUCCESS", target->name);
1900 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1901 if(!IsSuspended(target->channel_info))
1903 char reason2[MAXLEN];
1904 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1905 DelChannelUser(chanserv, channel, reason2, 0);
1907 UnlockChannel(channel);
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));
2790 reply("CSMSG_REASON_CHANGE", ban);
2794 if(exact && bData->expires)
2798 /* If the ban matches an existing one exactly,
2799 extend the expiration time if the provided
2800 duration is longer. */
2801 if(duration && ((time_t)(now + duration) > bData->expires))
2803 bData->expires = now + duration;
2814 /* Delete the expiration timeq entry and
2815 requeue if necessary. */
2816 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2819 timeq_add(bData->expires, expire_ban, bData);
2822 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2824 reply("CSMSG_BAN_ADDED", name, channel->name);
2829 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2836 if(match_ircglobs(ban, bData->mask))
2838 /* The ban we are adding makes previously existing
2839 bans redundant; silently remove them. */
2840 del_channel_ban(bData);
2844 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);
2846 name = ban = strdup(bData->mask);
2850 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2852 extern const char *hidden_host_suffix;
2853 const char *old_name = chanserv_conf.old_ban_names->list[n];
2855 unsigned int l1, l2;
2858 l2 = strlen(old_name);
2861 if(irccasecmp(ban + l1 - l2, old_name))
2863 new_mask = malloc(MAXLEN);
2864 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2866 name = ban = new_mask;
2871 if(action & ACTION_BAN)
2873 unsigned int exists;
2874 struct mod_chanmode *change;
2876 if(channel->banlist.used >= MAXBANS)
2878 reply("CSMSG_BANLIST_FULL", channel->name);
2883 exists = ChannelBanExists(channel, ban);
2884 change = mod_chanmode_alloc(victimCount + 1);
2885 for(n = 0; n < victimCount; ++n)
2887 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2888 change->args[n].member = victims[n];
2892 change->args[n].mode = MODE_BAN;
2893 change->args[n++].hostmask = ban;
2897 modcmd_chanmode_announce(change);
2899 mod_chanmode_announce(chanserv, channel, change);
2900 mod_chanmode_free(change);
2902 if(exists && (action == ACTION_BAN))
2904 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2910 if(action & ACTION_KICK)
2912 char kick_reason[MAXLEN];
2913 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2915 for(n = 0; n < victimCount; n++)
2916 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2921 /* No response, since it was automated. */
2923 else if(action & ACTION_ADD_BAN)
2926 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2928 reply("CSMSG_BAN_ADDED", name, channel->name);
2930 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2931 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2932 else if(action & ACTION_BAN)
2933 reply("CSMSG_BAN_DONE", name, channel->name);
2934 else if(action & ACTION_KICK && victimCount)
2935 reply("CSMSG_KICK_DONE", name, channel->name);
2941 static CHANSERV_FUNC(cmd_kickban)
2943 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2946 static CHANSERV_FUNC(cmd_kick)
2948 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2951 static CHANSERV_FUNC(cmd_ban)
2953 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2956 static CHANSERV_FUNC(cmd_addban)
2958 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2961 static CHANSERV_FUNC(cmd_addtimedban)
2963 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2966 static struct mod_chanmode *
2967 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2969 struct mod_chanmode *change;
2970 unsigned char *match;
2971 unsigned int ii, count;
2973 match = alloca(bans->used);
2976 for(ii = count = 0; ii < bans->used; ++ii)
2978 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2985 for(ii = count = 0; ii < bans->used; ++ii)
2987 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
2994 change = mod_chanmode_alloc(count);
2995 for(ii = count = 0; ii < bans->used; ++ii)
2999 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3000 change->args[count++].hostmask = bans->list[ii]->ban;
3006 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3008 struct userNode *actee;
3014 /* may want to allow a comma delimited list of users... */
3015 if(!(actee = GetUserH(argv[1])))
3017 if(!is_ircmask(argv[1]))
3019 reply("MSG_NICK_UNKNOWN", argv[1]);
3023 mask = strdup(argv[1]);
3026 /* We don't sanitize the mask here because ircu
3028 if(action & ACTION_UNBAN)
3030 struct mod_chanmode *change;
3031 change = find_matching_bans(&channel->banlist, actee, mask);
3034 modcmd_chanmode_announce(change);
3035 mod_chanmode_free(change);
3040 if(action & ACTION_DEL_BAN)
3042 struct banData *ban, *next;
3044 ban = channel->channel_info->bans;
3048 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3051 for( ; ban && !match_ircglobs(mask, ban->mask);
3056 del_channel_ban(ban);
3063 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3065 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3071 static CHANSERV_FUNC(cmd_unban)
3073 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3076 static CHANSERV_FUNC(cmd_delban)
3078 /* it doesn't necessarily have to remove the channel ban - may want
3079 to make that an option. */
3080 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3083 static CHANSERV_FUNC(cmd_unbanme)
3085 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3086 long flags = ACTION_UNBAN;
3088 /* remove permanent bans if the user has the proper access. */
3089 if(uData->access >= UL_MASTER)
3090 flags |= ACTION_DEL_BAN;
3092 argv[1] = user->nick;
3093 return unban_user(user, channel, 2, argv, cmd, flags);
3096 static CHANSERV_FUNC(cmd_unbanall)
3098 struct mod_chanmode *change;
3101 if(!channel->banlist.used)
3103 reply("CSMSG_NO_BANS", channel->name);
3107 change = mod_chanmode_alloc(channel->banlist.used);
3108 for(ii=0; ii<channel->banlist.used; ii++)
3110 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3111 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3113 modcmd_chanmode_announce(change);
3114 mod_chanmode_free(change);
3115 reply("CSMSG_BANS_REMOVED", channel->name);
3119 static CHANSERV_FUNC(cmd_open)
3121 struct mod_chanmode *change;
3123 change = find_matching_bans(&channel->banlist, user, NULL);
3125 change = mod_chanmode_alloc(0);
3126 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3127 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3128 && channel->channel_info->modes.modes_set)
3129 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3130 modcmd_chanmode_announce(change);
3131 reply("CSMSG_CHANNEL_OPENED", channel->name);
3132 mod_chanmode_free(change);
3136 static CHANSERV_FUNC(cmd_access)
3138 struct userNode *target;
3139 struct handle_info *target_handle;
3140 struct userData *uData;
3142 char prefix[MAXLEN];
3146 struct userData *uData;
3147 const char *chanName;
3150 target_handle = user->handle_info;
3153 reply("MSG_AUTHENTICATE");
3158 if(!IsHelping(user))
3160 reply("CSMSG_ACCESS_SELF_ONLY", argv[0]);
3164 if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3168 if(!target_handle->channels)
3170 reply("CSMSG_SQUAT_ACCESS");
3173 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3174 for(uData = target_handle->channels; uData; uData = uData->u_next)
3176 struct chanData *cData = uData->channel;
3178 if(uData->access > UL_OWNER)
3180 if(IsProtected(cData) && hide && !GetTrueChannelAccess(cData, user->handle_info))
3182 chanName = cData->channel->name;
3184 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3186 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3194 target_handle = target->handle_info;
3196 else if((target = GetUserH(argv[1])))
3198 target_handle = target->handle_info;
3200 else if(argv[1][0] == '*')
3202 if(!(target_handle = get_handle_info(argv[1]+1)))
3204 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3210 reply("MSG_NICK_UNKNOWN", argv[1]);
3214 assert(target || target_handle);
3216 if(target == chanserv)
3218 reply("CSMSG_IS_CHANSERV");
3226 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3231 reply("MSG_USER_AUTHENTICATE", target->nick);
3234 reply("MSG_AUTHENTICATE");
3240 const char *epithet = NULL, *type = NULL;
3243 epithet = chanserv_conf.irc_operator_epithet;
3246 else if(IsNetworkHelper(target))
3248 epithet = chanserv_conf.network_helper_epithet;
3249 type = "network helper";
3251 else if(IsSupportHelper(target))
3253 epithet = chanserv_conf.support_helper_epithet;
3254 type = "support helper";
3258 if(target_handle->epithet)
3259 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3261 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3263 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3267 sprintf(prefix, "%s", target_handle->handle);
3270 if(!channel->channel_info)
3272 reply("CSMSG_NOT_REGISTERED", channel->name);
3276 helping = HANDLE_FLAGGED(target_handle, HELPING)
3277 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3278 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3280 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3281 /* To prevent possible information leaks, only show infolines
3282 * if the requestor is in the channel or it's their own
3284 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3286 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3288 /* Likewise, only say it's suspended if the user has active
3289 * access in that channel or it's their own entry. */
3290 if(IsUserSuspended(uData)
3291 && (GetChannelUser(channel->channel_info, user->handle_info)
3292 || (user->handle_info == uData->handle)))
3294 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3299 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3306 zoot_list(struct listData *list)
3308 struct userData *uData;
3309 unsigned int start, curr, highest, lowest;
3310 struct helpfile_table tmp_table;
3311 const char **temp, *msg;
3313 if(list->table.length == 1)
3316 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3318 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3319 msg = user_find_message(list->user, "MSG_NONE");
3320 send_message_type(4, list->user, list->bot, " %s", msg);
3322 tmp_table.width = list->table.width;
3323 tmp_table.flags = list->table.flags;
3324 list->table.contents[0][0] = " ";
3325 highest = list->highest;
3326 if(list->lowest != 0)
3327 lowest = list->lowest;
3328 else if(highest < 100)
3331 lowest = highest - 100;
3332 for(start = curr = 1; curr < list->table.length; )
3334 uData = list->users[curr-1];
3335 list->table.contents[curr++][0] = " ";
3336 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3339 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3341 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3342 temp = list->table.contents[--start];
3343 list->table.contents[start] = list->table.contents[0];
3344 tmp_table.contents = list->table.contents + start;
3345 tmp_table.length = curr - start;
3346 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3347 list->table.contents[start] = temp;
3349 highest = lowest - 1;
3350 lowest = (highest < 100) ? 0 : (highest - 99);
3356 def_list(struct listData *list)
3360 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3362 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3363 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3364 if(list->table.length == 1)
3366 msg = user_find_message(list->user, "MSG_NONE");
3367 send_message_type(4, list->user, list->bot, " %s", msg);
3372 userData_access_comp(const void *arg_a, const void *arg_b)
3374 const struct userData *a = *(struct userData**)arg_a;
3375 const struct userData *b = *(struct userData**)arg_b;
3377 if(a->access != b->access)
3378 res = b->access - a->access;
3380 res = irccasecmp(a->handle->handle, b->handle->handle);
3385 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3387 void (*send_list)(struct listData *);
3388 struct userData *uData;
3389 struct listData lData;
3390 unsigned int matches;
3394 lData.bot = cmd->parent->bot;
3395 lData.channel = channel;
3396 lData.lowest = lowest;
3397 lData.highest = highest;
3398 lData.search = (argc > 1) ? argv[1] : NULL;
3399 send_list = zoot_list;
3401 if(user->handle_info)
3403 switch(user->handle_info->userlist_style)
3405 case HI_STYLE_DEF: send_list = def_list; break;
3406 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3410 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3412 for(uData = channel->channel_info->users; uData; uData = uData->next)
3414 if((uData->access < lowest)
3415 || (uData->access > highest)
3416 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3418 lData.users[matches++] = uData;
3420 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3422 lData.table.length = matches+1;
3423 lData.table.width = 4;
3424 lData.table.flags = TABLE_NO_FREE;
3425 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3426 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3427 lData.table.contents[0] = ary;
3430 ary[2] = "Last Seen";
3432 for(matches = 1; matches < lData.table.length; ++matches)
3434 struct userData *uData = lData.users[matches-1];
3435 char seen[INTERVALLEN];
3437 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3438 lData.table.contents[matches] = ary;
3439 ary[0] = strtab(uData->access);
3440 ary[1] = uData->handle->handle;
3443 else if(!uData->seen)
3446 ary[2] = intervalString(seen, now - uData->seen);
3447 ary[2] = strdup(ary[2]);
3448 if(IsUserSuspended(uData))
3449 ary[3] = "Suspended";
3450 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3451 ary[3] = "Vacation";
3456 for(matches = 1; matches < lData.table.length; ++matches)
3458 free((char*)lData.table.contents[matches][2]);
3459 free(lData.table.contents[matches]);
3461 free(lData.table.contents[0]);
3462 free(lData.table.contents);
3466 static CHANSERV_FUNC(cmd_users)
3468 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3471 static CHANSERV_FUNC(cmd_wlist)
3473 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3476 static CHANSERV_FUNC(cmd_clist)
3478 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3481 static CHANSERV_FUNC(cmd_mlist)
3483 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3486 static CHANSERV_FUNC(cmd_olist)
3488 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3491 static CHANSERV_FUNC(cmd_plist)
3493 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3496 static CHANSERV_FUNC(cmd_bans)
3498 struct helpfile_table tbl;
3499 unsigned int matches = 0, timed = 0, ii;
3500 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3501 const char *msg_never, *triggered, *expires;
3502 struct banData *ban, **bans;
3509 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3511 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3513 if(search && !match_ircglobs(search, ban->mask))
3515 bans[matches++] = ban;
3520 tbl.length = matches + 1;
3521 tbl.width = 4 + timed;
3523 tbl.flags = TABLE_NO_FREE;
3524 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3525 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3526 tbl.contents[0][0] = "Mask";
3527 tbl.contents[0][1] = "Set By";
3528 tbl.contents[0][2] = "Triggered";
3531 tbl.contents[0][3] = "Expires";
3532 tbl.contents[0][4] = "Reason";
3535 tbl.contents[0][3] = "Reason";
3538 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3543 msg_never = user_find_message(user, "MSG_NEVER");
3544 for(ii = 0; ii < matches; )
3550 else if(ban->expires)
3551 expires = intervalString(e_buffer, ban->expires - now);
3553 expires = msg_never;
3556 triggered = intervalString(t_buffer, now - ban->triggered);
3558 triggered = msg_never;
3560 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3561 tbl.contents[ii][0] = ban->mask;
3562 tbl.contents[ii][1] = ban->owner;
3563 tbl.contents[ii][2] = strdup(triggered);
3566 tbl.contents[ii][3] = strdup(expires);
3567 tbl.contents[ii][4] = ban->reason;
3570 tbl.contents[ii][3] = ban->reason;
3572 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3573 reply("MSG_MATCH_COUNT", matches);
3574 for(ii = 1; ii < tbl.length; ++ii)
3576 free((char*)tbl.contents[ii][2]);
3578 free((char*)tbl.contents[ii][3]);
3579 free(tbl.contents[ii]);
3581 free(tbl.contents[0]);
3587 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3589 struct chanData *cData = channel->channel_info;
3590 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3592 if(cData->topic_mask)
3593 return !match_ircglob(new_topic, cData->topic_mask);
3594 else if(cData->topic)
3595 return irccasecmp(new_topic, cData->topic);
3600 static CHANSERV_FUNC(cmd_topic)
3602 struct chanData *cData;
3605 cData = channel->channel_info;
3610 SetChannelTopic(channel, chanserv, cData->topic, 1);
3611 reply("CSMSG_TOPIC_SET", cData->topic);
3615 reply("CSMSG_NO_TOPIC", channel->name);
3619 topic = unsplit_string(argv + 1, argc - 1, NULL);
3620 /* If they say "!topic *", use an empty topic. */
3621 if((topic[0] == '*') && (topic[1] == 0))
3623 if(bad_topic(channel, user, topic))
3625 char *topic_mask = cData->topic_mask;
3628 char new_topic[TOPICLEN+1], tchar;
3629 int pos=0, starpos=-1, dpos=0, len;
3631 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3638 len = strlen(topic);
3639 if((dpos + len) > TOPICLEN)
3640 len = TOPICLEN + 1 - dpos;
3641 memcpy(new_topic+dpos, topic, len);
3645 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3646 default: new_topic[dpos++] = tchar; break;
3649 if((dpos > TOPICLEN) || tchar)
3652 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3653 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3656 new_topic[dpos] = 0;
3657 SetChannelTopic(channel, chanserv, new_topic, 1);
3659 reply("CSMSG_TOPIC_LOCKED", channel->name);
3664 SetChannelTopic(channel, chanserv, topic, 1);
3666 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3668 /* Grab the topic and save it as the default topic. */
3670 cData->topic = strdup(channel->topic);
3676 static CHANSERV_FUNC(cmd_mode)
3678 struct mod_chanmode *change;
3682 change = &channel->channel_info->modes;
3683 if(change->modes_set || change->modes_clear) {
3684 modcmd_chanmode_announce(change);
3685 reply("CSMSG_DEFAULTED_MODES", channel->name);
3687 reply("CSMSG_NO_MODES", channel->name);
3691 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3694 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3698 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3699 && mode_lock_violated(&channel->channel_info->modes, change))
3702 mod_chanmode_format(&channel->channel_info->modes, modes);
3703 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3707 modcmd_chanmode_announce(change);
3708 mod_chanmode_free(change);
3709 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3713 static CHANSERV_FUNC(cmd_invite)
3715 struct userData *uData;
3716 struct userNode *invite;
3718 uData = GetChannelUser(channel->channel_info, user->handle_info);
3722 if(!(invite = GetUserH(argv[1])))
3724 reply("MSG_NICK_UNKNOWN", argv[1]);
3731 if(GetUserMode(channel, invite))
3733 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3739 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3740 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3742 irc_invite(chanserv, invite, channel);
3744 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3749 static CHANSERV_FUNC(cmd_inviteme)
3751 if(GetUserMode(channel, user))
3753 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3756 if(channel->channel_info
3757 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3759 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3762 irc_invite(cmd->parent->bot, user, channel);
3767 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3770 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3772 /* We display things based on two dimensions:
3773 * - Issue time: present or absent
3774 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3775 * (in order of precedence, so something both expired and revoked
3776 * only counts as revoked)
3778 combo = (suspended->issued ? 4 : 0)
3779 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3781 case 0: /* no issue time, indefinite expiration */
3782 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3784 case 1: /* no issue time, expires in future */
3785 intervalString(buf1, suspended->expires-now);
3786 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3788 case 2: /* no issue time, expired */
3789 intervalString(buf1, now-suspended->expires);
3790 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3792 case 3: /* no issue time, revoked */
3793 intervalString(buf1, now-suspended->revoked);
3794 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3796 case 4: /* issue time set, indefinite expiration */
3797 intervalString(buf1, now-suspended->issued);
3798 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3800 case 5: /* issue time set, expires in future */
3801 intervalString(buf1, now-suspended->issued);
3802 intervalString(buf2, suspended->expires-now);
3803 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3805 case 6: /* issue time set, expired */
3806 intervalString(buf1, now-suspended->issued);
3807 intervalString(buf2, now-suspended->expires);
3808 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3810 case 7: /* issue time set, revoked */
3811 intervalString(buf1, now-suspended->issued);
3812 intervalString(buf2, now-suspended->revoked);
3813 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3816 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3821 static CHANSERV_FUNC(cmd_info)
3823 char modes[MAXLEN], buffer[INTERVALLEN];
3824 struct userData *uData, *owner;
3825 struct chanData *cData;
3826 struct do_not_register *dnr;
3831 cData = channel->channel_info;
3832 reply("CSMSG_CHANNEL_INFO", channel->name);
3834 uData = GetChannelUser(cData, user->handle_info);
3835 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3837 mod_chanmode_format(&cData->modes, modes);
3838 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3839 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3842 for(it = dict_first(cData->notes); it; it = iter_next(it))
3846 note = iter_data(it);
3847 if(!note_type_visible_to_user(cData, note->type, user))
3850 padding = PADLEN - 1 - strlen(iter_key(it));
3851 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3854 reply("CSMSG_CHANNEL_MAX", cData->max);
3855 for(owner = cData->users; owner; owner = owner->next)
3856 if(owner->access == UL_OWNER)
3857 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3858 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3859 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3860 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3861 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3863 privileged = IsStaff(user);
3864 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3865 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3867 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3868 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3870 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3872 struct suspended *suspended;
3873 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3874 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3875 show_suspension_info(cmd, user, suspended);
3877 else if(IsSuspended(cData))
3879 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3880 show_suspension_info(cmd, user, cData->suspended);
3885 static CHANSERV_FUNC(cmd_netinfo)
3887 extern time_t boot_time;
3888 extern unsigned long burst_length;
3889 char interval[INTERVALLEN];
3891 reply("CSMSG_NETWORK_INFO");
3892 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3893 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3894 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3895 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3896 reply("CSMSG_NETWORK_BANS", banCount);
3897 reply("CSMSG_CHANNEL_USERS", userCount);
3898 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3899 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3904 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3906 struct helpfile_table table;
3908 struct userNode *user;
3913 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3914 table.contents = alloca(list->used*sizeof(*table.contents));
3915 for(nn=0; nn<list->used; nn++)
3917 user = list->list[nn];
3918 if(user->modes & skip_flags)
3922 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3925 nick = alloca(strlen(user->nick)+3);
3926 sprintf(nick, "(%s)", user->nick);
3930 table.contents[table.length][0] = nick;
3933 table_send(chanserv, to->nick, 0, NULL, table);
3936 static CHANSERV_FUNC(cmd_ircops)
3938 reply("CSMSG_STAFF_OPERS");
3939 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3943 static CHANSERV_FUNC(cmd_helpers)
3945 reply("CSMSG_STAFF_HELPERS");
3946 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3950 static CHANSERV_FUNC(cmd_staff)
3952 reply("CSMSG_NETWORK_STAFF");
3953 cmd_ircops(CSFUNC_ARGS);
3954 cmd_helpers(CSFUNC_ARGS);
3958 static CHANSERV_FUNC(cmd_peek)
3960 struct modeNode *mn;
3961 char modes[MODELEN];
3963 struct helpfile_table table;
3965 irc_make_chanmode(channel, modes);
3967 reply("CSMSG_PEEK_INFO", channel->name);
3968 reply("CSMSG_PEEK_TOPIC", channel->topic);
3969 reply("CSMSG_PEEK_MODES", modes);
3970 reply("CSMSG_PEEK_USERS", channel->members.used);
3974 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3975 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3976 for(n = 0; n < channel->members.used; n++)
3978 mn = channel->members.list[n];
3979 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3981 table.contents[table.length] = alloca(sizeof(**table.contents));
3982 table.contents[table.length][0] = mn->user->nick;
3987 reply("CSMSG_PEEK_OPS");
3988 table_send(chanserv, user->nick, 0, NULL, table);
3991 reply("CSMSG_PEEK_NO_OPS");
3995 static MODCMD_FUNC(cmd_wipeinfo)
3997 struct handle_info *victim;
3998 struct userData *ud, *actor;
4001 actor = GetChannelUser(channel->channel_info, user->handle_info);
4002 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4004 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4006 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4009 if((ud->access >= actor->access) && (ud != actor))
4011 reply("MSG_USER_OUTRANKED", victim->handle);
4017 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4021 static CHANSERV_FUNC(cmd_resync)
4023 struct mod_chanmode *changes;
4024 struct chanData *cData = channel->channel_info;
4025 unsigned int ii, used;
4027 changes = mod_chanmode_alloc(channel->members.used * 2);
4028 for(ii = used = 0; ii < channel->members.used; ++ii)
4030 struct modeNode *mn = channel->members.list[ii];
4031 struct userData *uData;
4032 if(IsService(mn->user))
4034 /* must not change modes for this user */
4036 else if(!(uData = GetChannelAccess(cData, mn->user->handle_info)))
4040 changes->args[used].mode = MODE_REMOVE | mn->modes;
4041 changes->args[used++].member = mn;
4044 else if(uData->access < cData->lvlOpts[lvlGiveOps])
4046 if(mn->modes & MODE_CHANOP)
4048 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4049 changes->args[used++].member = mn;
4051 if(!(mn->modes & MODE_VOICE))
4053 changes->args[used].mode = MODE_VOICE;
4054 changes->args[used++].member = mn;
4059 if(!(mn->modes & MODE_CHANOP))
4061 changes->args[used].mode = MODE_CHANOP;
4062 changes->args[used++].member = mn;
4066 changes->argc = used;
4067 modcmd_chanmode_announce(changes);
4068 mod_chanmode_free(changes);
4069 reply("CSMSG_RESYNCED_USERS", channel->name);
4073 static CHANSERV_FUNC(cmd_seen)
4075 struct userData *uData;
4076 struct handle_info *handle;
4077 char seen[INTERVALLEN];
4081 if(!irccasecmp(argv[1], chanserv->nick))
4083 reply("CSMSG_IS_CHANSERV");
4087 if(!(handle = get_handle_info(argv[1])))
4089 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4093 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4095 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4100 reply("CSMSG_USER_PRESENT", handle->handle);
4101 else if(uData->seen)
4102 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4104 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4106 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4107 reply("CSMSG_USER_VACATION", handle->handle);
4112 static MODCMD_FUNC(cmd_names)
4114 struct userNode *targ;
4115 struct userData *targData;
4116 unsigned int ii, pos;
4119 for(ii=pos=0; ii<channel->members.used; ++ii)
4121 targ = channel->members.list[ii]->user;
4122 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4125 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4128 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4132 if(IsUserSuspended(targData))
4134 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4137 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4138 reply("CSMSG_END_NAMES", channel->name);
4143 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4145 switch(ntype->visible_type)
4147 case NOTE_VIS_ALL: return 1;
4148 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4149 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4154 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4156 struct userData *uData;
4158 switch(ntype->set_access_type)
4160 case NOTE_SET_CHANNEL_ACCESS:
4161 if(!user->handle_info)
4163 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4165 return uData->access >= ntype->set_access.min_ulevel;
4166 case NOTE_SET_CHANNEL_SETTER:
4167 return check_user_level(channel, user, lvlSetters, 1, 0);
4168 case NOTE_SET_PRIVILEGED: default:
4169 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4173 static CHANSERV_FUNC(cmd_note)
4175 struct chanData *cData;
4177 struct note_type *ntype;
4179 cData = channel->channel_info;
4182 reply("CSMSG_NOT_REGISTERED", channel->name);
4186 /* If no arguments, show all visible notes for the channel. */
4192 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4194 note = iter_data(it);
4195 if(!note_type_visible_to_user(cData, note->type, user))
4198 reply("CSMSG_NOTELIST_HEADER", channel->name);
4199 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4202 reply("CSMSG_NOTELIST_END", channel->name);
4204 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4206 /* If one argument, show the named note. */
4209 if((note = dict_find(cData->notes, argv[1], NULL))
4210 && note_type_visible_to_user(cData, note->type, user))
4212 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4214 else if((ntype = dict_find(note_types, argv[1], NULL))
4215 && note_type_visible_to_user(NULL, ntype, user))
4217 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4222 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4226 /* Assume they're trying to set a note. */
4230 ntype = dict_find(note_types, argv[1], NULL);
4233 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4236 else if(note_type_settable_by_user(channel, ntype, user))
4238 note_text = unsplit_string(argv+2, argc-2, NULL);
4239 if((note = dict_find(cData->notes, argv[1], NULL)))
4240 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4241 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4242 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4244 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4246 /* The note is viewable to staff only, so return 0
4247 to keep the invocation from getting logged (or
4248 regular users can see it in !events). */
4254 reply("CSMSG_NO_ACCESS");
4261 static CHANSERV_FUNC(cmd_delnote)
4266 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4267 || !note_type_settable_by_user(channel, note->type, user))
4269 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4272 dict_remove(channel->channel_info->notes, note->type->name);
4273 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4277 static CHANSERV_FUNC(cmd_events)
4279 struct logSearch discrim;
4280 struct logReport report;
4281 unsigned int matches, limit;
4283 limit = (argc > 1) ? atoi(argv[1]) : 10;
4284 if(limit < 1 || limit > 200) limit = 10;
4286 memset(&discrim, 0, sizeof(discrim));
4287 discrim.masks.bot = chanserv;
4288 discrim.masks.channel_name = channel->name;
4289 if(argc > 2) discrim.masks.command = argv[2];
4290 discrim.limit = limit;
4291 discrim.max_time = INT_MAX;
4292 discrim.severities = 1 << LOG_COMMAND;
4293 report.reporter = chanserv;
4295 reply("CSMSG_EVENT_SEARCH_RESULTS");
4296 matches = log_entry_search(&discrim, log_report_entry, &report);
4298 reply("MSG_MATCH_COUNT", matches);
4300 reply("MSG_NO_MATCHES");
4304 static CHANSERV_FUNC(cmd_say)
4310 msg = unsplit_string(argv + 1, argc - 1, NULL);
4311 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4313 else if(GetUserH(argv[1]))
4316 msg = unsplit_string(argv + 2, argc - 2, NULL);
4317 send_target_message(1, argv[1], cmd->parent->bot, "%s", msg);
4321 reply("You must specify the name of a channel or user.");
4327 static CHANSERV_FUNC(cmd_emote)
4333 /* CTCP is so annoying. */
4334 msg = unsplit_string(argv + 1, argc - 1, NULL);
4335 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4337 else if(GetUserH(argv[1]))
4339 msg = unsplit_string(argv + 2, argc - 2, NULL);
4340 send_target_message(1, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4344 reply("You must specify the name of a channel or user.");
4350 struct channelList *
4351 chanserv_support_channels(void)
4353 return &chanserv_conf.support_channels;
4356 static CHANSERV_FUNC(cmd_expire)
4358 int channel_count = registered_channels;
4359 expire_channels(NULL);
4360 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4365 chanserv_expire_suspension(void *data)
4367 struct suspended *suspended = data;
4368 struct chanNode *channel;
4369 struct mod_chanmode change;
4371 if(!suspended->expires || (now < suspended->expires))
4372 suspended->revoked = now;
4373 channel = suspended->cData->channel;
4374 suspended->cData->channel = channel;
4375 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4376 change.modes_set = change.modes_clear = 0;
4378 change.args[0].mode = MODE_CHANOP;
4379 change.args[0].member = AddChannelUser(chanserv, channel);
4380 mod_chanmode_announce(chanserv, channel, &change);
4383 static CHANSERV_FUNC(cmd_csuspend)
4385 struct suspended *suspended;
4386 char reason[MAXLEN];
4387 time_t expiry, duration;
4388 struct userData *uData;
4392 if(IsProtected(channel->channel_info))
4394 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4398 if(argv[1][0] == '!')
4400 else if(IsSuspended(channel->channel_info))
4402 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4403 show_suspension_info(cmd, user, channel->channel_info->suspended);
4407 if(!strcmp(argv[1], "0"))
4409 else if((duration = ParseInterval(argv[1])))
4410 expiry = now + duration;
4413 reply("MSG_INVALID_DURATION", argv[1]);
4417 unsplit_string(argv + 2, argc - 2, reason);
4419 suspended = calloc(1, sizeof(*suspended));
4420 suspended->revoked = 0;
4421 suspended->issued = now;
4422 suspended->suspender = strdup(user->handle_info->handle);
4423 suspended->expires = expiry;
4424 suspended->reason = strdup(reason);
4425 suspended->cData = channel->channel_info;
4426 suspended->previous = suspended->cData->suspended;
4427 suspended->cData->suspended = suspended;
4429 if(suspended->expires)
4430 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4432 if(IsSuspended(channel->channel_info))
4434 suspended->previous->revoked = now;
4435 if(suspended->previous->expires)
4436 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4437 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4438 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4442 /* Mark all users in channel as absent. */
4443 for(uData = channel->channel_info->users; uData; uData = uData->next)
4452 /* Mark the channel as suspended, then part. */
4453 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4454 DelChannelUser(chanserv, channel, suspended->reason, 0);
4455 reply("CSMSG_SUSPENDED", channel->name);
4456 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4457 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4462 static CHANSERV_FUNC(cmd_cunsuspend)
4464 struct suspended *suspended;
4465 char message[MAXLEN];
4467 if(!IsSuspended(channel->channel_info))
4469 reply("CSMSG_NOT_SUSPENDED", channel->name);
4473 suspended = channel->channel_info->suspended;
4475 /* Expire the suspension and join ChanServ to the channel. */
4476 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4477 chanserv_expire_suspension(suspended);
4478 reply("CSMSG_UNSUSPENDED", channel->name);
4479 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4480 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4484 typedef struct chanservSearch
4492 unsigned long flags;
4496 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4499 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4504 search = malloc(sizeof(struct chanservSearch));
4505 memset(search, 0, sizeof(*search));
4508 for(i = 0; i < argc; i++)
4510 /* Assume all criteria require arguments. */
4513 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4517 if(!irccasecmp(argv[i], "name"))
4518 search->name = argv[++i];
4519 else if(!irccasecmp(argv[i], "registrar"))
4520 search->registrar = argv[++i];
4521 else if(!irccasecmp(argv[i], "unvisited"))
4522 search->unvisited = ParseInterval(argv[++i]);
4523 else if(!irccasecmp(argv[i], "registered"))
4524 search->registered = ParseInterval(argv[++i]);
4525 else if(!irccasecmp(argv[i], "flags"))
4528 if(!irccasecmp(argv[i], "nodelete"))
4529 search->flags |= CHANNEL_NODELETE;
4530 else if(!irccasecmp(argv[i], "suspended"))
4531 search->flags |= CHANNEL_SUSPENDED;
4534 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4538 else if(!irccasecmp(argv[i], "limit"))
4539 search->limit = strtoul(argv[++i], NULL, 10);
4542 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4547 if(search->name && !strcmp(search->name, "*"))
4549 if(search->registrar && !strcmp(search->registrar, "*"))
4550 search->registrar = 0;
4559 chanserv_channel_match(struct chanData *channel, search_t search)
4561 const char *name = channel->channel->name;
4562 if((search->name && !match_ircglob(name, search->name)) ||
4563 (search->registrar && !channel->registrar) ||
4564 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4565 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4566 (search->registered && (now - channel->registered) > search->registered) ||
4567 (search->flags && ((search->flags & channel->flags) != search->flags)))
4574 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4576 struct chanData *channel;
4577 unsigned int matches = 0;
4579 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4581 if(!chanserv_channel_match(channel, search))
4591 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4596 search_print(struct chanData *channel, void *data)
4598 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4601 static CHANSERV_FUNC(cmd_search)
4604 unsigned int matches;
4605 channel_search_func action;
4609 if(!irccasecmp(argv[1], "count"))
4610 action = search_count;
4611 else if(!irccasecmp(argv[1], "print"))
4612 action = search_print;
4615 reply("CSMSG_ACTION_INVALID", argv[1]);
4619 search = chanserv_search_create(user, argc - 2, argv + 2);
4623 if(action == search_count)
4624 search->limit = INT_MAX;
4626 if(action == search_print)
4627 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4629 matches = chanserv_channel_search(search, action, user);
4632 reply("MSG_MATCH_COUNT", matches);
4634 reply("MSG_NO_MATCHES");
4640 static CHANSERV_FUNC(cmd_unvisited)
4642 struct chanData *cData;
4643 time_t interval = chanserv_conf.channel_expire_delay;
4644 char buffer[INTERVALLEN];
4645 unsigned int limit = 25, matches = 0;
4649 interval = ParseInterval(argv[1]);
4651 limit = atoi(argv[2]);
4654 intervalString(buffer, interval);
4655 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4657 for(cData = channelList; cData && matches < limit; cData = cData->next)
4659 if((now - cData->visited) < interval)
4662 intervalString(buffer, now - cData->visited);
4663 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4670 static MODCMD_FUNC(chan_opt_defaulttopic)
4676 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4678 reply("CSMSG_TOPIC_LOCKED", channel->name);
4682 topic = unsplit_string(argv+1, argc-1, NULL);
4684 free(channel->channel_info->topic);
4685 if(topic[0] == '*' && topic[1] == 0)
4687 topic = channel->channel_info->topic = NULL;
4691 topic = channel->channel_info->topic = strdup(topic);
4692 if(channel->channel_info->topic_mask
4693 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4694 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4696 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4699 if(channel->channel_info->topic)
4700 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4702 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4706 static MODCMD_FUNC(chan_opt_topicmask)
4710 struct chanData *cData = channel->channel_info;
4713 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4715 reply("CSMSG_TOPIC_LOCKED", channel->name);
4719 mask = unsplit_string(argv+1, argc-1, NULL);
4721 if(cData->topic_mask)
4722 free(cData->topic_mask);
4723 if(mask[0] == '*' && mask[1] == 0)
4725 cData->topic_mask = 0;
4729 cData->topic_mask = strdup(mask);
4731 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4732 else if(!match_ircglob(cData->topic, cData->topic_mask))
4733 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4737 if(channel->channel_info->topic_mask)
4738 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4740 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4744 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4748 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4752 if(greeting[0] == '*' && greeting[1] == 0)
4756 unsigned int length = strlen(greeting);
4757 if(length > chanserv_conf.greeting_length)
4759 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4762 *data = strdup(greeting);
4771 reply(name, user_find_message(user, "MSG_NONE"));
4775 static MODCMD_FUNC(chan_opt_greeting)
4777 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4780 static MODCMD_FUNC(chan_opt_usergreeting)
4782 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4785 static MODCMD_FUNC(chan_opt_modes)
4787 struct mod_chanmode *new_modes;
4788 char modes[MODELEN];
4792 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4794 reply("CSMSG_NO_ACCESS");
4797 if(argv[1][0] == '*' && argv[1][1] == 0)
4799 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4801 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4803 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4806 else if(new_modes->argc > 1)
4808 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4809 mod_chanmode_free(new_modes);
4814 channel->channel_info->modes = *new_modes;
4815 modcmd_chanmode_announce(new_modes);
4816 mod_chanmode_free(new_modes);
4820 mod_chanmode_format(&channel->channel_info->modes, modes);
4822 reply("CSMSG_SET_MODES", modes);
4824 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4828 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4830 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4832 struct chanData *cData = channel->channel_info;
4837 /* Set flag according to value. */
4838 if(enabled_string(argv[1]))
4840 cData->flags |= mask;
4843 else if(disabled_string(argv[1]))
4845 cData->flags &= ~mask;
4850 reply("MSG_INVALID_BINARY", argv[1]);
4856 /* Find current option value. */
4857 value = (cData->flags & mask) ? 1 : 0;
4861 reply(name, user_find_message(user, "MSG_ON"));
4863 reply(name, user_find_message(user, "MSG_OFF"));
4867 static MODCMD_FUNC(chan_opt_nodelete)
4869 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4871 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4875 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4878 static MODCMD_FUNC(chan_opt_dynlimit)
4880 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4883 /* TODO: reimplement
4885 static MODCMD_FUNC(chan_opt_userinfo)
4887 CHANNEL_BINARY_OPTION("CSMSG_SET_USERINFO", CHANNEL_INFO_LINES);
4890 static MODCMD_FUNC(chan_opt_voice)
4892 CHANNEL_BINARY_OPTION("CSMSG_SET_VOICE", CHANNEL_VOICE_ALL);
4895 static MODCMD_FUNC(chan_opt_topicsnarf)
4897 if((argc > 0) && !check_user_level(channel, user, lvlEnfTopic, 1, 0))
4899 reply("CSMSG_TOPIC_LOCKED", channel->name);
4902 CHANNEL_BINARY_OPTION("CSMSG_SET_TOPICSNARF", CHANNEL_TOPIC_SNARF);
4905 static MODCMD_FUNC(chan_opt_peoninvite)
4907 CHANNEL_BINARY_OPTION("CSMSG_SET_PEONINVITE", CHANNEL_PEON_INVITE);
4912 static MODCMD_FUNC(chan_opt_defaults)
4914 struct userData *uData;
4915 struct chanData *cData;
4916 const char *confirm;
4917 enum levelOption lvlOpt;
4918 enum charOption chOpt;
4920 cData = channel->channel_info;
4921 uData = GetChannelUser(cData, user->handle_info);
4922 if(!uData || (uData->access < UL_OWNER))
4924 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4927 confirm = make_confirmation_string(uData);
4928 if((argc < 2) || strcmp(argv[1], confirm))
4930 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4933 cData->flags = CHANNEL_DEFAULT_FLAGS;
4934 cData->modes = chanserv_conf.default_modes;
4935 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4936 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4937 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4938 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4939 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4944 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4946 struct chanData *cData = channel->channel_info;
4947 struct userData *uData;
4948 unsigned short value;
4952 if(!check_user_level(channel, user, option, 1, 1))
4954 reply("CSMSG_CANNOT_SET");
4957 value = user_level_from_name(argv[1], UL_OWNER+1);
4958 if(!value && strcmp(argv[1], "0"))
4960 reply("CSMSG_INVALID_ACCESS", argv[1]);
4963 uData = GetChannelUser(cData, user->handle_info);
4964 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4966 reply("CSMSG_BAD_SETLEVEL");
4972 if(value > cData->lvlOpts[lvlGiveOps])
4974 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4979 if(value < cData->lvlOpts[lvlGiveVoice])
4981 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4986 /* This test only applies to owners, since non-owners
4987 * trying to set an option to above their level get caught
4988 * by the CSMSG_BAD_SETLEVEL test above.
4990 if(value > uData->access)
4992 reply("CSMSG_BAD_SETTERS");
4999 cData->lvlOpts[option] = value;
5001 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5005 static MODCMD_FUNC(chan_opt_enfops)
5007 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5010 static MODCMD_FUNC(chan_opt_giveops)
5012 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5015 static MODCMD_FUNC(chan_opt_enfmodes)
5017 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5020 static MODCMD_FUNC(chan_opt_enftopic)
5022 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5025 static MODCMD_FUNC(chan_opt_pubcmd)
5027 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5030 static MODCMD_FUNC(chan_opt_setters)
5032 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5035 static MODCMD_FUNC(chan_opt_ctcpusers)
5037 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5040 static MODCMD_FUNC(chan_opt_userinfo)
5042 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5045 static MODCMD_FUNC(chan_opt_givevoice)
5047 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5050 static MODCMD_FUNC(chan_opt_topicsnarf)
5052 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5055 static MODCMD_FUNC(chan_opt_inviteme)
5057 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5061 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5063 struct chanData *cData = channel->channel_info;
5064 int count = charOptions[option].count, index;
5068 index = atoi(argv[1]);
5070 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5072 reply("CSMSG_INVALID_NUMERIC", index);
5073 /* Show possible values. */
5074 for(index = 0; index < count; index++)
5075 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5079 cData->chOpts[option] = charOptions[option].values[index].value;
5083 /* Find current option value. */
5086 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5090 /* Somehow, the option value is corrupt; reset it to the default. */
5091 cData->chOpts[option] = charOptions[option].default_value;
5096 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5100 static MODCMD_FUNC(chan_opt_protect)
5102 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5105 static MODCMD_FUNC(chan_opt_toys)
5107 return channel_multiple_option(chToys, CSFUNC_ARGS);
5110 static MODCMD_FUNC(chan_opt_ctcpreaction)
5112 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5115 static MODCMD_FUNC(chan_opt_topicrefresh)
5117 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5120 static struct svccmd_list set_shows_list;
5123 handle_svccmd_unbind(struct svccmd *target) {
5125 for(ii=0; ii<set_shows_list.used; ++ii)
5126 if(target == set_shows_list.list[ii])
5127 set_shows_list.used = 0;
5130 static CHANSERV_FUNC(cmd_set)
5132 struct svccmd *subcmd;
5136 /* Check if we need to (re-)initialize set_shows_list. */
5137 if(!set_shows_list.used)
5139 if(!set_shows_list.size)
5141 set_shows_list.size = chanserv_conf.set_shows->used;
5142 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5144 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5146 const char *name = chanserv_conf.set_shows->list[ii];
5147 sprintf(buf, "%s %s", argv[0], name);
5148 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5151 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5154 svccmd_list_append(&set_shows_list, subcmd);
5160 reply("CSMSG_CHANNEL_OPTIONS");
5161 for(ii = 0; ii < set_shows_list.used; ii++)
5163 subcmd = set_shows_list.list[ii];
5164 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5169 sprintf(buf, "%s %s", argv[0], argv[1]);
5170 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5173 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5176 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5178 reply("CSMSG_NO_ACCESS");
5182 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5186 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5188 struct userData *uData;
5190 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5193 reply("CSMSG_NOT_USER", channel->name);
5199 /* Just show current option value. */
5201 else if(enabled_string(argv[1]))
5203 uData->flags |= mask;
5205 else if(disabled_string(argv[1]))
5207 uData->flags &= ~mask;
5211 reply("MSG_INVALID_BINARY", argv[1]);
5215 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5219 static MODCMD_FUNC(user_opt_noautoop)
5221 struct userData *uData;
5223 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5226 reply("CSMSG_NOT_USER", channel->name);
5229 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5230 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5232 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5235 static MODCMD_FUNC(user_opt_autoinvite)
5237 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5240 static MODCMD_FUNC(user_opt_info)
5242 struct userData *uData;
5245 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5249 /* If they got past the command restrictions (which require access)
5250 * but fail this test, we have some fool with security override on.
5252 reply("CSMSG_NOT_USER", channel->name);
5258 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5261 if(infoline[0] == '*' && infoline[1] == 0)
5264 uData->info = strdup(infoline);
5267 reply("CSMSG_USET_INFO", uData->info);
5269 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5273 struct svccmd_list uset_shows_list;
5275 static CHANSERV_FUNC(cmd_uset)
5277 struct svccmd *subcmd;
5281 /* Check if we need to (re-)initialize uset_shows_list. */
5282 if(!uset_shows_list.used)
5286 "NoAutoOp", "AutoInvite", "Info"
5289 if(!uset_shows_list.size)
5291 uset_shows_list.size = ArrayLength(options);
5292 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5294 for(ii = 0; ii < ArrayLength(options); ii++)
5296 const char *name = options[ii];
5297 sprintf(buf, "%s %s", argv[0], name);
5298 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5301 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5304 svccmd_list_append(&uset_shows_list, subcmd);
5310 /* Do this so options are presented in a consistent order. */
5311 reply("CSMSG_USER_OPTIONS");
5312 for(ii = 0; ii < uset_shows_list.used; ii++)
5313 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5317 sprintf(buf, "%s %s", argv[0], argv[1]);
5318 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5321 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5325 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5328 static CHANSERV_FUNC(cmd_giveownership)
5330 struct handle_info *new_owner_hi;
5331 struct userData *new_owner, *curr_user;
5332 struct chanData *cData = channel->channel_info;
5333 struct do_not_register *dnr;
5335 unsigned short co_access;
5336 char reason[MAXLEN];
5339 curr_user = GetChannelAccess(cData, user->handle_info);
5340 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5341 if(!curr_user || (curr_user->access != UL_OWNER))
5343 struct userData *owner = NULL;
5344 for(curr_user = channel->channel_info->users;
5346 curr_user = curr_user->next)
5348 if(curr_user->access != UL_OWNER)
5352 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5359 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5361 if(new_owner_hi == user->handle_info)
5363 reply("CSMSG_NO_TRANSFER_SELF");
5366 new_owner = GetChannelAccess(cData, new_owner_hi);
5369 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5372 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5374 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5377 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5378 if(!IsHelping(user))
5379 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5381 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5384 if(new_owner->access >= UL_COOWNER)
5385 co_access = new_owner->access;
5387 co_access = UL_COOWNER;
5388 new_owner->access = UL_OWNER;
5390 curr_user->access = co_access;
5391 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5392 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5393 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5397 static CHANSERV_FUNC(cmd_suspend)
5399 struct handle_info *hi;
5400 struct userData *self, *target;
5403 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5404 self = GetChannelUser(channel->channel_info, user->handle_info);
5405 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5407 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5410 if(target->access >= self->access)
5412 reply("MSG_USER_OUTRANKED", hi->handle);
5415 if(target->flags & USER_SUSPENDED)
5417 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5422 target->present = 0;
5425 target->flags |= USER_SUSPENDED;
5426 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5430 static CHANSERV_FUNC(cmd_unsuspend)
5432 struct handle_info *hi;
5433 struct userData *self, *target;
5436 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5437 self = GetChannelUser(channel->channel_info, user->handle_info);
5438 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5440 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5443 if(target->access >= self->access)
5445 reply("MSG_USER_OUTRANKED", hi->handle);
5448 if(!(target->flags & USER_SUSPENDED))
5450 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5453 target->flags &= ~USER_SUSPENDED;
5454 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5458 static MODCMD_FUNC(cmd_deleteme)
5460 struct handle_info *hi;
5461 struct userData *target;
5462 const char *confirm_string;
5463 unsigned short access;
5466 hi = user->handle_info;
5467 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5469 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5472 if(target->access == UL_OWNER)
5474 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5477 confirm_string = make_confirmation_string(target);
5478 if((argc < 2) || strcmp(argv[1], confirm_string))
5480 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5483 access = target->access;
5484 channel_name = strdup(channel->name);
5485 del_channel_user(target, 1);
5486 reply("CSMSG_DELETED_YOU", access, channel_name);
5492 chanserv_refresh_topics(UNUSED_ARG(void *data))
5494 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5495 struct chanData *cData;
5498 for(cData = channelList; cData; cData = cData->next)
5500 if(IsSuspended(cData))
5502 opt = cData->chOpts[chTopicRefresh];
5505 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5508 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5509 cData->last_refresh = refresh_num;
5511 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5514 static CHANSERV_FUNC(cmd_unf)
5518 char response[MAXLEN];
5519 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5520 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5521 irc_privmsg(cmd->parent->bot, channel->name, response);
5524 reply("CSMSG_UNF_RESPONSE");
5528 static CHANSERV_FUNC(cmd_ping)
5532 char response[MAXLEN];
5533 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5534 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5535 irc_privmsg(cmd->parent->bot, channel->name, response);
5538 reply("CSMSG_PING_RESPONSE");
5542 static CHANSERV_FUNC(cmd_wut)
5546 char response[MAXLEN];
5547 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5548 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5549 irc_privmsg(cmd->parent->bot, channel->name, response);
5552 reply("CSMSG_WUT_RESPONSE");
5556 static CHANSERV_FUNC(cmd_8ball)
5558 unsigned int i, j, accum;
5563 for(i=1; i<argc; i++)
5564 for(j=0; argv[i][j]; j++)
5565 accum = (accum << 5) - accum + toupper(argv[i][j]);
5566 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5569 char response[MAXLEN];
5570 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5571 irc_privmsg(cmd->parent->bot, channel->name, response);
5574 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5578 static CHANSERV_FUNC(cmd_d)
5580 unsigned long sides, count, modifier, ii, total;
5581 char response[MAXLEN], *sep;
5585 if((count = strtoul(argv[1], &sep, 10)) < 1)
5595 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5596 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5600 else if((sep[0] == '-') && isdigit(sep[1]))
5601 modifier = strtoul(sep, NULL, 10);
5602 else if((sep[0] == '+') && isdigit(sep[1]))
5603 modifier = strtoul(sep+1, NULL, 10);
5610 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5615 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5618 for(total = ii = 0; ii < count; ++ii)
5619 total += (rand() % sides) + 1;
5622 if((count > 1) || modifier)
5624 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5625 sprintf(response, fmt, total, count, sides, modifier);
5629 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5630 sprintf(response, fmt, total, sides);
5633 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5635 send_message_type(4, user, cmd->parent->bot, "%s", response);
5639 static CHANSERV_FUNC(cmd_huggle)
5641 char response[MAXLEN];
5643 /* CTCP must be via PRIVMSG, never notice */
5646 fmt = user_find_message(user, "CSMSG_HUGGLES_HIM");
5647 sprintf(response, fmt, user->nick);
5648 irc_privmsg(cmd->parent->bot, channel->name, response);
5652 fmt = user_find_message(user, "CSMSG_HUGGLES_YOU");
5653 irc_privmsg(cmd->parent->bot, user->nick, fmt);
5659 chanserv_adjust_limit(void *data)
5661 struct mod_chanmode change;
5662 struct chanData *cData = data;
5663 struct chanNode *channel = cData->channel;
5666 if(IsSuspended(cData))
5669 cData->limitAdjusted = now;
5670 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5671 if(cData->modes.modes_set & MODE_LIMIT)
5673 if(limit > cData->modes.new_limit)
5674 limit = cData->modes.new_limit;
5675 else if(limit == cData->modes.new_limit)
5679 change.modes_set = MODE_LIMIT;
5680 change.modes_clear = 0;
5681 change.new_limit = limit;
5683 mod_chanmode_announce(chanserv, channel, &change);
5687 handle_new_channel(struct chanNode *channel)
5689 struct chanData *cData;
5691 if(!(cData = channel->channel_info))
5694 if(cData->modes.modes_set || cData->modes.modes_clear)
5695 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5697 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5698 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5701 /* Welcome to my worst nightmare. Warning: Read (or modify)
5702 the code below at your own risk. */
5704 handle_join(struct modeNode *mNode)
5706 struct mod_chanmode change;
5707 struct userNode *user = mNode->user;
5708 struct chanNode *channel = mNode->channel;
5709 struct chanData *cData;
5710 struct userData *uData = NULL;
5711 struct banData *bData;
5712 struct handle_info *handle;
5713 unsigned int modes = 0, info = 0;
5716 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5719 cData = channel->channel_info;
5720 if(channel->members.used > cData->max)
5721 cData->max = channel->members.used;
5723 /* Check for bans. If they're joining through a ban, one of two
5725 * 1: Join during a netburst, by riding the break. Kick them
5726 * unless they have ops or voice in the channel.
5727 * 2: They're allowed to join through the ban (an invite in
5728 * ircu2.10, or a +e on Hybrid, or something).
5729 * If they're not joining through a ban, and the banlist is not
5730 * full, see if they're on the banlist for the channel. If so,
5733 if(user->uplink->burst && !mNode->modes)
5736 for(ii = 0; ii < channel->banlist.used; ii++)
5738 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5740 /* Riding a netburst. Naughty. */
5741 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5747 change.modes_set = change.modes_clear = 0;
5749 if(channel->banlist.used < MAXBANS)
5751 /* Not joining through a ban. */
5752 for(bData = cData->bans;
5753 bData && !user_matches_glob(user, bData->mask, 1);
5754 bData = bData->next);
5758 char kick_reason[MAXLEN];
5759 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5761 bData->triggered = now;
5762 if(bData != cData->bans)
5764 /* Shuffle the ban to the head of the list. */
5765 if(bData->next) bData->next->prev = bData->prev;
5766 if(bData->prev) bData->prev->next = bData->next;
5769 bData->next = cData->bans;
5772 cData->bans->prev = bData;
5773 cData->bans = bData;
5776 change.args[0].mode = MODE_BAN;
5777 change.args[0].hostmask = bData->mask;
5778 mod_chanmode_announce(chanserv, channel, &change);
5779 KickChannelUser(user, channel, chanserv, kick_reason);
5784 /* ChanServ will not modify the limits in join-flooded channels.
5785 It will also skip DynLimit processing when the user (or srvx)
5786 is bursting in, because there are likely more incoming. */
5787 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5788 && !user->uplink->burst
5789 && !channel->join_flooded
5790 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5792 /* The user count has begun "bumping" into the channel limit,
5793 so set a timer to raise the limit a bit. Any previous
5794 timers are removed so three incoming users within the delay
5795 results in one limit change, not three. */
5797 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5798 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5801 if(channel->join_flooded)
5803 /* don't automatically give ops or voice during a join flood */
5805 else if(cData->lvlOpts[lvlGiveOps] == 0)
5806 modes |= MODE_CHANOP;
5807 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5808 modes |= MODE_VOICE;
5810 greeting = cData->greeting;
5811 if(user->handle_info)
5813 handle = user->handle_info;
5815 if(IsHelper(user) && !IsHelping(user))
5818 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5820 if(channel == chanserv_conf.support_channels.list[ii])
5822 HANDLE_SET_FLAG(user->handle_info, HELPING);
5828 uData = GetTrueChannelAccess(cData, handle);
5829 if(uData && !IsUserSuspended(uData))
5831 /* Ops and above were handled by the above case. */
5832 if(IsUserAutoOp(uData))
5834 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5835 modes |= MODE_CHANOP;
5836 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5837 modes |= MODE_VOICE;
5839 if(uData->access >= UL_PRESENT)
5840 cData->visited = now;
5841 if(cData->user_greeting)
5842 greeting = cData->user_greeting;
5844 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5845 && ((now - uData->seen) >= chanserv_conf.info_delay)
5852 if(!user->uplink->burst)
5856 if(modes & MODE_CHANOP)
5857 modes &= ~MODE_VOICE;
5858 change.args[0].mode = modes;
5859 change.args[0].member = mNode;
5860 mod_chanmode_announce(chanserv, channel, &change);
5862 if(greeting && !user->uplink->burst)
5863 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5865 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5871 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5873 struct mod_chanmode change;
5874 struct userData *channel;
5875 unsigned int ii, jj;
5877 if(!user->handle_info)
5880 change.modes_set = change.modes_clear = 0;
5882 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5884 struct chanNode *cn;
5885 struct modeNode *mn;
5886 if(IsUserSuspended(channel)
5887 || IsSuspended(channel->channel)
5888 || !(cn = channel->channel->channel))
5891 mn = GetUserMode(cn, user);
5894 if(!IsUserSuspended(channel)
5895 && IsUserAutoInvite(channel)
5896 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5897 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5899 irc_invite(chanserv, user, cn);
5903 if(channel->access >= UL_PRESENT)
5904 channel->channel->visited = now;
5906 if(IsUserAutoOp(channel))
5908 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5909 change.args[0].mode = MODE_CHANOP;
5910 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5911 change.args[0].mode = MODE_VOICE;
5912 change.args[0].member = mn;
5913 mod_chanmode_announce(chanserv, cn, &change);
5916 channel->seen = now;
5917 channel->present = 1;
5920 for(ii = 0; ii < user->channels.used; ++ii)
5922 struct chanNode *channel = user->channels.list[ii]->channel;
5923 struct banData *ban;
5925 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5926 || !channel->channel_info)
5928 for(jj = 0; jj < channel->banlist.used; ++jj)
5929 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5931 if(jj < channel->banlist.used)
5933 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5935 char kick_reason[MAXLEN];
5936 if(!user_matches_glob(user, ban->mask, 1))
5938 change.args[0].mode = MODE_BAN;
5939 change.args[0].hostmask = ban->mask;
5940 mod_chanmode_announce(chanserv, channel, &change);
5941 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5942 KickChannelUser(user, channel, chanserv, kick_reason);
5943 ban->triggered = now;
5948 if(IsSupportHelper(user))
5950 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5952 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5954 HANDLE_SET_FLAG(user->handle_info, HELPING);
5962 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5964 struct chanData *cData;
5965 struct userData *uData;
5967 cData = channel->channel_info;
5968 if(!cData || IsSuspended(cData) || IsLocal(user))
5971 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5973 /* Allow for a bit of padding so that the limit doesn't
5974 track the user count exactly, which could get annoying. */
5975 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5977 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5978 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5982 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5983 scan_user_presence(uData, user);
5985 if(IsHelping(user) && IsSupportHelper(user))
5987 unsigned int ii, jj;
5988 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5990 for(jj = 0; jj < user->channels.used; ++jj)
5991 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5993 if(jj < user->channels.used)
5996 if(ii == chanserv_conf.support_channels.used)
5997 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6002 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6004 if(!channel->channel_info || !kicker || IsService(kicker)
6005 || (kicker == victim) || IsSuspended(channel->channel_info)
6006 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6009 if(protect_user(victim, kicker, channel->channel_info))
6011 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6012 KickChannelUser(kicker, channel, chanserv, reason);
6017 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6019 struct chanData *cData;
6021 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6024 cData = channel->channel_info;
6025 if(bad_topic(channel, user, channel->topic))
6027 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6028 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6029 SetChannelTopic(channel, chanserv, old_topic, 1);
6030 else if(cData->topic)
6031 SetChannelTopic(channel, chanserv, cData->topic, 1);
6034 /* With topicsnarf, grab the topic and save it as the default topic. */
6035 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6038 cData->topic = strdup(channel->topic);
6044 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6046 struct mod_chanmode *bounce = NULL;
6047 unsigned int bnc, ii;
6050 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6053 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6054 && mode_lock_violated(&channel->channel_info->modes, change))
6056 char correct[MAXLEN];
6057 bounce = mod_chanmode_alloc(change->argc + 1);
6058 *bounce = channel->channel_info->modes;
6059 mod_chanmode_format(&channel->channel_info->modes, correct);
6060 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6062 for(ii = bnc = 0; ii < change->argc; ++ii)
6064 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6066 const struct userNode *victim = change->args[ii].member->user;
6067 if(!protect_user(victim, user, channel->channel_info))
6070 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6073 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6074 bounce->args[bnc].member = GetUserMode(channel, user);
6075 if(bounce->args[bnc].member)
6079 bounce->args[bnc].mode = MODE_CHANOP;
6080 bounce->args[bnc].member = change->args[ii].member;
6082 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6084 else if(change->args[ii].mode & MODE_CHANOP)
6086 const struct userNode *victim = change->args[ii].member->user;
6087 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6090 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6091 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6092 bounce->args[bnc].member = change->args[ii].member;
6095 else if(change->args[ii].mode & MODE_BAN)
6097 const char *ban = change->args[ii].hostmask;
6098 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6101 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6102 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6103 bounce->args[bnc].hostmask = ban;
6105 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6110 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6111 mod_chanmode_announce(chanserv, channel, bounce);
6112 mod_chanmode_free(bounce);
6117 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6119 struct chanNode *channel;
6120 struct banData *bData;
6121 struct mod_chanmode change;
6122 unsigned int ii, jj;
6123 char kick_reason[MAXLEN];
6125 change.modes_set = change.modes_clear = 0;
6127 change.args[0].mode = MODE_BAN;
6128 for(ii = 0; ii < user->channels.used; ++ii)
6130 channel = user->channels.list[ii]->channel;
6131 /* Need not check for bans if they're opped or voiced. */
6132 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6134 /* Need not check for bans unless channel registration is active. */
6135 if(!channel->channel_info || IsSuspended(channel->channel_info))
6137 /* Look for a matching ban already on the channel. */
6138 for(jj = 0; jj < channel->banlist.used; ++jj)
6139 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6141 /* Need not act if we found one. */
6142 if(jj < channel->banlist.used)
6144 /* Look for a matching ban in this channel. */
6145 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6147 if(!user_matches_glob(user, bData->mask, 1))
6149 change.args[0].hostmask = bData->mask;
6150 mod_chanmode_announce(chanserv, channel, &change);
6151 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6152 KickChannelUser(user, channel, chanserv, kick_reason);
6153 bData->triggered = now;
6154 break; /* we don't need to check any more bans in the channel */
6159 static void handle_rename(struct handle_info *handle, const char *old_handle)
6161 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6165 dict_remove2(handle_dnrs, old_handle, 1);
6166 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6167 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6172 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6174 struct userNode *h_user;
6176 if(handle->channels)
6178 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6179 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6181 while(handle->channels)
6182 del_channel_user(handle->channels, 1);
6187 handle_server_link(UNUSED_ARG(struct server *server))
6189 struct chanData *cData;
6191 for(cData = channelList; cData; cData = cData->next)
6193 if(!IsSuspended(cData))
6194 cData->may_opchan = 1;
6195 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6196 && !cData->channel->join_flooded
6197 && ((cData->channel->limit - cData->channel->members.used)
6198 < chanserv_conf.adjust_threshold))
6200 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6201 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6207 chanserv_conf_read(void)
6211 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6212 struct mod_chanmode *change;
6213 struct string_list *strlist;
6214 struct chanNode *chan;
6217 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6219 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6222 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6223 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6224 chanserv_conf.support_channels.used = 0;
6225 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6227 for(ii = 0; ii < strlist->used; ++ii)
6229 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6232 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6234 channelList_append(&chanserv_conf.support_channels, chan);
6237 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6240 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6243 chan = AddChannel(str, now, str2, NULL);
6245 channelList_append(&chanserv_conf.support_channels, chan);
6247 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6248 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6249 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6250 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6251 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6252 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6253 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6254 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6255 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6256 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6257 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6258 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6259 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6260 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6261 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6262 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6263 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6264 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6265 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6266 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6267 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6269 NickChange(chanserv, str, 0);
6270 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6271 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6272 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6273 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6274 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6275 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6276 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6277 chanserv_conf.max_owned = str ? atoi(str) : 5;
6278 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6279 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6280 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6281 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6282 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6283 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6284 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6287 safestrncpy(mode_line, str, sizeof(mode_line));
6288 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6289 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6291 chanserv_conf.default_modes = *change;
6292 mod_chanmode_free(change);
6294 free_string_list(chanserv_conf.set_shows);
6295 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6297 strlist = string_list_copy(strlist);
6300 static const char *list[] = {
6301 /* free form text */
6302 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6303 /* options based on user level */
6304 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6305 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6306 /* multiple choice options */
6307 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6308 /* binary options */
6309 "DynLimit", "NoDelete",
6314 strlist = alloc_string_list(ArrayLength(list)-1);
6315 for(ii=0; list[ii]; ii++)
6316 string_list_append(strlist, strdup(list[ii]));
6318 chanserv_conf.set_shows = strlist;
6319 /* We don't look things up now, in case the list refers to options
6320 * defined by modules initialized after this point. Just mark the
6321 * function list as invalid, so it will be initialized.
6323 set_shows_list.used = 0;
6324 free_string_list(chanserv_conf.eightball);
6325 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6328 strlist = string_list_copy(strlist);
6332 strlist = alloc_string_list(4);
6333 string_list_append(strlist, strdup("Yes."));
6334 string_list_append(strlist, strdup("No."));
6335 string_list_append(strlist, strdup("Maybe so."));
6337 chanserv_conf.eightball = strlist;
6338 free_string_list(chanserv_conf.old_ban_names);
6339 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6341 strlist = string_list_copy(strlist);
6343 strlist = alloc_string_list(2);
6344 chanserv_conf.old_ban_names = strlist;
6348 chanserv_note_type_read(const char *key, struct record_data *rd)
6351 struct note_type *ntype;
6354 if(!(obj = GET_RECORD_OBJECT(rd)))
6356 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6359 if(!(ntype = chanserv_create_note_type(key)))
6361 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6365 /* Figure out set access */
6366 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6368 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6369 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6371 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6373 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6374 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6376 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6378 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6382 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6383 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6384 ntype->set_access.min_opserv = 0;
6387 /* Figure out visibility */
6388 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6389 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6390 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6391 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6392 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6393 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6394 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6395 ntype->visible_type = NOTE_VIS_ALL;
6397 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6399 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6400 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6404 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6406 struct handle_info *handle;
6407 struct userData *uData;
6408 char *seen, *inf, *flags;
6410 unsigned short access;
6412 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6414 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6418 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6419 if(access > UL_OWNER)
6421 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6425 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6426 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6427 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6428 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6429 handle = get_handle_info(key);
6432 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6436 uData = add_channel_user(chan, handle, access, last_seen, inf);
6437 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6441 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6443 struct banData *bData;
6444 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6445 time_t set_time, triggered_time, expires_time;
6447 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6449 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6453 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6454 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6455 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6456 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6457 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6458 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6460 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6461 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6463 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6465 expires_time = set_time + atoi(s_duration);
6469 if(expires_time && (expires_time < now))
6472 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6475 static struct suspended *
6476 chanserv_read_suspended(dict_t obj)
6478 struct suspended *suspended = calloc(1, sizeof(*suspended));
6482 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6483 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6484 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6485 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6486 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6487 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6488 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6489 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6490 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6491 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6496 chanserv_channel_read(const char *key, struct record_data *hir)
6498 struct suspended *suspended;
6499 struct mod_chanmode *modes;
6500 struct chanNode *cNode;
6501 struct chanData *cData;
6502 struct dict *channel, *obj;
6503 char *str, *argv[10];
6507 channel = hir->d.object;
6509 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6512 cNode = AddChannel(key, now, NULL, NULL);
6515 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6518 cData = register_channel(cNode, str);
6521 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6525 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6527 enum levelOption lvlOpt;
6528 enum charOption chOpt;
6530 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6531 cData->flags = atoi(str);
6533 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6535 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6537 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6538 else if(levelOptions[lvlOpt].old_flag)
6540 if(cData->flags & levelOptions[lvlOpt].old_flag)
6541 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6543 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6547 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6549 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6551 cData->chOpts[chOpt] = str[0];
6554 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6556 enum levelOption lvlOpt;
6557 enum charOption chOpt;
6560 cData->flags = base64toint(str, 5);
6561 count = strlen(str += 5);
6562 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6565 if(levelOptions[lvlOpt].old_flag)
6567 if(cData->flags & levelOptions[lvlOpt].old_flag)
6568 lvl = levelOptions[lvlOpt].flag_value;
6570 lvl = levelOptions[lvlOpt].default_value;
6572 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6574 case 'c': lvl = UL_COOWNER; break;
6575 case 'm': lvl = UL_MASTER; break;
6576 case 'n': lvl = UL_OWNER+1; break;
6577 case 'o': lvl = UL_OP; break;
6578 case 'p': lvl = UL_PEON; break;
6579 case 'w': lvl = UL_OWNER; break;
6580 default: lvl = 0; break;
6582 cData->lvlOpts[lvlOpt] = lvl;
6584 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6585 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6588 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6590 suspended = chanserv_read_suspended(obj);
6591 cData->suspended = suspended;
6592 suspended->cData = cData;
6593 /* We could use suspended->expires and suspended->revoked to
6594 * set the CHANNEL_SUSPENDED flag, but we don't. */
6596 else if(cData->flags & CHANNEL_SUSPENDED)
6598 suspended = calloc(1, sizeof(*suspended));
6599 suspended->issued = 0;
6600 suspended->revoked = 0;
6601 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6602 suspended->expires = str ? atoi(str) : 0;
6603 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6604 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6605 suspended->reason = strdup(str ? str : "No reason");
6606 suspended->previous = NULL;
6607 cData->suspended = suspended;
6608 suspended->cData = cData;
6613 if((cData->flags & CHANNEL_SUSPENDED)
6614 && suspended->expires
6615 && (suspended->expires <= now))
6617 cData->flags &= ~CHANNEL_SUSPENDED;
6620 if(!(cData->flags & CHANNEL_SUSPENDED))
6622 struct mod_chanmode change;
6623 change.modes_set = change.modes_clear = 0;
6625 change.args[0].mode = MODE_CHANOP;
6626 change.args[0].member = AddChannelUser(chanserv, cNode);
6627 mod_chanmode_announce(chanserv, cNode, &change);
6629 else if(suspended->expires > now)
6631 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6634 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6635 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6636 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6637 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6638 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6639 cData->max = str ? atoi(str) : 0;
6640 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6641 cData->greeting = str ? strdup(str) : NULL;
6642 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6643 cData->user_greeting = str ? strdup(str) : NULL;
6644 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6645 cData->topic_mask = str ? strdup(str) : NULL;
6646 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6647 cData->topic = str ? strdup(str) : NULL;
6649 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6650 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6651 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6652 cData->modes = *modes;
6653 if(cData->modes.argc > 1)
6654 cData->modes.argc = 1;
6655 if(!IsSuspended(cData))
6656 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6657 mod_chanmode_free(modes);
6660 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6661 for(it = dict_first(obj); it; it = iter_next(it))
6662 user_read_helper(iter_key(it), iter_data(it), cData);
6664 if(!cData->users && !IsProtected(cData))
6666 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6667 unregister_channel(cData, "has empty user list.");
6671 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6672 for(it = dict_first(obj); it; it = iter_next(it))
6673 ban_read_helper(iter_key(it), iter_data(it), cData);
6675 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6676 for(it = dict_first(obj); it; it = iter_next(it))
6678 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6679 struct record_data *rd = iter_data(it);
6680 const char *note, *setter;
6682 if(rd->type != RECDB_OBJECT)
6684 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6688 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6690 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6692 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6696 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6697 if(!setter) setter = "<unknown>";
6698 chanserv_add_channel_note(cData, ntype, setter, note);
6706 chanserv_dnr_read(const char *key, struct record_data *hir)
6708 const char *setter, *reason, *str;
6709 struct do_not_register *dnr;
6711 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6714 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6717 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6720 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6723 dnr = chanserv_add_dnr(key, setter, reason);
6726 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6728 dnr->set = atoi(str);
6734 chanserv_saxdb_read(struct dict *database)
6736 struct dict *section;
6739 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6740 for(it = dict_first(section); it; it = iter_next(it))
6741 chanserv_note_type_read(iter_key(it), iter_data(it));
6743 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6744 for(it = dict_first(section); it; it = iter_next(it))
6745 chanserv_channel_read(iter_key(it), iter_data(it));
6747 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6748 for(it = dict_first(section); it; it = iter_next(it))
6749 chanserv_dnr_read(iter_key(it), iter_data(it));
6755 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6757 int high_present = 0;
6758 saxdb_start_record(ctx, KEY_USERS, 1);
6759 for(; uData; uData = uData->next)
6761 if((uData->access >= UL_PRESENT) && uData->present)
6763 saxdb_start_record(ctx, uData->handle->handle, 0);
6764 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6765 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6767 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6769 saxdb_write_string(ctx, KEY_INFO, uData->info);
6770 saxdb_end_record(ctx);
6772 saxdb_end_record(ctx);
6773 return high_present;
6777 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6781 saxdb_start_record(ctx, KEY_BANS, 1);
6782 for(; bData; bData = bData->next)
6784 saxdb_start_record(ctx, bData->mask, 0);
6785 saxdb_write_int(ctx, KEY_SET, bData->set);
6786 if(bData->triggered)
6787 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6789 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6791 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6793 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6794 saxdb_end_record(ctx);
6796 saxdb_end_record(ctx);
6800 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6802 saxdb_start_record(ctx, name, 0);
6803 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6804 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6806 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6808 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6810 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6812 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6813 saxdb_end_record(ctx);
6817 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6821 enum levelOption lvlOpt;
6822 enum charOption chOpt;
6824 saxdb_start_record(ctx, channel->channel->name, 1);
6826 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6827 saxdb_write_int(ctx, KEY_MAX, channel->max);
6829 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6830 if(channel->registrar)
6831 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6832 if(channel->greeting)
6833 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6834 if(channel->user_greeting)
6835 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6836 if(channel->topic_mask)
6837 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6838 if(channel->suspended)
6839 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6841 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6842 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6843 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6844 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6845 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6847 buf[0] = channel->chOpts[chOpt];
6849 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6851 saxdb_end_record(ctx);
6853 if(channel->modes.modes_set || channel->modes.modes_clear)
6855 mod_chanmode_format(&channel->modes, buf);
6856 saxdb_write_string(ctx, KEY_MODES, buf);
6859 high_present = chanserv_write_users(ctx, channel->users);
6860 chanserv_write_bans(ctx, channel->bans);
6862 if(dict_size(channel->notes))
6866 saxdb_start_record(ctx, KEY_NOTES, 1);
6867 for(it = dict_first(channel->notes); it; it = iter_next(it))
6869 struct note *note = iter_data(it);
6870 saxdb_start_record(ctx, iter_key(it), 0);
6871 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6872 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6873 saxdb_end_record(ctx);
6875 saxdb_end_record(ctx);
6878 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6879 saxdb_end_record(ctx);
6883 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6887 saxdb_start_record(ctx, ntype->name, 0);
6888 switch(ntype->set_access_type)
6890 case NOTE_SET_CHANNEL_ACCESS:
6891 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6893 case NOTE_SET_CHANNEL_SETTER:
6894 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6896 case NOTE_SET_PRIVILEGED: default:
6897 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6900 switch(ntype->visible_type)
6902 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6903 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6904 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6906 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6907 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6908 saxdb_end_record(ctx);
6912 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6914 struct do_not_register *dnr;
6917 for(it = dict_first(dnrs); it; it = iter_next(it))
6919 dnr = iter_data(it);
6920 saxdb_start_record(ctx, dnr->chan_name, 0);
6922 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6923 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6924 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6925 saxdb_end_record(ctx);
6930 chanserv_saxdb_write(struct saxdb_context *ctx)
6933 struct chanData *channel;
6936 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6937 for(it = dict_first(note_types); it; it = iter_next(it))
6938 chanserv_write_note_type(ctx, iter_data(it));
6939 saxdb_end_record(ctx);
6942 saxdb_start_record(ctx, KEY_DNR, 1);
6943 write_dnrs_helper(ctx, handle_dnrs);
6944 write_dnrs_helper(ctx, plain_dnrs);
6945 write_dnrs_helper(ctx, mask_dnrs);
6946 saxdb_end_record(ctx);
6949 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6950 for(channel = channelList; channel; channel = channel->next)
6951 chanserv_write_channel(ctx, channel);
6952 saxdb_end_record(ctx);
6958 chanserv_db_cleanup(void) {
6960 unreg_part_func(handle_part);
6962 unregister_channel(channelList, "terminating.");
6963 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6964 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6965 free(chanserv_conf.support_channels.list);
6966 dict_delete(handle_dnrs);
6967 dict_delete(plain_dnrs);
6968 dict_delete(mask_dnrs);
6969 dict_delete(note_types);
6970 free_string_list(chanserv_conf.eightball);
6971 free_string_list(chanserv_conf.old_ban_names);
6972 free_string_list(chanserv_conf.set_shows);
6973 free(set_shows_list.list);
6974 free(uset_shows_list.list);
6977 struct userData *helper = helperList;
6978 helperList = helperList->next;
6983 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6984 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6985 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6988 init_chanserv(const char *nick)
6990 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6991 conf_register_reload(chanserv_conf_read);
6993 reg_server_link_func(handle_server_link);
6995 reg_new_channel_func(handle_new_channel);
6996 reg_join_func(handle_join);
6997 reg_part_func(handle_part);
6998 reg_kick_func(handle_kick);
6999 reg_topic_func(handle_topic);
7000 reg_mode_change_func(handle_mode);
7001 reg_nick_change_func(handle_nick_change);
7003 reg_auth_func(handle_auth);
7004 reg_handle_rename_func(handle_rename);
7005 reg_unreg_func(handle_unreg);
7007 handle_dnrs = dict_new();
7008 dict_set_free_data(handle_dnrs, free);
7009 plain_dnrs = dict_new();
7010 dict_set_free_data(plain_dnrs, free);
7011 mask_dnrs = dict_new();
7012 dict_set_free_data(mask_dnrs, free);
7014 reg_svccmd_unbind_func(handle_svccmd_unbind);
7015 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7016 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7017 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7018 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7019 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7020 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7021 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7022 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7023 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7025 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7026 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7028 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7029 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7030 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7031 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7032 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7034 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7035 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7036 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7037 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7038 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7040 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7041 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", NULL);
7042 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7043 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7045 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7046 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7047 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7048 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7049 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7050 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7051 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7052 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7054 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7055 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7056 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7057 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7058 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7059 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7060 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7061 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7062 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7063 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7064 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7065 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7066 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7067 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7069 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7070 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7071 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7072 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7073 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7075 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7076 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7078 DEFINE_COMMAND(access, 1, 0, "flags", "+nolog,+acceptchan", NULL);
7079 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7080 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7081 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7082 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7083 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7084 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7085 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7086 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7087 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7089 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7090 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7092 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7093 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7094 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7095 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7097 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7098 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7099 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7100 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7101 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7103 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7104 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7105 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7106 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7107 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7108 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7110 /* Channel options */
7111 DEFINE_CHANNEL_OPTION(defaulttopic);
7112 DEFINE_CHANNEL_OPTION(topicmask);
7113 DEFINE_CHANNEL_OPTION(greeting);
7114 DEFINE_CHANNEL_OPTION(usergreeting);
7115 DEFINE_CHANNEL_OPTION(modes);
7116 DEFINE_CHANNEL_OPTION(enfops);
7117 DEFINE_CHANNEL_OPTION(giveops);
7118 DEFINE_CHANNEL_OPTION(protect);
7119 DEFINE_CHANNEL_OPTION(enfmodes);
7120 DEFINE_CHANNEL_OPTION(enftopic);
7121 DEFINE_CHANNEL_OPTION(pubcmd);
7122 DEFINE_CHANNEL_OPTION(givevoice);
7123 DEFINE_CHANNEL_OPTION(userinfo);
7124 DEFINE_CHANNEL_OPTION(dynlimit);
7125 DEFINE_CHANNEL_OPTION(topicsnarf);
7126 DEFINE_CHANNEL_OPTION(nodelete);
7127 DEFINE_CHANNEL_OPTION(toys);
7128 DEFINE_CHANNEL_OPTION(setters);
7129 DEFINE_CHANNEL_OPTION(topicrefresh);
7130 DEFINE_CHANNEL_OPTION(ctcpusers);
7131 DEFINE_CHANNEL_OPTION(ctcpreaction);
7132 DEFINE_CHANNEL_OPTION(inviteme);
7133 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7135 /* Alias set topic to set defaulttopic for compatibility. */
7136 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7139 DEFINE_USER_OPTION(noautoop);
7140 DEFINE_USER_OPTION(autoinvite);
7141 DEFINE_USER_OPTION(info);
7143 /* Alias uset autovoice to uset autoop. */
7144 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7146 note_types = dict_new();
7147 dict_set_free_data(note_types, chanserv_deref_note_type);
7150 chanserv = AddService(nick, "Channel Services");
7151 service_register(chanserv, '!');
7152 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7154 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7156 if(chanserv_conf.channel_expire_frequency)
7157 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7159 if(chanserv_conf.refresh_period)
7161 time_t next_refresh;
7162 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7163 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7166 reg_exit_func(chanserv_db_cleanup);
7167 message_register_table(msgtab);