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 unsigned int use_registered_mode;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->flags = CHANNEL_DEFAULT_FLAGS;
1084 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1085 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1086 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1087 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1089 channel->prev = NULL;
1090 channel->next = channelList;
1093 channelList->prev = channel;
1094 channelList = channel;
1095 registered_channels++;
1097 channel->channel = cNode;
1099 cNode->channel_info = channel;
1104 static struct userData*
1105 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1107 struct userData *ud;
1109 if(access > UL_OWNER)
1112 ud = calloc(1, sizeof(*ud));
1113 ud->channel = channel;
1114 ud->handle = handle;
1116 ud->access = access;
1117 ud->info = info ? strdup(info) : NULL;
1120 ud->next = channel->users;
1122 channel->users->prev = ud;
1123 channel->users = ud;
1125 channel->userCount++;
1129 ud->u_next = ud->handle->channels;
1131 ud->u_next->u_prev = ud;
1132 ud->handle->channels = ud;
1137 static void unregister_channel(struct chanData *channel, const char *reason);
1140 del_channel_user(struct userData *user, int do_gc)
1142 struct chanData *channel = user->channel;
1144 channel->userCount--;
1148 user->prev->next = user->next;
1150 channel->users = user->next;
1152 user->next->prev = user->prev;
1155 user->u_prev->u_next = user->u_next;
1157 user->handle->channels = user->u_next;
1159 user->u_next->u_prev = user->u_prev;
1163 if(do_gc && !channel->users && !IsProtected(channel))
1164 unregister_channel(channel, "lost all users.");
1167 static void expire_ban(void *data);
1169 static struct banData*
1170 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1173 unsigned int ii, l1, l2;
1178 bd = malloc(sizeof(struct banData));
1180 bd->channel = channel;
1182 bd->triggered = triggered;
1183 bd->expires = expires;
1185 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1187 extern const char *hidden_host_suffix;
1188 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1192 l2 = strlen(old_name);
1195 if(irccasecmp(mask + l1 - l2, old_name))
1197 new_mask = alloca(MAXLEN);
1198 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1201 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1203 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1204 bd->reason = reason ? strdup(reason) : NULL;
1207 timeq_add(expires, expire_ban, bd);
1210 bd->next = channel->bans;
1212 channel->bans->prev = bd;
1214 channel->banCount++;
1221 del_channel_ban(struct banData *ban)
1223 ban->channel->banCount--;
1227 ban->prev->next = ban->next;
1229 ban->channel->bans = ban->next;
1232 ban->next->prev = ban->prev;
1235 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1244 expire_ban(void *data)
1246 struct banData *bd = data;
1247 if(!IsSuspended(bd->channel))
1249 struct banList bans;
1250 struct mod_chanmode change;
1252 bans = bd->channel->channel->banlist;
1253 mod_chanmode_init(&change);
1254 for(ii=0; ii<bans.used; ii++)
1256 if(!strcmp(bans.list[ii]->ban, bd->mask))
1259 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1260 change.args[0].u.hostmask = bd->mask;
1261 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1267 del_channel_ban(bd);
1270 static void chanserv_expire_suspension(void *data);
1273 unregister_channel(struct chanData *channel, const char *reason)
1275 struct mod_chanmode change;
1276 char msgbuf[MAXLEN];
1278 /* After channel unregistration, the following must be cleaned
1280 - Channel information.
1283 - Channel suspension data.
1284 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1290 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1292 if(chanserv_conf.use_registered_mode)
1294 mod_chanmode_init(&change);
1295 change.modes_clear |= MODE_REGISTERED;
1296 mod_chanmode_announce(chanserv, channel->channel, &change);
1299 while(channel->users)
1300 del_channel_user(channel->users, 0);
1302 while(channel->bans)
1303 del_channel_ban(channel->bans);
1305 free(channel->topic);
1306 free(channel->registrar);
1307 free(channel->greeting);
1308 free(channel->user_greeting);
1309 free(channel->topic_mask);
1312 channel->prev->next = channel->next;
1314 channelList = channel->next;
1317 channel->next->prev = channel->prev;
1319 if(channel->suspended)
1321 struct chanNode *cNode = channel->channel;
1322 struct suspended *suspended, *next_suspended;
1324 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1326 next_suspended = suspended->previous;
1327 free(suspended->suspender);
1328 free(suspended->reason);
1329 if(suspended->expires)
1330 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1335 cNode->channel_info = NULL;
1337 channel->channel->channel_info = NULL;
1339 dict_delete(channel->notes);
1340 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1341 if(!IsSuspended(channel))
1342 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1343 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1344 UnlockChannel(channel->channel);
1346 registered_channels--;
1350 expire_channels(UNUSED_ARG(void *data))
1352 struct chanData *channel, *next;
1353 struct userData *user;
1354 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1356 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1357 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1359 for(channel = channelList; channel; channel = next)
1361 next = channel->next;
1363 /* See if the channel can be expired. */
1364 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1365 || IsProtected(channel))
1368 /* Make sure there are no high-ranking users still in the channel. */
1369 for(user=channel->users; user; user=user->next)
1370 if(user->present && (user->access >= UL_PRESENT))
1375 /* Unregister the channel */
1376 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1377 unregister_channel(channel, "registration expired.");
1380 if(chanserv_conf.channel_expire_frequency)
1381 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1385 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1387 char protect = channel->chOpts[chProtect];
1388 struct userData *cs_victim, *cs_aggressor;
1390 /* Don't protect if no one is to be protected, someone is attacking
1391 himself, or if the aggressor is an IRC Operator. */
1392 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1395 /* Don't protect if the victim isn't authenticated (because they
1396 can't be a channel user), unless we are to protect non-users
1398 cs_victim = GetChannelAccess(channel, victim->handle_info);
1399 if(protect != 'a' && !cs_victim)
1402 /* Protect if the aggressor isn't a user because at this point,
1403 the aggressor can only be less than or equal to the victim. */
1404 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1408 /* If the aggressor was a user, then the victim can't be helped. */
1415 if(cs_victim->access > cs_aggressor->access)
1420 if(cs_victim->access >= cs_aggressor->access)
1429 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1431 struct chanData *cData = channel->channel_info;
1432 struct userData *cs_victim;
1434 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1435 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1436 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1438 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1446 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1448 if(IsService(victim))
1450 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1454 if(protect_user(victim, user, channel->channel_info))
1456 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1463 static struct do_not_register *
1464 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1466 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1467 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1468 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1469 strcpy(dnr->reason, reason);
1471 if(dnr->chan_name[0] == '*')
1472 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1473 else if(strpbrk(dnr->chan_name, "*?"))
1474 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1476 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1480 static struct dnrList
1481 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1483 struct dnrList list;
1485 struct do_not_register *dnr;
1487 dnrList_init(&list);
1488 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1489 dnrList_append(&list, dnr);
1490 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1491 dnrList_append(&list, dnr);
1493 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1494 if(match_ircglob(chan_name, iter_key(it)))
1495 dnrList_append(&list, iter_data(it));
1500 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1502 struct dnrList list;
1503 struct do_not_register *dnr;
1505 char buf[INTERVALLEN];
1507 list = chanserv_find_dnrs(chan_name, handle);
1508 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1510 dnr = list.list[ii];
1513 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1514 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1517 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1520 reply("CSMSG_MORE_DNRS", list.used - ii);
1525 struct do_not_register *
1526 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1528 struct do_not_register *dnr;
1531 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1535 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1537 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1538 if(match_ircglob(chan_name, iter_key(it)))
1539 return iter_data(it);
1544 static CHANSERV_FUNC(cmd_noregister)
1547 struct do_not_register *dnr;
1548 char buf[INTERVALLEN];
1549 unsigned int matches;
1555 reply("CSMSG_DNR_SEARCH_RESULTS");
1557 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1559 dnr = iter_data(it);
1561 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1563 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1566 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1568 dnr = iter_data(it);
1570 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1572 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1575 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1577 dnr = iter_data(it);
1579 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1581 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1586 reply("MSG_MATCH_COUNT", matches);
1588 reply("MSG_NO_MATCHES");
1594 if(!IsChannelName(target) && (*target != '*'))
1596 reply("CSMSG_NOT_DNR", target);
1602 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1603 if((*target == '*') && !get_handle_info(target + 1))
1605 reply("MSG_HANDLE_UNKNOWN", target + 1);
1608 chanserv_add_dnr(target, user->handle_info->handle, reason);
1609 reply("CSMSG_NOREGISTER_CHANNEL", target);
1613 reply("CSMSG_DNR_SEARCH_RESULTS");
1615 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1617 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1619 reply("MSG_NO_MATCHES");
1623 static CHANSERV_FUNC(cmd_allowregister)
1625 const char *chan_name = argv[1];
1627 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1629 dict_remove(handle_dnrs, chan_name+1);
1630 reply("CSMSG_DNR_REMOVED", chan_name);
1632 else if(dict_find(plain_dnrs, chan_name, NULL))
1634 dict_remove(plain_dnrs, chan_name);
1635 reply("CSMSG_DNR_REMOVED", chan_name);
1637 else if(dict_find(mask_dnrs, chan_name, NULL))
1639 dict_remove(mask_dnrs, chan_name);
1640 reply("CSMSG_DNR_REMOVED", chan_name);
1644 reply("CSMSG_NO_SUCH_DNR", chan_name);
1651 chanserv_get_owned_count(struct handle_info *hi)
1653 struct userData *cList;
1656 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1657 if(cList->access == UL_OWNER)
1662 static CHANSERV_FUNC(cmd_register)
1664 struct mod_chanmode *change;
1665 struct handle_info *handle;
1666 struct chanData *cData;
1667 struct modeNode *mn;
1668 char reason[MAXLEN];
1670 unsigned int new_channel, force=0;
1671 struct do_not_register *dnr;
1675 if(channel->channel_info)
1677 reply("CSMSG_ALREADY_REGGED", channel->name);
1681 if(channel->bad_channel)
1683 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1687 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1689 reply("CSMSG_MUST_BE_OPPED", channel->name);
1694 chan_name = channel->name;
1698 if((argc < 2) || !IsChannelName(argv[1]))
1700 reply("MSG_NOT_CHANNEL_NAME");
1704 if(opserv_bad_channel(argv[1]))
1706 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1711 chan_name = argv[1];
1714 if(argc >= (new_channel+2))
1716 if(!IsHelping(user))
1718 reply("CSMSG_PROXY_FORBIDDEN");
1722 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1724 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1725 dnr = chanserv_is_dnr(chan_name, handle);
1729 handle = user->handle_info;
1730 dnr = chanserv_is_dnr(chan_name, handle);
1734 if(!IsHelping(user))
1735 reply("CSMSG_DNR_CHANNEL", chan_name);
1737 chanserv_show_dnrs(user, cmd, chan_name, handle);
1741 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1743 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1748 channel = AddChannel(argv[1], now, NULL, NULL);
1750 cData = register_channel(channel, user->handle_info->handle);
1751 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1752 cData->modes = chanserv_conf.default_modes;
1753 if(chanserv_conf.use_registered_mode)
1754 cData->modes.modes_set |= MODE_REGISTERED;
1755 change = mod_chanmode_dup(&cData->modes, 1);
1756 change->args[change->argc].mode = MODE_CHANOP;
1757 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1759 mod_chanmode_announce(chanserv, channel, change);
1760 mod_chanmode_free(change);
1762 /* Initialize the channel's max user record. */
1763 cData->max = channel->members.used;
1765 if(handle != user->handle_info)
1766 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1768 reply("CSMSG_REG_SUCCESS", channel->name);
1770 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1771 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1776 make_confirmation_string(struct userData *uData)
1778 static char strbuf[16];
1783 for(src = uData->handle->handle; *src; )
1784 accum = accum * 31 + toupper(*src++);
1786 for(src = uData->channel->channel->name; *src; )
1787 accum = accum * 31 + toupper(*src++);
1788 sprintf(strbuf, "%08x", accum);
1792 static CHANSERV_FUNC(cmd_unregister)
1795 char reason[MAXLEN];
1796 struct chanData *cData;
1797 struct userData *uData;
1799 cData = channel->channel_info;
1802 reply("CSMSG_NOT_REGISTERED", channel->name);
1806 uData = GetChannelUser(cData, user->handle_info);
1807 if(!uData || (uData->access < UL_OWNER))
1809 reply("CSMSG_NO_ACCESS");
1813 if(IsProtected(cData))
1815 reply("CSMSG_UNREG_NODELETE", channel->name);
1819 if(!IsHelping(user))
1821 const char *confirm_string;
1822 if(IsSuspended(cData))
1824 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1827 confirm_string = make_confirmation_string(uData);
1828 if((argc < 2) || strcmp(argv[1], confirm_string))
1830 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1835 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1836 name = strdup(channel->name);
1837 unregister_channel(cData, reason);
1838 reply("CSMSG_UNREG_SUCCESS", name);
1843 static CHANSERV_FUNC(cmd_move)
1845 struct chanNode *target;
1846 struct modeNode *mn;
1847 struct userData *uData;
1848 char reason[MAXLEN];
1849 struct do_not_register *dnr;
1853 if(IsProtected(channel->channel_info))
1855 reply("CSMSG_MOVE_NODELETE", channel->name);
1859 if(!IsChannelName(argv[1]))
1861 reply("MSG_NOT_CHANNEL_NAME");
1865 if(opserv_bad_channel(argv[1]))
1867 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1871 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1873 for(uData = channel->channel_info->users; uData; uData = uData->next)
1875 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1877 if(!IsHelping(user))
1878 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1880 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1886 if(!(target = GetChannel(argv[1])))
1888 target = AddChannel(argv[1], now, NULL, NULL);
1889 if(!IsSuspended(channel->channel_info))
1890 AddChannelUser(chanserv, target);
1892 else if(target->channel_info)
1894 reply("CSMSG_ALREADY_REGGED", target->name);
1897 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1898 && !IsHelping(user))
1900 reply("CSMSG_MUST_BE_OPPED", target->name);
1903 else if(!IsSuspended(channel->channel_info))
1905 struct mod_chanmode change;
1906 mod_chanmode_init(&change);
1908 change.args[0].mode = MODE_CHANOP;
1909 change.args[0].u.member = AddChannelUser(chanserv, target);
1910 mod_chanmode_announce(chanserv, target, &change);
1913 /* Move the channel_info to the target channel; it
1914 shouldn't be necessary to clear timeq callbacks
1915 for the old channel. */
1916 target->channel_info = channel->channel_info;
1917 target->channel_info->channel = target;
1918 channel->channel_info = NULL;
1920 reply("CSMSG_MOVE_SUCCESS", target->name);
1922 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1923 if(!IsSuspended(target->channel_info))
1925 char reason2[MAXLEN];
1926 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1927 DelChannelUser(chanserv, channel, reason2, 0);
1929 UnlockChannel(channel);
1930 LockChannel(target);
1931 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1936 merge_users(struct chanData *source, struct chanData *target)
1938 struct userData *suData, *tuData, *next;
1944 /* Insert the source's users into the scratch area. */
1945 for(suData = source->users; suData; suData = suData->next)
1946 dict_insert(merge, suData->handle->handle, suData);
1948 /* Iterate through the target's users, looking for
1949 users common to both channels. The lower access is
1950 removed from either the scratch area or target user
1952 for(tuData = target->users; tuData; tuData = next)
1954 struct userData *choice;
1956 next = tuData->next;
1958 /* If a source user exists with the same handle as a target
1959 channel's user, resolve the conflict by removing one. */
1960 suData = dict_find(merge, tuData->handle->handle, NULL);
1964 /* Pick the data we want to keep. */
1965 /* If the access is the same, use the later seen time. */
1966 if(suData->access == tuData->access)
1967 choice = (suData->seen > tuData->seen) ? suData : tuData;
1968 else /* Otherwise, keep the higher access level. */
1969 choice = (suData->access > tuData->access) ? suData : tuData;
1971 /* Remove the user that wasn't picked. */
1972 if(choice == tuData)
1974 dict_remove(merge, suData->handle->handle);
1975 del_channel_user(suData, 0);
1978 del_channel_user(tuData, 0);
1981 /* Move the remaining users to the target channel. */
1982 for(it = dict_first(merge); it; it = iter_next(it))
1984 suData = iter_data(it);
1986 /* Insert the user into the target channel's linked list. */
1987 suData->prev = NULL;
1988 suData->next = target->users;
1989 suData->channel = target;
1992 target->users->prev = suData;
1993 target->users = suData;
1995 /* Update the user counts for the target channel; the
1996 source counts are left alone. */
1997 target->userCount++;
2000 /* Possible to assert (source->users == NULL) here. */
2001 source->users = NULL;
2006 merge_bans(struct chanData *source, struct chanData *target)
2008 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2010 /* Hold on to the original head of the target ban list
2011 to avoid comparing source bans with source bans. */
2012 tFront = target->bans;
2014 /* Perform a totally expensive O(n*m) merge, ick. */
2015 for(sbData = source->bans; sbData; sbData = sNext)
2017 /* Flag to track whether the ban's been moved
2018 to the destination yet. */
2021 /* Possible to assert (sbData->prev == NULL) here. */
2022 sNext = sbData->next;
2024 for(tbData = tFront; tbData; tbData = tNext)
2026 tNext = tbData->next;
2028 /* Perform two comparisons between each source
2029 and target ban, conflicts are resolved by
2030 keeping the broader ban and copying the later
2031 expiration and triggered time. */
2032 if(match_ircglobs(tbData->mask, sbData->mask))
2034 /* There is a broader ban in the target channel that
2035 overrides one in the source channel; remove the
2036 source ban and break. */
2037 if(sbData->expires > tbData->expires)
2038 tbData->expires = sbData->expires;
2039 if(sbData->triggered > tbData->triggered)
2040 tbData->triggered = sbData->triggered;
2041 del_channel_ban(sbData);
2044 else if(match_ircglobs(sbData->mask, tbData->mask))
2046 /* There is a broader ban in the source channel that
2047 overrides one in the target channel; remove the
2048 target ban, fall through and move the source over. */
2049 if(tbData->expires > sbData->expires)
2050 sbData->expires = tbData->expires;
2051 if(tbData->triggered > sbData->triggered)
2052 sbData->triggered = tbData->triggered;
2053 if(tbData == tFront)
2055 del_channel_ban(tbData);
2058 /* Source bans can override multiple target bans, so
2059 we allow a source to run through this loop multiple
2060 times, but we can only move it once. */
2065 /* Remove the source ban from the source ban list. */
2067 sbData->next->prev = sbData->prev;
2069 /* Modify the source ban's associated channel. */
2070 sbData->channel = target;
2072 /* Insert the ban into the target channel's linked list. */
2073 sbData->prev = NULL;
2074 sbData->next = target->bans;
2077 target->bans->prev = sbData;
2078 target->bans = sbData;
2080 /* Update the user counts for the target channel. */
2085 /* Possible to assert (source->bans == NULL) here. */
2086 source->bans = NULL;
2090 merge_data(struct chanData *source, struct chanData *target)
2092 if(source->visited > target->visited)
2093 target->visited = source->visited;
2097 merge_channel(struct chanData *source, struct chanData *target)
2099 merge_users(source, target);
2100 merge_bans(source, target);
2101 merge_data(source, target);
2104 static CHANSERV_FUNC(cmd_merge)
2106 struct userData *target_user;
2107 struct chanNode *target;
2108 char reason[MAXLEN];
2112 /* Make sure the target channel exists and is registered to the user
2113 performing the command. */
2114 if(!(target = GetChannel(argv[1])))
2116 reply("MSG_INVALID_CHANNEL");
2120 if(!target->channel_info)
2122 reply("CSMSG_NOT_REGISTERED", target->name);
2126 if(IsProtected(channel->channel_info))
2128 reply("CSMSG_MERGE_NODELETE");
2132 if(IsSuspended(target->channel_info))
2134 reply("CSMSG_MERGE_SUSPENDED");
2138 if(channel == target)
2140 reply("CSMSG_MERGE_SELF");
2144 target_user = GetChannelUser(target->channel_info, user->handle_info);
2145 if(!target_user || (target_user->access < UL_OWNER))
2147 reply("CSMSG_MERGE_NOT_OWNER");
2151 /* Merge the channel structures and associated data. */
2152 merge_channel(channel->channel_info, target->channel_info);
2153 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2154 unregister_channel(channel->channel_info, reason);
2155 reply("CSMSG_MERGE_SUCCESS", target->name);
2159 static CHANSERV_FUNC(cmd_opchan)
2161 struct mod_chanmode change;
2162 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2164 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2167 channel->channel_info->may_opchan = 0;
2168 mod_chanmode_init(&change);
2170 change.args[0].mode = MODE_CHANOP;
2171 change.args[0].u.member = GetUserMode(channel, chanserv);
2172 mod_chanmode_announce(chanserv, channel, &change);
2173 reply("CSMSG_OPCHAN_DONE", channel->name);
2177 static CHANSERV_FUNC(cmd_adduser)
2179 struct userData *actee;
2180 struct userData *actor;
2181 struct handle_info *handle;
2182 unsigned short access;
2186 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2188 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2192 access = user_level_from_name(argv[2], UL_OWNER);
2195 reply("CSMSG_INVALID_ACCESS", argv[2]);
2199 actor = GetChannelUser(channel->channel_info, user->handle_info);
2200 if(actor->access <= access)
2202 reply("CSMSG_NO_BUMP_ACCESS");
2206 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2209 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2211 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2215 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2216 scan_user_presence(actee, NULL);
2217 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2221 static CHANSERV_FUNC(cmd_clvl)
2223 struct handle_info *handle;
2224 struct userData *victim;
2225 struct userData *actor;
2226 unsigned short new_access;
2227 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2231 actor = GetChannelUser(channel->channel_info, user->handle_info);
2233 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2236 if(handle == user->handle_info && !privileged)
2238 reply("CSMSG_NO_SELF_CLVL");
2242 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2244 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2248 if(actor->access <= victim->access && !privileged)
2250 reply("MSG_USER_OUTRANKED", handle->handle);
2254 new_access = user_level_from_name(argv[2], UL_OWNER);
2258 reply("CSMSG_INVALID_ACCESS", argv[2]);
2262 if(new_access >= actor->access && !privileged)
2264 reply("CSMSG_NO_BUMP_ACCESS");
2268 victim->access = new_access;
2269 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2273 static CHANSERV_FUNC(cmd_deluser)
2275 struct handle_info *handle;
2276 struct userData *victim;
2277 struct userData *actor;
2278 unsigned short access;
2283 actor = GetChannelUser(channel->channel_info, user->handle_info);
2285 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2288 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2290 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2296 access = user_level_from_name(argv[1], UL_OWNER);
2299 reply("CSMSG_INVALID_ACCESS", argv[1]);
2302 if(access != victim->access)
2304 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2310 access = victim->access;
2313 if((actor->access <= victim->access) && !IsHelping(user))
2315 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2319 chan_name = strdup(channel->name);
2320 del_channel_user(victim, 1);
2321 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2327 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2329 struct userData *actor, *uData, *next;
2331 actor = GetChannelUser(channel->channel_info, user->handle_info);
2333 if(min_access > max_access)
2335 reply("CSMSG_BAD_RANGE", min_access, max_access);
2339 if((actor->access <= max_access) && !IsHelping(user))
2341 reply("CSMSG_NO_ACCESS");
2345 for(uData = channel->channel_info->users; uData; uData = next)
2349 if((uData->access >= min_access)
2350 && (uData->access <= max_access)
2351 && match_ircglob(uData->handle->handle, mask))
2352 del_channel_user(uData, 1);
2355 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2359 static CHANSERV_FUNC(cmd_mdelowner)
2361 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2364 static CHANSERV_FUNC(cmd_mdelcoowner)
2366 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2369 static CHANSERV_FUNC(cmd_mdelmaster)
2371 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2374 static CHANSERV_FUNC(cmd_mdelop)
2376 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2379 static CHANSERV_FUNC(cmd_mdelpeon)
2381 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2385 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2387 struct banData *bData, *next;
2388 char interval[INTERVALLEN];
2393 limit = now - duration;
2394 for(bData = channel->channel_info->bans; bData; bData = next)
2398 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2401 del_channel_ban(bData);
2405 intervalString(interval, duration, user->handle_info);
2406 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2411 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2413 struct userData *actor, *uData, *next;
2414 char interval[INTERVALLEN];
2418 actor = GetChannelUser(channel->channel_info, user->handle_info);
2419 if(min_access > max_access)
2421 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2425 if((actor->access <= max_access) && !IsHelping(user))
2427 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2432 limit = now - duration;
2433 for(uData = channel->channel_info->users; uData; uData = next)
2437 if((uData->seen > limit) || uData->present)
2440 if(((uData->access >= min_access) && (uData->access <= max_access))
2441 || (!max_access && (uData->access < actor->access)))
2443 del_channel_user(uData, 1);
2451 max_access = UL_OWNER;
2453 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2457 static CHANSERV_FUNC(cmd_trim)
2459 unsigned long duration;
2460 unsigned short min_level, max_level;
2464 duration = ParseInterval(argv[2]);
2467 reply("CSMSG_CANNOT_TRIM");
2471 if(!irccasecmp(argv[1], "bans"))
2473 cmd_trim_bans(user, channel, duration);
2476 else if(!irccasecmp(argv[1], "users"))
2478 cmd_trim_users(user, channel, 0, 0, duration);
2481 else if(parse_level_range(&min_level, &max_level, argv[1]))
2483 cmd_trim_users(user, channel, min_level, max_level, duration);
2486 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2488 cmd_trim_users(user, channel, min_level, min_level, duration);
2493 reply("CSMSG_INVALID_TRIM", argv[1]);
2498 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2499 to the user. cmd_all takes advantage of this. */
2500 static CHANSERV_FUNC(cmd_up)
2502 struct mod_chanmode change;
2503 struct userData *uData;
2506 mod_chanmode_init(&change);
2508 change.args[0].u.member = GetUserMode(channel, user);
2509 if(!change.args[0].u.member)
2512 reply("MSG_CHANNEL_ABSENT", channel->name);
2516 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2520 reply("CSMSG_GODMODE_UP", argv[0]);
2523 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2525 change.args[0].mode = MODE_CHANOP;
2526 errmsg = "CSMSG_ALREADY_OPPED";
2528 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2530 change.args[0].mode = MODE_VOICE;
2531 errmsg = "CSMSG_ALREADY_VOICED";
2536 reply("CSMSG_NO_ACCESS");
2539 change.args[0].mode &= ~change.args[0].u.member->modes;
2540 if(!change.args[0].mode)
2543 reply(errmsg, channel->name);
2546 modcmd_chanmode_announce(&change);
2550 static CHANSERV_FUNC(cmd_down)
2552 struct mod_chanmode change;
2554 mod_chanmode_init(&change);
2556 change.args[0].u.member = GetUserMode(channel, user);
2557 if(!change.args[0].u.member)
2560 reply("MSG_CHANNEL_ABSENT", channel->name);
2564 if(!change.args[0].u.member->modes)
2567 reply("CSMSG_ALREADY_DOWN", channel->name);
2571 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2572 modcmd_chanmode_announce(&change);
2576 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)
2578 struct userData *cList;
2580 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2582 if(IsSuspended(cList->channel)
2583 || IsUserSuspended(cList)
2584 || !GetUserMode(cList->channel->channel, user))
2587 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2593 static CHANSERV_FUNC(cmd_upall)
2595 return cmd_all(CSFUNC_ARGS, cmd_up);
2598 static CHANSERV_FUNC(cmd_downall)
2600 return cmd_all(CSFUNC_ARGS, cmd_down);
2603 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2604 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2607 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)
2609 unsigned int ii, valid;
2610 struct userNode *victim;
2611 struct mod_chanmode *change;
2613 change = mod_chanmode_alloc(argc - 1);
2615 for(ii=valid=0; ++ii < argc; )
2617 if(!(victim = GetUserH(argv[ii])))
2619 change->args[valid].mode = mode;
2620 change->args[valid].u.member = GetUserMode(channel, victim);
2621 if(!change->args[valid].u.member)
2623 if(validate && !validate(user, channel, victim))
2628 change->argc = valid;
2629 if(valid < (argc-1))
2630 reply("CSMSG_PROCESS_FAILED");
2633 modcmd_chanmode_announce(change);
2634 reply(action, channel->name);
2636 mod_chanmode_free(change);
2640 static CHANSERV_FUNC(cmd_op)
2642 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2645 static CHANSERV_FUNC(cmd_deop)
2647 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2650 static CHANSERV_FUNC(cmd_voice)
2652 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2655 static CHANSERV_FUNC(cmd_devoice)
2657 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2661 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2667 for(ii=0; ii<channel->members.used; ii++)
2669 struct modeNode *mn = channel->members.list[ii];
2671 if(IsService(mn->user))
2674 if(!user_matches_glob(mn->user, ban, 1))
2677 if(protect_user(mn->user, user, channel->channel_info))
2681 victims[(*victimCount)++] = mn;
2687 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2689 struct userNode *victim;
2690 struct modeNode **victims;
2691 unsigned int offset, n, victimCount, duration = 0;
2692 char *reason = "Bye.", *ban, *name;
2693 char interval[INTERVALLEN];
2695 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2696 REQUIRE_PARAMS(offset);
2699 reason = unsplit_string(argv + offset, argc - offset, NULL);
2700 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2702 /* Truncate the reason to a length of TOPICLEN, as
2703 the ircd does; however, leave room for an ellipsis
2704 and the kicker's nick. */
2705 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2709 if((victim = GetUserH(argv[1])))
2711 victims = alloca(sizeof(victims[0]));
2712 victims[0] = GetUserMode(channel, victim);
2713 /* XXX: The comparison with ACTION_KICK is just because all
2714 * other actions can work on users outside the channel, and we
2715 * want to allow those (e.g. unbans) in that case. If we add
2716 * some other ejection action for in-channel users, change
2718 victimCount = victims[0] ? 1 : 0;
2720 if(IsService(victim))
2722 reply("MSG_SERVICE_IMMUNE", victim->nick);
2726 if((action == ACTION_KICK) && !victimCount)
2728 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2732 if(protect_user(victim, user, channel->channel_info))
2734 reply("CSMSG_USER_PROTECTED", victim->nick);
2738 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2739 name = victim->nick;
2743 if(!is_ircmask(argv[1]))
2745 reply("MSG_NICK_UNKNOWN", argv[1]);
2749 victims = alloca(sizeof(victims[0]) * channel->members.used);
2751 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2753 reply("CSMSG_MASK_PROTECTED", argv[1]);
2757 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2759 reply("CSMSG_LAME_MASK", argv[1]);
2763 if((action == ACTION_KICK) && (victimCount == 0))
2765 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2769 name = ban = strdup(argv[1]);
2772 /* Truncate the ban in place if necessary; we must ensure
2773 that 'ban' is a valid ban mask before sanitizing it. */
2774 sanitize_ircmask(ban);
2776 if(action & ACTION_ADD_BAN)
2778 struct banData *bData, *next;
2780 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2782 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2787 if(action & ACTION_ADD_TIMED_BAN)
2789 duration = ParseInterval(argv[2]);
2793 reply("CSMSG_DURATION_TOO_LOW");
2797 else if(duration > (86400 * 365 * 2))
2799 reply("CSMSG_DURATION_TOO_HIGH");
2805 for(bData = channel->channel_info->bans; bData; bData = next)
2807 if(match_ircglobs(bData->mask, ban))
2809 int exact = !irccasecmp(bData->mask, ban);
2811 /* The ban is redundant; there is already a ban
2812 with the same effect in place. */
2816 free(bData->reason);
2817 bData->reason = strdup(reason);
2818 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2820 reply("CSMSG_REASON_CHANGE", ban);
2824 if(exact && bData->expires)
2828 /* If the ban matches an existing one exactly,
2829 extend the expiration time if the provided
2830 duration is longer. */
2831 if(duration && ((time_t)(now + duration) > bData->expires))
2833 bData->expires = now + duration;
2844 /* Delete the expiration timeq entry and
2845 requeue if necessary. */
2846 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2849 timeq_add(bData->expires, expire_ban, bData);
2853 /* automated kickban */
2856 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2858 reply("CSMSG_BAN_ADDED", name, channel->name);
2864 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2871 if(match_ircglobs(ban, bData->mask))
2873 /* The ban we are adding makes previously existing
2874 bans redundant; silently remove them. */
2875 del_channel_ban(bData);
2879 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);
2881 name = ban = strdup(bData->mask);
2885 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2887 extern const char *hidden_host_suffix;
2888 const char *old_name = chanserv_conf.old_ban_names->list[n];
2890 unsigned int l1, l2;
2893 l2 = strlen(old_name);
2896 if(irccasecmp(ban + l1 - l2, old_name))
2898 new_mask = malloc(MAXLEN);
2899 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2901 name = ban = new_mask;
2906 if(action & ACTION_BAN)
2908 unsigned int exists;
2909 struct mod_chanmode *change;
2911 if(channel->banlist.used >= MAXBANS)
2914 reply("CSMSG_BANLIST_FULL", channel->name);
2919 exists = ChannelBanExists(channel, ban);
2920 change = mod_chanmode_alloc(victimCount + 1);
2921 for(n = 0; n < victimCount; ++n)
2923 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2924 change->args[n].u.member = victims[n];
2928 change->args[n].mode = MODE_BAN;
2929 change->args[n++].u.hostmask = ban;
2933 modcmd_chanmode_announce(change);
2935 mod_chanmode_announce(chanserv, channel, change);
2936 mod_chanmode_free(change);
2938 if(exists && (action == ACTION_BAN))
2941 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2947 if(action & ACTION_KICK)
2949 char kick_reason[MAXLEN];
2950 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2952 for(n = 0; n < victimCount; n++)
2953 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2958 /* No response, since it was automated. */
2960 else if(action & ACTION_ADD_BAN)
2963 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2965 reply("CSMSG_BAN_ADDED", name, channel->name);
2967 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2968 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2969 else if(action & ACTION_BAN)
2970 reply("CSMSG_BAN_DONE", name, channel->name);
2971 else if(action & ACTION_KICK && victimCount)
2972 reply("CSMSG_KICK_DONE", name, channel->name);
2978 static CHANSERV_FUNC(cmd_kickban)
2980 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2983 static CHANSERV_FUNC(cmd_kick)
2985 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2988 static CHANSERV_FUNC(cmd_ban)
2990 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2993 static CHANSERV_FUNC(cmd_addban)
2995 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2998 static CHANSERV_FUNC(cmd_addtimedban)
3000 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3003 static struct mod_chanmode *
3004 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3006 struct mod_chanmode *change;
3007 unsigned char *match;
3008 unsigned int ii, count;
3010 match = alloca(bans->used);
3013 for(ii = count = 0; ii < bans->used; ++ii)
3015 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3022 for(ii = count = 0; ii < bans->used; ++ii)
3024 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3031 change = mod_chanmode_alloc(count);
3032 for(ii = count = 0; ii < bans->used; ++ii)
3036 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3037 change->args[count++].u.hostmask = bans->list[ii]->ban;
3043 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3045 struct userNode *actee;
3051 /* may want to allow a comma delimited list of users... */
3052 if(!(actee = GetUserH(argv[1])))
3054 if(!is_ircmask(argv[1]))
3056 reply("MSG_NICK_UNKNOWN", argv[1]);
3060 mask = strdup(argv[1]);
3063 /* We don't sanitize the mask here because ircu
3065 if(action & ACTION_UNBAN)
3067 struct mod_chanmode *change;
3068 change = find_matching_bans(&channel->banlist, actee, mask);
3071 modcmd_chanmode_announce(change);
3072 mod_chanmode_free(change);
3077 if(action & ACTION_DEL_BAN)
3079 struct banData *ban, *next;
3081 ban = channel->channel_info->bans;
3085 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3088 for( ; ban && !match_ircglobs(mask, ban->mask);
3093 del_channel_ban(ban);
3100 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3102 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3108 static CHANSERV_FUNC(cmd_unban)
3110 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3113 static CHANSERV_FUNC(cmd_delban)
3115 /* it doesn't necessarily have to remove the channel ban - may want
3116 to make that an option. */
3117 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3120 static CHANSERV_FUNC(cmd_unbanme)
3122 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3123 long flags = ACTION_UNBAN;
3125 /* remove permanent bans if the user has the proper access. */
3126 if(uData->access >= UL_MASTER)
3127 flags |= ACTION_DEL_BAN;
3129 argv[1] = user->nick;
3130 return unban_user(user, channel, 2, argv, cmd, flags);
3133 static CHANSERV_FUNC(cmd_unbanall)
3135 struct mod_chanmode *change;
3138 if(!channel->banlist.used)
3140 reply("CSMSG_NO_BANS", channel->name);
3144 change = mod_chanmode_alloc(channel->banlist.used);
3145 for(ii=0; ii<channel->banlist.used; ii++)
3147 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3148 change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
3150 modcmd_chanmode_announce(change);
3151 mod_chanmode_free(change);
3152 reply("CSMSG_BANS_REMOVED", channel->name);
3156 static CHANSERV_FUNC(cmd_open)
3158 struct mod_chanmode *change;
3160 change = find_matching_bans(&channel->banlist, user, NULL);
3162 change = mod_chanmode_alloc(0);
3163 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3164 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3165 && channel->channel_info->modes.modes_set)
3166 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3167 modcmd_chanmode_announce(change);
3168 reply("CSMSG_CHANNEL_OPENED", channel->name);
3169 mod_chanmode_free(change);
3173 static CHANSERV_FUNC(cmd_myaccess)
3175 static struct string_buffer sbuf;
3176 struct handle_info *target_handle;
3177 struct userData *uData;
3180 target_handle = user->handle_info;
3181 else if(!IsHelping(user))
3183 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3186 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3189 if(!target_handle->channels)
3191 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3195 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3196 for(uData = target_handle->channels; uData; uData = uData->u_next)
3198 struct chanData *cData = uData->channel;
3200 if(uData->access > UL_OWNER)
3202 if(IsProtected(cData)
3203 && (target_handle != user->handle_info)
3204 && !GetTrueChannelAccess(cData, user->handle_info))
3207 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3208 if(uData->flags != USER_AUTO_OP)
3209 string_buffer_append(&sbuf, ',');
3210 if(IsUserSuspended(uData))
3211 string_buffer_append(&sbuf, 's');
3212 if(IsUserAutoOp(uData))
3214 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3215 string_buffer_append(&sbuf, 'o');
3216 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3217 string_buffer_append(&sbuf, 'v');
3219 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3220 string_buffer_append(&sbuf, 'i');
3222 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3224 string_buffer_append_string(&sbuf, ")]");
3225 string_buffer_append(&sbuf, '\0');
3226 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3232 static CHANSERV_FUNC(cmd_access)
3234 struct userNode *target;
3235 struct handle_info *target_handle;
3236 struct userData *uData;
3238 char prefix[MAXLEN];
3243 target_handle = target->handle_info;
3245 else if((target = GetUserH(argv[1])))
3247 target_handle = target->handle_info;
3249 else if(argv[1][0] == '*')
3251 if(!(target_handle = get_handle_info(argv[1]+1)))
3253 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3259 reply("MSG_NICK_UNKNOWN", argv[1]);
3263 assert(target || target_handle);
3265 if(target == chanserv)
3267 reply("CSMSG_IS_CHANSERV");
3275 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3280 reply("MSG_USER_AUTHENTICATE", target->nick);
3283 reply("MSG_AUTHENTICATE");
3289 const char *epithet = NULL, *type = NULL;
3292 epithet = chanserv_conf.irc_operator_epithet;
3295 else if(IsNetworkHelper(target))
3297 epithet = chanserv_conf.network_helper_epithet;
3298 type = "network helper";
3300 else if(IsSupportHelper(target))
3302 epithet = chanserv_conf.support_helper_epithet;
3303 type = "support helper";
3307 if(target_handle->epithet)
3308 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3310 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3312 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3316 sprintf(prefix, "%s", target_handle->handle);
3319 if(!channel->channel_info)
3321 reply("CSMSG_NOT_REGISTERED", channel->name);
3325 helping = HANDLE_FLAGGED(target_handle, HELPING)
3326 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3327 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3329 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3330 /* To prevent possible information leaks, only show infolines
3331 * if the requestor is in the channel or it's their own
3333 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3335 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3337 /* Likewise, only say it's suspended if the user has active
3338 * access in that channel or it's their own entry. */
3339 if(IsUserSuspended(uData)
3340 && (GetChannelUser(channel->channel_info, user->handle_info)
3341 || (user->handle_info == uData->handle)))
3343 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3348 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3355 zoot_list(struct listData *list)
3357 struct userData *uData;
3358 unsigned int start, curr, highest, lowest;
3359 struct helpfile_table tmp_table;
3360 const char **temp, *msg;
3362 if(list->table.length == 1)
3365 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3367 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3368 msg = user_find_message(list->user, "MSG_NONE");
3369 send_message_type(4, list->user, list->bot, " %s", msg);
3371 tmp_table.width = list->table.width;
3372 tmp_table.flags = list->table.flags;
3373 list->table.contents[0][0] = " ";
3374 highest = list->highest;
3375 if(list->lowest != 0)
3376 lowest = list->lowest;
3377 else if(highest < 100)
3380 lowest = highest - 100;
3381 for(start = curr = 1; curr < list->table.length; )
3383 uData = list->users[curr-1];
3384 list->table.contents[curr++][0] = " ";
3385 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3388 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3390 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3391 temp = list->table.contents[--start];
3392 list->table.contents[start] = list->table.contents[0];
3393 tmp_table.contents = list->table.contents + start;
3394 tmp_table.length = curr - start;
3395 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3396 list->table.contents[start] = temp;
3398 highest = lowest - 1;
3399 lowest = (highest < 100) ? 0 : (highest - 99);
3405 def_list(struct listData *list)
3409 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3411 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3412 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3413 if(list->table.length == 1)
3415 msg = user_find_message(list->user, "MSG_NONE");
3416 send_message_type(4, list->user, list->bot, " %s", msg);
3421 userData_access_comp(const void *arg_a, const void *arg_b)
3423 const struct userData *a = *(struct userData**)arg_a;
3424 const struct userData *b = *(struct userData**)arg_b;
3426 if(a->access != b->access)
3427 res = b->access - a->access;
3429 res = irccasecmp(a->handle->handle, b->handle->handle);
3434 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3436 void (*send_list)(struct listData *);
3437 struct userData *uData;
3438 struct listData lData;
3439 unsigned int matches;
3443 lData.bot = cmd->parent->bot;
3444 lData.channel = channel;
3445 lData.lowest = lowest;
3446 lData.highest = highest;
3447 lData.search = (argc > 1) ? argv[1] : NULL;
3448 send_list = def_list;
3449 (void)zoot_list; /* since it doesn't show user levels */
3451 if(user->handle_info)
3453 switch(user->handle_info->userlist_style)
3455 case HI_STYLE_DEF: send_list = def_list; break;
3456 case HI_STYLE_ZOOT: send_list = def_list; break;
3460 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3462 for(uData = channel->channel_info->users; uData; uData = uData->next)
3464 if((uData->access < lowest)
3465 || (uData->access > highest)
3466 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3468 lData.users[matches++] = uData;
3470 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3472 lData.table.length = matches+1;
3473 lData.table.width = 4;
3474 lData.table.flags = TABLE_NO_FREE;
3475 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3476 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3477 lData.table.contents[0] = ary;
3480 ary[2] = "Last Seen";
3482 for(matches = 1; matches < lData.table.length; ++matches)
3484 struct userData *uData = lData.users[matches-1];
3485 char seen[INTERVALLEN];
3487 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3488 lData.table.contents[matches] = ary;
3489 ary[0] = strtab(uData->access);
3490 ary[1] = uData->handle->handle;
3493 else if(!uData->seen)
3496 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3497 ary[2] = strdup(ary[2]);
3498 if(IsUserSuspended(uData))
3499 ary[3] = "Suspended";
3500 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3501 ary[3] = "Vacation";
3506 for(matches = 1; matches < lData.table.length; ++matches)
3508 free((char*)lData.table.contents[matches][2]);
3509 free(lData.table.contents[matches]);
3511 free(lData.table.contents[0]);
3512 free(lData.table.contents);
3516 static CHANSERV_FUNC(cmd_users)
3518 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3521 static CHANSERV_FUNC(cmd_wlist)
3523 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3526 static CHANSERV_FUNC(cmd_clist)
3528 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3531 static CHANSERV_FUNC(cmd_mlist)
3533 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3536 static CHANSERV_FUNC(cmd_olist)
3538 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3541 static CHANSERV_FUNC(cmd_plist)
3543 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3546 static CHANSERV_FUNC(cmd_bans)
3548 struct helpfile_table tbl;
3549 unsigned int matches = 0, timed = 0, ii;
3550 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3551 const char *msg_never, *triggered, *expires;
3552 struct banData *ban, **bans;
3559 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3561 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3563 if(search && !match_ircglobs(search, ban->mask))
3565 bans[matches++] = ban;
3570 tbl.length = matches + 1;
3571 tbl.width = 4 + timed;
3573 tbl.flags = TABLE_NO_FREE;
3574 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3575 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3576 tbl.contents[0][0] = "Mask";
3577 tbl.contents[0][1] = "Set By";
3578 tbl.contents[0][2] = "Triggered";
3581 tbl.contents[0][3] = "Expires";
3582 tbl.contents[0][4] = "Reason";
3585 tbl.contents[0][3] = "Reason";
3588 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3590 free(tbl.contents[0]);
3595 msg_never = user_find_message(user, "MSG_NEVER");
3596 for(ii = 0; ii < matches; )
3602 else if(ban->expires)
3603 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3605 expires = msg_never;
3608 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3610 triggered = msg_never;
3612 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3613 tbl.contents[ii][0] = ban->mask;
3614 tbl.contents[ii][1] = ban->owner;
3615 tbl.contents[ii][2] = strdup(triggered);
3618 tbl.contents[ii][3] = strdup(expires);
3619 tbl.contents[ii][4] = ban->reason;
3622 tbl.contents[ii][3] = ban->reason;
3624 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3625 reply("MSG_MATCH_COUNT", matches);
3626 for(ii = 1; ii < tbl.length; ++ii)
3628 free((char*)tbl.contents[ii][2]);
3630 free((char*)tbl.contents[ii][3]);
3631 free(tbl.contents[ii]);
3633 free(tbl.contents[0]);
3639 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3641 struct chanData *cData = channel->channel_info;
3642 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3644 if(cData->topic_mask)
3645 return !match_ircglob(new_topic, cData->topic_mask);
3646 else if(cData->topic)
3647 return irccasecmp(new_topic, cData->topic);
3652 static CHANSERV_FUNC(cmd_topic)
3654 struct chanData *cData;
3657 cData = channel->channel_info;
3662 SetChannelTopic(channel, chanserv, cData->topic, 1);
3663 reply("CSMSG_TOPIC_SET", cData->topic);
3667 reply("CSMSG_NO_TOPIC", channel->name);
3671 topic = unsplit_string(argv + 1, argc - 1, NULL);
3672 /* If they say "!topic *", use an empty topic. */
3673 if((topic[0] == '*') && (topic[1] == 0))
3675 if(bad_topic(channel, user, topic))
3677 char *topic_mask = cData->topic_mask;
3680 char new_topic[TOPICLEN+1], tchar;
3681 int pos=0, starpos=-1, dpos=0, len;
3683 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3690 len = strlen(topic);
3691 if((dpos + len) > TOPICLEN)
3692 len = TOPICLEN + 1 - dpos;
3693 memcpy(new_topic+dpos, topic, len);
3697 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3698 default: new_topic[dpos++] = tchar; break;
3701 if((dpos > TOPICLEN) || tchar)
3704 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3705 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3708 new_topic[dpos] = 0;
3709 SetChannelTopic(channel, chanserv, new_topic, 1);
3711 reply("CSMSG_TOPIC_LOCKED", channel->name);
3716 SetChannelTopic(channel, chanserv, topic, 1);
3718 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3720 /* Grab the topic and save it as the default topic. */
3722 cData->topic = strdup(channel->topic);
3728 static CHANSERV_FUNC(cmd_mode)
3730 struct mod_chanmode *change;
3734 change = &channel->channel_info->modes;
3735 if(change->modes_set || change->modes_clear) {
3736 modcmd_chanmode_announce(change);
3737 reply("CSMSG_DEFAULTED_MODES", channel->name);
3739 reply("CSMSG_NO_MODES", channel->name);
3743 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3746 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3750 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3751 && mode_lock_violated(&channel->channel_info->modes, change))
3754 mod_chanmode_format(&channel->channel_info->modes, modes);
3755 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3759 modcmd_chanmode_announce(change);
3760 mod_chanmode_free(change);
3761 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3765 static CHANSERV_FUNC(cmd_invite)
3767 struct userData *uData;
3768 struct userNode *invite;
3770 uData = GetChannelUser(channel->channel_info, user->handle_info);
3774 if(!(invite = GetUserH(argv[1])))
3776 reply("MSG_NICK_UNKNOWN", argv[1]);
3783 if(GetUserMode(channel, invite))
3785 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3793 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3794 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3797 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3799 irc_invite(chanserv, invite, channel);
3801 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3806 static CHANSERV_FUNC(cmd_inviteme)
3808 if(GetUserMode(channel, user))
3810 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3813 if(channel->channel_info
3814 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3816 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3819 irc_invite(cmd->parent->bot, user, channel);
3824 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3827 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3829 /* We display things based on two dimensions:
3830 * - Issue time: present or absent
3831 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3832 * (in order of precedence, so something both expired and revoked
3833 * only counts as revoked)
3835 combo = (suspended->issued ? 4 : 0)
3836 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3838 case 0: /* no issue time, indefinite expiration */
3839 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3841 case 1: /* no issue time, expires in future */
3842 intervalString(buf1, suspended->expires-now, user->handle_info);
3843 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3845 case 2: /* no issue time, expired */
3846 intervalString(buf1, now-suspended->expires, user->handle_info);
3847 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3849 case 3: /* no issue time, revoked */
3850 intervalString(buf1, now-suspended->revoked, user->handle_info);
3851 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3853 case 4: /* issue time set, indefinite expiration */
3854 intervalString(buf1, now-suspended->issued, user->handle_info);
3855 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3857 case 5: /* issue time set, expires in future */
3858 intervalString(buf1, now-suspended->issued, user->handle_info);
3859 intervalString(buf2, suspended->expires-now, user->handle_info);
3860 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3862 case 6: /* issue time set, expired */
3863 intervalString(buf1, now-suspended->issued, user->handle_info);
3864 intervalString(buf2, now-suspended->expires, user->handle_info);
3865 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3867 case 7: /* issue time set, revoked */
3868 intervalString(buf1, now-suspended->issued, user->handle_info);
3869 intervalString(buf2, now-suspended->revoked, user->handle_info);
3870 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3873 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3878 static CHANSERV_FUNC(cmd_info)
3880 char modes[MAXLEN], buffer[INTERVALLEN];
3881 struct userData *uData, *owner;
3882 struct chanData *cData;
3883 struct do_not_register *dnr;
3888 cData = channel->channel_info;
3889 reply("CSMSG_CHANNEL_INFO", channel->name);
3891 uData = GetChannelUser(cData, user->handle_info);
3892 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3894 mod_chanmode_format(&cData->modes, modes);
3895 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3896 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3899 for(it = dict_first(cData->notes); it; it = iter_next(it))
3903 note = iter_data(it);
3904 if(!note_type_visible_to_user(cData, note->type, user))
3907 padding = PADLEN - 1 - strlen(iter_key(it));
3908 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3911 reply("CSMSG_CHANNEL_MAX", cData->max);
3912 for(owner = cData->users; owner; owner = owner->next)
3913 if(owner->access == UL_OWNER)
3914 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3915 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3916 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3917 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3918 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3920 privileged = IsStaff(user);
3921 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3922 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3924 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3925 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3927 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3929 struct suspended *suspended;
3930 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3931 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3932 show_suspension_info(cmd, user, suspended);
3934 else if(IsSuspended(cData))
3936 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3937 show_suspension_info(cmd, user, cData->suspended);
3942 static CHANSERV_FUNC(cmd_netinfo)
3944 extern time_t boot_time;
3945 extern unsigned long burst_length;
3946 char interval[INTERVALLEN];
3948 reply("CSMSG_NETWORK_INFO");
3949 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3950 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3951 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3952 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3953 reply("CSMSG_NETWORK_BANS", banCount);
3954 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3955 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3956 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3961 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3963 struct helpfile_table table;
3965 struct userNode *user;
3970 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3971 table.contents = alloca(list->used*sizeof(*table.contents));
3972 for(nn=0; nn<list->used; nn++)
3974 user = list->list[nn];
3975 if(user->modes & skip_flags)
3979 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3982 nick = alloca(strlen(user->nick)+3);
3983 sprintf(nick, "(%s)", user->nick);
3987 table.contents[table.length][0] = nick;
3990 table_send(chanserv, to->nick, 0, NULL, table);
3993 static CHANSERV_FUNC(cmd_ircops)
3995 reply("CSMSG_STAFF_OPERS");
3996 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4000 static CHANSERV_FUNC(cmd_helpers)
4002 reply("CSMSG_STAFF_HELPERS");
4003 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4007 static CHANSERV_FUNC(cmd_staff)
4009 reply("CSMSG_NETWORK_STAFF");
4010 cmd_ircops(CSFUNC_ARGS);
4011 cmd_helpers(CSFUNC_ARGS);
4015 static CHANSERV_FUNC(cmd_peek)
4017 struct modeNode *mn;
4018 char modes[MODELEN];
4020 struct helpfile_table table;
4022 irc_make_chanmode(channel, modes);
4024 reply("CSMSG_PEEK_INFO", channel->name);
4025 reply("CSMSG_PEEK_TOPIC", channel->topic);
4026 reply("CSMSG_PEEK_MODES", modes);
4027 reply("CSMSG_PEEK_USERS", channel->members.used);
4031 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4032 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4033 for(n = 0; n < channel->members.used; n++)
4035 mn = channel->members.list[n];
4036 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4038 table.contents[table.length] = alloca(sizeof(**table.contents));
4039 table.contents[table.length][0] = mn->user->nick;
4044 reply("CSMSG_PEEK_OPS");
4045 table_send(chanserv, user->nick, 0, NULL, table);
4048 reply("CSMSG_PEEK_NO_OPS");
4052 static MODCMD_FUNC(cmd_wipeinfo)
4054 struct handle_info *victim;
4055 struct userData *ud, *actor;
4058 actor = GetChannelUser(channel->channel_info, user->handle_info);
4059 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4061 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4063 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4066 if((ud->access >= actor->access) && (ud != actor))
4068 reply("MSG_USER_OUTRANKED", victim->handle);
4074 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4078 static CHANSERV_FUNC(cmd_resync)
4080 struct mod_chanmode *changes;
4081 struct chanData *cData = channel->channel_info;
4082 unsigned int ii, used;
4084 changes = mod_chanmode_alloc(channel->members.used * 2);
4085 for(ii = used = 0; ii < channel->members.used; ++ii)
4087 struct modeNode *mn = channel->members.list[ii];
4088 struct userData *uData;
4090 if(IsService(mn->user))
4093 uData = GetChannelAccess(cData, mn->user->handle_info);
4094 if(!cData->lvlOpts[lvlGiveOps]
4095 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4097 if(!(mn->modes & MODE_CHANOP))
4099 changes->args[used].mode = MODE_CHANOP;
4100 changes->args[used++].u.member = mn;
4103 else if(!cData->lvlOpts[lvlGiveVoice]
4104 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4106 if(mn->modes & MODE_CHANOP)
4108 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4109 changes->args[used++].u.member = mn;
4111 if(!(mn->modes & MODE_VOICE))
4113 changes->args[used].mode = MODE_VOICE;
4114 changes->args[used++].u.member = mn;
4121 changes->args[used].mode = MODE_REMOVE | mn->modes;
4122 changes->args[used++].u.member = mn;
4126 changes->argc = used;
4127 modcmd_chanmode_announce(changes);
4128 mod_chanmode_free(changes);
4129 reply("CSMSG_RESYNCED_USERS", channel->name);
4133 static CHANSERV_FUNC(cmd_seen)
4135 struct userData *uData;
4136 struct handle_info *handle;
4137 char seen[INTERVALLEN];
4141 if(!irccasecmp(argv[1], chanserv->nick))
4143 reply("CSMSG_IS_CHANSERV");
4147 if(!(handle = get_handle_info(argv[1])))
4149 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4153 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4155 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4160 reply("CSMSG_USER_PRESENT", handle->handle);
4161 else if(uData->seen)
4162 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4164 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4166 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4167 reply("CSMSG_USER_VACATION", handle->handle);
4172 static MODCMD_FUNC(cmd_names)
4174 struct userNode *targ;
4175 struct userData *targData;
4176 unsigned int ii, pos;
4179 for(ii=pos=0; ii<channel->members.used; ++ii)
4181 targ = channel->members.list[ii]->user;
4182 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4185 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4188 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4192 if(IsUserSuspended(targData))
4194 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4197 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4198 reply("CSMSG_END_NAMES", channel->name);
4203 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4205 switch(ntype->visible_type)
4207 case NOTE_VIS_ALL: return 1;
4208 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4209 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4214 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4216 struct userData *uData;
4218 switch(ntype->set_access_type)
4220 case NOTE_SET_CHANNEL_ACCESS:
4221 if(!user->handle_info)
4223 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4225 return uData->access >= ntype->set_access.min_ulevel;
4226 case NOTE_SET_CHANNEL_SETTER:
4227 return check_user_level(channel, user, lvlSetters, 1, 0);
4228 case NOTE_SET_PRIVILEGED: default:
4229 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4233 static CHANSERV_FUNC(cmd_note)
4235 struct chanData *cData;
4237 struct note_type *ntype;
4239 cData = channel->channel_info;
4242 reply("CSMSG_NOT_REGISTERED", channel->name);
4246 /* If no arguments, show all visible notes for the channel. */
4252 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4254 note = iter_data(it);
4255 if(!note_type_visible_to_user(cData, note->type, user))
4258 reply("CSMSG_NOTELIST_HEADER", channel->name);
4259 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4262 reply("CSMSG_NOTELIST_END", channel->name);
4264 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4266 /* If one argument, show the named note. */
4269 if((note = dict_find(cData->notes, argv[1], NULL))
4270 && note_type_visible_to_user(cData, note->type, user))
4272 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4274 else if((ntype = dict_find(note_types, argv[1], NULL))
4275 && note_type_visible_to_user(NULL, ntype, user))
4277 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4282 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4286 /* Assume they're trying to set a note. */
4290 ntype = dict_find(note_types, argv[1], NULL);
4293 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4296 else if(note_type_settable_by_user(channel, ntype, user))
4298 note_text = unsplit_string(argv+2, argc-2, NULL);
4299 if((note = dict_find(cData->notes, argv[1], NULL)))
4300 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4301 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4302 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4304 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4306 /* The note is viewable to staff only, so return 0
4307 to keep the invocation from getting logged (or
4308 regular users can see it in !events). */
4314 reply("CSMSG_NO_ACCESS");
4321 static CHANSERV_FUNC(cmd_delnote)
4326 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4327 || !note_type_settable_by_user(channel, note->type, user))
4329 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4332 dict_remove(channel->channel_info->notes, note->type->name);
4333 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4337 static CHANSERV_FUNC(cmd_events)
4339 struct logSearch discrim;
4340 struct logReport report;
4341 unsigned int matches, limit;
4343 limit = (argc > 1) ? atoi(argv[1]) : 10;
4344 if(limit < 1 || limit > 200)
4347 memset(&discrim, 0, sizeof(discrim));
4348 discrim.masks.bot = chanserv;
4349 discrim.masks.channel_name = channel->name;
4351 discrim.masks.command = argv[2];
4352 discrim.limit = limit;
4353 discrim.max_time = INT_MAX;
4354 discrim.severities = 1 << LOG_COMMAND;
4355 report.reporter = chanserv;
4357 reply("CSMSG_EVENT_SEARCH_RESULTS");
4358 matches = log_entry_search(&discrim, log_report_entry, &report);
4360 reply("MSG_MATCH_COUNT", matches);
4362 reply("MSG_NO_MATCHES");
4366 static CHANSERV_FUNC(cmd_say)
4372 msg = unsplit_string(argv + 1, argc - 1, NULL);
4373 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4375 else if(GetUserH(argv[1]))
4378 msg = unsplit_string(argv + 2, argc - 2, NULL);
4379 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4383 reply("MSG_NOT_TARGET_NAME");
4389 static CHANSERV_FUNC(cmd_emote)
4395 /* CTCP is so annoying. */
4396 msg = unsplit_string(argv + 1, argc - 1, NULL);
4397 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4399 else if(GetUserH(argv[1]))
4401 msg = unsplit_string(argv + 2, argc - 2, NULL);
4402 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4406 reply("MSG_NOT_TARGET_NAME");
4412 struct channelList *
4413 chanserv_support_channels(void)
4415 return &chanserv_conf.support_channels;
4418 static CHANSERV_FUNC(cmd_expire)
4420 int channel_count = registered_channels;
4421 expire_channels(NULL);
4422 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4427 chanserv_expire_suspension(void *data)
4429 struct suspended *suspended = data;
4430 struct chanNode *channel;
4431 struct mod_chanmode change;
4433 if(!suspended->expires || (now < suspended->expires))
4434 suspended->revoked = now;
4435 channel = suspended->cData->channel;
4436 suspended->cData->channel = channel;
4437 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4438 mod_chanmode_init(&change);
4440 change.args[0].mode = MODE_CHANOP;
4441 change.args[0].u.member = AddChannelUser(chanserv, channel);
4442 mod_chanmode_announce(chanserv, channel, &change);
4445 static CHANSERV_FUNC(cmd_csuspend)
4447 struct suspended *suspended;
4448 char reason[MAXLEN];
4449 time_t expiry, duration;
4450 struct userData *uData;
4454 if(IsProtected(channel->channel_info))
4456 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4460 if(argv[1][0] == '!')
4462 else if(IsSuspended(channel->channel_info))
4464 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4465 show_suspension_info(cmd, user, channel->channel_info->suspended);
4469 if(!strcmp(argv[1], "0"))
4471 else if((duration = ParseInterval(argv[1])))
4472 expiry = now + duration;
4475 reply("MSG_INVALID_DURATION", argv[1]);
4479 unsplit_string(argv + 2, argc - 2, reason);
4481 suspended = calloc(1, sizeof(*suspended));
4482 suspended->revoked = 0;
4483 suspended->issued = now;
4484 suspended->suspender = strdup(user->handle_info->handle);
4485 suspended->expires = expiry;
4486 suspended->reason = strdup(reason);
4487 suspended->cData = channel->channel_info;
4488 suspended->previous = suspended->cData->suspended;
4489 suspended->cData->suspended = suspended;
4491 if(suspended->expires)
4492 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4494 if(IsSuspended(channel->channel_info))
4496 suspended->previous->revoked = now;
4497 if(suspended->previous->expires)
4498 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4499 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4500 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4504 /* Mark all users in channel as absent. */
4505 for(uData = channel->channel_info->users; uData; uData = uData->next)
4514 /* Mark the channel as suspended, then part. */
4515 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4516 DelChannelUser(chanserv, channel, suspended->reason, 0);
4517 reply("CSMSG_SUSPENDED", channel->name);
4518 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4519 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4524 static CHANSERV_FUNC(cmd_cunsuspend)
4526 struct suspended *suspended;
4527 char message[MAXLEN];
4529 if(!IsSuspended(channel->channel_info))
4531 reply("CSMSG_NOT_SUSPENDED", channel->name);
4535 suspended = channel->channel_info->suspended;
4537 /* Expire the suspension and join ChanServ to the channel. */
4538 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4539 chanserv_expire_suspension(suspended);
4540 reply("CSMSG_UNSUSPENDED", channel->name);
4541 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4542 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4546 typedef struct chanservSearch
4554 unsigned long flags;
4558 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4561 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4566 search = malloc(sizeof(struct chanservSearch));
4567 memset(search, 0, sizeof(*search));
4570 for(i = 0; i < argc; i++)
4572 /* Assume all criteria require arguments. */
4575 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4579 if(!irccasecmp(argv[i], "name"))
4580 search->name = argv[++i];
4581 else if(!irccasecmp(argv[i], "registrar"))
4582 search->registrar = argv[++i];
4583 else if(!irccasecmp(argv[i], "unvisited"))
4584 search->unvisited = ParseInterval(argv[++i]);
4585 else if(!irccasecmp(argv[i], "registered"))
4586 search->registered = ParseInterval(argv[++i]);
4587 else if(!irccasecmp(argv[i], "flags"))
4590 if(!irccasecmp(argv[i], "nodelete"))
4591 search->flags |= CHANNEL_NODELETE;
4592 else if(!irccasecmp(argv[i], "suspended"))
4593 search->flags |= CHANNEL_SUSPENDED;
4596 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4600 else if(!irccasecmp(argv[i], "limit"))
4601 search->limit = strtoul(argv[++i], NULL, 10);
4604 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4609 if(search->name && !strcmp(search->name, "*"))
4611 if(search->registrar && !strcmp(search->registrar, "*"))
4612 search->registrar = 0;
4621 chanserv_channel_match(struct chanData *channel, search_t search)
4623 const char *name = channel->channel->name;
4624 if((search->name && !match_ircglob(name, search->name)) ||
4625 (search->registrar && !channel->registrar) ||
4626 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4627 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4628 (search->registered && (now - channel->registered) > search->registered) ||
4629 (search->flags && ((search->flags & channel->flags) != search->flags)))
4636 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4638 struct chanData *channel;
4639 unsigned int matches = 0;
4641 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4643 if(!chanserv_channel_match(channel, search))
4653 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4658 search_print(struct chanData *channel, void *data)
4660 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4663 static CHANSERV_FUNC(cmd_search)
4666 unsigned int matches;
4667 channel_search_func action;
4671 if(!irccasecmp(argv[1], "count"))
4672 action = search_count;
4673 else if(!irccasecmp(argv[1], "print"))
4674 action = search_print;
4677 reply("CSMSG_ACTION_INVALID", argv[1]);
4681 search = chanserv_search_create(user, argc - 2, argv + 2);
4685 if(action == search_count)
4686 search->limit = INT_MAX;
4688 if(action == search_print)
4689 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4691 matches = chanserv_channel_search(search, action, user);
4694 reply("MSG_MATCH_COUNT", matches);
4696 reply("MSG_NO_MATCHES");
4702 static CHANSERV_FUNC(cmd_unvisited)
4704 struct chanData *cData;
4705 time_t interval = chanserv_conf.channel_expire_delay;
4706 char buffer[INTERVALLEN];
4707 unsigned int limit = 25, matches = 0;
4711 interval = ParseInterval(argv[1]);
4713 limit = atoi(argv[2]);
4716 intervalString(buffer, interval, user->handle_info);
4717 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4719 for(cData = channelList; cData && matches < limit; cData = cData->next)
4721 if((now - cData->visited) < interval)
4724 intervalString(buffer, now - cData->visited, user->handle_info);
4725 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4732 static MODCMD_FUNC(chan_opt_defaulttopic)
4738 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4740 reply("CSMSG_TOPIC_LOCKED", channel->name);
4744 topic = unsplit_string(argv+1, argc-1, NULL);
4746 free(channel->channel_info->topic);
4747 if(topic[0] == '*' && topic[1] == 0)
4749 topic = channel->channel_info->topic = NULL;
4753 topic = channel->channel_info->topic = strdup(topic);
4754 if(channel->channel_info->topic_mask
4755 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4756 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4758 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4761 if(channel->channel_info->topic)
4762 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4764 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4768 static MODCMD_FUNC(chan_opt_topicmask)
4772 struct chanData *cData = channel->channel_info;
4775 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4777 reply("CSMSG_TOPIC_LOCKED", channel->name);
4781 mask = unsplit_string(argv+1, argc-1, NULL);
4783 if(cData->topic_mask)
4784 free(cData->topic_mask);
4785 if(mask[0] == '*' && mask[1] == 0)
4787 cData->topic_mask = 0;
4791 cData->topic_mask = strdup(mask);
4793 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4794 else if(!match_ircglob(cData->topic, cData->topic_mask))
4795 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4799 if(channel->channel_info->topic_mask)
4800 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4802 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4806 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4810 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4814 if(greeting[0] == '*' && greeting[1] == 0)
4818 unsigned int length = strlen(greeting);
4819 if(length > chanserv_conf.greeting_length)
4821 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4824 *data = strdup(greeting);
4833 reply(name, user_find_message(user, "MSG_NONE"));
4837 static MODCMD_FUNC(chan_opt_greeting)
4839 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4842 static MODCMD_FUNC(chan_opt_usergreeting)
4844 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4847 static MODCMD_FUNC(chan_opt_modes)
4849 struct mod_chanmode *new_modes;
4850 char modes[MODELEN];
4854 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4856 reply("CSMSG_NO_ACCESS");
4859 if(argv[1][0] == '*' && argv[1][1] == 0)
4861 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4863 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4865 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4868 else if(new_modes->argc > 1)
4870 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4871 mod_chanmode_free(new_modes);
4876 channel->channel_info->modes = *new_modes;
4877 modcmd_chanmode_announce(new_modes);
4878 mod_chanmode_free(new_modes);
4882 mod_chanmode_format(&channel->channel_info->modes, modes);
4884 reply("CSMSG_SET_MODES", modes);
4886 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4890 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4892 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4894 struct chanData *cData = channel->channel_info;
4899 /* Set flag according to value. */
4900 if(enabled_string(argv[1]))
4902 cData->flags |= mask;
4905 else if(disabled_string(argv[1]))
4907 cData->flags &= ~mask;
4912 reply("MSG_INVALID_BINARY", argv[1]);
4918 /* Find current option value. */
4919 value = (cData->flags & mask) ? 1 : 0;
4923 reply(name, user_find_message(user, "MSG_ON"));
4925 reply(name, user_find_message(user, "MSG_OFF"));
4929 static MODCMD_FUNC(chan_opt_nodelete)
4931 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4933 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4937 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4940 static MODCMD_FUNC(chan_opt_dynlimit)
4942 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4945 static MODCMD_FUNC(chan_opt_offchannel)
4947 struct chanData *cData = channel->channel_info;
4952 /* Set flag according to value. */
4953 if(enabled_string(argv[1]))
4955 if(!IsOffChannel(cData))
4956 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4957 cData->flags |= CHANNEL_OFFCHANNEL;
4960 else if(disabled_string(argv[1]))
4962 if(IsOffChannel(cData))
4964 struct mod_chanmode change;
4965 mod_chanmode_init(&change);
4967 change.args[0].mode = MODE_CHANOP;
4968 change.args[0].u.member = AddChannelUser(chanserv, channel);
4969 mod_chanmode_announce(chanserv, channel, &change);
4971 cData->flags &= ~CHANNEL_OFFCHANNEL;
4976 reply("MSG_INVALID_BINARY", argv[1]);
4982 /* Find current option value. */
4983 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4987 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4989 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4993 static MODCMD_FUNC(chan_opt_defaults)
4995 struct userData *uData;
4996 struct chanData *cData;
4997 const char *confirm;
4998 enum levelOption lvlOpt;
4999 enum charOption chOpt;
5001 cData = channel->channel_info;
5002 uData = GetChannelUser(cData, user->handle_info);
5003 if(!uData || (uData->access < UL_OWNER))
5005 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5008 confirm = make_confirmation_string(uData);
5009 if((argc < 2) || strcmp(argv[1], confirm))
5011 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5014 cData->flags = CHANNEL_DEFAULT_FLAGS;
5015 cData->modes = chanserv_conf.default_modes;
5016 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5017 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5018 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5019 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5020 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5025 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5027 struct chanData *cData = channel->channel_info;
5028 struct userData *uData;
5029 unsigned short value;
5033 if(!check_user_level(channel, user, option, 1, 1))
5035 reply("CSMSG_CANNOT_SET");
5038 value = user_level_from_name(argv[1], UL_OWNER+1);
5039 if(!value && strcmp(argv[1], "0"))
5041 reply("CSMSG_INVALID_ACCESS", argv[1]);
5044 uData = GetChannelUser(cData, user->handle_info);
5045 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5047 reply("CSMSG_BAD_SETLEVEL");
5053 if(value > cData->lvlOpts[lvlGiveOps])
5055 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5060 if(value < cData->lvlOpts[lvlGiveVoice])
5062 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5067 /* This test only applies to owners, since non-owners
5068 * trying to set an option to above their level get caught
5069 * by the CSMSG_BAD_SETLEVEL test above.
5071 if(value > uData->access)
5073 reply("CSMSG_BAD_SETTERS");
5080 cData->lvlOpts[option] = value;
5082 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5086 static MODCMD_FUNC(chan_opt_enfops)
5088 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5091 static MODCMD_FUNC(chan_opt_giveops)
5093 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5096 static MODCMD_FUNC(chan_opt_enfmodes)
5098 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5101 static MODCMD_FUNC(chan_opt_enftopic)
5103 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5106 static MODCMD_FUNC(chan_opt_pubcmd)
5108 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5111 static MODCMD_FUNC(chan_opt_setters)
5113 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5116 static MODCMD_FUNC(chan_opt_ctcpusers)
5118 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5121 static MODCMD_FUNC(chan_opt_userinfo)
5123 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5126 static MODCMD_FUNC(chan_opt_givevoice)
5128 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5131 static MODCMD_FUNC(chan_opt_topicsnarf)
5133 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5136 static MODCMD_FUNC(chan_opt_inviteme)
5138 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5142 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5144 struct chanData *cData = channel->channel_info;
5145 int count = charOptions[option].count, index;
5149 index = atoi(argv[1]);
5151 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5153 reply("CSMSG_INVALID_NUMERIC", index);
5154 /* Show possible values. */
5155 for(index = 0; index < count; index++)
5156 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5160 cData->chOpts[option] = charOptions[option].values[index].value;
5164 /* Find current option value. */
5167 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5171 /* Somehow, the option value is corrupt; reset it to the default. */
5172 cData->chOpts[option] = charOptions[option].default_value;
5177 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5181 static MODCMD_FUNC(chan_opt_protect)
5183 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5186 static MODCMD_FUNC(chan_opt_toys)
5188 return channel_multiple_option(chToys, CSFUNC_ARGS);
5191 static MODCMD_FUNC(chan_opt_ctcpreaction)
5193 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5196 static MODCMD_FUNC(chan_opt_topicrefresh)
5198 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5201 static struct svccmd_list set_shows_list;
5204 handle_svccmd_unbind(struct svccmd *target) {
5206 for(ii=0; ii<set_shows_list.used; ++ii)
5207 if(target == set_shows_list.list[ii])
5208 set_shows_list.used = 0;
5211 static CHANSERV_FUNC(cmd_set)
5213 struct svccmd *subcmd;
5217 /* Check if we need to (re-)initialize set_shows_list. */
5218 if(!set_shows_list.used)
5220 if(!set_shows_list.size)
5222 set_shows_list.size = chanserv_conf.set_shows->used;
5223 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5225 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5227 const char *name = chanserv_conf.set_shows->list[ii];
5228 sprintf(buf, "%s %s", argv[0], name);
5229 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5232 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5235 svccmd_list_append(&set_shows_list, subcmd);
5241 reply("CSMSG_CHANNEL_OPTIONS");
5242 for(ii = 0; ii < set_shows_list.used; ii++)
5244 subcmd = set_shows_list.list[ii];
5245 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5250 sprintf(buf, "%s %s", argv[0], argv[1]);
5251 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5254 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5257 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5259 reply("CSMSG_NO_ACCESS");
5263 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5267 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5269 struct userData *uData;
5271 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5274 reply("CSMSG_NOT_USER", channel->name);
5280 /* Just show current option value. */
5282 else if(enabled_string(argv[1]))
5284 uData->flags |= mask;
5286 else if(disabled_string(argv[1]))
5288 uData->flags &= ~mask;
5292 reply("MSG_INVALID_BINARY", argv[1]);
5296 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5300 static MODCMD_FUNC(user_opt_noautoop)
5302 struct userData *uData;
5304 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5307 reply("CSMSG_NOT_USER", channel->name);
5310 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5311 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5313 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5316 static MODCMD_FUNC(user_opt_autoinvite)
5318 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5321 static MODCMD_FUNC(user_opt_info)
5323 struct userData *uData;
5326 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5330 /* If they got past the command restrictions (which require access)
5331 * but fail this test, we have some fool with security override on.
5333 reply("CSMSG_NOT_USER", channel->name);
5340 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5341 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5343 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5346 bp = strcspn(infoline, "\001");
5349 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5354 if(infoline[0] == '*' && infoline[1] == 0)
5357 uData->info = strdup(infoline);
5360 reply("CSMSG_USET_INFO", uData->info);
5362 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5366 struct svccmd_list uset_shows_list;
5368 static CHANSERV_FUNC(cmd_uset)
5370 struct svccmd *subcmd;
5374 /* Check if we need to (re-)initialize uset_shows_list. */
5375 if(!uset_shows_list.used)
5379 "NoAutoOp", "AutoInvite", "Info"
5382 if(!uset_shows_list.size)
5384 uset_shows_list.size = ArrayLength(options);
5385 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5387 for(ii = 0; ii < ArrayLength(options); ii++)
5389 const char *name = options[ii];
5390 sprintf(buf, "%s %s", argv[0], name);
5391 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5394 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5397 svccmd_list_append(&uset_shows_list, subcmd);
5403 /* Do this so options are presented in a consistent order. */
5404 reply("CSMSG_USER_OPTIONS");
5405 for(ii = 0; ii < uset_shows_list.used; ii++)
5406 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5410 sprintf(buf, "%s %s", argv[0], argv[1]);
5411 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5414 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5418 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5421 static CHANSERV_FUNC(cmd_giveownership)
5423 struct handle_info *new_owner_hi;
5424 struct userData *new_owner, *curr_user;
5425 struct chanData *cData = channel->channel_info;
5426 struct do_not_register *dnr;
5428 unsigned short co_access;
5429 char reason[MAXLEN];
5432 curr_user = GetChannelAccess(cData, user->handle_info);
5433 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5434 if(!curr_user || (curr_user->access != UL_OWNER))
5436 struct userData *owner = NULL;
5437 for(curr_user = channel->channel_info->users;
5439 curr_user = curr_user->next)
5441 if(curr_user->access != UL_OWNER)
5445 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5452 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5454 if(new_owner_hi == user->handle_info)
5456 reply("CSMSG_NO_TRANSFER_SELF");
5459 new_owner = GetChannelAccess(cData, new_owner_hi);
5462 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5465 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5467 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5470 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5471 if(!IsHelping(user))
5472 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5474 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5477 if(new_owner->access >= UL_COOWNER)
5478 co_access = new_owner->access;
5480 co_access = UL_COOWNER;
5481 new_owner->access = UL_OWNER;
5483 curr_user->access = co_access;
5484 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5485 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5486 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5490 static CHANSERV_FUNC(cmd_suspend)
5492 struct handle_info *hi;
5493 struct userData *self, *target;
5496 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5497 self = GetChannelUser(channel->channel_info, user->handle_info);
5498 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5500 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5503 if(target->access >= self->access)
5505 reply("MSG_USER_OUTRANKED", hi->handle);
5508 if(target->flags & USER_SUSPENDED)
5510 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5515 target->present = 0;
5518 target->flags |= USER_SUSPENDED;
5519 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5523 static CHANSERV_FUNC(cmd_unsuspend)
5525 struct handle_info *hi;
5526 struct userData *self, *target;
5529 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5530 self = GetChannelUser(channel->channel_info, user->handle_info);
5531 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5533 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5536 if(target->access >= self->access)
5538 reply("MSG_USER_OUTRANKED", hi->handle);
5541 if(!(target->flags & USER_SUSPENDED))
5543 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5546 target->flags &= ~USER_SUSPENDED;
5547 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5551 static MODCMD_FUNC(cmd_deleteme)
5553 struct handle_info *hi;
5554 struct userData *target;
5555 const char *confirm_string;
5556 unsigned short access;
5559 hi = user->handle_info;
5560 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5562 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5565 if(target->access == UL_OWNER)
5567 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5570 confirm_string = make_confirmation_string(target);
5571 if((argc < 2) || strcmp(argv[1], confirm_string))
5573 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5576 access = target->access;
5577 channel_name = strdup(channel->name);
5578 del_channel_user(target, 1);
5579 reply("CSMSG_DELETED_YOU", access, channel_name);
5585 chanserv_refresh_topics(UNUSED_ARG(void *data))
5587 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5588 struct chanData *cData;
5591 for(cData = channelList; cData; cData = cData->next)
5593 if(IsSuspended(cData))
5595 opt = cData->chOpts[chTopicRefresh];
5598 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5601 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5602 cData->last_refresh = refresh_num;
5604 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5607 static CHANSERV_FUNC(cmd_unf)
5611 char response[MAXLEN];
5612 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5613 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5614 irc_privmsg(cmd->parent->bot, channel->name, response);
5617 reply("CSMSG_UNF_RESPONSE");
5621 static CHANSERV_FUNC(cmd_ping)
5625 char response[MAXLEN];
5626 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5627 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5628 irc_privmsg(cmd->parent->bot, channel->name, response);
5631 reply("CSMSG_PING_RESPONSE");
5635 static CHANSERV_FUNC(cmd_wut)
5639 char response[MAXLEN];
5640 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5641 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5642 irc_privmsg(cmd->parent->bot, channel->name, response);
5645 reply("CSMSG_WUT_RESPONSE");
5649 static CHANSERV_FUNC(cmd_8ball)
5651 unsigned int i, j, accum;
5656 for(i=1; i<argc; i++)
5657 for(j=0; argv[i][j]; j++)
5658 accum = (accum << 5) - accum + toupper(argv[i][j]);
5659 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5662 char response[MAXLEN];
5663 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5664 irc_privmsg(cmd->parent->bot, channel->name, response);
5667 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5671 static CHANSERV_FUNC(cmd_d)
5673 unsigned long sides, count, modifier, ii, total;
5674 char response[MAXLEN], *sep;
5678 if((count = strtoul(argv[1], &sep, 10)) < 1)
5688 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5689 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5693 else if((sep[0] == '-') && isdigit(sep[1]))
5694 modifier = strtoul(sep, NULL, 10);
5695 else if((sep[0] == '+') && isdigit(sep[1]))
5696 modifier = strtoul(sep+1, NULL, 10);
5703 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5708 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5711 for(total = ii = 0; ii < count; ++ii)
5712 total += (rand() % sides) + 1;
5715 if((count > 1) || modifier)
5717 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5718 sprintf(response, fmt, total, count, sides, modifier);
5722 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5723 sprintf(response, fmt, total, sides);
5726 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5728 send_message_type(4, user, cmd->parent->bot, "%s", response);
5732 static CHANSERV_FUNC(cmd_huggle)
5734 /* CTCP must be via PRIVMSG, never notice */
5736 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5738 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5743 chanserv_adjust_limit(void *data)
5745 struct mod_chanmode change;
5746 struct chanData *cData = data;
5747 struct chanNode *channel = cData->channel;
5750 if(IsSuspended(cData))
5753 cData->limitAdjusted = now;
5754 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5755 if(cData->modes.modes_set & MODE_LIMIT)
5757 if(limit > cData->modes.new_limit)
5758 limit = cData->modes.new_limit;
5759 else if(limit == cData->modes.new_limit)
5763 mod_chanmode_init(&change);
5764 change.modes_set = MODE_LIMIT;
5765 change.new_limit = limit;
5766 mod_chanmode_announce(chanserv, channel, &change);
5770 handle_new_channel(struct chanNode *channel)
5772 struct chanData *cData;
5774 if(!(cData = channel->channel_info))
5777 if(cData->modes.modes_set || cData->modes.modes_clear)
5778 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5780 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5781 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5784 /* Welcome to my worst nightmare. Warning: Read (or modify)
5785 the code below at your own risk. */
5787 handle_join(struct modeNode *mNode)
5789 struct mod_chanmode change;
5790 struct userNode *user = mNode->user;
5791 struct chanNode *channel = mNode->channel;
5792 struct chanData *cData;
5793 struct userData *uData = NULL;
5794 struct banData *bData;
5795 struct handle_info *handle;
5796 unsigned int modes = 0, info = 0;
5799 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5802 cData = channel->channel_info;
5803 if(channel->members.used > cData->max)
5804 cData->max = channel->members.used;
5806 /* Check for bans. If they're joining through a ban, one of two
5808 * 1: Join during a netburst, by riding the break. Kick them
5809 * unless they have ops or voice in the channel.
5810 * 2: They're allowed to join through the ban (an invite in
5811 * ircu2.10, or a +e on Hybrid, or something).
5812 * If they're not joining through a ban, and the banlist is not
5813 * full, see if they're on the banlist for the channel. If so,
5816 if(user->uplink->burst && !mNode->modes)
5819 for(ii = 0; ii < channel->banlist.used; ii++)
5821 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5823 /* Riding a netburst. Naughty. */
5824 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5830 mod_chanmode_init(&change);
5832 if(channel->banlist.used < MAXBANS)
5834 /* Not joining through a ban. */
5835 for(bData = cData->bans;
5836 bData && !user_matches_glob(user, bData->mask, 1);
5837 bData = bData->next);
5841 char kick_reason[MAXLEN];
5842 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5844 bData->triggered = now;
5845 if(bData != cData->bans)
5847 /* Shuffle the ban to the head of the list. */
5849 bData->next->prev = bData->prev;
5851 bData->prev->next = bData->next;
5854 bData->next = cData->bans;
5857 cData->bans->prev = bData;
5858 cData->bans = bData;
5861 change.args[0].mode = MODE_BAN;
5862 change.args[0].u.hostmask = bData->mask;
5863 mod_chanmode_announce(chanserv, channel, &change);
5864 KickChannelUser(user, channel, chanserv, kick_reason);
5869 /* ChanServ will not modify the limits in join-flooded channels.
5870 It will also skip DynLimit processing when the user (or srvx)
5871 is bursting in, because there are likely more incoming. */
5872 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5873 && !user->uplink->burst
5874 && !channel->join_flooded
5875 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5877 /* The user count has begun "bumping" into the channel limit,
5878 so set a timer to raise the limit a bit. Any previous
5879 timers are removed so three incoming users within the delay
5880 results in one limit change, not three. */
5882 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5883 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5886 if(channel->join_flooded)
5888 /* don't automatically give ops or voice during a join flood */
5890 else if(cData->lvlOpts[lvlGiveOps] == 0)
5891 modes |= MODE_CHANOP;
5892 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5893 modes |= MODE_VOICE;
5895 greeting = cData->greeting;
5896 if(user->handle_info)
5898 handle = user->handle_info;
5900 if(IsHelper(user) && !IsHelping(user))
5903 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5905 if(channel == chanserv_conf.support_channels.list[ii])
5907 HANDLE_SET_FLAG(user->handle_info, HELPING);
5913 uData = GetTrueChannelAccess(cData, handle);
5914 if(uData && !IsUserSuspended(uData))
5916 /* Ops and above were handled by the above case. */
5917 if(IsUserAutoOp(uData))
5919 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5920 modes |= MODE_CHANOP;
5921 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5922 modes |= MODE_VOICE;
5924 if(uData->access >= UL_PRESENT)
5925 cData->visited = now;
5926 if(cData->user_greeting)
5927 greeting = cData->user_greeting;
5929 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5930 && ((now - uData->seen) >= chanserv_conf.info_delay)
5937 if(!user->uplink->burst)
5941 if(modes & MODE_CHANOP)
5942 modes &= ~MODE_VOICE;
5943 change.args[0].mode = modes;
5944 change.args[0].u.member = mNode;
5945 mod_chanmode_announce(chanserv, channel, &change);
5947 if(greeting && !user->uplink->burst)
5948 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5950 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5956 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5958 struct mod_chanmode change;
5959 struct userData *channel;
5960 unsigned int ii, jj;
5962 if(!user->handle_info)
5965 mod_chanmode_init(&change);
5967 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5969 struct chanNode *cn;
5970 struct modeNode *mn;
5971 if(IsUserSuspended(channel)
5972 || IsSuspended(channel->channel)
5973 || !(cn = channel->channel->channel))
5976 mn = GetUserMode(cn, user);
5979 if(!IsUserSuspended(channel)
5980 && IsUserAutoInvite(channel)
5981 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5983 && !user->uplink->burst)
5984 irc_invite(chanserv, user, cn);
5988 if(channel->access >= UL_PRESENT)
5989 channel->channel->visited = now;
5991 if(IsUserAutoOp(channel))
5993 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5994 change.args[0].mode = MODE_CHANOP;
5995 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5996 change.args[0].mode = MODE_VOICE;
5998 change.args[0].mode = 0;
5999 change.args[0].u.member = mn;
6000 if(change.args[0].mode)
6001 mod_chanmode_announce(chanserv, cn, &change);
6004 channel->seen = now;
6005 channel->present = 1;
6008 for(ii = 0; ii < user->channels.used; ++ii)
6010 struct chanNode *channel = user->channels.list[ii]->channel;
6011 struct banData *ban;
6013 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6014 || !channel->channel_info)
6016 for(jj = 0; jj < channel->banlist.used; ++jj)
6017 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6019 if(jj < channel->banlist.used)
6021 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6023 char kick_reason[MAXLEN];
6024 if(!user_matches_glob(user, ban->mask, 1))
6026 change.args[0].mode = MODE_BAN;
6027 change.args[0].u.hostmask = ban->mask;
6028 mod_chanmode_announce(chanserv, channel, &change);
6029 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6030 KickChannelUser(user, channel, chanserv, kick_reason);
6031 ban->triggered = now;
6036 if(IsSupportHelper(user))
6038 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6040 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6042 HANDLE_SET_FLAG(user->handle_info, HELPING);
6050 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6052 struct chanData *cData;
6053 struct userData *uData;
6055 cData = mn->channel->channel_info;
6056 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6059 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6061 /* Allow for a bit of padding so that the limit doesn't
6062 track the user count exactly, which could get annoying. */
6063 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6065 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6066 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6070 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6072 scan_user_presence(uData, mn->user);
6076 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6078 unsigned int ii, jj;
6079 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6081 for(jj = 0; jj < mn->user->channels.used; ++jj)
6082 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6084 if(jj < mn->user->channels.used)
6087 if(ii == chanserv_conf.support_channels.used)
6088 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6093 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6095 struct userData *uData;
6097 if(!channel->channel_info || !kicker || IsService(kicker)
6098 || (kicker == victim) || IsSuspended(channel->channel_info)
6099 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6102 if(protect_user(victim, kicker, channel->channel_info))
6104 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6105 KickChannelUser(kicker, channel, chanserv, reason);
6108 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6113 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6115 struct chanData *cData;
6117 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6120 cData = channel->channel_info;
6121 if(bad_topic(channel, user, channel->topic))
6123 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6124 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6125 SetChannelTopic(channel, chanserv, old_topic, 1);
6126 else if(cData->topic)
6127 SetChannelTopic(channel, chanserv, cData->topic, 1);
6130 /* With topicsnarf, grab the topic and save it as the default topic. */
6131 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6134 cData->topic = strdup(channel->topic);
6140 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6142 struct mod_chanmode *bounce = NULL;
6143 unsigned int bnc, ii;
6146 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6149 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6150 && mode_lock_violated(&channel->channel_info->modes, change))
6152 char correct[MAXLEN];
6153 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6154 mod_chanmode_format(&channel->channel_info->modes, correct);
6155 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6157 for(ii = bnc = 0; ii < change->argc; ++ii)
6159 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6161 const struct userNode *victim = change->args[ii].u.member->user;
6162 if(!protect_user(victim, user, channel->channel_info))
6165 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6168 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6169 bounce->args[bnc].u.member = GetUserMode(channel, user);
6170 if(bounce->args[bnc].u.member)
6174 bounce->args[bnc].mode = MODE_CHANOP;
6175 bounce->args[bnc].u.member = change->args[ii].u.member;
6177 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6179 else if(change->args[ii].mode & MODE_CHANOP)
6181 const struct userNode *victim = change->args[ii].u.member->user;
6182 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6185 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6186 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6187 bounce->args[bnc].u.member = change->args[ii].u.member;
6190 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6192 const char *ban = change->args[ii].u.hostmask;
6193 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6196 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6197 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6198 bounce->args[bnc].u.hostmask = ban;
6200 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6205 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6206 mod_chanmode_announce(chanserv, channel, bounce);
6207 mod_chanmode_free(bounce);
6212 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6214 struct chanNode *channel;
6215 struct banData *bData;
6216 struct mod_chanmode change;
6217 unsigned int ii, jj;
6218 char kick_reason[MAXLEN];
6220 mod_chanmode_init(&change);
6222 change.args[0].mode = MODE_BAN;
6223 for(ii = 0; ii < user->channels.used; ++ii)
6225 channel = user->channels.list[ii]->channel;
6226 /* Need not check for bans if they're opped or voiced. */
6227 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6229 /* Need not check for bans unless channel registration is active. */
6230 if(!channel->channel_info || IsSuspended(channel->channel_info))
6232 /* Look for a matching ban already on the channel. */
6233 for(jj = 0; jj < channel->banlist.used; ++jj)
6234 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6236 /* Need not act if we found one. */
6237 if(jj < channel->banlist.used)
6239 /* Look for a matching ban in this channel. */
6240 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6242 if(!user_matches_glob(user, bData->mask, 1))
6244 change.args[0].u.hostmask = bData->mask;
6245 mod_chanmode_announce(chanserv, channel, &change);
6246 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6247 KickChannelUser(user, channel, chanserv, kick_reason);
6248 bData->triggered = now;
6249 break; /* we don't need to check any more bans in the channel */
6254 static void handle_rename(struct handle_info *handle, const char *old_handle)
6256 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6260 dict_remove2(handle_dnrs, old_handle, 1);
6261 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6262 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6267 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6269 struct userNode *h_user;
6271 if(handle->channels)
6273 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6274 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6276 while(handle->channels)
6277 del_channel_user(handle->channels, 1);
6282 handle_server_link(UNUSED_ARG(struct server *server))
6284 struct chanData *cData;
6286 for(cData = channelList; cData; cData = cData->next)
6288 if(!IsSuspended(cData))
6289 cData->may_opchan = 1;
6290 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6291 && !cData->channel->join_flooded
6292 && ((cData->channel->limit - cData->channel->members.used)
6293 < chanserv_conf.adjust_threshold))
6295 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6296 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6302 chanserv_conf_read(void)
6306 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6307 struct mod_chanmode *change;
6308 struct string_list *strlist;
6309 struct chanNode *chan;
6312 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6314 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6317 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6318 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6319 chanserv_conf.support_channels.used = 0;
6320 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6322 for(ii = 0; ii < strlist->used; ++ii)
6324 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6327 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6329 channelList_append(&chanserv_conf.support_channels, chan);
6332 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6335 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6338 chan = AddChannel(str, now, str2, NULL);
6340 channelList_append(&chanserv_conf.support_channels, chan);
6342 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6343 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6344 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6345 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6346 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6347 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6348 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6349 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6350 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6351 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6352 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6353 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6354 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6355 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6356 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6357 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6358 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6359 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6360 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6361 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6362 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6363 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6364 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6366 NickChange(chanserv, str, 0);
6367 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6368 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6369 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6370 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6371 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6372 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6373 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6374 chanserv_conf.max_owned = str ? atoi(str) : 5;
6375 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6376 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6377 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6378 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6379 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6380 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6381 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6384 safestrncpy(mode_line, str, sizeof(mode_line));
6385 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6386 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6388 chanserv_conf.default_modes = *change;
6389 mod_chanmode_free(change);
6391 free_string_list(chanserv_conf.set_shows);
6392 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6394 strlist = string_list_copy(strlist);
6397 static const char *list[] = {
6398 /* free form text */
6399 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6400 /* options based on user level */
6401 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6402 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6403 /* multiple choice options */
6404 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6405 /* binary options */
6406 "DynLimit", "NoDelete",
6411 strlist = alloc_string_list(ArrayLength(list)-1);
6412 for(ii=0; list[ii]; ii++)
6413 string_list_append(strlist, strdup(list[ii]));
6415 chanserv_conf.set_shows = strlist;
6416 /* We don't look things up now, in case the list refers to options
6417 * defined by modules initialized after this point. Just mark the
6418 * function list as invalid, so it will be initialized.
6420 set_shows_list.used = 0;
6421 free_string_list(chanserv_conf.eightball);
6422 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6425 strlist = string_list_copy(strlist);
6429 strlist = alloc_string_list(4);
6430 string_list_append(strlist, strdup("Yes."));
6431 string_list_append(strlist, strdup("No."));
6432 string_list_append(strlist, strdup("Maybe so."));
6434 chanserv_conf.eightball = strlist;
6435 free_string_list(chanserv_conf.old_ban_names);
6436 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6438 strlist = string_list_copy(strlist);
6440 strlist = alloc_string_list(2);
6441 chanserv_conf.old_ban_names = strlist;
6442 /* the variable itself is actually declared in proto-common.c; this is equally
6444 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6445 off_channel = (str && enabled_string(str)) ? 1 : 0;
6446 str = database_get_data(conf_node, "use_registered_mode", RECDB_QSTRING);
6447 chanserv_conf.use_registered_mode = (str && enabled_string(str)) ? 1 : 0;
6451 chanserv_note_type_read(const char *key, struct record_data *rd)
6454 struct note_type *ntype;
6457 if(!(obj = GET_RECORD_OBJECT(rd)))
6459 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6462 if(!(ntype = chanserv_create_note_type(key)))
6464 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6468 /* Figure out set access */
6469 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6471 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6472 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6474 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6476 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6477 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6479 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6481 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6485 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6486 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6487 ntype->set_access.min_opserv = 0;
6490 /* Figure out visibility */
6491 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6492 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6493 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6494 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6495 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6496 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6497 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6498 ntype->visible_type = NOTE_VIS_ALL;
6500 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6502 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6503 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6507 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6509 struct handle_info *handle;
6510 struct userData *uData;
6511 char *seen, *inf, *flags;
6513 unsigned short access;
6515 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6517 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6521 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6522 if(access > UL_OWNER)
6524 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6528 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6529 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6530 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6531 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6532 handle = get_handle_info(key);
6535 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6539 uData = add_channel_user(chan, handle, access, last_seen, inf);
6540 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6544 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6546 struct banData *bData;
6547 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6548 time_t set_time, triggered_time, expires_time;
6550 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6552 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6556 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6557 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6558 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6559 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6560 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6561 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6563 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6564 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6566 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6568 expires_time = set_time + atoi(s_duration);
6572 if(expires_time && (expires_time < now))
6575 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6578 static struct suspended *
6579 chanserv_read_suspended(dict_t obj)
6581 struct suspended *suspended = calloc(1, sizeof(*suspended));
6585 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6586 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6587 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6588 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6589 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6590 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6591 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6592 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6593 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6594 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6599 chanserv_channel_read(const char *key, struct record_data *hir)
6601 struct suspended *suspended;
6602 struct mod_chanmode *modes;
6603 struct chanNode *cNode;
6604 struct chanData *cData;
6605 struct dict *channel, *obj;
6606 char *str, *argv[10];
6610 channel = hir->d.object;
6612 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6615 cNode = AddChannel(key, now, NULL, NULL);
6618 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6621 cData = register_channel(cNode, str);
6624 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6628 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6630 enum levelOption lvlOpt;
6631 enum charOption chOpt;
6633 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6634 cData->flags = atoi(str);
6636 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6638 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6640 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6641 else if(levelOptions[lvlOpt].old_flag)
6643 if(cData->flags & levelOptions[lvlOpt].old_flag)
6644 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6646 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6650 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6652 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6654 cData->chOpts[chOpt] = str[0];
6657 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6659 enum levelOption lvlOpt;
6660 enum charOption chOpt;
6663 cData->flags = base64toint(str, 5);
6664 count = strlen(str += 5);
6665 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6668 if(levelOptions[lvlOpt].old_flag)
6670 if(cData->flags & levelOptions[lvlOpt].old_flag)
6671 lvl = levelOptions[lvlOpt].flag_value;
6673 lvl = levelOptions[lvlOpt].default_value;
6675 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6677 case 'c': lvl = UL_COOWNER; break;
6678 case 'm': lvl = UL_MASTER; break;
6679 case 'n': lvl = UL_OWNER+1; break;
6680 case 'o': lvl = UL_OP; break;
6681 case 'p': lvl = UL_PEON; break;
6682 case 'w': lvl = UL_OWNER; break;
6683 default: lvl = 0; break;
6685 cData->lvlOpts[lvlOpt] = lvl;
6687 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6688 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6691 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6693 suspended = chanserv_read_suspended(obj);
6694 cData->suspended = suspended;
6695 suspended->cData = cData;
6696 /* We could use suspended->expires and suspended->revoked to
6697 * set the CHANNEL_SUSPENDED flag, but we don't. */
6699 else if(IsSuspended(cData))
6701 suspended = calloc(1, sizeof(*suspended));
6702 suspended->issued = 0;
6703 suspended->revoked = 0;
6704 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6705 suspended->expires = str ? atoi(str) : 0;
6706 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6707 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6708 suspended->reason = strdup(str ? str : "No reason");
6709 suspended->previous = NULL;
6710 cData->suspended = suspended;
6711 suspended->cData = cData;
6714 suspended = NULL; /* to squelch a warning */
6716 if(IsSuspended(cData)) {
6717 if(suspended->expires > now)
6718 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6719 else if(suspended->expires)
6720 cData->flags &= ~CHANNEL_SUSPENDED;
6723 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6724 struct mod_chanmode change;
6725 mod_chanmode_init(&change);
6727 change.args[0].mode = MODE_CHANOP;
6728 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6729 mod_chanmode_announce(chanserv, cNode, &change);
6732 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6733 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6734 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6735 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6736 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6737 cData->max = str ? atoi(str) : 0;
6738 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6739 cData->greeting = str ? strdup(str) : NULL;
6740 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6741 cData->user_greeting = str ? strdup(str) : NULL;
6742 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6743 cData->topic_mask = str ? strdup(str) : NULL;
6744 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6745 cData->topic = str ? strdup(str) : NULL;
6747 if(!IsSuspended(cData)
6748 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6749 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6750 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6751 cData->modes = *modes;
6752 if(chanserv_conf.use_registered_mode)
6753 cData->modes.modes_set |= MODE_REGISTERED;
6754 if(cData->modes.argc > 1)
6755 cData->modes.argc = 1;
6756 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6757 mod_chanmode_free(modes);
6760 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6761 for(it = dict_first(obj); it; it = iter_next(it))
6762 user_read_helper(iter_key(it), iter_data(it), cData);
6764 if(!cData->users && !IsProtected(cData))
6766 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6767 unregister_channel(cData, "has empty user list.");
6771 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6772 for(it = dict_first(obj); it; it = iter_next(it))
6773 ban_read_helper(iter_key(it), iter_data(it), cData);
6775 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6776 for(it = dict_first(obj); it; it = iter_next(it))
6778 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6779 struct record_data *rd = iter_data(it);
6780 const char *note, *setter;
6782 if(rd->type != RECDB_OBJECT)
6784 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6788 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6790 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6792 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6796 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6797 if(!setter) setter = "<unknown>";
6798 chanserv_add_channel_note(cData, ntype, setter, note);
6806 chanserv_dnr_read(const char *key, struct record_data *hir)
6808 const char *setter, *reason, *str;
6809 struct do_not_register *dnr;
6811 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6814 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6817 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6820 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6823 dnr = chanserv_add_dnr(key, setter, reason);
6826 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6828 dnr->set = atoi(str);
6834 chanserv_saxdb_read(struct dict *database)
6836 struct dict *section;
6839 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6840 for(it = dict_first(section); it; it = iter_next(it))
6841 chanserv_note_type_read(iter_key(it), iter_data(it));
6843 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6844 for(it = dict_first(section); it; it = iter_next(it))
6845 chanserv_channel_read(iter_key(it), iter_data(it));
6847 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6848 for(it = dict_first(section); it; it = iter_next(it))
6849 chanserv_dnr_read(iter_key(it), iter_data(it));
6855 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6857 int high_present = 0;
6858 saxdb_start_record(ctx, KEY_USERS, 1);
6859 for(; uData; uData = uData->next)
6861 if((uData->access >= UL_PRESENT) && uData->present)
6863 saxdb_start_record(ctx, uData->handle->handle, 0);
6864 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6865 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6867 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6869 saxdb_write_string(ctx, KEY_INFO, uData->info);
6870 saxdb_end_record(ctx);
6872 saxdb_end_record(ctx);
6873 return high_present;
6877 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6881 saxdb_start_record(ctx, KEY_BANS, 1);
6882 for(; bData; bData = bData->next)
6884 saxdb_start_record(ctx, bData->mask, 0);
6885 saxdb_write_int(ctx, KEY_SET, bData->set);
6886 if(bData->triggered)
6887 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6889 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6891 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6893 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6894 saxdb_end_record(ctx);
6896 saxdb_end_record(ctx);
6900 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6902 saxdb_start_record(ctx, name, 0);
6903 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6904 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6906 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6908 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6910 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6912 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6913 saxdb_end_record(ctx);
6917 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6921 enum levelOption lvlOpt;
6922 enum charOption chOpt;
6924 saxdb_start_record(ctx, channel->channel->name, 1);
6926 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6927 saxdb_write_int(ctx, KEY_MAX, channel->max);
6929 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6930 if(channel->registrar)
6931 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6932 if(channel->greeting)
6933 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6934 if(channel->user_greeting)
6935 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6936 if(channel->topic_mask)
6937 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6938 if(channel->suspended)
6939 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6941 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6942 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6943 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6944 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6945 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6947 buf[0] = channel->chOpts[chOpt];
6949 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6951 saxdb_end_record(ctx);
6953 if(channel->modes.modes_set || channel->modes.modes_clear)
6955 mod_chanmode_format(&channel->modes, buf);
6956 saxdb_write_string(ctx, KEY_MODES, buf);
6959 high_present = chanserv_write_users(ctx, channel->users);
6960 chanserv_write_bans(ctx, channel->bans);
6962 if(dict_size(channel->notes))
6966 saxdb_start_record(ctx, KEY_NOTES, 1);
6967 for(it = dict_first(channel->notes); it; it = iter_next(it))
6969 struct note *note = iter_data(it);
6970 saxdb_start_record(ctx, iter_key(it), 0);
6971 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6972 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6973 saxdb_end_record(ctx);
6975 saxdb_end_record(ctx);
6978 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6979 saxdb_end_record(ctx);
6983 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6987 saxdb_start_record(ctx, ntype->name, 0);
6988 switch(ntype->set_access_type)
6990 case NOTE_SET_CHANNEL_ACCESS:
6991 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6993 case NOTE_SET_CHANNEL_SETTER:
6994 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6996 case NOTE_SET_PRIVILEGED: default:
6997 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7000 switch(ntype->visible_type)
7002 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7003 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7004 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7006 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7007 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7008 saxdb_end_record(ctx);
7012 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7014 struct do_not_register *dnr;
7017 for(it = dict_first(dnrs); it; it = iter_next(it))
7019 dnr = iter_data(it);
7020 saxdb_start_record(ctx, dnr->chan_name, 0);
7022 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7023 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7024 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7025 saxdb_end_record(ctx);
7030 chanserv_saxdb_write(struct saxdb_context *ctx)
7033 struct chanData *channel;
7036 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7037 for(it = dict_first(note_types); it; it = iter_next(it))
7038 chanserv_write_note_type(ctx, iter_data(it));
7039 saxdb_end_record(ctx);
7042 saxdb_start_record(ctx, KEY_DNR, 1);
7043 write_dnrs_helper(ctx, handle_dnrs);
7044 write_dnrs_helper(ctx, plain_dnrs);
7045 write_dnrs_helper(ctx, mask_dnrs);
7046 saxdb_end_record(ctx);
7049 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7050 for(channel = channelList; channel; channel = channel->next)
7051 chanserv_write_channel(ctx, channel);
7052 saxdb_end_record(ctx);
7058 chanserv_db_cleanup(void) {
7060 unreg_part_func(handle_part);
7062 unregister_channel(channelList, "terminating.");
7063 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7064 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7065 free(chanserv_conf.support_channels.list);
7066 dict_delete(handle_dnrs);
7067 dict_delete(plain_dnrs);
7068 dict_delete(mask_dnrs);
7069 dict_delete(note_types);
7070 free_string_list(chanserv_conf.eightball);
7071 free_string_list(chanserv_conf.old_ban_names);
7072 free_string_list(chanserv_conf.set_shows);
7073 free(set_shows_list.list);
7074 free(uset_shows_list.list);
7077 struct userData *helper = helperList;
7078 helperList = helperList->next;
7083 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7084 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7085 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7088 init_chanserv(const char *nick)
7090 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7091 conf_register_reload(chanserv_conf_read);
7093 reg_server_link_func(handle_server_link);
7095 reg_new_channel_func(handle_new_channel);
7096 reg_join_func(handle_join);
7097 reg_part_func(handle_part);
7098 reg_kick_func(handle_kick);
7099 reg_topic_func(handle_topic);
7100 reg_mode_change_func(handle_mode);
7101 reg_nick_change_func(handle_nick_change);
7103 reg_auth_func(handle_auth);
7104 reg_handle_rename_func(handle_rename);
7105 reg_unreg_func(handle_unreg);
7107 handle_dnrs = dict_new();
7108 dict_set_free_data(handle_dnrs, free);
7109 plain_dnrs = dict_new();
7110 dict_set_free_data(plain_dnrs, free);
7111 mask_dnrs = dict_new();
7112 dict_set_free_data(mask_dnrs, free);
7114 reg_svccmd_unbind_func(handle_svccmd_unbind);
7115 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7116 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7117 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7118 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7119 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7120 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7121 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7122 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7123 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7125 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7126 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7128 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7129 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7130 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7131 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7132 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7134 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7135 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7136 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7137 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7138 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7140 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7141 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7142 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7143 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7145 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7146 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7147 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7148 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7149 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7150 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7151 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7152 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7154 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7155 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7156 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7157 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7158 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7159 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7160 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7161 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7162 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7163 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7164 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7165 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7166 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7167 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7169 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7170 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7171 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7172 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7173 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7175 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7176 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7178 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7179 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7180 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7181 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7182 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7183 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7184 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7185 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7186 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7187 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7188 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7190 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7191 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7193 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7194 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7195 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7196 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7198 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7199 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7200 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7201 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7202 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7204 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7205 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7206 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7207 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7208 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7209 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7211 /* Channel options */
7212 DEFINE_CHANNEL_OPTION(defaulttopic);
7213 DEFINE_CHANNEL_OPTION(topicmask);
7214 DEFINE_CHANNEL_OPTION(greeting);
7215 DEFINE_CHANNEL_OPTION(usergreeting);
7216 DEFINE_CHANNEL_OPTION(modes);
7217 DEFINE_CHANNEL_OPTION(enfops);
7218 DEFINE_CHANNEL_OPTION(giveops);
7219 DEFINE_CHANNEL_OPTION(protect);
7220 DEFINE_CHANNEL_OPTION(enfmodes);
7221 DEFINE_CHANNEL_OPTION(enftopic);
7222 DEFINE_CHANNEL_OPTION(pubcmd);
7223 DEFINE_CHANNEL_OPTION(givevoice);
7224 DEFINE_CHANNEL_OPTION(userinfo);
7225 DEFINE_CHANNEL_OPTION(dynlimit);
7226 DEFINE_CHANNEL_OPTION(topicsnarf);
7227 DEFINE_CHANNEL_OPTION(nodelete);
7228 DEFINE_CHANNEL_OPTION(toys);
7229 DEFINE_CHANNEL_OPTION(setters);
7230 DEFINE_CHANNEL_OPTION(topicrefresh);
7231 DEFINE_CHANNEL_OPTION(ctcpusers);
7232 DEFINE_CHANNEL_OPTION(ctcpreaction);
7233 DEFINE_CHANNEL_OPTION(inviteme);
7235 DEFINE_CHANNEL_OPTION(offchannel);
7236 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7238 /* Alias set topic to set defaulttopic for compatibility. */
7239 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7242 DEFINE_USER_OPTION(noautoop);
7243 DEFINE_USER_OPTION(autoinvite);
7244 DEFINE_USER_OPTION(info);
7246 /* Alias uset autovoice to uset autoop. */
7247 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7249 note_types = dict_new();
7250 dict_set_free_data(note_types, chanserv_deref_note_type);
7253 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7254 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7255 service_register(chanserv)->trigger = '!';
7256 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7258 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7260 if(chanserv_conf.channel_expire_frequency)
7261 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7263 if(chanserv_conf.refresh_period)
7265 time_t next_refresh;
7266 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7267 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7270 reg_exit_func(chanserv_db_cleanup);
7271 message_register_table(msgtab);