1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
46 #define KEY_SWITCH_TIMEOUT "switch_timeout"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
199 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
202 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
203 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
204 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
205 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
206 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
207 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
208 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
209 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
210 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
211 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
212 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
213 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
214 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
215 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
216 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
217 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
219 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
221 /* Channel management */
222 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
223 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
224 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
226 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
227 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
228 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
229 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
230 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
231 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
232 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
234 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
235 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
236 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
237 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
238 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
239 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
240 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
241 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
242 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
243 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
244 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
245 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
246 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
247 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
248 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
249 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
250 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
251 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
252 { "CSMSG_SET_MODES", "$bModes $b %s" },
253 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
254 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
255 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
256 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
257 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
258 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
259 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
260 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
261 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
262 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
263 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
264 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
265 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
266 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
267 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
268 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
269 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
270 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
271 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
272 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
273 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
274 { "CSMSG_USET_INFO", "$bInfo $b %s" },
276 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
277 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
278 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
279 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
280 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
281 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
282 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
283 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
284 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
286 { "CSMSG_PROTECT_NONE", "No users will be protected." },
287 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
288 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
289 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
290 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
291 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
292 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
293 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
294 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
295 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
300 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
301 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
302 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
303 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
304 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
305 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
306 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
307 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
309 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
310 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
311 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
313 /* Channel userlist */
314 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
315 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
316 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
317 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
319 /* Channel note list */
320 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
321 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
322 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
323 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
324 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
325 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
326 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
327 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
328 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
329 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
330 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
331 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
332 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
333 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
334 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
336 /* Channel [un]suspension */
337 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
338 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
339 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
340 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
341 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
342 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
343 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
345 /* Access information */
346 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
347 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
348 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
349 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
350 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
351 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
352 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
353 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
354 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
355 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
356 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
358 /* Seen information */
359 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
360 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
361 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
362 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
364 /* Names information */
365 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
366 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
368 /* Channel information */
369 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
370 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
371 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
372 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
373 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
374 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
375 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
376 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
377 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
378 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
379 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
380 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
388 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
389 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
391 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
392 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
393 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
394 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
395 { "CSMSG_PEEK_OPS", "$bOps:$b" },
396 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
398 /* Network information */
399 { "CSMSG_NETWORK_INFO", "Network Information:" },
400 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
401 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
402 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
403 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
404 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
405 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
406 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
407 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
410 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
411 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
412 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
414 /* Channel searches */
415 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
416 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
417 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
418 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
420 /* Channel configuration */
421 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
422 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
423 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
426 { "CSMSG_USER_OPTIONS", "User Options:" },
427 { "CSMSG_USER_PROTECTED", "That user is protected." },
430 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
431 { "CSMSG_PING_RESPONSE", "Pong!" },
432 { "CSMSG_WUT_RESPONSE", "wut" },
433 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
434 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
435 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
436 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
437 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
438 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
439 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
442 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
446 /* eject_user and unban_user flags */
447 #define ACTION_KICK 0x0001
448 #define ACTION_BAN 0x0002
449 #define ACTION_ADD_BAN 0x0004
450 #define ACTION_ADD_TIMED_BAN 0x0008
451 #define ACTION_UNBAN 0x0010
452 #define ACTION_DEL_BAN 0x0020
454 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
455 #define MODELEN 40 + KEYLEN
459 #define CSFUNC_ARGS user, channel, argc, argv, cmd
461 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
462 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
463 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
464 reply("MSG_MISSING_PARAMS", argv[0]); \
468 DECLARE_LIST(dnrList, struct do_not_register *);
469 DEFINE_LIST(dnrList, struct do_not_register *);
471 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
473 struct userNode *chanserv;
476 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
477 static struct log_type *CS_LOG;
481 struct channelList support_channels;
482 struct mod_chanmode default_modes;
484 unsigned long db_backup_frequency;
485 unsigned long channel_expire_frequency;
488 unsigned int adjust_delay;
489 long channel_expire_delay;
490 unsigned int nodelete_level;
492 unsigned int adjust_threshold;
493 int join_flood_threshold;
495 unsigned int greeting_length;
496 unsigned int refresh_period;
498 unsigned int max_owned;
499 unsigned int max_chan_users;
500 unsigned int max_chan_bans;
501 unsigned int max_userinfo_length;
503 struct string_list *set_shows;
504 struct string_list *eightball;
505 struct string_list *old_ban_names;
507 const char *ctcp_short_ban_duration;
508 const char *ctcp_long_ban_duration;
510 const char *irc_operator_epithet;
511 const char *network_helper_epithet;
512 const char *support_helper_epithet;
517 struct userNode *user;
518 struct userNode *bot;
519 struct chanNode *channel;
521 unsigned short lowest;
522 unsigned short highest;
523 struct userData **users;
524 struct helpfile_table table;
527 enum note_access_type
529 NOTE_SET_CHANNEL_ACCESS,
530 NOTE_SET_CHANNEL_SETTER,
534 enum note_visible_type
537 NOTE_VIS_CHANNEL_USERS,
543 enum note_access_type set_access_type;
545 unsigned int min_opserv;
546 unsigned short min_ulevel;
548 enum note_visible_type visible_type;
549 unsigned int max_length;
556 struct note_type *type;
557 char setter[NICKSERV_HANDLE_LEN+1];
561 static unsigned int registered_channels;
562 static unsigned int banCount;
564 static const struct {
567 unsigned short level;
570 { "peon", "Peon", UL_PEON, '+' },
571 { "op", "Op", UL_OP, '@' },
572 { "master", "Master", UL_MASTER, '%' },
573 { "coowner", "Coowner", UL_COOWNER, '*' },
574 { "owner", "Owner", UL_OWNER, '!' },
575 { "helper", "BUG:", UL_HELPER, 'X' }
578 static const struct {
581 unsigned short default_value;
582 unsigned int old_idx;
583 unsigned int old_flag;
584 unsigned short flag_value;
586 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
587 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
588 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
589 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
590 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
591 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
592 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
593 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
594 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
595 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
596 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
599 struct charOptionValues {
602 } protectValues[] = {
603 { 'a', "CSMSG_PROTECT_ALL" },
604 { 'e', "CSMSG_PROTECT_EQUAL" },
605 { 'l', "CSMSG_PROTECT_LOWER" },
606 { 'n', "CSMSG_PROTECT_NONE" }
608 { 'd', "CSMSG_TOYS_DISABLED" },
609 { 'n', "CSMSG_TOYS_PRIVATE" },
610 { 'p', "CSMSG_TOYS_PUBLIC" }
611 }, topicRefreshValues[] = {
612 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
613 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
614 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
615 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
616 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
617 }, ctcpReactionValues[] = {
618 { 'k', "CSMSG_CTCPREACTION_KICK" },
619 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
620 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
621 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
624 static const struct {
628 unsigned int old_idx;
630 struct charOptionValues *values;
632 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
633 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
634 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
635 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
638 struct userData *helperList;
639 struct chanData *channelList;
640 static struct module *chanserv_module;
641 static unsigned int userCount;
643 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
644 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
645 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
648 user_level_from_name(const char *name, unsigned short clamp_level)
650 unsigned int level = 0, ii;
652 level = strtoul(name, NULL, 10);
653 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
654 if(!irccasecmp(name, accessLevels[ii].name))
655 level = accessLevels[ii].level;
656 if(level > clamp_level)
662 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
665 *minl = strtoul(arg, &sep, 10);
673 *maxl = strtoul(sep+1, &sep, 10);
681 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
683 struct userData *uData, **head;
685 if(!channel || !handle)
688 if(override && HANDLE_FLAGGED(handle, HELPING)
689 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
691 for(uData = helperList;
692 uData && uData->handle != handle;
693 uData = uData->next);
697 uData = calloc(1, sizeof(struct userData));
698 uData->handle = handle;
700 uData->access = UL_HELPER;
706 uData->next = helperList;
708 helperList->prev = uData;
716 for(uData = channel->users; uData; uData = uData->next)
717 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
720 head = &(channel->users);
723 if(uData && (uData != *head))
725 /* Shuffle the user to the head of whatever list he was in. */
727 uData->next->prev = uData->prev;
729 uData->prev->next = uData->next;
735 (**head).prev = uData;
742 /* Returns non-zero if user has at least the minimum access.
743 * exempt_owner is set when handling !set, so the owner can set things
746 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
748 struct userData *uData;
749 struct chanData *cData = channel->channel_info;
750 unsigned short minimum = cData->lvlOpts[opt];
753 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
756 if(minimum <= uData->access)
758 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
763 /* Scan for other users authenticated to the same handle
764 still in the channel. If so, keep them listed as present.
766 user is optional, if not null, it skips checking that userNode
767 (for the handle_part function) */
769 scan_user_presence(struct userData *uData, struct userNode *user)
773 if(IsSuspended(uData->channel)
774 || IsUserSuspended(uData)
775 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
787 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
789 unsigned int eflags, argc;
791 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
793 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
794 if(!channel->channel_info
795 || IsSuspended(channel->channel_info)
797 || !ircncasecmp(text, "ACTION ", 7))
799 /* Figure out the minimum level needed to CTCP the channel */
800 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
802 /* We need to enforce against them; do so. */
805 argv[1] = user->nick;
807 if(GetUserMode(channel, user))
808 eflags |= ACTION_KICK;
809 switch(channel->channel_info->chOpts[chCTCPReaction]) {
810 default: case 'k': /* just do the kick */ break;
812 eflags |= ACTION_BAN;
815 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
816 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
823 argv[argc++] = bad_ctcp_reason;
824 eject_user(chanserv, channel, argc, argv, NULL, eflags);
828 chanserv_create_note_type(const char *name)
830 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
831 strcpy(ntype->name, name);
833 dict_insert(note_types, ntype->name, ntype);
838 chanserv_deref_note_type(void *data)
840 struct note_type *ntype = data;
842 if(--ntype->refs > 0)
848 chanserv_flush_note_type(struct note_type *ntype)
850 struct chanData *cData;
851 for(cData = channelList; cData; cData = cData->next)
852 dict_remove(cData->notes, ntype->name);
856 chanserv_truncate_notes(struct note_type *ntype)
858 struct chanData *cData;
860 unsigned int size = sizeof(*note) + ntype->max_length;
862 for(cData = channelList; cData; cData = cData->next) {
863 note = dict_find(cData->notes, ntype->name, NULL);
866 if(strlen(note->note) <= ntype->max_length)
868 dict_remove2(cData->notes, ntype->name, 1);
869 note = realloc(note, size);
870 note->note[ntype->max_length] = 0;
871 dict_insert(cData->notes, ntype->name, note);
875 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
878 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
881 unsigned int len = strlen(text);
883 if(len > type->max_length) len = type->max_length;
884 note = calloc(1, sizeof(*note) + len);
886 strncpy(note->setter, setter, sizeof(note->setter)-1);
887 memcpy(note->note, text, len);
889 dict_insert(channel->notes, type->name, note);
895 chanserv_free_note(void *data)
897 struct note *note = data;
899 chanserv_deref_note_type(note->type);
900 assert(note->type->refs > 0); /* must use delnote to remove the type */
904 static MODCMD_FUNC(cmd_createnote) {
905 struct note_type *ntype;
906 unsigned int arg = 1, existed = 0, max_length;
908 if((ntype = dict_find(note_types, argv[1], NULL)))
911 ntype = chanserv_create_note_type(argv[arg]);
912 if(!irccasecmp(argv[++arg], "privileged"))
915 ntype->set_access_type = NOTE_SET_PRIVILEGED;
916 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
918 else if(!irccasecmp(argv[arg], "channel"))
920 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
923 reply("CSMSG_INVALID_ACCESS", argv[arg]);
926 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
927 ntype->set_access.min_ulevel = ulvl;
929 else if(!irccasecmp(argv[arg], "setter"))
931 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
935 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
939 if(!irccasecmp(argv[++arg], "privileged"))
940 ntype->visible_type = NOTE_VIS_PRIVILEGED;
941 else if(!irccasecmp(argv[arg], "channel_users"))
942 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
943 else if(!irccasecmp(argv[arg], "all"))
944 ntype->visible_type = NOTE_VIS_ALL;
946 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
950 if((arg+1) >= argc) {
951 reply("MSG_MISSING_PARAMS", argv[0]);
954 max_length = strtoul(argv[++arg], NULL, 0);
955 if(max_length < 20 || max_length > 450)
957 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
960 if(existed && (max_length < ntype->max_length))
962 ntype->max_length = max_length;
963 chanserv_truncate_notes(ntype);
965 ntype->max_length = max_length;
968 reply("CSMSG_NOTE_MODIFIED", ntype->name);
970 reply("CSMSG_NOTE_CREATED", ntype->name);
975 dict_remove(note_types, ntype->name);
979 static MODCMD_FUNC(cmd_removenote) {
980 struct note_type *ntype;
983 ntype = dict_find(note_types, argv[1], NULL);
984 force = (argc > 2) && !irccasecmp(argv[2], "force");
987 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
994 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
997 chanserv_flush_note_type(ntype);
999 dict_remove(note_types, argv[1]);
1000 reply("CSMSG_NOTE_DELETED", argv[1]);
1005 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1009 if(orig->modes_set & change->modes_clear)
1011 if(orig->modes_clear & change->modes_set)
1013 if((orig->modes_set & MODE_KEY)
1014 && strcmp(orig->new_key, change->new_key))
1016 if((orig->modes_set & MODE_LIMIT)
1017 && (orig->new_limit != change->new_limit))
1022 static char max_length_text[MAXLEN+1][16];
1024 static struct helpfile_expansion
1025 chanserv_expand_variable(const char *variable)
1027 struct helpfile_expansion exp;
1029 if(!irccasecmp(variable, "notes"))
1032 exp.type = HF_TABLE;
1033 exp.value.table.length = 1;
1034 exp.value.table.width = 3;
1035 exp.value.table.flags = 0;
1036 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1037 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1038 exp.value.table.contents[0][0] = "Note Type";
1039 exp.value.table.contents[0][1] = "Visibility";
1040 exp.value.table.contents[0][2] = "Max Length";
1041 for(it=dict_first(note_types); it; it=iter_next(it))
1043 struct note_type *ntype = iter_data(it);
1046 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1047 row = exp.value.table.length++;
1048 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1049 exp.value.table.contents[row][0] = ntype->name;
1050 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1051 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1053 if(!max_length_text[ntype->max_length][0])
1054 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1055 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1060 exp.type = HF_STRING;
1061 exp.value.str = NULL;
1065 static struct chanData*
1066 register_channel(struct chanNode *cNode, char *registrar)
1068 struct chanData *channel;
1069 enum levelOption lvlOpt;
1070 enum charOption chOpt;
1072 channel = calloc(1, sizeof(struct chanData));
1074 channel->notes = dict_new();
1075 dict_set_free_data(channel->notes, chanserv_free_note);
1077 channel->registrar = strdup(registrar);
1078 channel->registered = now;
1079 channel->visited = now;
1080 channel->limitAdjusted = now;
1081 channel->flags = CHANNEL_DEFAULT_FLAGS;
1082 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1083 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1084 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1085 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1087 channel->prev = NULL;
1088 channel->next = channelList;
1091 channelList->prev = channel;
1092 channelList = channel;
1093 registered_channels++;
1095 channel->channel = cNode;
1097 cNode->channel_info = channel;
1102 static struct userData*
1103 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1105 struct userData *ud;
1107 if(access > UL_OWNER)
1110 ud = calloc(1, sizeof(*ud));
1111 ud->channel = channel;
1112 ud->handle = handle;
1114 ud->access = access;
1115 ud->info = info ? strdup(info) : NULL;
1118 ud->next = channel->users;
1120 channel->users->prev = ud;
1121 channel->users = ud;
1123 channel->userCount++;
1127 ud->u_next = ud->handle->channels;
1129 ud->u_next->u_prev = ud;
1130 ud->handle->channels = ud;
1135 static void unregister_channel(struct chanData *channel, const char *reason);
1138 del_channel_user(struct userData *user, int do_gc)
1140 struct chanData *channel = user->channel;
1142 channel->userCount--;
1146 user->prev->next = user->next;
1148 channel->users = user->next;
1150 user->next->prev = user->prev;
1153 user->u_prev->u_next = user->u_next;
1155 user->handle->channels = user->u_next;
1157 user->u_next->u_prev = user->u_prev;
1161 if(do_gc && !channel->users && !IsProtected(channel))
1162 unregister_channel(channel, "lost all users.");
1165 static void expire_ban(void *data);
1167 static struct banData*
1168 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1171 unsigned int ii, l1, l2;
1176 bd = malloc(sizeof(struct banData));
1178 bd->channel = channel;
1180 bd->triggered = triggered;
1181 bd->expires = expires;
1183 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1185 extern const char *hidden_host_suffix;
1186 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1190 l2 = strlen(old_name);
1193 if(irccasecmp(mask + l1 - l2, old_name))
1195 new_mask = alloca(MAXLEN);
1196 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1199 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1201 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1202 bd->reason = reason ? strdup(reason) : NULL;
1205 timeq_add(expires, expire_ban, bd);
1208 bd->next = channel->bans;
1210 channel->bans->prev = bd;
1212 channel->banCount++;
1219 del_channel_ban(struct banData *ban)
1221 ban->channel->banCount--;
1225 ban->prev->next = ban->next;
1227 ban->channel->bans = ban->next;
1230 ban->next->prev = ban->prev;
1233 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1242 expire_ban(void *data)
1244 struct banData *bd = data;
1245 if(!IsSuspended(bd->channel))
1247 struct banList bans;
1248 struct mod_chanmode change;
1250 bans = bd->channel->channel->banlist;
1251 mod_chanmode_init(&change);
1252 for(ii=0; ii<bans.used; ii++)
1254 if(!strcmp(bans.list[ii]->ban, bd->mask))
1257 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1258 change.args[0].hostmask = bd->mask;
1259 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1265 del_channel_ban(bd);
1268 static void chanserv_expire_suspension(void *data);
1271 unregister_channel(struct chanData *channel, const char *reason)
1273 struct mod_chanmode change;
1274 char msgbuf[MAXLEN];
1276 /* After channel unregistration, the following must be cleaned
1278 - Channel information.
1281 - Channel suspension data.
1282 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1288 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1290 mod_chanmode_init(&change);
1291 change.modes_clear |= MODE_REGISTERED;
1292 mod_chanmode_announce(chanserv, channel->channel, &change);
1294 while(channel->users)
1295 del_channel_user(channel->users, 0);
1297 while(channel->bans)
1298 del_channel_ban(channel->bans);
1300 free(channel->topic);
1301 free(channel->registrar);
1302 free(channel->greeting);
1303 free(channel->user_greeting);
1304 free(channel->topic_mask);
1307 channel->prev->next = channel->next;
1309 channelList = channel->next;
1312 channel->next->prev = channel->prev;
1314 if(channel->suspended)
1316 struct chanNode *cNode = channel->channel;
1317 struct suspended *suspended, *next_suspended;
1319 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1321 next_suspended = suspended->previous;
1322 free(suspended->suspender);
1323 free(suspended->reason);
1324 if(suspended->expires)
1325 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1330 cNode->channel_info = NULL;
1332 channel->channel->channel_info = NULL;
1334 dict_delete(channel->notes);
1335 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1336 if(!IsSuspended(channel))
1337 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1338 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1339 UnlockChannel(channel->channel);
1341 registered_channels--;
1345 expire_channels(UNUSED_ARG(void *data))
1347 struct chanData *channel, *next;
1348 struct userData *user;
1349 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1351 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1352 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1354 for(channel = channelList; channel; channel = next)
1356 next = channel->next;
1358 /* See if the channel can be expired. */
1359 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1360 || IsProtected(channel))
1363 /* Make sure there are no high-ranking users still in the channel. */
1364 for(user=channel->users; user; user=user->next)
1365 if(user->present && (user->access >= UL_PRESENT))
1370 /* Unregister the channel */
1371 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1372 unregister_channel(channel, "registration expired.");
1375 if(chanserv_conf.channel_expire_frequency)
1376 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1380 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1382 char protect = channel->chOpts[chProtect];
1383 struct userData *cs_victim, *cs_aggressor;
1385 /* Don't protect if no one is to be protected, someone is attacking
1386 himself, or if the aggressor is an IRC Operator. */
1387 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1390 /* Don't protect if the victim isn't authenticated (because they
1391 can't be a channel user), unless we are to protect non-users
1393 cs_victim = GetChannelAccess(channel, victim->handle_info);
1394 if(protect != 'a' && !cs_victim)
1397 /* Protect if the aggressor isn't a user because at this point,
1398 the aggressor can only be less than or equal to the victim. */
1399 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1403 /* If the aggressor was a user, then the victim can't be helped. */
1410 if(cs_victim->access > cs_aggressor->access)
1415 if(cs_victim->access >= cs_aggressor->access)
1424 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1426 struct chanData *cData = channel->channel_info;
1427 struct userData *cs_victim;
1429 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1430 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1431 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1433 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1441 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1443 if(IsService(victim))
1445 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1449 if(protect_user(victim, user, channel->channel_info))
1451 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1458 static struct do_not_register *
1459 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1461 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1462 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1463 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1464 strcpy(dnr->reason, reason);
1466 if(dnr->chan_name[0] == '*')
1467 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1468 else if(strpbrk(dnr->chan_name, "*?"))
1469 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1471 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1475 static struct dnrList
1476 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1478 struct dnrList list;
1480 struct do_not_register *dnr;
1482 dnrList_init(&list);
1483 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1484 dnrList_append(&list, dnr);
1485 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1486 dnrList_append(&list, dnr);
1488 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1489 if(match_ircglob(chan_name, iter_key(it)))
1490 dnrList_append(&list, iter_data(it));
1495 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1497 struct dnrList list;
1498 struct do_not_register *dnr;
1500 char buf[INTERVALLEN];
1502 list = chanserv_find_dnrs(chan_name, handle);
1503 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1505 dnr = list.list[ii];
1508 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1509 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1512 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1515 reply("CSMSG_MORE_DNRS", list.used - ii);
1520 struct do_not_register *
1521 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1523 struct do_not_register *dnr;
1526 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1530 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1532 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1533 if(match_ircglob(chan_name, iter_key(it)))
1534 return iter_data(it);
1539 static CHANSERV_FUNC(cmd_noregister)
1542 struct do_not_register *dnr;
1543 char buf[INTERVALLEN];
1544 unsigned int matches;
1550 reply("CSMSG_DNR_SEARCH_RESULTS");
1552 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1554 dnr = iter_data(it);
1556 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1558 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1561 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1563 dnr = iter_data(it);
1565 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1567 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1570 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1572 dnr = iter_data(it);
1574 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1576 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1581 reply("MSG_MATCH_COUNT", matches);
1583 reply("MSG_NO_MATCHES");
1589 if(!IsChannelName(target) && (*target != '*'))
1591 reply("CSMSG_NOT_DNR", target);
1597 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1598 if((*target == '*') && !get_handle_info(target + 1))
1600 reply("MSG_HANDLE_UNKNOWN", target + 1);
1603 chanserv_add_dnr(target, user->handle_info->handle, reason);
1604 reply("CSMSG_NOREGISTER_CHANNEL", target);
1608 reply("CSMSG_DNR_SEARCH_RESULTS");
1610 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1612 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1614 reply("MSG_NO_MATCHES");
1618 static CHANSERV_FUNC(cmd_allowregister)
1620 const char *chan_name = argv[1];
1622 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1624 dict_remove(handle_dnrs, chan_name+1);
1625 reply("CSMSG_DNR_REMOVED", chan_name);
1627 else if(dict_find(plain_dnrs, chan_name, NULL))
1629 dict_remove(plain_dnrs, chan_name);
1630 reply("CSMSG_DNR_REMOVED", chan_name);
1632 else if(dict_find(mask_dnrs, chan_name, NULL))
1634 dict_remove(mask_dnrs, chan_name);
1635 reply("CSMSG_DNR_REMOVED", chan_name);
1639 reply("CSMSG_NO_SUCH_DNR", chan_name);
1646 chanserv_get_owned_count(struct handle_info *hi)
1648 struct userData *cList;
1651 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1652 if(cList->access == UL_OWNER)
1657 static CHANSERV_FUNC(cmd_register)
1659 struct mod_chanmode *change;
1660 struct handle_info *handle;
1661 struct chanData *cData;
1662 struct modeNode *mn;
1663 char reason[MAXLEN];
1665 unsigned int new_channel, force=0;
1666 struct do_not_register *dnr;
1670 if(channel->channel_info)
1672 reply("CSMSG_ALREADY_REGGED", channel->name);
1676 if(channel->bad_channel)
1678 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1682 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1684 reply("CSMSG_MUST_BE_OPPED", channel->name);
1689 chan_name = channel->name;
1693 if((argc < 2) || !IsChannelName(argv[1]))
1695 reply("MSG_NOT_CHANNEL_NAME");
1699 if(opserv_bad_channel(argv[1]))
1701 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1706 chan_name = argv[1];
1709 if(argc >= (new_channel+2))
1711 if(!IsHelping(user))
1713 reply("CSMSG_PROXY_FORBIDDEN");
1717 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1719 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1720 dnr = chanserv_is_dnr(chan_name, handle);
1724 handle = user->handle_info;
1725 dnr = chanserv_is_dnr(chan_name, handle);
1729 if(!IsHelping(user))
1730 reply("CSMSG_DNR_CHANNEL", chan_name);
1732 chanserv_show_dnrs(user, cmd, chan_name, handle);
1736 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1738 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1743 channel = AddChannel(argv[1], now, NULL, NULL);
1745 cData = register_channel(channel, user->handle_info->handle);
1746 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1747 cData->modes = chanserv_conf.default_modes;
1748 change = mod_chanmode_dup(&cData->modes, 1);
1749 change->args[change->argc].mode = MODE_CHANOP;
1750 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1752 mod_chanmode_announce(chanserv, channel, change);
1753 mod_chanmode_free(change);
1755 /* Initialize the channel's max user record. */
1756 cData->max = channel->members.used;
1758 if(handle != user->handle_info)
1759 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1761 reply("CSMSG_REG_SUCCESS", channel->name);
1763 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1764 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1769 make_confirmation_string(struct userData *uData)
1771 static char strbuf[16];
1776 for(src = uData->handle->handle; *src; )
1777 accum = accum * 31 + toupper(*src++);
1779 for(src = uData->channel->channel->name; *src; )
1780 accum = accum * 31 + toupper(*src++);
1781 sprintf(strbuf, "%08x", accum);
1785 static CHANSERV_FUNC(cmd_unregister)
1788 char reason[MAXLEN];
1789 struct chanData *cData;
1790 struct userData *uData;
1792 cData = channel->channel_info;
1795 reply("CSMSG_NOT_REGISTERED", channel->name);
1799 uData = GetChannelUser(cData, user->handle_info);
1800 if(!uData || (uData->access < UL_OWNER))
1802 reply("CSMSG_NO_ACCESS");
1806 if(IsProtected(cData))
1808 reply("CSMSG_UNREG_NODELETE", channel->name);
1812 if(!IsHelping(user))
1814 const char *confirm_string;
1815 if(IsSuspended(cData))
1817 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1820 confirm_string = make_confirmation_string(uData);
1821 if((argc < 2) || strcmp(argv[1], confirm_string))
1823 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1828 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1829 name = strdup(channel->name);
1830 unregister_channel(cData, reason);
1831 reply("CSMSG_UNREG_SUCCESS", name);
1836 static CHANSERV_FUNC(cmd_move)
1838 struct chanNode *target;
1839 struct modeNode *mn;
1840 struct userData *uData;
1841 char reason[MAXLEN];
1842 struct do_not_register *dnr;
1846 if(IsProtected(channel->channel_info))
1848 reply("CSMSG_MOVE_NODELETE", channel->name);
1852 if(!IsChannelName(argv[1]))
1854 reply("MSG_NOT_CHANNEL_NAME");
1858 if(opserv_bad_channel(argv[1]))
1860 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1864 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1866 for(uData = channel->channel_info->users; uData; uData = uData->next)
1868 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1870 if(!IsHelping(user))
1871 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1873 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1879 if(!(target = GetChannel(argv[1])))
1881 target = AddChannel(argv[1], now, NULL, NULL);
1882 if(!IsSuspended(channel->channel_info))
1883 AddChannelUser(chanserv, target);
1885 else if(target->channel_info)
1887 reply("CSMSG_ALREADY_REGGED", target->name);
1890 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1891 && !IsHelping(user))
1893 reply("CSMSG_MUST_BE_OPPED", target->name);
1896 else if(!IsSuspended(channel->channel_info))
1898 struct mod_chanmode change;
1899 mod_chanmode_init(&change);
1901 change.args[0].mode = MODE_CHANOP;
1902 change.args[0].member = AddChannelUser(chanserv, target);
1903 mod_chanmode_announce(chanserv, target, &change);
1906 /* Move the channel_info to the target channel; it
1907 shouldn't be necessary to clear timeq callbacks
1908 for the old channel. */
1909 target->channel_info = channel->channel_info;
1910 target->channel_info->channel = target;
1911 channel->channel_info = NULL;
1913 reply("CSMSG_MOVE_SUCCESS", target->name);
1915 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1916 if(!IsSuspended(target->channel_info))
1918 char reason2[MAXLEN];
1919 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1920 DelChannelUser(chanserv, channel, reason2, 0);
1922 UnlockChannel(channel);
1923 LockChannel(target);
1924 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1929 merge_users(struct chanData *source, struct chanData *target)
1931 struct userData *suData, *tuData, *next;
1937 /* Insert the source's users into the scratch area. */
1938 for(suData = source->users; suData; suData = suData->next)
1939 dict_insert(merge, suData->handle->handle, suData);
1941 /* Iterate through the target's users, looking for
1942 users common to both channels. The lower access is
1943 removed from either the scratch area or target user
1945 for(tuData = target->users; tuData; tuData = next)
1947 struct userData *choice;
1949 next = tuData->next;
1951 /* If a source user exists with the same handle as a target
1952 channel's user, resolve the conflict by removing one. */
1953 suData = dict_find(merge, tuData->handle->handle, NULL);
1957 /* Pick the data we want to keep. */
1958 /* If the access is the same, use the later seen time. */
1959 if(suData->access == tuData->access)
1960 choice = (suData->seen > tuData->seen) ? suData : tuData;
1961 else /* Otherwise, keep the higher access level. */
1962 choice = (suData->access > tuData->access) ? suData : tuData;
1964 /* Remove the user that wasn't picked. */
1965 if(choice == tuData)
1967 dict_remove(merge, suData->handle->handle);
1968 del_channel_user(suData, 0);
1971 del_channel_user(tuData, 0);
1974 /* Move the remaining users to the target channel. */
1975 for(it = dict_first(merge); it; it = iter_next(it))
1977 suData = iter_data(it);
1979 /* Insert the user into the target channel's linked list. */
1980 suData->prev = NULL;
1981 suData->next = target->users;
1982 suData->channel = target;
1985 target->users->prev = suData;
1986 target->users = suData;
1988 /* Update the user counts for the target channel; the
1989 source counts are left alone. */
1990 target->userCount++;
1993 /* Possible to assert (source->users == NULL) here. */
1994 source->users = NULL;
1999 merge_bans(struct chanData *source, struct chanData *target)
2001 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2003 /* Hold on to the original head of the target ban list
2004 to avoid comparing source bans with source bans. */
2005 tFront = target->bans;
2007 /* Perform a totally expensive O(n*m) merge, ick. */
2008 for(sbData = source->bans; sbData; sbData = sNext)
2010 /* Flag to track whether the ban's been moved
2011 to the destination yet. */
2014 /* Possible to assert (sbData->prev == NULL) here. */
2015 sNext = sbData->next;
2017 for(tbData = tFront; tbData; tbData = tNext)
2019 tNext = tbData->next;
2021 /* Perform two comparisons between each source
2022 and target ban, conflicts are resolved by
2023 keeping the broader ban and copying the later
2024 expiration and triggered time. */
2025 if(match_ircglobs(tbData->mask, sbData->mask))
2027 /* There is a broader ban in the target channel that
2028 overrides one in the source channel; remove the
2029 source ban and break. */
2030 if(sbData->expires > tbData->expires)
2031 tbData->expires = sbData->expires;
2032 if(sbData->triggered > tbData->triggered)
2033 tbData->triggered = sbData->triggered;
2034 del_channel_ban(sbData);
2037 else if(match_ircglobs(sbData->mask, tbData->mask))
2039 /* There is a broader ban in the source channel that
2040 overrides one in the target channel; remove the
2041 target ban, fall through and move the source over. */
2042 if(tbData->expires > sbData->expires)
2043 sbData->expires = tbData->expires;
2044 if(tbData->triggered > sbData->triggered)
2045 sbData->triggered = tbData->triggered;
2046 if(tbData == tFront)
2048 del_channel_ban(tbData);
2051 /* Source bans can override multiple target bans, so
2052 we allow a source to run through this loop multiple
2053 times, but we can only move it once. */
2058 /* Remove the source ban from the source ban list. */
2060 sbData->next->prev = sbData->prev;
2062 /* Modify the source ban's associated channel. */
2063 sbData->channel = target;
2065 /* Insert the ban into the target channel's linked list. */
2066 sbData->prev = NULL;
2067 sbData->next = target->bans;
2070 target->bans->prev = sbData;
2071 target->bans = sbData;
2073 /* Update the user counts for the target channel. */
2078 /* Possible to assert (source->bans == NULL) here. */
2079 source->bans = NULL;
2083 merge_data(struct chanData *source, struct chanData *target)
2085 if(source->visited > target->visited)
2086 target->visited = source->visited;
2090 merge_channel(struct chanData *source, struct chanData *target)
2092 merge_users(source, target);
2093 merge_bans(source, target);
2094 merge_data(source, target);
2097 static CHANSERV_FUNC(cmd_merge)
2099 struct userData *target_user;
2100 struct chanNode *target;
2101 char reason[MAXLEN];
2105 /* Make sure the target channel exists and is registered to the user
2106 performing the command. */
2107 if(!(target = GetChannel(argv[1])))
2109 reply("MSG_INVALID_CHANNEL");
2113 if(!target->channel_info)
2115 reply("CSMSG_NOT_REGISTERED", target->name);
2119 if(IsProtected(channel->channel_info))
2121 reply("CSMSG_MERGE_NODELETE");
2125 if(IsSuspended(target->channel_info))
2127 reply("CSMSG_MERGE_SUSPENDED");
2131 if(channel == target)
2133 reply("CSMSG_MERGE_SELF");
2137 target_user = GetChannelUser(target->channel_info, user->handle_info);
2138 if(!target_user || (target_user->access < UL_OWNER))
2140 reply("CSMSG_MERGE_NOT_OWNER");
2144 /* Merge the channel structures and associated data. */
2145 merge_channel(channel->channel_info, target->channel_info);
2146 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2147 unregister_channel(channel->channel_info, reason);
2148 reply("CSMSG_MERGE_SUCCESS", target->name);
2152 static CHANSERV_FUNC(cmd_opchan)
2154 struct mod_chanmode change;
2155 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2157 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2160 channel->channel_info->may_opchan = 0;
2161 mod_chanmode_init(&change);
2163 change.args[0].mode = MODE_CHANOP;
2164 change.args[0].member = GetUserMode(channel, chanserv);
2165 mod_chanmode_announce(chanserv, channel, &change);
2166 reply("CSMSG_OPCHAN_DONE", channel->name);
2170 static CHANSERV_FUNC(cmd_adduser)
2172 struct userData *actee;
2173 struct userData *actor;
2174 struct handle_info *handle;
2175 unsigned short access;
2179 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2181 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2185 access = user_level_from_name(argv[2], UL_OWNER);
2188 reply("CSMSG_INVALID_ACCESS", argv[2]);
2192 actor = GetChannelUser(channel->channel_info, user->handle_info);
2193 if(actor->access <= access)
2195 reply("CSMSG_NO_BUMP_ACCESS");
2199 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2202 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2204 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2208 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2209 scan_user_presence(actee, NULL);
2210 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2214 static CHANSERV_FUNC(cmd_clvl)
2216 struct handle_info *handle;
2217 struct userData *victim;
2218 struct userData *actor;
2219 unsigned short new_access;
2220 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2224 actor = GetChannelUser(channel->channel_info, user->handle_info);
2226 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2229 if(handle == user->handle_info && !privileged)
2231 reply("CSMSG_NO_SELF_CLVL");
2235 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2237 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2241 if(actor->access <= victim->access && !privileged)
2243 reply("MSG_USER_OUTRANKED", handle->handle);
2247 new_access = user_level_from_name(argv[2], UL_OWNER);
2251 reply("CSMSG_INVALID_ACCESS", argv[2]);
2255 if(new_access >= actor->access && !privileged)
2257 reply("CSMSG_NO_BUMP_ACCESS");
2261 victim->access = new_access;
2262 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2266 static CHANSERV_FUNC(cmd_deluser)
2268 struct handle_info *handle;
2269 struct userData *victim;
2270 struct userData *actor;
2271 unsigned short access;
2276 actor = GetChannelUser(channel->channel_info, user->handle_info);
2278 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2281 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2283 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2289 access = user_level_from_name(argv[1], UL_OWNER);
2292 reply("CSMSG_INVALID_ACCESS", argv[1]);
2295 if(access != victim->access)
2297 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2303 access = victim->access;
2306 if((actor->access <= victim->access) && !IsHelping(user))
2308 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2312 chan_name = strdup(channel->name);
2313 del_channel_user(victim, 1);
2314 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2320 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2322 struct userData *actor, *uData, *next;
2324 actor = GetChannelUser(channel->channel_info, user->handle_info);
2326 if(min_access > max_access)
2328 reply("CSMSG_BAD_RANGE", min_access, max_access);
2332 if((actor->access <= max_access) && !IsHelping(user))
2334 reply("CSMSG_NO_ACCESS");
2338 for(uData = channel->channel_info->users; uData; uData = next)
2342 if((uData->access >= min_access)
2343 && (uData->access <= max_access)
2344 && match_ircglob(uData->handle->handle, mask))
2345 del_channel_user(uData, 1);
2348 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2352 static CHANSERV_FUNC(cmd_mdelowner)
2354 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2357 static CHANSERV_FUNC(cmd_mdelcoowner)
2359 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2362 static CHANSERV_FUNC(cmd_mdelmaster)
2364 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2367 static CHANSERV_FUNC(cmd_mdelop)
2369 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2372 static CHANSERV_FUNC(cmd_mdelpeon)
2374 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2378 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2380 struct banData *bData, *next;
2381 char interval[INTERVALLEN];
2386 limit = now - duration;
2387 for(bData = channel->channel_info->bans; bData; bData = next)
2391 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2394 del_channel_ban(bData);
2398 intervalString(interval, duration, user->handle_info);
2399 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2404 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2406 struct userData *actor, *uData, *next;
2407 char interval[INTERVALLEN];
2411 actor = GetChannelUser(channel->channel_info, user->handle_info);
2412 if(min_access > max_access)
2414 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2418 if((actor->access <= max_access) && !IsHelping(user))
2420 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2425 limit = now - duration;
2426 for(uData = channel->channel_info->users; uData; uData = next)
2430 if((uData->seen > limit) || uData->present)
2433 if(((uData->access >= min_access) && (uData->access <= max_access))
2434 || (!max_access && (uData->access < actor->access)))
2436 del_channel_user(uData, 1);
2444 max_access = UL_OWNER;
2446 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2450 static CHANSERV_FUNC(cmd_trim)
2452 unsigned long duration;
2453 unsigned short min_level, max_level;
2457 duration = ParseInterval(argv[2]);
2460 reply("CSMSG_CANNOT_TRIM");
2464 if(!irccasecmp(argv[1], "bans"))
2466 cmd_trim_bans(user, channel, duration);
2469 else if(!irccasecmp(argv[1], "users"))
2471 cmd_trim_users(user, channel, 0, 0, duration);
2474 else if(parse_level_range(&min_level, &max_level, argv[1]))
2476 cmd_trim_users(user, channel, min_level, max_level, duration);
2479 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2481 cmd_trim_users(user, channel, min_level, min_level, duration);
2486 reply("CSMSG_INVALID_TRIM", argv[1]);
2491 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2492 to the user. cmd_all takes advantage of this. */
2493 static CHANSERV_FUNC(cmd_up)
2495 struct mod_chanmode change;
2496 struct userData *uData;
2499 mod_chanmode_init(&change);
2501 change.args[0].member = GetUserMode(channel, user);
2502 if(!change.args[0].member)
2505 reply("MSG_CHANNEL_ABSENT", channel->name);
2509 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2513 reply("CSMSG_GODMODE_UP", argv[0]);
2516 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2518 change.args[0].mode = MODE_CHANOP;
2519 errmsg = "CSMSG_ALREADY_OPPED";
2521 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2523 change.args[0].mode = MODE_VOICE;
2524 errmsg = "CSMSG_ALREADY_VOICED";
2529 reply("CSMSG_NO_ACCESS");
2532 change.args[0].mode &= ~change.args[0].member->modes;
2533 if(!change.args[0].mode)
2536 reply(errmsg, channel->name);
2539 modcmd_chanmode_announce(&change);
2543 static CHANSERV_FUNC(cmd_down)
2545 struct mod_chanmode change;
2547 mod_chanmode_init(&change);
2549 change.args[0].member = GetUserMode(channel, user);
2550 if(!change.args[0].member)
2553 reply("MSG_CHANNEL_ABSENT", channel->name);
2557 if(!change.args[0].member->modes)
2560 reply("CSMSG_ALREADY_DOWN", channel->name);
2564 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2565 modcmd_chanmode_announce(&change);
2569 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2571 struct userData *cList;
2573 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2575 if(IsSuspended(cList->channel)
2576 || IsUserSuspended(cList)
2577 || !GetUserMode(cList->channel->channel, user))
2580 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2586 static CHANSERV_FUNC(cmd_upall)
2588 return cmd_all(CSFUNC_ARGS, cmd_up);
2591 static CHANSERV_FUNC(cmd_downall)
2593 return cmd_all(CSFUNC_ARGS, cmd_down);
2596 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2597 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2600 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
2602 unsigned int ii, valid;
2603 struct userNode *victim;
2604 struct mod_chanmode *change;
2606 change = mod_chanmode_alloc(argc - 1);
2608 for(ii=valid=0; ++ii < argc; )
2610 if(!(victim = GetUserH(argv[ii])))
2612 change->args[valid].mode = mode;
2613 change->args[valid].member = GetUserMode(channel, victim);
2614 if(!change->args[valid].member)
2616 if(validate && !validate(user, channel, victim))
2621 change->argc = valid;
2622 if(valid < (argc-1))
2623 reply("CSMSG_PROCESS_FAILED");
2626 modcmd_chanmode_announce(change);
2627 reply(action, channel->name);
2629 mod_chanmode_free(change);
2633 static CHANSERV_FUNC(cmd_op)
2635 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2638 static CHANSERV_FUNC(cmd_deop)
2640 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2643 static CHANSERV_FUNC(cmd_voice)
2645 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2648 static CHANSERV_FUNC(cmd_devoice)
2650 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2654 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2660 for(ii=0; ii<channel->members.used; ii++)
2662 struct modeNode *mn = channel->members.list[ii];
2664 if(IsService(mn->user))
2667 if(!user_matches_glob(mn->user, ban, 1))
2670 if(protect_user(mn->user, user, channel->channel_info))
2674 victims[(*victimCount)++] = mn;
2680 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2682 struct userNode *victim;
2683 struct modeNode **victims;
2684 unsigned int offset, n, victimCount, duration = 0;
2685 char *reason = "Bye.", *ban, *name;
2686 char interval[INTERVALLEN];
2688 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2689 REQUIRE_PARAMS(offset);
2692 reason = unsplit_string(argv + offset, argc - offset, NULL);
2693 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2695 /* Truncate the reason to a length of TOPICLEN, as
2696 the ircd does; however, leave room for an ellipsis
2697 and the kicker's nick. */
2698 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2702 if((victim = GetUserH(argv[1])))
2704 victims = alloca(sizeof(victims[0]));
2705 victims[0] = GetUserMode(channel, victim);
2706 /* XXX: The comparison with ACTION_KICK is just because all
2707 * other actions can work on users outside the channel, and we
2708 * want to allow those (e.g. unbans) in that case. If we add
2709 * some other ejection action for in-channel users, change
2711 victimCount = victims[0] ? 1 : 0;
2713 if(IsService(victim))
2715 reply("MSG_SERVICE_IMMUNE", victim->nick);
2719 if((action == ACTION_KICK) && !victimCount)
2721 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2725 if(protect_user(victim, user, channel->channel_info))
2727 reply("CSMSG_USER_PROTECTED", victim->nick);
2731 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2732 name = victim->nick;
2736 if(!is_ircmask(argv[1]))
2738 reply("MSG_NICK_UNKNOWN", argv[1]);
2742 victims = alloca(sizeof(victims[0]) * channel->members.used);
2744 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2746 reply("CSMSG_MASK_PROTECTED", argv[1]);
2750 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2752 reply("CSMSG_LAME_MASK", argv[1]);
2756 if((action == ACTION_KICK) && (victimCount == 0))
2758 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2762 name = ban = strdup(argv[1]);
2765 /* Truncate the ban in place if necessary; we must ensure
2766 that 'ban' is a valid ban mask before sanitizing it. */
2767 sanitize_ircmask(ban);
2769 if(action & ACTION_ADD_BAN)
2771 struct banData *bData, *next;
2773 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2775 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2780 if(action & ACTION_ADD_TIMED_BAN)
2782 duration = ParseInterval(argv[2]);
2786 reply("CSMSG_DURATION_TOO_LOW");
2790 else if(duration > (86400 * 365 * 2))
2792 reply("CSMSG_DURATION_TOO_HIGH");
2798 for(bData = channel->channel_info->bans; bData; bData = next)
2800 if(match_ircglobs(bData->mask, ban))
2802 int exact = !irccasecmp(bData->mask, ban);
2804 /* The ban is redundant; there is already a ban
2805 with the same effect in place. */
2809 free(bData->reason);
2810 bData->reason = strdup(reason);
2811 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2813 reply("CSMSG_REASON_CHANGE", ban);
2817 if(exact && bData->expires)
2821 /* If the ban matches an existing one exactly,
2822 extend the expiration time if the provided
2823 duration is longer. */
2824 if(duration && ((time_t)(now + duration) > bData->expires))
2826 bData->expires = now + duration;
2837 /* Delete the expiration timeq entry and
2838 requeue if necessary. */
2839 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2842 timeq_add(bData->expires, expire_ban, bData);
2846 /* automated kickban */
2849 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2851 reply("CSMSG_BAN_ADDED", name, channel->name);
2857 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2864 if(match_ircglobs(ban, bData->mask))
2866 /* The ban we are adding makes previously existing
2867 bans redundant; silently remove them. */
2868 del_channel_ban(bData);
2872 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
2874 name = ban = strdup(bData->mask);
2878 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2880 extern const char *hidden_host_suffix;
2881 const char *old_name = chanserv_conf.old_ban_names->list[n];
2883 unsigned int l1, l2;
2886 l2 = strlen(old_name);
2889 if(irccasecmp(ban + l1 - l2, old_name))
2891 new_mask = malloc(MAXLEN);
2892 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2894 name = ban = new_mask;
2899 if(action & ACTION_BAN)
2901 unsigned int exists;
2902 struct mod_chanmode *change;
2904 if(channel->banlist.used >= MAXBANS)
2907 reply("CSMSG_BANLIST_FULL", channel->name);
2912 exists = ChannelBanExists(channel, ban);
2913 change = mod_chanmode_alloc(victimCount + 1);
2914 for(n = 0; n < victimCount; ++n)
2916 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2917 change->args[n].member = victims[n];
2921 change->args[n].mode = MODE_BAN;
2922 change->args[n++].hostmask = ban;
2926 modcmd_chanmode_announce(change);
2928 mod_chanmode_announce(chanserv, channel, change);
2929 mod_chanmode_free(change);
2931 if(exists && (action == ACTION_BAN))
2934 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2940 if(action & ACTION_KICK)
2942 char kick_reason[MAXLEN];
2943 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2945 for(n = 0; n < victimCount; n++)
2946 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2951 /* No response, since it was automated. */
2953 else if(action & ACTION_ADD_BAN)
2956 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2958 reply("CSMSG_BAN_ADDED", name, channel->name);
2960 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2961 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2962 else if(action & ACTION_BAN)
2963 reply("CSMSG_BAN_DONE", name, channel->name);
2964 else if(action & ACTION_KICK && victimCount)
2965 reply("CSMSG_KICK_DONE", name, channel->name);
2971 static CHANSERV_FUNC(cmd_kickban)
2973 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2976 static CHANSERV_FUNC(cmd_kick)
2978 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2981 static CHANSERV_FUNC(cmd_ban)
2983 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2986 static CHANSERV_FUNC(cmd_addban)
2988 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2991 static CHANSERV_FUNC(cmd_addtimedban)
2993 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2996 static struct mod_chanmode *
2997 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2999 struct mod_chanmode *change;
3000 unsigned char *match;
3001 unsigned int ii, count;
3003 match = alloca(bans->used);
3006 for(ii = count = 0; ii < bans->used; ++ii)
3008 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3015 for(ii = count = 0; ii < bans->used; ++ii)
3017 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3024 change = mod_chanmode_alloc(count);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3029 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3030 change->args[count++].hostmask = bans->list[ii]->ban;
3036 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3038 struct userNode *actee;
3044 /* may want to allow a comma delimited list of users... */
3045 if(!(actee = GetUserH(argv[1])))
3047 if(!is_ircmask(argv[1]))
3049 reply("MSG_NICK_UNKNOWN", argv[1]);
3053 mask = strdup(argv[1]);
3056 /* We don't sanitize the mask here because ircu
3058 if(action & ACTION_UNBAN)
3060 struct mod_chanmode *change;
3061 change = find_matching_bans(&channel->banlist, actee, mask);
3064 modcmd_chanmode_announce(change);
3065 mod_chanmode_free(change);
3070 if(action & ACTION_DEL_BAN)
3072 struct banData *ban, *next;
3074 ban = channel->channel_info->bans;
3078 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3081 for( ; ban && !match_ircglobs(mask, ban->mask);
3086 del_channel_ban(ban);
3093 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3095 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3101 static CHANSERV_FUNC(cmd_unban)
3103 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3106 static CHANSERV_FUNC(cmd_delban)
3108 /* it doesn't necessarily have to remove the channel ban - may want
3109 to make that an option. */
3110 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3113 static CHANSERV_FUNC(cmd_unbanme)
3115 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3116 long flags = ACTION_UNBAN;
3118 /* remove permanent bans if the user has the proper access. */
3119 if(uData->access >= UL_MASTER)
3120 flags |= ACTION_DEL_BAN;
3122 argv[1] = user->nick;
3123 return unban_user(user, channel, 2, argv, cmd, flags);
3126 static CHANSERV_FUNC(cmd_unbanall)
3128 struct mod_chanmode *change;
3131 if(!channel->banlist.used)
3133 reply("CSMSG_NO_BANS", channel->name);
3137 change = mod_chanmode_alloc(channel->banlist.used);
3138 for(ii=0; ii<channel->banlist.used; ii++)
3140 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3141 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3143 modcmd_chanmode_announce(change);
3144 mod_chanmode_free(change);
3145 reply("CSMSG_BANS_REMOVED", channel->name);
3149 static CHANSERV_FUNC(cmd_open)
3151 struct mod_chanmode *change;
3153 change = find_matching_bans(&channel->banlist, user, NULL);
3155 change = mod_chanmode_alloc(0);
3156 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3157 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3158 && channel->channel_info->modes.modes_set)
3159 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3160 modcmd_chanmode_announce(change);
3161 reply("CSMSG_CHANNEL_OPENED", channel->name);
3162 mod_chanmode_free(change);
3166 static CHANSERV_FUNC(cmd_myaccess)
3168 static struct string_buffer sbuf;
3169 struct handle_info *target_handle;
3170 struct userData *uData;
3173 target_handle = user->handle_info;
3174 else if(!IsHelping(user))
3176 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3179 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3182 if(!target_handle->channels)
3184 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3188 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3189 for(uData = target_handle->channels; uData; uData = uData->u_next)
3191 struct chanData *cData = uData->channel;
3193 if(uData->access > UL_OWNER)
3195 if(IsProtected(cData)
3196 && (target_handle != user->handle_info)
3197 && !GetTrueChannelAccess(cData, user->handle_info))
3200 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3201 if(uData->flags != USER_AUTO_OP)
3202 string_buffer_append(&sbuf, ',');
3203 if(IsUserSuspended(uData))
3204 string_buffer_append(&sbuf, 's');
3205 if(IsUserAutoOp(uData))
3207 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3208 string_buffer_append(&sbuf, 'o');
3209 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3210 string_buffer_append(&sbuf, 'v');
3212 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3213 string_buffer_append(&sbuf, 'i');
3215 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3217 string_buffer_append_string(&sbuf, ")]");
3218 string_buffer_append(&sbuf, '\0');
3219 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3225 static CHANSERV_FUNC(cmd_access)
3227 struct userNode *target;
3228 struct handle_info *target_handle;
3229 struct userData *uData;
3231 char prefix[MAXLEN];
3236 target_handle = target->handle_info;
3238 else if((target = GetUserH(argv[1])))
3240 target_handle = target->handle_info;
3242 else if(argv[1][0] == '*')
3244 if(!(target_handle = get_handle_info(argv[1]+1)))
3246 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3252 reply("MSG_NICK_UNKNOWN", argv[1]);
3256 assert(target || target_handle);
3258 if(target == chanserv)
3260 reply("CSMSG_IS_CHANSERV");
3268 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3273 reply("MSG_USER_AUTHENTICATE", target->nick);
3276 reply("MSG_AUTHENTICATE");
3282 const char *epithet = NULL, *type = NULL;
3285 epithet = chanserv_conf.irc_operator_epithet;
3288 else if(IsNetworkHelper(target))
3290 epithet = chanserv_conf.network_helper_epithet;
3291 type = "network helper";
3293 else if(IsSupportHelper(target))
3295 epithet = chanserv_conf.support_helper_epithet;
3296 type = "support helper";
3300 if(target_handle->epithet)
3301 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3303 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3305 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3309 sprintf(prefix, "%s", target_handle->handle);
3312 if(!channel->channel_info)
3314 reply("CSMSG_NOT_REGISTERED", channel->name);
3318 helping = HANDLE_FLAGGED(target_handle, HELPING)
3319 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3320 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3322 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3323 /* To prevent possible information leaks, only show infolines
3324 * if the requestor is in the channel or it's their own
3326 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3328 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3330 /* Likewise, only say it's suspended if the user has active
3331 * access in that channel or it's their own entry. */
3332 if(IsUserSuspended(uData)
3333 && (GetChannelUser(channel->channel_info, user->handle_info)
3334 || (user->handle_info == uData->handle)))
3336 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3341 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3348 zoot_list(struct listData *list)
3350 struct userData *uData;
3351 unsigned int start, curr, highest, lowest;
3352 struct helpfile_table tmp_table;
3353 const char **temp, *msg;
3355 if(list->table.length == 1)
3358 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3360 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3361 msg = user_find_message(list->user, "MSG_NONE");
3362 send_message_type(4, list->user, list->bot, " %s", msg);
3364 tmp_table.width = list->table.width;
3365 tmp_table.flags = list->table.flags;
3366 list->table.contents[0][0] = " ";
3367 highest = list->highest;
3368 if(list->lowest != 0)
3369 lowest = list->lowest;
3370 else if(highest < 100)
3373 lowest = highest - 100;
3374 for(start = curr = 1; curr < list->table.length; )
3376 uData = list->users[curr-1];
3377 list->table.contents[curr++][0] = " ";
3378 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3381 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3383 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3384 temp = list->table.contents[--start];
3385 list->table.contents[start] = list->table.contents[0];
3386 tmp_table.contents = list->table.contents + start;
3387 tmp_table.length = curr - start;
3388 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3389 list->table.contents[start] = temp;
3391 highest = lowest - 1;
3392 lowest = (highest < 100) ? 0 : (highest - 99);
3398 def_list(struct listData *list)
3402 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3404 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3405 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3406 if(list->table.length == 1)
3408 msg = user_find_message(list->user, "MSG_NONE");
3409 send_message_type(4, list->user, list->bot, " %s", msg);
3414 userData_access_comp(const void *arg_a, const void *arg_b)
3416 const struct userData *a = *(struct userData**)arg_a;
3417 const struct userData *b = *(struct userData**)arg_b;
3419 if(a->access != b->access)
3420 res = b->access - a->access;
3422 res = irccasecmp(a->handle->handle, b->handle->handle);
3427 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3429 void (*send_list)(struct listData *);
3430 struct userData *uData;
3431 struct listData lData;
3432 unsigned int matches;
3436 lData.bot = cmd->parent->bot;
3437 lData.channel = channel;
3438 lData.lowest = lowest;
3439 lData.highest = highest;
3440 lData.search = (argc > 1) ? argv[1] : NULL;
3441 send_list = zoot_list;
3443 if(user->handle_info)
3445 switch(user->handle_info->userlist_style)
3447 case HI_STYLE_DEF: send_list = def_list; break;
3448 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3452 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3454 for(uData = channel->channel_info->users; uData; uData = uData->next)
3456 if((uData->access < lowest)
3457 || (uData->access > highest)
3458 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3460 lData.users[matches++] = uData;
3462 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3464 lData.table.length = matches+1;
3465 lData.table.width = 4;
3466 lData.table.flags = TABLE_NO_FREE;
3467 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3468 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3469 lData.table.contents[0] = ary;
3472 ary[2] = "Last Seen";
3474 for(matches = 1; matches < lData.table.length; ++matches)
3476 struct userData *uData = lData.users[matches-1];
3477 char seen[INTERVALLEN];
3479 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3480 lData.table.contents[matches] = ary;
3481 ary[0] = strtab(uData->access);
3482 ary[1] = uData->handle->handle;
3485 else if(!uData->seen)
3488 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3489 ary[2] = strdup(ary[2]);
3490 if(IsUserSuspended(uData))
3491 ary[3] = "Suspended";
3492 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3493 ary[3] = "Vacation";
3498 for(matches = 1; matches < lData.table.length; ++matches)
3500 free((char*)lData.table.contents[matches][2]);
3501 free(lData.table.contents[matches]);
3503 free(lData.table.contents[0]);
3504 free(lData.table.contents);
3508 static CHANSERV_FUNC(cmd_users)
3510 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3513 static CHANSERV_FUNC(cmd_wlist)
3515 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3518 static CHANSERV_FUNC(cmd_clist)
3520 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3523 static CHANSERV_FUNC(cmd_mlist)
3525 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3528 static CHANSERV_FUNC(cmd_olist)
3530 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3533 static CHANSERV_FUNC(cmd_plist)
3535 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3538 static CHANSERV_FUNC(cmd_bans)
3540 struct helpfile_table tbl;
3541 unsigned int matches = 0, timed = 0, ii;
3542 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3543 const char *msg_never, *triggered, *expires;
3544 struct banData *ban, **bans;
3551 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3553 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3555 if(search && !match_ircglobs(search, ban->mask))
3557 bans[matches++] = ban;
3562 tbl.length = matches + 1;
3563 tbl.width = 4 + timed;
3565 tbl.flags = TABLE_NO_FREE;
3566 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3567 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3568 tbl.contents[0][0] = "Mask";
3569 tbl.contents[0][1] = "Set By";
3570 tbl.contents[0][2] = "Triggered";
3573 tbl.contents[0][3] = "Expires";
3574 tbl.contents[0][4] = "Reason";
3577 tbl.contents[0][3] = "Reason";
3580 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3582 free(tbl.contents[0]);
3587 msg_never = user_find_message(user, "MSG_NEVER");
3588 for(ii = 0; ii < matches; )
3594 else if(ban->expires)
3595 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3597 expires = msg_never;
3600 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3602 triggered = msg_never;
3604 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3605 tbl.contents[ii][0] = ban->mask;
3606 tbl.contents[ii][1] = ban->owner;
3607 tbl.contents[ii][2] = strdup(triggered);
3610 tbl.contents[ii][3] = strdup(expires);
3611 tbl.contents[ii][4] = ban->reason;
3614 tbl.contents[ii][3] = ban->reason;
3616 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3617 reply("MSG_MATCH_COUNT", matches);
3618 for(ii = 1; ii < tbl.length; ++ii)
3620 free((char*)tbl.contents[ii][2]);
3622 free((char*)tbl.contents[ii][3]);
3623 free(tbl.contents[ii]);
3625 free(tbl.contents[0]);
3631 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3633 struct chanData *cData = channel->channel_info;
3634 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3636 if(cData->topic_mask)
3637 return !match_ircglob(new_topic, cData->topic_mask);
3638 else if(cData->topic)
3639 return irccasecmp(new_topic, cData->topic);
3644 static CHANSERV_FUNC(cmd_topic)
3646 struct chanData *cData;
3649 cData = channel->channel_info;
3654 SetChannelTopic(channel, chanserv, cData->topic, 1);
3655 reply("CSMSG_TOPIC_SET", cData->topic);
3659 reply("CSMSG_NO_TOPIC", channel->name);
3663 topic = unsplit_string(argv + 1, argc - 1, NULL);
3664 /* If they say "!topic *", use an empty topic. */
3665 if((topic[0] == '*') && (topic[1] == 0))
3667 if(bad_topic(channel, user, topic))
3669 char *topic_mask = cData->topic_mask;
3672 char new_topic[TOPICLEN+1], tchar;
3673 int pos=0, starpos=-1, dpos=0, len;
3675 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3682 len = strlen(topic);
3683 if((dpos + len) > TOPICLEN)
3684 len = TOPICLEN + 1 - dpos;
3685 memcpy(new_topic+dpos, topic, len);
3689 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3690 default: new_topic[dpos++] = tchar; break;
3693 if((dpos > TOPICLEN) || tchar)
3696 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3697 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3700 new_topic[dpos] = 0;
3701 SetChannelTopic(channel, chanserv, new_topic, 1);
3703 reply("CSMSG_TOPIC_LOCKED", channel->name);
3708 SetChannelTopic(channel, chanserv, topic, 1);
3710 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3712 /* Grab the topic and save it as the default topic. */
3714 cData->topic = strdup(channel->topic);
3720 static CHANSERV_FUNC(cmd_mode)
3722 struct mod_chanmode *change;
3726 change = &channel->channel_info->modes;
3727 if(change->modes_set || change->modes_clear) {
3728 modcmd_chanmode_announce(change);
3729 reply("CSMSG_DEFAULTED_MODES", channel->name);
3731 reply("CSMSG_NO_MODES", channel->name);
3735 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3738 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3742 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3743 && mode_lock_violated(&channel->channel_info->modes, change))
3746 mod_chanmode_format(&channel->channel_info->modes, modes);
3747 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3751 modcmd_chanmode_announce(change);
3752 mod_chanmode_free(change);
3753 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3757 static CHANSERV_FUNC(cmd_invite)
3759 struct userData *uData;
3760 struct userNode *invite;
3762 uData = GetChannelUser(channel->channel_info, user->handle_info);
3766 if(!(invite = GetUserH(argv[1])))
3768 reply("MSG_NICK_UNKNOWN", argv[1]);
3775 if(GetUserMode(channel, invite))
3777 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3785 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3786 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3789 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3791 irc_invite(chanserv, invite, channel);
3793 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3798 static CHANSERV_FUNC(cmd_inviteme)
3800 if(GetUserMode(channel, user))
3802 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3805 if(channel->channel_info
3806 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3808 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3811 irc_invite(cmd->parent->bot, user, channel);
3816 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3819 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3821 /* We display things based on two dimensions:
3822 * - Issue time: present or absent
3823 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3824 * (in order of precedence, so something both expired and revoked
3825 * only counts as revoked)
3827 combo = (suspended->issued ? 4 : 0)
3828 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3830 case 0: /* no issue time, indefinite expiration */
3831 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3833 case 1: /* no issue time, expires in future */
3834 intervalString(buf1, suspended->expires-now, user->handle_info);
3835 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3837 case 2: /* no issue time, expired */
3838 intervalString(buf1, now-suspended->expires, user->handle_info);
3839 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3841 case 3: /* no issue time, revoked */
3842 intervalString(buf1, now-suspended->revoked, user->handle_info);
3843 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3845 case 4: /* issue time set, indefinite expiration */
3846 intervalString(buf1, now-suspended->issued, user->handle_info);
3847 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3849 case 5: /* issue time set, expires in future */
3850 intervalString(buf1, now-suspended->issued, user->handle_info);
3851 intervalString(buf2, suspended->expires-now, user->handle_info);
3852 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3854 case 6: /* issue time set, expired */
3855 intervalString(buf1, now-suspended->issued, user->handle_info);
3856 intervalString(buf2, now-suspended->expires, user->handle_info);
3857 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3859 case 7: /* issue time set, revoked */
3860 intervalString(buf1, now-suspended->issued, user->handle_info);
3861 intervalString(buf2, now-suspended->revoked, user->handle_info);
3862 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3865 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3870 static CHANSERV_FUNC(cmd_info)
3872 char modes[MAXLEN], buffer[INTERVALLEN];
3873 struct userData *uData, *owner;
3874 struct chanData *cData;
3875 struct do_not_register *dnr;
3880 cData = channel->channel_info;
3881 reply("CSMSG_CHANNEL_INFO", channel->name);
3883 uData = GetChannelUser(cData, user->handle_info);
3884 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3886 mod_chanmode_format(&cData->modes, modes);
3887 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3888 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3891 for(it = dict_first(cData->notes); it; it = iter_next(it))
3895 note = iter_data(it);
3896 if(!note_type_visible_to_user(cData, note->type, user))
3899 padding = PADLEN - 1 - strlen(iter_key(it));
3900 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3903 reply("CSMSG_CHANNEL_MAX", cData->max);
3904 for(owner = cData->users; owner; owner = owner->next)
3905 if(owner->access == UL_OWNER)
3906 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3907 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3908 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3909 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3910 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3912 privileged = IsStaff(user);
3913 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3914 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3916 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3917 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3919 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3921 struct suspended *suspended;
3922 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3923 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3924 show_suspension_info(cmd, user, suspended);
3926 else if(IsSuspended(cData))
3928 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3929 show_suspension_info(cmd, user, cData->suspended);
3934 static CHANSERV_FUNC(cmd_netinfo)
3936 extern time_t boot_time;
3937 extern unsigned long burst_length;
3938 char interval[INTERVALLEN];
3940 reply("CSMSG_NETWORK_INFO");
3941 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3942 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3943 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3944 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3945 reply("CSMSG_NETWORK_BANS", banCount);
3946 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3947 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3948 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3953 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3955 struct helpfile_table table;
3957 struct userNode *user;
3962 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3963 table.contents = alloca(list->used*sizeof(*table.contents));
3964 for(nn=0; nn<list->used; nn++)
3966 user = list->list[nn];
3967 if(user->modes & skip_flags)
3971 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3974 nick = alloca(strlen(user->nick)+3);
3975 sprintf(nick, "(%s)", user->nick);
3979 table.contents[table.length][0] = nick;
3982 table_send(chanserv, to->nick, 0, NULL, table);
3985 static CHANSERV_FUNC(cmd_ircops)
3987 reply("CSMSG_STAFF_OPERS");
3988 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3992 static CHANSERV_FUNC(cmd_helpers)
3994 reply("CSMSG_STAFF_HELPERS");
3995 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3999 static CHANSERV_FUNC(cmd_staff)
4001 reply("CSMSG_NETWORK_STAFF");
4002 cmd_ircops(CSFUNC_ARGS);
4003 cmd_helpers(CSFUNC_ARGS);
4007 static CHANSERV_FUNC(cmd_peek)
4009 struct modeNode *mn;
4010 char modes[MODELEN];
4012 struct helpfile_table table;
4014 irc_make_chanmode(channel, modes);
4016 reply("CSMSG_PEEK_INFO", channel->name);
4017 reply("CSMSG_PEEK_TOPIC", channel->topic);
4018 reply("CSMSG_PEEK_MODES", modes);
4019 reply("CSMSG_PEEK_USERS", channel->members.used);
4023 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4024 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4025 for(n = 0; n < channel->members.used; n++)
4027 mn = channel->members.list[n];
4028 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4030 table.contents[table.length] = alloca(sizeof(**table.contents));
4031 table.contents[table.length][0] = mn->user->nick;
4036 reply("CSMSG_PEEK_OPS");
4037 table_send(chanserv, user->nick, 0, NULL, table);
4040 reply("CSMSG_PEEK_NO_OPS");
4044 static MODCMD_FUNC(cmd_wipeinfo)
4046 struct handle_info *victim;
4047 struct userData *ud, *actor;
4050 actor = GetChannelUser(channel->channel_info, user->handle_info);
4051 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4053 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4055 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4058 if((ud->access >= actor->access) && (ud != actor))
4060 reply("MSG_USER_OUTRANKED", victim->handle);
4066 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4070 static CHANSERV_FUNC(cmd_resync)
4072 struct mod_chanmode *changes;
4073 struct chanData *cData = channel->channel_info;
4074 unsigned int ii, used;
4076 changes = mod_chanmode_alloc(channel->members.used * 2);
4077 for(ii = used = 0; ii < channel->members.used; ++ii)
4079 struct modeNode *mn = channel->members.list[ii];
4080 struct userData *uData;
4082 if(IsService(mn->user))
4085 uData = GetChannelAccess(cData, mn->user->handle_info);
4086 if(!cData->lvlOpts[lvlGiveOps]
4087 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4089 if(!(mn->modes & MODE_CHANOP))
4091 changes->args[used].mode = MODE_CHANOP;
4092 changes->args[used++].member = mn;
4095 else if(!cData->lvlOpts[lvlGiveVoice]
4096 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4098 if(mn->modes & MODE_CHANOP)
4100 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4101 changes->args[used++].member = mn;
4103 if(!(mn->modes & MODE_VOICE))
4105 changes->args[used].mode = MODE_VOICE;
4106 changes->args[used++].member = mn;
4113 changes->args[used].mode = MODE_REMOVE | mn->modes;
4114 changes->args[used++].member = mn;
4118 changes->argc = used;
4119 modcmd_chanmode_announce(changes);
4120 mod_chanmode_free(changes);
4121 reply("CSMSG_RESYNCED_USERS", channel->name);
4125 static CHANSERV_FUNC(cmd_seen)
4127 struct userData *uData;
4128 struct handle_info *handle;
4129 char seen[INTERVALLEN];
4133 if(!irccasecmp(argv[1], chanserv->nick))
4135 reply("CSMSG_IS_CHANSERV");
4139 if(!(handle = get_handle_info(argv[1])))
4141 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4145 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4147 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4152 reply("CSMSG_USER_PRESENT", handle->handle);
4153 else if(uData->seen)
4154 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4156 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4158 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4159 reply("CSMSG_USER_VACATION", handle->handle);
4164 static MODCMD_FUNC(cmd_names)
4166 struct userNode *targ;
4167 struct userData *targData;
4168 unsigned int ii, pos;
4171 for(ii=pos=0; ii<channel->members.used; ++ii)
4173 targ = channel->members.list[ii]->user;
4174 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4177 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4180 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4184 if(IsUserSuspended(targData))
4186 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4189 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4190 reply("CSMSG_END_NAMES", channel->name);
4195 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4197 switch(ntype->visible_type)
4199 case NOTE_VIS_ALL: return 1;
4200 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4201 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4206 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4208 struct userData *uData;
4210 switch(ntype->set_access_type)
4212 case NOTE_SET_CHANNEL_ACCESS:
4213 if(!user->handle_info)
4215 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4217 return uData->access >= ntype->set_access.min_ulevel;
4218 case NOTE_SET_CHANNEL_SETTER:
4219 return check_user_level(channel, user, lvlSetters, 1, 0);
4220 case NOTE_SET_PRIVILEGED: default:
4221 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4225 static CHANSERV_FUNC(cmd_note)
4227 struct chanData *cData;
4229 struct note_type *ntype;
4231 cData = channel->channel_info;
4234 reply("CSMSG_NOT_REGISTERED", channel->name);
4238 /* If no arguments, show all visible notes for the channel. */
4244 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4246 note = iter_data(it);
4247 if(!note_type_visible_to_user(cData, note->type, user))
4250 reply("CSMSG_NOTELIST_HEADER", channel->name);
4251 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4254 reply("CSMSG_NOTELIST_END", channel->name);
4256 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4258 /* If one argument, show the named note. */
4261 if((note = dict_find(cData->notes, argv[1], NULL))
4262 && note_type_visible_to_user(cData, note->type, user))
4264 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4266 else if((ntype = dict_find(note_types, argv[1], NULL))
4267 && note_type_visible_to_user(NULL, ntype, user))
4269 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4274 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4278 /* Assume they're trying to set a note. */
4282 ntype = dict_find(note_types, argv[1], NULL);
4285 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4288 else if(note_type_settable_by_user(channel, ntype, user))
4290 note_text = unsplit_string(argv+2, argc-2, NULL);
4291 if((note = dict_find(cData->notes, argv[1], NULL)))
4292 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4293 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4294 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4296 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4298 /* The note is viewable to staff only, so return 0
4299 to keep the invocation from getting logged (or
4300 regular users can see it in !events). */
4306 reply("CSMSG_NO_ACCESS");
4313 static CHANSERV_FUNC(cmd_delnote)
4318 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4319 || !note_type_settable_by_user(channel, note->type, user))
4321 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4324 dict_remove(channel->channel_info->notes, note->type->name);
4325 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4329 static CHANSERV_FUNC(cmd_events)
4331 struct logSearch discrim;
4332 struct logReport report;
4333 unsigned int matches, limit;
4335 limit = (argc > 1) ? atoi(argv[1]) : 10;
4336 if(limit < 1 || limit > 200)
4339 memset(&discrim, 0, sizeof(discrim));
4340 discrim.masks.bot = chanserv;
4341 discrim.masks.channel_name = channel->name;
4343 discrim.masks.command = argv[2];
4344 discrim.limit = limit;
4345 discrim.max_time = INT_MAX;
4346 discrim.severities = 1 << LOG_COMMAND;
4347 report.reporter = chanserv;
4349 reply("CSMSG_EVENT_SEARCH_RESULTS");
4350 matches = log_entry_search(&discrim, log_report_entry, &report);
4352 reply("MSG_MATCH_COUNT", matches);
4354 reply("MSG_NO_MATCHES");
4358 static CHANSERV_FUNC(cmd_say)
4364 msg = unsplit_string(argv + 1, argc - 1, NULL);
4365 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4367 else if(GetUserH(argv[1]))
4370 msg = unsplit_string(argv + 2, argc - 2, NULL);
4371 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4375 reply("MSG_NOT_TARGET_NAME");
4381 static CHANSERV_FUNC(cmd_emote)
4387 /* CTCP is so annoying. */
4388 msg = unsplit_string(argv + 1, argc - 1, NULL);
4389 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4391 else if(GetUserH(argv[1]))
4393 msg = unsplit_string(argv + 2, argc - 2, NULL);
4394 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4398 reply("MSG_NOT_TARGET_NAME");
4404 struct channelList *
4405 chanserv_support_channels(void)
4407 return &chanserv_conf.support_channels;
4410 static CHANSERV_FUNC(cmd_expire)
4412 int channel_count = registered_channels;
4413 expire_channels(NULL);
4414 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4419 chanserv_expire_suspension(void *data)
4421 struct suspended *suspended = data;
4422 struct chanNode *channel;
4423 struct mod_chanmode change;
4425 if(!suspended->expires || (now < suspended->expires))
4426 suspended->revoked = now;
4427 channel = suspended->cData->channel;
4428 suspended->cData->channel = channel;
4429 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4430 mod_chanmode_init(&change);
4432 change.args[0].mode = MODE_CHANOP;
4433 change.args[0].member = AddChannelUser(chanserv, channel);
4434 mod_chanmode_announce(chanserv, channel, &change);
4437 static CHANSERV_FUNC(cmd_csuspend)
4439 struct suspended *suspended;
4440 char reason[MAXLEN];
4441 time_t expiry, duration;
4442 struct userData *uData;
4446 if(IsProtected(channel->channel_info))
4448 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4452 if(argv[1][0] == '!')
4454 else if(IsSuspended(channel->channel_info))
4456 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4457 show_suspension_info(cmd, user, channel->channel_info->suspended);
4461 if(!strcmp(argv[1], "0"))
4463 else if((duration = ParseInterval(argv[1])))
4464 expiry = now + duration;
4467 reply("MSG_INVALID_DURATION", argv[1]);
4471 unsplit_string(argv + 2, argc - 2, reason);
4473 suspended = calloc(1, sizeof(*suspended));
4474 suspended->revoked = 0;
4475 suspended->issued = now;
4476 suspended->suspender = strdup(user->handle_info->handle);
4477 suspended->expires = expiry;
4478 suspended->reason = strdup(reason);
4479 suspended->cData = channel->channel_info;
4480 suspended->previous = suspended->cData->suspended;
4481 suspended->cData->suspended = suspended;
4483 if(suspended->expires)
4484 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4486 if(IsSuspended(channel->channel_info))
4488 suspended->previous->revoked = now;
4489 if(suspended->previous->expires)
4490 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4491 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4492 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4496 /* Mark all users in channel as absent. */
4497 for(uData = channel->channel_info->users; uData; uData = uData->next)
4506 /* Mark the channel as suspended, then part. */
4507 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4508 DelChannelUser(chanserv, channel, suspended->reason, 0);
4509 reply("CSMSG_SUSPENDED", channel->name);
4510 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4511 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4516 static CHANSERV_FUNC(cmd_cunsuspend)
4518 struct suspended *suspended;
4519 char message[MAXLEN];
4521 if(!IsSuspended(channel->channel_info))
4523 reply("CSMSG_NOT_SUSPENDED", channel->name);
4527 suspended = channel->channel_info->suspended;
4529 /* Expire the suspension and join ChanServ to the channel. */
4530 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4531 chanserv_expire_suspension(suspended);
4532 reply("CSMSG_UNSUSPENDED", channel->name);
4533 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4534 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4538 typedef struct chanservSearch
4546 unsigned long flags;
4550 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4553 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4558 search = malloc(sizeof(struct chanservSearch));
4559 memset(search, 0, sizeof(*search));
4562 for(i = 0; i < argc; i++)
4564 /* Assume all criteria require arguments. */
4567 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4571 if(!irccasecmp(argv[i], "name"))
4572 search->name = argv[++i];
4573 else if(!irccasecmp(argv[i], "registrar"))
4574 search->registrar = argv[++i];
4575 else if(!irccasecmp(argv[i], "unvisited"))
4576 search->unvisited = ParseInterval(argv[++i]);
4577 else if(!irccasecmp(argv[i], "registered"))
4578 search->registered = ParseInterval(argv[++i]);
4579 else if(!irccasecmp(argv[i], "flags"))
4582 if(!irccasecmp(argv[i], "nodelete"))
4583 search->flags |= CHANNEL_NODELETE;
4584 else if(!irccasecmp(argv[i], "suspended"))
4585 search->flags |= CHANNEL_SUSPENDED;
4588 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4592 else if(!irccasecmp(argv[i], "limit"))
4593 search->limit = strtoul(argv[++i], NULL, 10);
4596 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4601 if(search->name && !strcmp(search->name, "*"))
4603 if(search->registrar && !strcmp(search->registrar, "*"))
4604 search->registrar = 0;
4613 chanserv_channel_match(struct chanData *channel, search_t search)
4615 const char *name = channel->channel->name;
4616 if((search->name && !match_ircglob(name, search->name)) ||
4617 (search->registrar && !channel->registrar) ||
4618 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4619 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4620 (search->registered && (now - channel->registered) > search->registered) ||
4621 (search->flags && ((search->flags & channel->flags) != search->flags)))
4628 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4630 struct chanData *channel;
4631 unsigned int matches = 0;
4633 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4635 if(!chanserv_channel_match(channel, search))
4645 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4650 search_print(struct chanData *channel, void *data)
4652 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4655 static CHANSERV_FUNC(cmd_search)
4658 unsigned int matches;
4659 channel_search_func action;
4663 if(!irccasecmp(argv[1], "count"))
4664 action = search_count;
4665 else if(!irccasecmp(argv[1], "print"))
4666 action = search_print;
4669 reply("CSMSG_ACTION_INVALID", argv[1]);
4673 search = chanserv_search_create(user, argc - 2, argv + 2);
4677 if(action == search_count)
4678 search->limit = INT_MAX;
4680 if(action == search_print)
4681 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4683 matches = chanserv_channel_search(search, action, user);
4686 reply("MSG_MATCH_COUNT", matches);
4688 reply("MSG_NO_MATCHES");
4694 static CHANSERV_FUNC(cmd_unvisited)
4696 struct chanData *cData;
4697 time_t interval = chanserv_conf.channel_expire_delay;
4698 char buffer[INTERVALLEN];
4699 unsigned int limit = 25, matches = 0;
4703 interval = ParseInterval(argv[1]);
4705 limit = atoi(argv[2]);
4708 intervalString(buffer, interval, user->handle_info);
4709 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4711 for(cData = channelList; cData && matches < limit; cData = cData->next)
4713 if((now - cData->visited) < interval)
4716 intervalString(buffer, now - cData->visited, user->handle_info);
4717 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4724 static MODCMD_FUNC(chan_opt_defaulttopic)
4730 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4732 reply("CSMSG_TOPIC_LOCKED", channel->name);
4736 topic = unsplit_string(argv+1, argc-1, NULL);
4738 free(channel->channel_info->topic);
4739 if(topic[0] == '*' && topic[1] == 0)
4741 topic = channel->channel_info->topic = NULL;
4745 topic = channel->channel_info->topic = strdup(topic);
4746 if(channel->channel_info->topic_mask
4747 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4748 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4750 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4753 if(channel->channel_info->topic)
4754 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4756 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4760 static MODCMD_FUNC(chan_opt_topicmask)
4764 struct chanData *cData = channel->channel_info;
4767 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4769 reply("CSMSG_TOPIC_LOCKED", channel->name);
4773 mask = unsplit_string(argv+1, argc-1, NULL);
4775 if(cData->topic_mask)
4776 free(cData->topic_mask);
4777 if(mask[0] == '*' && mask[1] == 0)
4779 cData->topic_mask = 0;
4783 cData->topic_mask = strdup(mask);
4785 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4786 else if(!match_ircglob(cData->topic, cData->topic_mask))
4787 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4791 if(channel->channel_info->topic_mask)
4792 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4794 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4798 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4802 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4806 if(greeting[0] == '*' && greeting[1] == 0)
4810 unsigned int length = strlen(greeting);
4811 if(length > chanserv_conf.greeting_length)
4813 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4816 *data = strdup(greeting);
4825 reply(name, user_find_message(user, "MSG_NONE"));
4829 static MODCMD_FUNC(chan_opt_greeting)
4831 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4834 static MODCMD_FUNC(chan_opt_usergreeting)
4836 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4839 static MODCMD_FUNC(chan_opt_modes)
4841 struct mod_chanmode *new_modes;
4842 char modes[MODELEN];
4846 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4848 reply("CSMSG_NO_ACCESS");
4851 if(argv[1][0] == '*' && argv[1][1] == 0)
4853 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4855 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4857 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4860 else if(new_modes->argc > 1)
4862 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4863 mod_chanmode_free(new_modes);
4868 channel->channel_info->modes = *new_modes;
4869 modcmd_chanmode_announce(new_modes);
4870 mod_chanmode_free(new_modes);
4874 mod_chanmode_format(&channel->channel_info->modes, modes);
4876 reply("CSMSG_SET_MODES", modes);
4878 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4882 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4884 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4886 struct chanData *cData = channel->channel_info;
4891 /* Set flag according to value. */
4892 if(enabled_string(argv[1]))
4894 cData->flags |= mask;
4897 else if(disabled_string(argv[1]))
4899 cData->flags &= ~mask;
4904 reply("MSG_INVALID_BINARY", argv[1]);
4910 /* Find current option value. */
4911 value = (cData->flags & mask) ? 1 : 0;
4915 reply(name, user_find_message(user, "MSG_ON"));
4917 reply(name, user_find_message(user, "MSG_OFF"));
4921 static MODCMD_FUNC(chan_opt_nodelete)
4923 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4925 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4929 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4932 static MODCMD_FUNC(chan_opt_dynlimit)
4934 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4937 static MODCMD_FUNC(chan_opt_offchannel)
4939 struct chanData *cData = channel->channel_info;
4944 /* Set flag according to value. */
4945 if(enabled_string(argv[1]))
4947 if(!IsOffChannel(cData))
4948 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4949 cData->flags |= CHANNEL_OFFCHANNEL;
4952 else if(disabled_string(argv[1]))
4954 if(IsOffChannel(cData))
4956 struct mod_chanmode change;
4957 mod_chanmode_init(&change);
4959 change.args[0].mode = MODE_CHANOP;
4960 change.args[0].member = AddChannelUser(chanserv, channel);
4961 mod_chanmode_announce(chanserv, channel, &change);
4963 cData->flags &= ~CHANNEL_OFFCHANNEL;
4968 reply("MSG_INVALID_BINARY", argv[1]);
4974 /* Find current option value. */
4975 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4979 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4981 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4985 static MODCMD_FUNC(chan_opt_defaults)
4987 struct userData *uData;
4988 struct chanData *cData;
4989 const char *confirm;
4990 enum levelOption lvlOpt;
4991 enum charOption chOpt;
4993 cData = channel->channel_info;
4994 uData = GetChannelUser(cData, user->handle_info);
4995 if(!uData || (uData->access < UL_OWNER))
4997 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5000 confirm = make_confirmation_string(uData);
5001 if((argc < 2) || strcmp(argv[1], confirm))
5003 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5006 cData->flags = CHANNEL_DEFAULT_FLAGS;
5007 cData->modes = chanserv_conf.default_modes;
5008 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5009 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5010 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5011 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5012 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5017 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5019 struct chanData *cData = channel->channel_info;
5020 struct userData *uData;
5021 unsigned short value;
5025 if(!check_user_level(channel, user, option, 1, 1))
5027 reply("CSMSG_CANNOT_SET");
5030 value = user_level_from_name(argv[1], UL_OWNER+1);
5031 if(!value && strcmp(argv[1], "0"))
5033 reply("CSMSG_INVALID_ACCESS", argv[1]);
5036 uData = GetChannelUser(cData, user->handle_info);
5037 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5039 reply("CSMSG_BAD_SETLEVEL");
5045 if(value > cData->lvlOpts[lvlGiveOps])
5047 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5052 if(value < cData->lvlOpts[lvlGiveVoice])
5054 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5059 /* This test only applies to owners, since non-owners
5060 * trying to set an option to above their level get caught
5061 * by the CSMSG_BAD_SETLEVEL test above.
5063 if(value > uData->access)
5065 reply("CSMSG_BAD_SETTERS");
5072 cData->lvlOpts[option] = value;
5074 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5078 static MODCMD_FUNC(chan_opt_enfops)
5080 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5083 static MODCMD_FUNC(chan_opt_giveops)
5085 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5088 static MODCMD_FUNC(chan_opt_enfmodes)
5090 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5093 static MODCMD_FUNC(chan_opt_enftopic)
5095 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5098 static MODCMD_FUNC(chan_opt_pubcmd)
5100 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5103 static MODCMD_FUNC(chan_opt_setters)
5105 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5108 static MODCMD_FUNC(chan_opt_ctcpusers)
5110 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5113 static MODCMD_FUNC(chan_opt_userinfo)
5115 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5118 static MODCMD_FUNC(chan_opt_givevoice)
5120 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5123 static MODCMD_FUNC(chan_opt_topicsnarf)
5125 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5128 static MODCMD_FUNC(chan_opt_inviteme)
5130 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5134 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5136 struct chanData *cData = channel->channel_info;
5137 int count = charOptions[option].count, index;
5141 index = atoi(argv[1]);
5143 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5145 reply("CSMSG_INVALID_NUMERIC", index);
5146 /* Show possible values. */
5147 for(index = 0; index < count; index++)
5148 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5152 cData->chOpts[option] = charOptions[option].values[index].value;
5156 /* Find current option value. */
5159 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5163 /* Somehow, the option value is corrupt; reset it to the default. */
5164 cData->chOpts[option] = charOptions[option].default_value;
5169 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5173 static MODCMD_FUNC(chan_opt_protect)
5175 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5178 static MODCMD_FUNC(chan_opt_toys)
5180 return channel_multiple_option(chToys, CSFUNC_ARGS);
5183 static MODCMD_FUNC(chan_opt_ctcpreaction)
5185 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5188 static MODCMD_FUNC(chan_opt_topicrefresh)
5190 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5193 static struct svccmd_list set_shows_list;
5196 handle_svccmd_unbind(struct svccmd *target) {
5198 for(ii=0; ii<set_shows_list.used; ++ii)
5199 if(target == set_shows_list.list[ii])
5200 set_shows_list.used = 0;
5203 static CHANSERV_FUNC(cmd_set)
5205 struct svccmd *subcmd;
5209 /* Check if we need to (re-)initialize set_shows_list. */
5210 if(!set_shows_list.used)
5212 if(!set_shows_list.size)
5214 set_shows_list.size = chanserv_conf.set_shows->used;
5215 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5217 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5219 const char *name = chanserv_conf.set_shows->list[ii];
5220 sprintf(buf, "%s %s", argv[0], name);
5221 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5224 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5227 svccmd_list_append(&set_shows_list, subcmd);
5233 reply("CSMSG_CHANNEL_OPTIONS");
5234 for(ii = 0; ii < set_shows_list.used; ii++)
5236 subcmd = set_shows_list.list[ii];
5237 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5242 sprintf(buf, "%s %s", argv[0], argv[1]);
5243 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5246 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5249 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5251 reply("CSMSG_NO_ACCESS");
5255 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5259 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5261 struct userData *uData;
5263 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5266 reply("CSMSG_NOT_USER", channel->name);
5272 /* Just show current option value. */
5274 else if(enabled_string(argv[1]))
5276 uData->flags |= mask;
5278 else if(disabled_string(argv[1]))
5280 uData->flags &= ~mask;
5284 reply("MSG_INVALID_BINARY", argv[1]);
5288 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5292 static MODCMD_FUNC(user_opt_noautoop)
5294 struct userData *uData;
5296 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5299 reply("CSMSG_NOT_USER", channel->name);
5302 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5303 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5305 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5308 static MODCMD_FUNC(user_opt_autoinvite)
5310 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5313 static MODCMD_FUNC(user_opt_info)
5315 struct userData *uData;
5318 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5322 /* If they got past the command restrictions (which require access)
5323 * but fail this test, we have some fool with security override on.
5325 reply("CSMSG_NOT_USER", channel->name);
5332 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5333 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5335 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5338 bp = strcspn(infoline, "\001");
5341 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5346 if(infoline[0] == '*' && infoline[1] == 0)
5349 uData->info = strdup(infoline);
5352 reply("CSMSG_USET_INFO", uData->info);
5354 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5358 struct svccmd_list uset_shows_list;
5360 static CHANSERV_FUNC(cmd_uset)
5362 struct svccmd *subcmd;
5366 /* Check if we need to (re-)initialize uset_shows_list. */
5367 if(!uset_shows_list.used)
5371 "NoAutoOp", "AutoInvite", "Info"
5374 if(!uset_shows_list.size)
5376 uset_shows_list.size = ArrayLength(options);
5377 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5379 for(ii = 0; ii < ArrayLength(options); ii++)
5381 const char *name = options[ii];
5382 sprintf(buf, "%s %s", argv[0], name);
5383 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5386 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5389 svccmd_list_append(&uset_shows_list, subcmd);
5395 /* Do this so options are presented in a consistent order. */
5396 reply("CSMSG_USER_OPTIONS");
5397 for(ii = 0; ii < uset_shows_list.used; ii++)
5398 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5402 sprintf(buf, "%s %s", argv[0], argv[1]);
5403 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5406 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5410 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5413 static CHANSERV_FUNC(cmd_giveownership)
5415 struct handle_info *new_owner_hi;
5416 struct userData *new_owner, *curr_user;
5417 struct chanData *cData = channel->channel_info;
5418 struct do_not_register *dnr;
5420 unsigned short co_access;
5421 char reason[MAXLEN];
5424 curr_user = GetChannelAccess(cData, user->handle_info);
5425 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5426 if(!curr_user || (curr_user->access != UL_OWNER))
5428 struct userData *owner = NULL;
5429 for(curr_user = channel->channel_info->users;
5431 curr_user = curr_user->next)
5433 if(curr_user->access != UL_OWNER)
5437 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5444 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5446 if(new_owner_hi == user->handle_info)
5448 reply("CSMSG_NO_TRANSFER_SELF");
5451 new_owner = GetChannelAccess(cData, new_owner_hi);
5454 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5457 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5459 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5462 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5463 if(!IsHelping(user))
5464 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5466 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5469 if(new_owner->access >= UL_COOWNER)
5470 co_access = new_owner->access;
5472 co_access = UL_COOWNER;
5473 new_owner->access = UL_OWNER;
5475 curr_user->access = co_access;
5476 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5477 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5478 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5482 static CHANSERV_FUNC(cmd_suspend)
5484 struct handle_info *hi;
5485 struct userData *self, *target;
5488 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5489 self = GetChannelUser(channel->channel_info, user->handle_info);
5490 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5492 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5495 if(target->access >= self->access)
5497 reply("MSG_USER_OUTRANKED", hi->handle);
5500 if(target->flags & USER_SUSPENDED)
5502 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5507 target->present = 0;
5510 target->flags |= USER_SUSPENDED;
5511 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5515 static CHANSERV_FUNC(cmd_unsuspend)
5517 struct handle_info *hi;
5518 struct userData *self, *target;
5521 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5522 self = GetChannelUser(channel->channel_info, user->handle_info);
5523 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5525 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5528 if(target->access >= self->access)
5530 reply("MSG_USER_OUTRANKED", hi->handle);
5533 if(!(target->flags & USER_SUSPENDED))
5535 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5538 target->flags &= ~USER_SUSPENDED;
5539 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5543 static MODCMD_FUNC(cmd_deleteme)
5545 struct handle_info *hi;
5546 struct userData *target;
5547 const char *confirm_string;
5548 unsigned short access;
5551 hi = user->handle_info;
5552 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5554 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5557 if(target->access == UL_OWNER)
5559 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5562 confirm_string = make_confirmation_string(target);
5563 if((argc < 2) || strcmp(argv[1], confirm_string))
5565 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5568 access = target->access;
5569 channel_name = strdup(channel->name);
5570 del_channel_user(target, 1);
5571 reply("CSMSG_DELETED_YOU", access, channel_name);
5577 chanserv_refresh_topics(UNUSED_ARG(void *data))
5579 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5580 struct chanData *cData;
5583 for(cData = channelList; cData; cData = cData->next)
5585 if(IsSuspended(cData))
5587 opt = cData->chOpts[chTopicRefresh];
5590 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5593 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5594 cData->last_refresh = refresh_num;
5596 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5599 static CHANSERV_FUNC(cmd_unf)
5603 char response[MAXLEN];
5604 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5605 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5606 irc_privmsg(cmd->parent->bot, channel->name, response);
5609 reply("CSMSG_UNF_RESPONSE");
5613 static CHANSERV_FUNC(cmd_ping)
5617 char response[MAXLEN];
5618 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5619 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5620 irc_privmsg(cmd->parent->bot, channel->name, response);
5623 reply("CSMSG_PING_RESPONSE");
5627 static CHANSERV_FUNC(cmd_wut)
5631 char response[MAXLEN];
5632 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5633 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5634 irc_privmsg(cmd->parent->bot, channel->name, response);
5637 reply("CSMSG_WUT_RESPONSE");
5641 static CHANSERV_FUNC(cmd_8ball)
5643 unsigned int i, j, accum;
5648 for(i=1; i<argc; i++)
5649 for(j=0; argv[i][j]; j++)
5650 accum = (accum << 5) - accum + toupper(argv[i][j]);
5651 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5654 char response[MAXLEN];
5655 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5656 irc_privmsg(cmd->parent->bot, channel->name, response);
5659 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5663 static CHANSERV_FUNC(cmd_d)
5665 unsigned long sides, count, modifier, ii, total;
5666 char response[MAXLEN], *sep;
5670 if((count = strtoul(argv[1], &sep, 10)) < 1)
5680 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5681 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5685 else if((sep[0] == '-') && isdigit(sep[1]))
5686 modifier = strtoul(sep, NULL, 10);
5687 else if((sep[0] == '+') && isdigit(sep[1]))
5688 modifier = strtoul(sep+1, NULL, 10);
5695 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5700 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5703 for(total = ii = 0; ii < count; ++ii)
5704 total += (rand() % sides) + 1;
5707 if((count > 1) || modifier)
5709 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5710 sprintf(response, fmt, total, count, sides, modifier);
5714 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5715 sprintf(response, fmt, total, sides);
5718 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5720 send_message_type(4, user, cmd->parent->bot, "%s", response);
5724 static CHANSERV_FUNC(cmd_huggle)
5726 /* CTCP must be via PRIVMSG, never notice */
5728 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5730 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5735 chanserv_adjust_limit(void *data)
5737 struct mod_chanmode change;
5738 struct chanData *cData = data;
5739 struct chanNode *channel = cData->channel;
5742 if(IsSuspended(cData))
5745 cData->limitAdjusted = now;
5746 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5747 if(cData->modes.modes_set & MODE_LIMIT)
5749 if(limit > cData->modes.new_limit)
5750 limit = cData->modes.new_limit;
5751 else if(limit == cData->modes.new_limit)
5755 mod_chanmode_init(&change);
5756 change.modes_set = MODE_LIMIT;
5757 change.new_limit = limit;
5758 mod_chanmode_announce(chanserv, channel, &change);
5762 handle_new_channel(struct chanNode *channel)
5764 struct chanData *cData;
5766 if(!(cData = channel->channel_info))
5769 if(cData->modes.modes_set || cData->modes.modes_clear)
5770 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5772 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5773 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5776 /* Welcome to my worst nightmare. Warning: Read (or modify)
5777 the code below at your own risk. */
5779 handle_join(struct modeNode *mNode)
5781 struct mod_chanmode change;
5782 struct userNode *user = mNode->user;
5783 struct chanNode *channel = mNode->channel;
5784 struct chanData *cData;
5785 struct userData *uData = NULL;
5786 struct banData *bData;
5787 struct handle_info *handle;
5788 unsigned int modes = 0, info = 0;
5791 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5794 cData = channel->channel_info;
5795 if(channel->members.used > cData->max)
5796 cData->max = channel->members.used;
5798 /* Check for bans. If they're joining through a ban, one of two
5800 * 1: Join during a netburst, by riding the break. Kick them
5801 * unless they have ops or voice in the channel.
5802 * 2: They're allowed to join through the ban (an invite in
5803 * ircu2.10, or a +e on Hybrid, or something).
5804 * If they're not joining through a ban, and the banlist is not
5805 * full, see if they're on the banlist for the channel. If so,
5808 if(user->uplink->burst && !mNode->modes)
5811 for(ii = 0; ii < channel->banlist.used; ii++)
5813 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5815 /* Riding a netburst. Naughty. */
5816 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5822 mod_chanmode_init(&change);
5824 if(channel->banlist.used < MAXBANS)
5826 /* Not joining through a ban. */
5827 for(bData = cData->bans;
5828 bData && !user_matches_glob(user, bData->mask, 1);
5829 bData = bData->next);
5833 char kick_reason[MAXLEN];
5834 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5836 bData->triggered = now;
5837 if(bData != cData->bans)
5839 /* Shuffle the ban to the head of the list. */
5841 bData->next->prev = bData->prev;
5843 bData->prev->next = bData->next;
5846 bData->next = cData->bans;
5849 cData->bans->prev = bData;
5850 cData->bans = bData;
5853 change.args[0].mode = MODE_BAN;
5854 change.args[0].hostmask = bData->mask;
5855 mod_chanmode_announce(chanserv, channel, &change);
5856 KickChannelUser(user, channel, chanserv, kick_reason);
5861 /* ChanServ will not modify the limits in join-flooded channels.
5862 It will also skip DynLimit processing when the user (or srvx)
5863 is bursting in, because there are likely more incoming. */
5864 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5865 && !user->uplink->burst
5866 && !channel->join_flooded
5867 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5869 /* The user count has begun "bumping" into the channel limit,
5870 so set a timer to raise the limit a bit. Any previous
5871 timers are removed so three incoming users within the delay
5872 results in one limit change, not three. */
5874 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5875 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5878 if(channel->join_flooded)
5880 /* don't automatically give ops or voice during a join flood */
5882 else if(cData->lvlOpts[lvlGiveOps] == 0)
5883 modes |= MODE_CHANOP;
5884 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5885 modes |= MODE_VOICE;
5887 greeting = cData->greeting;
5888 if(user->handle_info)
5890 handle = user->handle_info;
5892 if(IsHelper(user) && !IsHelping(user))
5895 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5897 if(channel == chanserv_conf.support_channels.list[ii])
5899 HANDLE_SET_FLAG(user->handle_info, HELPING);
5905 uData = GetTrueChannelAccess(cData, handle);
5906 if(uData && !IsUserSuspended(uData))
5908 /* Ops and above were handled by the above case. */
5909 if(IsUserAutoOp(uData))
5911 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5912 modes |= MODE_CHANOP;
5913 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5914 modes |= MODE_VOICE;
5916 if(uData->access >= UL_PRESENT)
5917 cData->visited = now;
5918 if(cData->user_greeting)
5919 greeting = cData->user_greeting;
5921 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5922 && ((now - uData->seen) >= chanserv_conf.info_delay)
5929 if(!user->uplink->burst)
5933 if(modes & MODE_CHANOP)
5934 modes &= ~MODE_VOICE;
5935 change.args[0].mode = modes;
5936 change.args[0].member = mNode;
5937 mod_chanmode_announce(chanserv, channel, &change);
5939 if(greeting && !user->uplink->burst)
5940 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5942 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5948 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5950 struct mod_chanmode change;
5951 struct userData *channel;
5952 unsigned int ii, jj;
5954 if(!user->handle_info)
5957 mod_chanmode_init(&change);
5959 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5961 struct chanNode *cn;
5962 struct modeNode *mn;
5963 if(IsUserSuspended(channel)
5964 || IsSuspended(channel->channel)
5965 || !(cn = channel->channel->channel))
5968 mn = GetUserMode(cn, user);
5971 if(!IsUserSuspended(channel)
5972 && IsUserAutoInvite(channel)
5973 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5975 && !user->uplink->burst)
5976 irc_invite(chanserv, user, cn);
5980 if(channel->access >= UL_PRESENT)
5981 channel->channel->visited = now;
5983 if(IsUserAutoOp(channel))
5985 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5986 change.args[0].mode = MODE_CHANOP;
5987 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5988 change.args[0].mode = MODE_VOICE;
5990 change.args[0].mode = 0;
5991 change.args[0].member = mn;
5992 if(change.args[0].mode)
5993 mod_chanmode_announce(chanserv, cn, &change);
5996 channel->seen = now;
5997 channel->present = 1;
6000 for(ii = 0; ii < user->channels.used; ++ii)
6002 struct chanNode *channel = user->channels.list[ii]->channel;
6003 struct banData *ban;
6005 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6006 || !channel->channel_info)
6008 for(jj = 0; jj < channel->banlist.used; ++jj)
6009 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6011 if(jj < channel->banlist.used)
6013 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6015 char kick_reason[MAXLEN];
6016 if(!user_matches_glob(user, ban->mask, 1))
6018 change.args[0].mode = MODE_BAN;
6019 change.args[0].hostmask = ban->mask;
6020 mod_chanmode_announce(chanserv, channel, &change);
6021 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6022 KickChannelUser(user, channel, chanserv, kick_reason);
6023 ban->triggered = now;
6028 if(IsSupportHelper(user))
6030 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6032 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6034 HANDLE_SET_FLAG(user->handle_info, HELPING);
6042 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6044 struct chanData *cData;
6045 struct userData *uData;
6047 cData = mn->channel->channel_info;
6048 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6051 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6053 /* Allow for a bit of padding so that the limit doesn't
6054 track the user count exactly, which could get annoying. */
6055 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6057 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6058 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6062 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6064 scan_user_presence(uData, mn->user);
6068 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6070 unsigned int ii, jj;
6071 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6073 for(jj = 0; jj < mn->user->channels.used; ++jj)
6074 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6076 if(jj < mn->user->channels.used)
6079 if(ii == chanserv_conf.support_channels.used)
6080 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6085 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6087 struct userData *uData;
6089 if(!channel->channel_info || !kicker || IsService(kicker)
6090 || (kicker == victim) || IsSuspended(channel->channel_info)
6091 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6094 if(protect_user(victim, kicker, channel->channel_info))
6096 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6097 KickChannelUser(kicker, channel, chanserv, reason);
6100 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6105 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6107 struct chanData *cData;
6109 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6112 cData = channel->channel_info;
6113 if(bad_topic(channel, user, channel->topic))
6115 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6116 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6117 SetChannelTopic(channel, chanserv, old_topic, 1);
6118 else if(cData->topic)
6119 SetChannelTopic(channel, chanserv, cData->topic, 1);
6122 /* With topicsnarf, grab the topic and save it as the default topic. */
6123 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6126 cData->topic = strdup(channel->topic);
6132 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6134 struct mod_chanmode *bounce = NULL;
6135 unsigned int bnc, ii;
6138 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6141 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6142 && mode_lock_violated(&channel->channel_info->modes, change))
6144 char correct[MAXLEN];
6145 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6146 mod_chanmode_format(&channel->channel_info->modes, correct);
6147 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6149 for(ii = bnc = 0; ii < change->argc; ++ii)
6151 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6153 const struct userNode *victim = change->args[ii].member->user;
6154 if(!protect_user(victim, user, channel->channel_info))
6157 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6160 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6161 bounce->args[bnc].member = GetUserMode(channel, user);
6162 if(bounce->args[bnc].member)
6166 bounce->args[bnc].mode = MODE_CHANOP;
6167 bounce->args[bnc].member = change->args[ii].member;
6169 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6171 else if(change->args[ii].mode & MODE_CHANOP)
6173 const struct userNode *victim = change->args[ii].member->user;
6174 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6177 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6178 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6179 bounce->args[bnc].member = change->args[ii].member;
6182 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6184 const char *ban = change->args[ii].hostmask;
6185 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6188 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6189 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6190 bounce->args[bnc].hostmask = ban;
6192 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6197 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6198 mod_chanmode_announce(chanserv, channel, bounce);
6199 mod_chanmode_free(bounce);
6204 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6206 struct chanNode *channel;
6207 struct banData *bData;
6208 struct mod_chanmode change;
6209 unsigned int ii, jj;
6210 char kick_reason[MAXLEN];
6212 mod_chanmode_init(&change);
6214 change.args[0].mode = MODE_BAN;
6215 for(ii = 0; ii < user->channels.used; ++ii)
6217 channel = user->channels.list[ii]->channel;
6218 /* Need not check for bans if they're opped or voiced. */
6219 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6221 /* Need not check for bans unless channel registration is active. */
6222 if(!channel->channel_info || IsSuspended(channel->channel_info))
6224 /* Look for a matching ban already on the channel. */
6225 for(jj = 0; jj < channel->banlist.used; ++jj)
6226 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6228 /* Need not act if we found one. */
6229 if(jj < channel->banlist.used)
6231 /* Look for a matching ban in this channel. */
6232 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6234 if(!user_matches_glob(user, bData->mask, 1))
6236 change.args[0].hostmask = bData->mask;
6237 mod_chanmode_announce(chanserv, channel, &change);
6238 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6239 KickChannelUser(user, channel, chanserv, kick_reason);
6240 bData->triggered = now;
6241 break; /* we don't need to check any more bans in the channel */
6246 static void handle_rename(struct handle_info *handle, const char *old_handle)
6248 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6252 dict_remove2(handle_dnrs, old_handle, 1);
6253 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6254 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6259 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6261 struct userNode *h_user;
6263 if(handle->channels)
6265 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6266 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6268 while(handle->channels)
6269 del_channel_user(handle->channels, 1);
6274 handle_server_link(UNUSED_ARG(struct server *server))
6276 struct chanData *cData;
6278 for(cData = channelList; cData; cData = cData->next)
6280 if(!IsSuspended(cData))
6281 cData->may_opchan = 1;
6282 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6283 && !cData->channel->join_flooded
6284 && ((cData->channel->limit - cData->channel->members.used)
6285 < chanserv_conf.adjust_threshold))
6287 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6288 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6294 chanserv_conf_read(void)
6298 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6299 struct mod_chanmode *change;
6300 struct string_list *strlist;
6301 struct chanNode *chan;
6304 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6306 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6309 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6310 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6311 chanserv_conf.support_channels.used = 0;
6312 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6314 for(ii = 0; ii < strlist->used; ++ii)
6316 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6319 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6321 channelList_append(&chanserv_conf.support_channels, chan);
6324 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6327 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6330 chan = AddChannel(str, now, str2, NULL);
6332 channelList_append(&chanserv_conf.support_channels, chan);
6334 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6335 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6336 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6337 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6338 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6339 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6340 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6341 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6342 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6343 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6344 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6345 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6346 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6347 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6348 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6349 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6350 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6351 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6352 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6353 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6354 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6355 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6356 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6358 NickChange(chanserv, str, 0);
6359 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6360 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6361 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6362 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6363 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6364 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6365 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6366 chanserv_conf.max_owned = str ? atoi(str) : 5;
6367 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6368 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6369 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6370 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6371 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6372 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6373 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6376 safestrncpy(mode_line, str, sizeof(mode_line));
6377 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6378 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6380 chanserv_conf.default_modes = *change;
6381 mod_chanmode_free(change);
6383 free_string_list(chanserv_conf.set_shows);
6384 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6386 strlist = string_list_copy(strlist);
6389 static const char *list[] = {
6390 /* free form text */
6391 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6392 /* options based on user level */
6393 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6394 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6395 /* multiple choice options */
6396 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6397 /* binary options */
6398 "DynLimit", "NoDelete",
6403 strlist = alloc_string_list(ArrayLength(list)-1);
6404 for(ii=0; list[ii]; ii++)
6405 string_list_append(strlist, strdup(list[ii]));
6407 chanserv_conf.set_shows = strlist;
6408 /* We don't look things up now, in case the list refers to options
6409 * defined by modules initialized after this point. Just mark the
6410 * function list as invalid, so it will be initialized.
6412 set_shows_list.used = 0;
6413 free_string_list(chanserv_conf.eightball);
6414 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6417 strlist = string_list_copy(strlist);
6421 strlist = alloc_string_list(4);
6422 string_list_append(strlist, strdup("Yes."));
6423 string_list_append(strlist, strdup("No."));
6424 string_list_append(strlist, strdup("Maybe so."));
6426 chanserv_conf.eightball = strlist;
6427 free_string_list(chanserv_conf.old_ban_names);
6428 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6430 strlist = string_list_copy(strlist);
6432 strlist = alloc_string_list(2);
6433 chanserv_conf.old_ban_names = strlist;
6434 /* the variable itself is actually declared in proto-common.c; this is equally
6436 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6437 off_channel = (str && enabled_string(str)) ? 1 : 0;
6441 chanserv_note_type_read(const char *key, struct record_data *rd)
6444 struct note_type *ntype;
6447 if(!(obj = GET_RECORD_OBJECT(rd)))
6449 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6452 if(!(ntype = chanserv_create_note_type(key)))
6454 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6458 /* Figure out set access */
6459 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6461 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6462 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6464 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6466 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6467 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6469 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6471 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6475 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6476 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6477 ntype->set_access.min_opserv = 0;
6480 /* Figure out visibility */
6481 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6482 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6483 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6484 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6485 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6486 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6487 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6488 ntype->visible_type = NOTE_VIS_ALL;
6490 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6492 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6493 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6497 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6499 struct handle_info *handle;
6500 struct userData *uData;
6501 char *seen, *inf, *flags;
6503 unsigned short access;
6505 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6507 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6511 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6512 if(access > UL_OWNER)
6514 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6518 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6519 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6520 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6521 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6522 handle = get_handle_info(key);
6525 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6529 uData = add_channel_user(chan, handle, access, last_seen, inf);
6530 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6534 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6536 struct banData *bData;
6537 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6538 time_t set_time, triggered_time, expires_time;
6540 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6542 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6546 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6547 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6548 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6549 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6550 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6551 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6553 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6554 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6556 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6558 expires_time = set_time + atoi(s_duration);
6562 if(expires_time && (expires_time < now))
6565 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6568 static struct suspended *
6569 chanserv_read_suspended(dict_t obj)
6571 struct suspended *suspended = calloc(1, sizeof(*suspended));
6575 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6576 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6577 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6578 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6579 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6580 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6581 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6582 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6583 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6584 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6589 chanserv_channel_read(const char *key, struct record_data *hir)
6591 struct suspended *suspended;
6592 struct mod_chanmode *modes;
6593 struct chanNode *cNode;
6594 struct chanData *cData;
6595 struct dict *channel, *obj;
6596 char *str, *argv[10];
6600 channel = hir->d.object;
6602 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6605 cNode = AddChannel(key, now, NULL, NULL);
6608 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6611 cData = register_channel(cNode, str);
6614 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6618 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6620 enum levelOption lvlOpt;
6621 enum charOption chOpt;
6623 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6624 cData->flags = atoi(str);
6626 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6628 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6630 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6631 else if(levelOptions[lvlOpt].old_flag)
6633 if(cData->flags & levelOptions[lvlOpt].old_flag)
6634 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6636 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6640 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6642 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6644 cData->chOpts[chOpt] = str[0];
6647 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6649 enum levelOption lvlOpt;
6650 enum charOption chOpt;
6653 cData->flags = base64toint(str, 5);
6654 count = strlen(str += 5);
6655 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6658 if(levelOptions[lvlOpt].old_flag)
6660 if(cData->flags & levelOptions[lvlOpt].old_flag)
6661 lvl = levelOptions[lvlOpt].flag_value;
6663 lvl = levelOptions[lvlOpt].default_value;
6665 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6667 case 'c': lvl = UL_COOWNER; break;
6668 case 'm': lvl = UL_MASTER; break;
6669 case 'n': lvl = UL_OWNER+1; break;
6670 case 'o': lvl = UL_OP; break;
6671 case 'p': lvl = UL_PEON; break;
6672 case 'w': lvl = UL_OWNER; break;
6673 default: lvl = 0; break;
6675 cData->lvlOpts[lvlOpt] = lvl;
6677 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6678 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6681 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6683 suspended = chanserv_read_suspended(obj);
6684 cData->suspended = suspended;
6685 suspended->cData = cData;
6686 /* We could use suspended->expires and suspended->revoked to
6687 * set the CHANNEL_SUSPENDED flag, but we don't. */
6689 else if(IsSuspended(cData))
6691 suspended = calloc(1, sizeof(*suspended));
6692 suspended->issued = 0;
6693 suspended->revoked = 0;
6694 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6695 suspended->expires = str ? atoi(str) : 0;
6696 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6697 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6698 suspended->reason = strdup(str ? str : "No reason");
6699 suspended->previous = NULL;
6700 cData->suspended = suspended;
6701 suspended->cData = cData;
6704 suspended = NULL; /* to squelch a warning */
6706 if(IsSuspended(cData)) {
6707 if(suspended->expires > now)
6708 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6709 else if(suspended->expires)
6710 cData->flags &= ~CHANNEL_SUSPENDED;
6713 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6714 struct mod_chanmode change;
6715 mod_chanmode_init(&change);
6717 change.args[0].mode = MODE_CHANOP;
6718 change.args[0].member = AddChannelUser(chanserv, cNode);
6719 mod_chanmode_announce(chanserv, cNode, &change);
6722 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6723 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6724 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6725 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6726 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6727 cData->max = str ? atoi(str) : 0;
6728 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6729 cData->greeting = str ? strdup(str) : NULL;
6730 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6731 cData->user_greeting = str ? strdup(str) : NULL;
6732 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6733 cData->topic_mask = str ? strdup(str) : NULL;
6734 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6735 cData->topic = str ? strdup(str) : NULL;
6737 if(!IsSuspended(cData)
6738 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6739 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6740 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6741 cData->modes = *modes;
6742 cData->modes.modes_set |= MODE_REGISTERED;
6743 if(cData->modes.argc > 1)
6744 cData->modes.argc = 1;
6745 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6746 mod_chanmode_free(modes);
6749 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6750 for(it = dict_first(obj); it; it = iter_next(it))
6751 user_read_helper(iter_key(it), iter_data(it), cData);
6753 if(!cData->users && !IsProtected(cData))
6755 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6756 unregister_channel(cData, "has empty user list.");
6760 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6761 for(it = dict_first(obj); it; it = iter_next(it))
6762 ban_read_helper(iter_key(it), iter_data(it), cData);
6764 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6765 for(it = dict_first(obj); it; it = iter_next(it))
6767 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6768 struct record_data *rd = iter_data(it);
6769 const char *note, *setter;
6771 if(rd->type != RECDB_OBJECT)
6773 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6777 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6779 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6781 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6785 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6786 if(!setter) setter = "<unknown>";
6787 chanserv_add_channel_note(cData, ntype, setter, note);
6795 chanserv_dnr_read(const char *key, struct record_data *hir)
6797 const char *setter, *reason, *str;
6798 struct do_not_register *dnr;
6800 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6803 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6806 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6809 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6812 dnr = chanserv_add_dnr(key, setter, reason);
6815 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6817 dnr->set = atoi(str);
6823 chanserv_saxdb_read(struct dict *database)
6825 struct dict *section;
6828 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6829 for(it = dict_first(section); it; it = iter_next(it))
6830 chanserv_note_type_read(iter_key(it), iter_data(it));
6832 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6833 for(it = dict_first(section); it; it = iter_next(it))
6834 chanserv_channel_read(iter_key(it), iter_data(it));
6836 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6837 for(it = dict_first(section); it; it = iter_next(it))
6838 chanserv_dnr_read(iter_key(it), iter_data(it));
6844 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6846 int high_present = 0;
6847 saxdb_start_record(ctx, KEY_USERS, 1);
6848 for(; uData; uData = uData->next)
6850 if((uData->access >= UL_PRESENT) && uData->present)
6852 saxdb_start_record(ctx, uData->handle->handle, 0);
6853 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6854 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6856 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6858 saxdb_write_string(ctx, KEY_INFO, uData->info);
6859 saxdb_end_record(ctx);
6861 saxdb_end_record(ctx);
6862 return high_present;
6866 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6870 saxdb_start_record(ctx, KEY_BANS, 1);
6871 for(; bData; bData = bData->next)
6873 saxdb_start_record(ctx, bData->mask, 0);
6874 saxdb_write_int(ctx, KEY_SET, bData->set);
6875 if(bData->triggered)
6876 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6878 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6880 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6882 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6883 saxdb_end_record(ctx);
6885 saxdb_end_record(ctx);
6889 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6891 saxdb_start_record(ctx, name, 0);
6892 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6893 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6895 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6897 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6899 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6901 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6902 saxdb_end_record(ctx);
6906 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6910 enum levelOption lvlOpt;
6911 enum charOption chOpt;
6913 saxdb_start_record(ctx, channel->channel->name, 1);
6915 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6916 saxdb_write_int(ctx, KEY_MAX, channel->max);
6918 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6919 if(channel->registrar)
6920 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6921 if(channel->greeting)
6922 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6923 if(channel->user_greeting)
6924 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6925 if(channel->topic_mask)
6926 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6927 if(channel->suspended)
6928 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6930 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6931 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6932 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6933 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6934 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6936 buf[0] = channel->chOpts[chOpt];
6938 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6940 saxdb_end_record(ctx);
6942 if(channel->modes.modes_set || channel->modes.modes_clear)
6944 mod_chanmode_format(&channel->modes, buf);
6945 saxdb_write_string(ctx, KEY_MODES, buf);
6948 high_present = chanserv_write_users(ctx, channel->users);
6949 chanserv_write_bans(ctx, channel->bans);
6951 if(dict_size(channel->notes))
6955 saxdb_start_record(ctx, KEY_NOTES, 1);
6956 for(it = dict_first(channel->notes); it; it = iter_next(it))
6958 struct note *note = iter_data(it);
6959 saxdb_start_record(ctx, iter_key(it), 0);
6960 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6961 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6962 saxdb_end_record(ctx);
6964 saxdb_end_record(ctx);
6967 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6968 saxdb_end_record(ctx);
6972 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6976 saxdb_start_record(ctx, ntype->name, 0);
6977 switch(ntype->set_access_type)
6979 case NOTE_SET_CHANNEL_ACCESS:
6980 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6982 case NOTE_SET_CHANNEL_SETTER:
6983 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6985 case NOTE_SET_PRIVILEGED: default:
6986 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6989 switch(ntype->visible_type)
6991 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6992 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6993 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6995 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6996 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6997 saxdb_end_record(ctx);
7001 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7003 struct do_not_register *dnr;
7006 for(it = dict_first(dnrs); it; it = iter_next(it))
7008 dnr = iter_data(it);
7009 saxdb_start_record(ctx, dnr->chan_name, 0);
7011 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7012 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7013 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7014 saxdb_end_record(ctx);
7019 chanserv_saxdb_write(struct saxdb_context *ctx)
7022 struct chanData *channel;
7025 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7026 for(it = dict_first(note_types); it; it = iter_next(it))
7027 chanserv_write_note_type(ctx, iter_data(it));
7028 saxdb_end_record(ctx);
7031 saxdb_start_record(ctx, KEY_DNR, 1);
7032 write_dnrs_helper(ctx, handle_dnrs);
7033 write_dnrs_helper(ctx, plain_dnrs);
7034 write_dnrs_helper(ctx, mask_dnrs);
7035 saxdb_end_record(ctx);
7038 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7039 for(channel = channelList; channel; channel = channel->next)
7040 chanserv_write_channel(ctx, channel);
7041 saxdb_end_record(ctx);
7047 chanserv_db_cleanup(void) {
7049 unreg_part_func(handle_part);
7051 unregister_channel(channelList, "terminating.");
7052 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7053 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7054 free(chanserv_conf.support_channels.list);
7055 dict_delete(handle_dnrs);
7056 dict_delete(plain_dnrs);
7057 dict_delete(mask_dnrs);
7058 dict_delete(note_types);
7059 free_string_list(chanserv_conf.eightball);
7060 free_string_list(chanserv_conf.old_ban_names);
7061 free_string_list(chanserv_conf.set_shows);
7062 free(set_shows_list.list);
7063 free(uset_shows_list.list);
7066 struct userData *helper = helperList;
7067 helperList = helperList->next;
7072 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7073 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7074 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7077 init_chanserv(const char *nick)
7079 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7080 conf_register_reload(chanserv_conf_read);
7082 reg_server_link_func(handle_server_link);
7084 reg_new_channel_func(handle_new_channel);
7085 reg_join_func(handle_join);
7086 reg_part_func(handle_part);
7087 reg_kick_func(handle_kick);
7088 reg_topic_func(handle_topic);
7089 reg_mode_change_func(handle_mode);
7090 reg_nick_change_func(handle_nick_change);
7092 reg_auth_func(handle_auth);
7093 reg_handle_rename_func(handle_rename);
7094 reg_unreg_func(handle_unreg);
7096 handle_dnrs = dict_new();
7097 dict_set_free_data(handle_dnrs, free);
7098 plain_dnrs = dict_new();
7099 dict_set_free_data(plain_dnrs, free);
7100 mask_dnrs = dict_new();
7101 dict_set_free_data(mask_dnrs, free);
7103 reg_svccmd_unbind_func(handle_svccmd_unbind);
7104 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7105 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7106 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7107 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7108 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7109 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7110 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7111 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7112 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7114 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7115 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7117 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7118 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7119 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7120 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7121 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7123 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7124 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7125 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7126 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7127 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7129 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7130 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7131 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7132 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7134 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7135 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7136 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7137 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7138 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7139 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7140 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7141 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7143 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7144 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7145 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7146 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7147 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7148 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7149 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7150 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7151 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7152 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7153 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7154 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7155 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7156 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7158 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7159 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7160 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7161 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7162 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7164 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7165 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7167 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7168 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7169 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7170 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7171 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7172 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7173 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7174 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7175 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7176 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7177 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7179 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7180 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7182 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7183 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7184 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7185 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7187 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7188 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7189 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7190 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7191 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7193 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7194 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7195 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7196 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7197 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7198 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7200 /* Channel options */
7201 DEFINE_CHANNEL_OPTION(defaulttopic);
7202 DEFINE_CHANNEL_OPTION(topicmask);
7203 DEFINE_CHANNEL_OPTION(greeting);
7204 DEFINE_CHANNEL_OPTION(usergreeting);
7205 DEFINE_CHANNEL_OPTION(modes);
7206 DEFINE_CHANNEL_OPTION(enfops);
7207 DEFINE_CHANNEL_OPTION(giveops);
7208 DEFINE_CHANNEL_OPTION(protect);
7209 DEFINE_CHANNEL_OPTION(enfmodes);
7210 DEFINE_CHANNEL_OPTION(enftopic);
7211 DEFINE_CHANNEL_OPTION(pubcmd);
7212 DEFINE_CHANNEL_OPTION(givevoice);
7213 DEFINE_CHANNEL_OPTION(userinfo);
7214 DEFINE_CHANNEL_OPTION(dynlimit);
7215 DEFINE_CHANNEL_OPTION(topicsnarf);
7216 DEFINE_CHANNEL_OPTION(nodelete);
7217 DEFINE_CHANNEL_OPTION(toys);
7218 DEFINE_CHANNEL_OPTION(setters);
7219 DEFINE_CHANNEL_OPTION(topicrefresh);
7220 DEFINE_CHANNEL_OPTION(ctcpusers);
7221 DEFINE_CHANNEL_OPTION(ctcpreaction);
7222 DEFINE_CHANNEL_OPTION(inviteme);
7224 DEFINE_CHANNEL_OPTION(offchannel);
7225 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7227 /* Alias set topic to set defaulttopic for compatibility. */
7228 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7231 DEFINE_USER_OPTION(noautoop);
7232 DEFINE_USER_OPTION(autoinvite);
7233 DEFINE_USER_OPTION(info);
7235 /* Alias uset autovoice to uset autoop. */
7236 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7238 note_types = dict_new();
7239 dict_set_free_data(note_types, chanserv_deref_note_type);
7242 chanserv = AddService(nick, "Channel Services", NULL);
7243 service_register(chanserv)->trigger = '!';
7244 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7246 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7248 if(chanserv_conf.channel_expire_frequency)
7249 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7251 if(chanserv_conf.refresh_period)
7253 time_t next_refresh;
7254 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7255 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7258 reg_exit_func(chanserv_db_cleanup);
7259 message_register_table(msgtab);