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"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
104 #define KEY_LEVEL "level"
105 #define KEY_INFO "info"
106 #define KEY_SEEN "seen"
109 #define KEY_OWNER "owner"
110 #define KEY_REASON "reason"
111 #define KEY_SET "set"
112 #define KEY_DURATION "duration"
113 #define KEY_EXPIRES "expires"
114 #define KEY_TRIGGERED "triggered"
116 #define CHANNEL_DEFAULT_FLAGS (0)
117 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
119 /* Administrative messages */
120 static const struct message_entry msgtab[] = {
121 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
123 /* Channel registration */
124 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
125 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
126 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
127 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
128 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
129 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
131 /* Do-not-register channels */
132 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
133 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
134 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
135 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
136 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
137 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
138 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
139 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
140 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
141 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
142 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
144 /* Channel unregistration */
145 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
146 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
147 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
148 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
151 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
152 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
154 /* Channel merging */
155 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
156 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
157 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
158 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
159 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
161 /* Handle unregistration */
162 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
165 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
166 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
167 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
168 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
169 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
170 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
171 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
172 { "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." },
173 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
174 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
175 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
176 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
177 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
179 /* Removing yourself from a channel. */
180 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
181 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
182 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
184 /* User management */
185 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
186 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
187 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
188 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
189 { "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." },
190 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
191 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
192 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
194 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
195 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
196 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
197 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
198 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
201 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
202 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
203 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
204 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
205 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
206 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
207 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
208 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
209 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
210 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
211 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
212 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
213 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
214 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
215 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
216 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
218 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
220 /* Channel management */
221 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
222 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
223 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
225 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
226 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
227 { "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" },
228 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
229 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
230 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
231 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
233 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
234 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
235 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
236 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
237 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
238 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
239 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you muse use 'set defaults %s'." },
240 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
241 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
242 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
243 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
244 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
245 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
246 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
247 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
248 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
249 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
250 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
251 { "CSMSG_SET_MODES", "$bModes $b %s" },
252 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
253 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
254 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
255 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
256 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
257 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
258 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
259 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
260 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
261 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
262 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
263 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
264 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
265 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
266 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
267 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
268 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
269 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
270 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
271 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
272 { "CSMSG_USET_INFO", "$bInfo $b %s" },
274 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
275 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
276 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
277 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
278 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
279 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
280 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
281 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
282 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
283 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
284 { "CSMSG_PROTECT_NONE", "No users will be protected." },
285 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
286 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
287 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
288 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
289 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
290 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
291 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
292 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
293 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
294 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
295 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
298 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
299 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s%s%s" },
300 { "CSMSG_ALREADY_PRESENT", "%s is $balready in %s$b." },
301 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
302 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
304 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
305 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
306 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
308 /* Channel userlist */
309 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
310 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
311 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
312 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
314 /* Channel note list */
315 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
316 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
317 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
318 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
319 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
320 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
321 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
322 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
323 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
324 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
325 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
326 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
327 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
328 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
329 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
331 /* Channel [un]suspension */
332 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
333 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
334 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
335 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
336 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
337 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
338 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
340 /* Access information */
341 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
342 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
343 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
344 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
345 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
346 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
347 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
348 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
349 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
350 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
351 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
353 /* Seen information */
354 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
355 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
356 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
357 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
359 /* Names information */
360 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
361 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
363 /* Channel information */
364 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
365 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
366 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
367 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
368 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
369 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
370 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
371 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
372 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
373 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
374 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
375 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
376 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
378 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
379 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
380 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
383 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
384 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
386 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
387 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
388 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
389 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
390 { "CSMSG_PEEK_OPS", "$bOps:$b" },
391 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
393 /* Network information */
394 { "CSMSG_NETWORK_INFO", "Network Information:" },
395 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
396 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
397 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
398 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
399 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
400 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
401 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
404 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
405 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
406 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
408 /* Channel searches */
409 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
410 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
411 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
412 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
414 /* Channel configuration */
415 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
416 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
417 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
420 { "CSMSG_USER_OPTIONS", "User Options:" },
421 { "CSMSG_USER_PROTECTED", "That user is protected." },
424 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
425 { "CSMSG_PING_RESPONSE", "Pong!" },
426 { "CSMSG_WUT_RESPONSE", "wut" },
427 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
428 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
429 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
430 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
431 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
432 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
433 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
436 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
440 /* eject_user and unban_user flags */
441 #define ACTION_KICK 0x0001
442 #define ACTION_BAN 0x0002
443 #define ACTION_ADD_BAN 0x0004
444 #define ACTION_ADD_TIMED_BAN 0x0008
445 #define ACTION_UNBAN 0x0010
446 #define ACTION_DEL_BAN 0x0020
448 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
449 #define MODELEN 40 + KEYLEN
453 #define CSFUNC_ARGS user, channel, argc, argv, cmd
455 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
456 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
457 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
458 reply("MSG_MISSING_PARAMS", argv[0]); \
462 DECLARE_LIST(dnrList, struct do_not_register *);
463 DEFINE_LIST(dnrList, struct do_not_register *);
465 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
467 struct userNode *chanserv;
469 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
470 static struct log_type *CS_LOG;
474 struct channelList support_channels;
475 struct mod_chanmode default_modes;
477 unsigned long db_backup_frequency;
478 unsigned long channel_expire_frequency;
481 unsigned int adjust_delay;
482 long channel_expire_delay;
483 unsigned int nodelete_level;
485 unsigned int adjust_threshold;
486 int join_flood_threshold;
488 unsigned int greeting_length;
489 unsigned int refresh_period;
491 unsigned int max_owned;
492 unsigned int max_chan_users;
493 unsigned int max_chan_bans;
495 struct string_list *set_shows;
496 struct string_list *eightball;
497 struct string_list *old_ban_names;
499 const char *ctcp_short_ban_duration;
500 const char *ctcp_long_ban_duration;
502 const char *irc_operator_epithet;
503 const char *network_helper_epithet;
504 const char *support_helper_epithet;
509 struct userNode *user;
510 struct userNode *bot;
511 struct chanNode *channel;
513 unsigned short lowest;
514 unsigned short highest;
515 struct userData **users;
516 struct helpfile_table table;
519 enum note_access_type
521 NOTE_SET_CHANNEL_ACCESS,
522 NOTE_SET_CHANNEL_SETTER,
526 enum note_visible_type
529 NOTE_VIS_CHANNEL_USERS,
535 enum note_access_type set_access_type;
537 unsigned int min_opserv;
538 unsigned short min_ulevel;
540 enum note_visible_type visible_type;
541 unsigned int max_length;
548 struct note_type *type;
549 char setter[NICKSERV_HANDLE_LEN+1];
553 static unsigned int registered_channels;
554 static unsigned int banCount;
556 static const struct {
559 unsigned short level;
562 { "peon", "Peon", UL_PEON, '+' },
563 { "op", "Op", UL_OP, '@' },
564 { "master", "Master", UL_MASTER, '%' },
565 { "coowner", "Coowner", UL_COOWNER, '*' },
566 { "owner", "Owner", UL_OWNER, '!' },
567 { "helper", "BUG:", UL_HELPER, 'X' }
570 static const struct {
573 unsigned short default_value;
574 unsigned int old_idx;
575 unsigned int old_flag;
576 unsigned short flag_value;
578 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
579 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
580 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
581 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
582 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
583 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
584 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
585 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
586 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
587 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
588 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
591 struct charOptionValues {
594 } protectValues[] = {
595 { 'a', "CSMSG_PROTECT_ALL" },
596 { 'e', "CSMSG_PROTECT_EQUAL" },
597 { 'l', "CSMSG_PROTECT_LOWER" },
598 { 'n', "CSMSG_PROTECT_NONE" }
600 { 'd', "CSMSG_TOYS_DISABLED" },
601 { 'n', "CSMSG_TOYS_PRIVATE" },
602 { 'p', "CSMSG_TOYS_PUBLIC" }
603 }, topicRefreshValues[] = {
604 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
605 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
606 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
607 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
608 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
609 }, ctcpReactionValues[] = {
610 { 'k', "CSMSG_CTCPREACTION_KICK" },
611 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
612 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
613 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
616 static const struct {
620 unsigned int old_idx;
622 struct charOptionValues *values;
624 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
625 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
626 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
627 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
630 struct userData *helperList;
631 struct chanData *channelList;
632 static struct module *chanserv_module;
633 static unsigned int userCount;
635 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
636 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
637 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
640 user_level_from_name(const char *name, unsigned short clamp_level)
642 unsigned int level = 0, ii;
645 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
646 if(!irccasecmp(name, accessLevels[ii].name))
647 level = accessLevels[ii].level;
648 if(level > clamp_level)
654 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
657 *minl = strtoul(arg, &sep, 10);
665 *maxl = strtoul(sep+1, &sep, 10);
673 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
675 struct userData *uData, **head;
677 if(!channel || !handle)
680 if(override && HANDLE_FLAGGED(handle, HELPING)
681 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
683 for(uData = helperList;
684 uData && uData->handle != handle;
685 uData = uData->next);
689 uData = calloc(1, sizeof(struct userData));
690 uData->handle = handle;
692 uData->access = UL_HELPER;
698 uData->next = helperList;
700 helperList->prev = uData;
708 for(uData = channel->users; uData; uData = uData->next)
709 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
712 head = &(channel->users);
715 if(uData && (uData != *head))
717 /* Shuffle the user to the head of whatever list he was in. */
719 uData->next->prev = uData->prev;
721 uData->prev->next = uData->next;
727 (**head).prev = uData;
734 /* Returns non-zero if user has at least the minimum access.
735 * exempt_owner is set when handling !set, so the owner can set things
738 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
740 struct userData *uData;
741 struct chanData *cData = channel->channel_info;
742 unsigned short minimum = cData->lvlOpts[opt];
745 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
748 if(minimum <= uData->access)
750 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
755 /* Scan for other users authenticated to the same handle
756 still in the channel. If so, keep them listed as present.
758 user is optional, if not null, it skips checking that userNode
759 (for the handle_part function) */
761 scan_user_presence(struct userData *uData, struct userNode *user)
765 if(IsSuspended(uData->channel)
766 || IsUserSuspended(uData)
767 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
779 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
781 unsigned int eflags, argc;
783 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
785 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
786 if(!channel->channel_info
787 || IsSuspended(channel->channel_info)
789 || !ircncasecmp(text, "ACTION ", 7))
791 /* Figure out the minimum level needed to CTCP the channel */
792 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
794 /* We need to enforce against them; do so. */
797 argv[1] = user->nick;
799 if(GetUserMode(channel, user))
800 eflags |= ACTION_KICK;
801 switch(channel->channel_info->chOpts[chCTCPReaction]) {
802 default: case 'k': /* just do the kick */ break;
804 eflags |= ACTION_BAN;
807 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
808 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
811 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
812 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
815 argv[argc++] = bad_ctcp_reason;
816 eject_user(chanserv, channel, argc, argv, NULL, eflags);
820 chanserv_create_note_type(const char *name)
822 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
823 strcpy(ntype->name, name);
825 dict_insert(note_types, ntype->name, ntype);
830 chanserv_deref_note_type(void *data)
832 struct note_type *ntype = data;
834 if(--ntype->refs > 0)
840 chanserv_flush_note_type(struct note_type *ntype)
842 struct chanData *cData;
843 for(cData = channelList; cData; cData = cData->next)
844 dict_remove(cData->notes, ntype->name);
848 chanserv_truncate_notes(struct note_type *ntype)
850 struct chanData *cData;
852 unsigned int size = sizeof(*note) + ntype->max_length;
854 for(cData = channelList; cData; cData = cData->next) {
855 note = dict_find(cData->notes, ntype->name, NULL);
858 if(strlen(note->note) <= ntype->max_length)
860 dict_remove2(cData->notes, ntype->name, 1);
861 note = realloc(note, size);
862 note->note[ntype->max_length] = 0;
863 dict_insert(cData->notes, ntype->name, note);
867 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
870 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
873 unsigned int len = strlen(text);
875 if(len > type->max_length) len = type->max_length;
876 note = calloc(1, sizeof(*note) + len);
878 strncpy(note->setter, setter, sizeof(note->setter)-1);
879 memcpy(note->note, text, len);
881 dict_insert(channel->notes, type->name, note);
887 chanserv_free_note(void *data)
889 struct note *note = data;
891 chanserv_deref_note_type(note->type);
892 assert(note->type->refs > 0); /* must use delnote to remove the type */
896 static MODCMD_FUNC(cmd_createnote) {
897 struct note_type *ntype;
898 unsigned int arg = 1, existed = 0, max_length;
900 if((ntype = dict_find(note_types, argv[1], NULL)))
903 ntype = chanserv_create_note_type(argv[arg]);
904 if(!irccasecmp(argv[++arg], "privileged"))
907 ntype->set_access_type = NOTE_SET_PRIVILEGED;
908 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
910 else if(!irccasecmp(argv[arg], "channel"))
912 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
915 reply("CSMSG_INVALID_ACCESS", argv[arg]);
918 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
919 ntype->set_access.min_ulevel = ulvl;
921 else if(!irccasecmp(argv[arg], "setter"))
923 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
927 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
931 if(!irccasecmp(argv[++arg], "privileged"))
932 ntype->visible_type = NOTE_VIS_PRIVILEGED;
933 else if(!irccasecmp(argv[arg], "channel_users"))
934 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
935 else if(!irccasecmp(argv[arg], "all"))
936 ntype->visible_type = NOTE_VIS_ALL;
938 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
942 if((arg+1) >= argc) {
943 reply("MSG_MISSING_PARAMS", argv[0]);
946 max_length = strtoul(argv[++arg], NULL, 0);
947 if(max_length < 20 || max_length > 450)
949 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
952 if(existed && (max_length < ntype->max_length))
954 ntype->max_length = max_length;
955 chanserv_truncate_notes(ntype);
957 ntype->max_length = max_length;
960 reply("CSMSG_NOTE_MODIFIED", ntype->name);
962 reply("CSMSG_NOTE_CREATED", ntype->name);
967 dict_remove(note_types, ntype->name);
971 static MODCMD_FUNC(cmd_removenote) {
972 struct note_type *ntype;
975 ntype = dict_find(note_types, argv[1], NULL);
976 force = (argc > 2) && !irccasecmp(argv[2], "force");
979 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
986 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
989 chanserv_flush_note_type(ntype);
991 dict_remove(note_types, argv[1]);
992 reply("CSMSG_NOTE_DELETED", argv[1]);
997 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1001 if(orig->modes_set & change->modes_clear)
1003 if(orig->modes_clear & change->modes_set)
1005 if((orig->modes_set & MODE_KEY)
1006 && strcmp(orig->new_key, change->new_key))
1008 if((orig->modes_set & MODE_LIMIT)
1009 && (orig->new_limit != change->new_limit))
1014 static char max_length_text[MAXLEN+1][16];
1016 static struct helpfile_expansion
1017 chanserv_expand_variable(const char *variable)
1019 struct helpfile_expansion exp;
1021 if(!irccasecmp(variable, "notes"))
1024 exp.type = HF_TABLE;
1025 exp.value.table.length = 1;
1026 exp.value.table.width = 3;
1027 exp.value.table.flags = 0;
1028 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1029 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1030 exp.value.table.contents[0][0] = "Note Type";
1031 exp.value.table.contents[0][1] = "Visibility";
1032 exp.value.table.contents[0][2] = "Max Length";
1033 for(it=dict_first(note_types); it; it=iter_next(it))
1035 struct note_type *ntype = iter_data(it);
1038 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1039 row = exp.value.table.length++;
1040 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1041 exp.value.table.contents[row][0] = ntype->name;
1042 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1043 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1045 if(!max_length_text[ntype->max_length][0])
1046 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1047 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1052 exp.type = HF_STRING;
1053 exp.value.str = NULL;
1057 static struct chanData*
1058 register_channel(struct chanNode *cNode, char *registrar)
1060 struct chanData *channel;
1061 enum levelOption lvlOpt;
1062 enum charOption chOpt;
1064 channel = calloc(1, sizeof(struct chanData));
1066 channel->notes = dict_new();
1067 dict_set_free_data(channel->notes, chanserv_free_note);
1069 channel->registrar = strdup(registrar);
1070 channel->registered = now;
1071 channel->visited = now;
1072 channel->limitAdjusted = now;
1073 channel->flags = CHANNEL_DEFAULT_FLAGS;
1074 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1075 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1076 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1077 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1079 channel->prev = NULL;
1080 channel->next = channelList;
1083 channelList->prev = channel;
1084 channelList = channel;
1085 registered_channels++;
1087 channel->channel = cNode;
1089 cNode->channel_info = channel;
1094 static struct userData*
1095 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1097 struct userData *ud;
1099 if(access > UL_OWNER)
1102 ud = calloc(1, sizeof(*ud));
1103 ud->channel = channel;
1104 ud->handle = handle;
1106 ud->access = access;
1107 ud->info = info ? strdup(info) : NULL;
1110 ud->next = channel->users;
1112 channel->users->prev = ud;
1113 channel->users = ud;
1115 channel->userCount++;
1119 ud->u_next = ud->handle->channels;
1121 ud->u_next->u_prev = ud;
1122 ud->handle->channels = ud;
1127 static void unregister_channel(struct chanData *channel, const char *reason);
1130 del_channel_user(struct userData *user, int do_gc)
1132 struct chanData *channel = user->channel;
1134 channel->userCount--;
1138 user->prev->next = user->next;
1140 channel->users = user->next;
1142 user->next->prev = user->prev;
1145 user->u_prev->u_next = user->u_next;
1147 user->handle->channels = user->u_next;
1149 user->u_next->u_prev = user->u_prev;
1153 if(do_gc && !channel->users && !IsProtected(channel))
1154 unregister_channel(channel, "lost all users.");
1157 static void expire_ban(void *data);
1159 static struct banData*
1160 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1163 unsigned int ii, l1, l2;
1168 bd = malloc(sizeof(struct banData));
1170 bd->channel = channel;
1172 bd->triggered = triggered;
1173 bd->expires = expires;
1175 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1177 extern const char *hidden_host_suffix;
1178 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1182 l2 = strlen(old_name);
1185 if(irccasecmp(mask + l1 - l2, old_name))
1187 new_mask = alloca(MAXLEN);
1188 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1191 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1193 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1194 bd->reason = reason ? strdup(reason) : NULL;
1197 timeq_add(expires, expire_ban, bd);
1200 bd->next = channel->bans;
1202 channel->bans->prev = bd;
1204 channel->banCount++;
1211 del_channel_ban(struct banData *ban)
1213 ban->channel->banCount--;
1217 ban->prev->next = ban->next;
1219 ban->channel->bans = ban->next;
1222 ban->next->prev = ban->prev;
1225 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1234 expire_ban(void *data)
1236 struct banData *bd = data;
1237 if(!IsSuspended(bd->channel))
1239 struct banList bans;
1240 struct mod_chanmode change;
1242 bans = bd->channel->channel->banlist;
1243 mod_chanmode_init(&change);
1244 for(ii=0; ii<bans.used; ii++)
1246 if(!strcmp(bans.list[ii]->ban, bd->mask))
1249 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1250 change.args[0].hostmask = bd->mask;
1251 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1257 del_channel_ban(bd);
1260 static void chanserv_expire_suspension(void *data);
1263 unregister_channel(struct chanData *channel, const char *reason)
1265 char msgbuf[MAXLEN];
1267 /* After channel unregistration, the following must be cleaned
1269 - Channel information.
1272 - Channel suspension data.
1273 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1279 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1281 while(channel->users)
1282 del_channel_user(channel->users, 0);
1284 while(channel->bans)
1285 del_channel_ban(channel->bans);
1287 if(channel->topic) free(channel->topic);
1288 if(channel->registrar) free(channel->registrar);
1289 if(channel->greeting) free(channel->greeting);
1290 if(channel->user_greeting) free(channel->user_greeting);
1291 if(channel->topic_mask) free(channel->topic_mask);
1293 if(channel->prev) channel->prev->next = channel->next;
1294 else channelList = channel->next;
1296 if(channel->next) channel->next->prev = channel->prev;
1298 if(channel->suspended)
1300 struct chanNode *cNode = channel->channel;
1301 struct suspended *suspended, *next_suspended;
1303 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1305 next_suspended = suspended->previous;
1306 free(suspended->suspender);
1307 free(suspended->reason);
1308 if(suspended->expires)
1309 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1314 cNode->channel_info = NULL;
1316 channel->channel->channel_info = NULL;
1319 dict_delete(channel->notes);
1320 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1321 if(!IsSuspended(channel))
1322 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1323 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1324 UnlockChannel(channel->channel);
1326 registered_channels--;
1330 expire_channels(UNUSED_ARG(void *data))
1332 struct chanData *channel, *next;
1333 struct userData *user;
1334 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1336 intervalString(delay, chanserv_conf.channel_expire_delay);
1337 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1339 for(channel = channelList; channel; channel = next)
1341 next = channel->next;
1343 /* See if the channel can be expired. */
1344 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1345 || IsProtected(channel))
1348 /* Make sure there are no high-ranking users still in the channel. */
1349 for(user=channel->users; user; user=user->next)
1350 if(user->present && (user->access >= UL_PRESENT))
1355 /* Unregister the channel */
1356 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1357 unregister_channel(channel, "registration expired.");
1360 if(chanserv_conf.channel_expire_frequency)
1361 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1365 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1367 char protect = channel->chOpts[chProtect];
1368 struct userData *cs_victim, *cs_aggressor;
1370 /* Don't protect if no one is to be protected, someone is attacking
1371 himself, or if the aggressor is an IRC Operator. */
1372 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1375 /* Don't protect if the victim isn't authenticated (because they
1376 can't be a channel user), unless we are to protect non-users
1378 cs_victim = GetChannelAccess(channel, victim->handle_info);
1379 if(protect != 'a' && !cs_victim)
1382 /* Protect if the aggressor isn't a user because at this point,
1383 the aggressor can only be less than or equal to the victim. */
1384 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1388 /* If the aggressor was a user, then the victim can't be helped. */
1395 if(cs_victim->access > cs_aggressor->access)
1400 if(cs_victim->access >= cs_aggressor->access)
1409 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1411 struct chanData *cData = channel->channel_info;
1412 struct userData *cs_victim;
1414 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1415 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1416 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1418 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1426 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1428 if(IsService(victim))
1430 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1434 if(protect_user(victim, user, channel->channel_info))
1436 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1443 static struct do_not_register *
1444 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1446 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1447 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1448 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1449 strcpy(dnr->reason, reason);
1451 if(dnr->chan_name[0] == '*')
1452 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1453 else if(strpbrk(dnr->chan_name, "*?"))
1454 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1456 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1460 static struct dnrList
1461 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1463 struct dnrList list;
1465 struct do_not_register *dnr;
1467 dnrList_init(&list);
1468 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1469 dnrList_append(&list, dnr);
1470 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1471 dnrList_append(&list, dnr);
1473 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1474 if(match_ircglob(chan_name, iter_key(it)))
1475 dnrList_append(&list, iter_data(it));
1480 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1482 struct dnrList list;
1483 struct do_not_register *dnr;
1485 char buf[INTERVALLEN];
1487 list = chanserv_find_dnrs(chan_name, handle);
1488 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1490 dnr = list.list[ii];
1493 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1494 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1497 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1500 reply("CSMSG_MORE_DNRS", list.used - ii);
1505 struct do_not_register *
1506 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1508 struct do_not_register *dnr;
1511 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1515 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1517 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1518 if(match_ircglob(chan_name, iter_key(it)))
1519 return iter_data(it);
1524 static CHANSERV_FUNC(cmd_noregister)
1527 struct do_not_register *dnr;
1528 char buf[INTERVALLEN];
1529 unsigned int matches;
1535 reply("CSMSG_DNR_SEARCH_RESULTS");
1537 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1539 dnr = iter_data(it);
1541 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1543 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1546 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1548 dnr = iter_data(it);
1550 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1552 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1555 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1557 dnr = iter_data(it);
1559 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set), dnr->setter, dnr->reason);
1561 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1566 reply("MSG_MATCH_COUNT", matches);
1568 reply("MSG_NO_MATCHES");
1574 if(!IsChannelName(target) && (*target != '*'))
1576 reply("CSMSG_NOT_DNR", target);
1582 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1583 if((*target == '*') && !get_handle_info(target + 1))
1585 reply("MSG_HANDLE_UNKNOWN", target + 1);
1588 chanserv_add_dnr(target, user->handle_info->handle, reason);
1589 reply("CSMSG_NOREGISTER_CHANNEL", target);
1593 reply("CSMSG_DNR_SEARCH_RESULTS");
1595 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1597 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1599 reply("MSG_NO_MATCHES");
1603 static CHANSERV_FUNC(cmd_allowregister)
1605 const char *chan_name = argv[1];
1607 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1609 dict_remove(handle_dnrs, chan_name+1);
1610 reply("CSMSG_DNR_REMOVED", chan_name);
1612 else if(dict_find(plain_dnrs, chan_name, NULL))
1614 dict_remove(plain_dnrs, chan_name);
1615 reply("CSMSG_DNR_REMOVED", chan_name);
1617 else if(dict_find(mask_dnrs, chan_name, NULL))
1619 dict_remove(mask_dnrs, chan_name);
1620 reply("CSMSG_DNR_REMOVED", chan_name);
1624 reply("CSMSG_NO_SUCH_DNR", chan_name);
1631 chanserv_get_owned_count(struct handle_info *hi)
1633 struct userData *cList;
1636 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1637 if(cList->access == UL_OWNER)
1642 static CHANSERV_FUNC(cmd_register)
1644 struct mod_chanmode *change;
1645 struct handle_info *handle;
1646 struct chanData *cData;
1647 struct modeNode *mn;
1648 char reason[MAXLEN];
1650 unsigned int new_channel, force=0;
1651 struct do_not_register *dnr;
1655 if(channel->channel_info)
1657 reply("CSMSG_ALREADY_REGGED", channel->name);
1661 if(channel->bad_channel)
1663 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1667 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1669 reply("CSMSG_MUST_BE_OPPED", channel->name);
1674 chan_name = channel->name;
1678 if((argc < 2) || !IsChannelName(argv[1]))
1680 reply("MSG_NOT_CHANNEL_NAME");
1684 if(opserv_bad_channel(argv[1]))
1686 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1691 chan_name = argv[1];
1694 if(argc >= (new_channel+2))
1696 if(!IsHelping(user))
1698 reply("CSMSG_PROXY_FORBIDDEN");
1702 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1704 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1705 dnr = chanserv_is_dnr(chan_name, handle);
1709 handle = user->handle_info;
1710 dnr = chanserv_is_dnr(chan_name, handle);
1714 if(!IsHelping(user))
1715 reply("CSMSG_DNR_CHANNEL", chan_name);
1717 chanserv_show_dnrs(user, cmd, chan_name, handle);
1721 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1723 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1728 channel = AddChannel(argv[1], now, NULL, NULL);
1730 cData = register_channel(channel, user->handle_info->handle);
1731 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1732 cData->modes = chanserv_conf.default_modes;
1733 change = mod_chanmode_dup(&cData->modes, 1);
1734 change->args[change->argc].mode = MODE_CHANOP;
1735 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1737 mod_chanmode_announce(chanserv, channel, change);
1738 mod_chanmode_free(change);
1740 /* Initialize the channel's max user record. */
1741 cData->max = channel->members.used;
1743 if(handle != user->handle_info)
1744 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1746 reply("CSMSG_REG_SUCCESS", channel->name);
1748 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1749 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1754 make_confirmation_string(struct userData *uData)
1756 static char strbuf[16];
1761 for(src = uData->handle->handle; *src; )
1762 accum = accum * 31 + toupper(*src++);
1764 for(src = uData->channel->channel->name; *src; )
1765 accum = accum * 31 + toupper(*src++);
1766 sprintf(strbuf, "%08x", accum);
1770 static CHANSERV_FUNC(cmd_unregister)
1773 char reason[MAXLEN];
1774 struct chanData *cData;
1775 struct userData *uData;
1777 cData = channel->channel_info;
1780 reply("CSMSG_NOT_REGISTERED", channel->name);
1784 uData = GetChannelUser(cData, user->handle_info);
1785 if(!uData || (uData->access < UL_OWNER))
1787 reply("CSMSG_NO_ACCESS");
1791 if(IsProtected(cData))
1793 reply("CSMSG_UNREG_NODELETE", channel->name);
1797 if(!IsHelping(user))
1799 const char *confirm_string;
1800 if(IsSuspended(cData))
1802 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1805 confirm_string = make_confirmation_string(uData);
1806 if((argc < 2) || strcmp(argv[1], confirm_string))
1808 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1813 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1814 name = strdup(channel->name);
1815 unregister_channel(cData, reason);
1816 reply("CSMSG_UNREG_SUCCESS", name);
1821 static CHANSERV_FUNC(cmd_move)
1823 struct chanNode *target;
1824 struct modeNode *mn;
1825 struct userData *uData;
1826 char reason[MAXLEN];
1827 struct do_not_register *dnr;
1831 if(IsProtected(channel->channel_info))
1833 reply("CSMSG_MOVE_NODELETE", channel->name);
1837 if(!IsChannelName(argv[1]))
1839 reply("MSG_NOT_CHANNEL_NAME");
1843 if(opserv_bad_channel(argv[1]))
1845 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1849 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1851 for(uData = channel->channel_info->users; uData; uData = uData->next)
1853 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1855 if(!IsHelping(user))
1856 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1858 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1864 if(!(target = GetChannel(argv[1])))
1866 target = AddChannel(argv[1], now, NULL, NULL);
1867 if(!IsSuspended(channel->channel_info))
1868 AddChannelUser(chanserv, target);
1870 else if(target->channel_info)
1872 reply("CSMSG_ALREADY_REGGED", target->name);
1875 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1876 && !IsHelping(user))
1878 reply("CSMSG_MUST_BE_OPPED", target->name);
1881 else if(!IsSuspended(channel->channel_info))
1883 struct mod_chanmode change;
1884 mod_chanmode_init(&change);
1886 change.args[0].mode = MODE_CHANOP;
1887 change.args[0].member = AddChannelUser(chanserv, target);
1888 mod_chanmode_announce(chanserv, target, &change);
1891 /* Move the channel_info to the target channel; it
1892 shouldn't be necessary to clear timeq callbacks
1893 for the old channel. */
1894 target->channel_info = channel->channel_info;
1895 target->channel_info->channel = target;
1896 channel->channel_info = NULL;
1898 reply("CSMSG_MOVE_SUCCESS", target->name);
1900 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1901 if(!IsSuspended(target->channel_info))
1903 char reason2[MAXLEN];
1904 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1905 DelChannelUser(chanserv, channel, reason2, 0);
1907 UnlockChannel(channel);
1908 LockChannel(target);
1909 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1914 merge_users(struct chanData *source, struct chanData *target)
1916 struct userData *suData, *tuData, *next;
1922 /* Insert the source's users into the scratch area. */
1923 for(suData = source->users; suData; suData = suData->next)
1924 dict_insert(merge, suData->handle->handle, suData);
1926 /* Iterate through the target's users, looking for
1927 users common to both channels. The lower access is
1928 removed from either the scratch area or target user
1930 for(tuData = target->users; tuData; tuData = next)
1932 struct userData *choice;
1934 next = tuData->next;
1936 /* If a source user exists with the same handle as a target
1937 channel's user, resolve the conflict by removing one. */
1938 suData = dict_find(merge, tuData->handle->handle, NULL);
1942 /* Pick the data we want to keep. */
1943 /* If the access is the same, use the later seen time. */
1944 if(suData->access == tuData->access)
1945 choice = (suData->seen > tuData->seen) ? suData : tuData;
1946 else /* Otherwise, keep the higher access level. */
1947 choice = (suData->access > tuData->access) ? suData : tuData;
1949 /* Remove the user that wasn't picked. */
1950 if(choice == tuData)
1952 dict_remove(merge, suData->handle->handle);
1953 del_channel_user(suData, 0);
1956 del_channel_user(tuData, 0);
1959 /* Move the remaining users to the target channel. */
1960 for(it = dict_first(merge); it; it = iter_next(it))
1962 suData = iter_data(it);
1964 /* Insert the user into the target channel's linked list. */
1965 suData->prev = NULL;
1966 suData->next = target->users;
1967 suData->channel = target;
1970 target->users->prev = suData;
1971 target->users = suData;
1973 /* Update the user counts for the target channel; the
1974 source counts are left alone. */
1975 target->userCount++;
1978 /* Possible to assert (source->users == NULL) here. */
1979 source->users = NULL;
1984 merge_bans(struct chanData *source, struct chanData *target)
1986 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1988 /* Hold on to the original head of the target ban list
1989 to avoid comparing source bans with source bans. */
1990 tFront = target->bans;
1992 /* Perform a totally expensive O(n*m) merge, ick. */
1993 for(sbData = source->bans; sbData; sbData = sNext)
1995 /* Flag to track whether the ban's been moved
1996 to the destination yet. */
1999 /* Possible to assert (sbData->prev == NULL) here. */
2000 sNext = sbData->next;
2002 for(tbData = tFront; tbData; tbData = tNext)
2004 tNext = tbData->next;
2006 /* Perform two comparisons between each source
2007 and target ban, conflicts are resolved by
2008 keeping the broader ban and copying the later
2009 expiration and triggered time. */
2010 if(match_ircglobs(tbData->mask, sbData->mask))
2012 /* There is a broader ban in the target channel that
2013 overrides one in the source channel; remove the
2014 source ban and break. */
2015 if(sbData->expires > tbData->expires)
2016 tbData->expires = sbData->expires;
2017 if(sbData->triggered > tbData->triggered)
2018 tbData->triggered = sbData->triggered;
2019 del_channel_ban(sbData);
2022 else if(match_ircglobs(sbData->mask, tbData->mask))
2024 /* There is a broader ban in the source channel that
2025 overrides one in the target channel; remove the
2026 target ban, fall through and move the source over. */
2027 if(tbData->expires > sbData->expires)
2028 sbData->expires = tbData->expires;
2029 if(tbData->triggered > sbData->triggered)
2030 sbData->triggered = tbData->triggered;
2031 if(tbData == tFront)
2033 del_channel_ban(tbData);
2036 /* Source bans can override multiple target bans, so
2037 we allow a source to run through this loop multiple
2038 times, but we can only move it once. */
2043 /* Remove the source ban from the source ban list. */
2045 sbData->next->prev = sbData->prev;
2047 /* Modify the source ban's associated channel. */
2048 sbData->channel = target;
2050 /* Insert the ban into the target channel's linked list. */
2051 sbData->prev = NULL;
2052 sbData->next = target->bans;
2055 target->bans->prev = sbData;
2056 target->bans = sbData;
2058 /* Update the user counts for the target channel. */
2063 /* Possible to assert (source->bans == NULL) here. */
2064 source->bans = NULL;
2068 merge_data(struct chanData *source, struct chanData *target)
2070 if(source->visited > target->visited)
2071 target->visited = source->visited;
2075 merge_channel(struct chanData *source, struct chanData *target)
2077 merge_users(source, target);
2078 merge_bans(source, target);
2079 merge_data(source, target);
2082 static CHANSERV_FUNC(cmd_merge)
2084 struct userData *target_user;
2085 struct chanNode *target;
2086 char reason[MAXLEN];
2090 /* Make sure the target channel exists and is registered to the user
2091 performing the command. */
2092 if(!(target = GetChannel(argv[1])))
2094 reply("MSG_INVALID_CHANNEL");
2098 if(!target->channel_info)
2100 reply("CSMSG_NOT_REGISTERED", target->name);
2104 if(IsProtected(channel->channel_info))
2106 reply("CSMSG_MERGE_NODELETE");
2110 if(IsSuspended(target->channel_info))
2112 reply("CSMSG_MERGE_SUSPENDED");
2116 if(channel == target)
2118 reply("CSMSG_MERGE_SELF");
2122 target_user = GetChannelUser(target->channel_info, user->handle_info);
2123 if(!target_user || (target_user->access < UL_OWNER))
2125 reply("CSMSG_MERGE_NOT_OWNER");
2129 /* Merge the channel structures and associated data. */
2130 merge_channel(channel->channel_info, target->channel_info);
2131 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2132 unregister_channel(channel->channel_info, reason);
2133 reply("CSMSG_MERGE_SUCCESS", target->name);
2137 static CHANSERV_FUNC(cmd_opchan)
2139 struct mod_chanmode change;
2140 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2142 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2145 channel->channel_info->may_opchan = 0;
2146 mod_chanmode_init(&change);
2148 change.args[0].mode = MODE_CHANOP;
2149 change.args[0].member = GetUserMode(channel, chanserv);
2150 mod_chanmode_announce(chanserv, channel, &change);
2151 reply("CSMSG_OPCHAN_DONE", channel->name);
2155 static CHANSERV_FUNC(cmd_adduser)
2157 struct userData *actee;
2158 struct userData *actor;
2159 struct handle_info *handle;
2160 unsigned short access;
2164 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2166 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2170 access = user_level_from_name(argv[2], UL_OWNER);
2173 reply("CSMSG_INVALID_ACCESS", argv[2]);
2177 actor = GetChannelUser(channel->channel_info, user->handle_info);
2178 if(actor->access <= access)
2180 reply("CSMSG_NO_BUMP_ACCESS");
2184 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2187 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2189 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2193 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2194 scan_user_presence(actee, NULL);
2195 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2199 static CHANSERV_FUNC(cmd_clvl)
2201 struct handle_info *handle;
2202 struct userData *victim;
2203 struct userData *actor;
2204 unsigned short new_access;
2205 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2209 actor = GetChannelUser(channel->channel_info, user->handle_info);
2211 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2214 if(handle == user->handle_info && !privileged)
2216 reply("CSMSG_NO_SELF_CLVL");
2220 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2222 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2226 if(actor->access <= victim->access && !privileged)
2228 reply("MSG_USER_OUTRANKED", handle->handle);
2232 new_access = user_level_from_name(argv[2], UL_OWNER);
2236 reply("CSMSG_INVALID_ACCESS", argv[2]);
2240 if(new_access >= actor->access && !privileged)
2242 reply("CSMSG_NO_BUMP_ACCESS");
2246 victim->access = new_access;
2247 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2251 static CHANSERV_FUNC(cmd_deluser)
2253 struct handle_info *handle;
2254 struct userData *victim;
2255 struct userData *actor;
2256 unsigned short access;
2261 actor = GetChannelUser(channel->channel_info, user->handle_info);
2263 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2266 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2268 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2274 access = user_level_from_name(argv[1], UL_OWNER);
2277 reply("CSMSG_INVALID_ACCESS", argv[1]);
2280 if(access != victim->access)
2282 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2288 access = victim->access;
2291 if((actor->access <= victim->access) && !IsHelping(user))
2293 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2297 chan_name = strdup(channel->name);
2298 del_channel_user(victim, 1);
2299 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2305 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2307 struct userData *actor, *uData, *next;
2309 actor = GetChannelUser(channel->channel_info, user->handle_info);
2311 if(min_access > max_access)
2313 reply("CSMSG_BAD_RANGE", min_access, max_access);
2317 if((actor->access <= max_access) && !IsHelping(user))
2319 reply("CSMSG_NO_ACCESS");
2323 for(uData = channel->channel_info->users; uData; uData = next)
2327 if((uData->access >= min_access)
2328 && (uData->access <= max_access)
2329 && match_ircglob(uData->handle->handle, mask))
2330 del_channel_user(uData, 1);
2333 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2337 static CHANSERV_FUNC(cmd_mdelowner)
2339 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2342 static CHANSERV_FUNC(cmd_mdelcoowner)
2344 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2347 static CHANSERV_FUNC(cmd_mdelmaster)
2349 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2352 static CHANSERV_FUNC(cmd_mdelop)
2354 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2357 static CHANSERV_FUNC(cmd_mdelpeon)
2359 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2363 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2365 struct banData *bData, *next;
2366 char interval[INTERVALLEN];
2371 limit = now - duration;
2372 for(bData = channel->channel_info->bans; bData; bData = next)
2376 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2379 del_channel_ban(bData);
2383 intervalString(interval, duration);
2384 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2389 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2391 struct userData *actor, *uData, *next;
2392 char interval[INTERVALLEN];
2396 actor = GetChannelUser(channel->channel_info, user->handle_info);
2397 if(min_access > max_access)
2399 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2403 if((actor->access <= max_access) && !IsHelping(user))
2405 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2410 limit = now - duration;
2411 for(uData = channel->channel_info->users; uData; uData = next)
2415 if((uData->seen > limit) || uData->present)
2418 if(((uData->access >= min_access) && (uData->access <= max_access))
2419 || (!max_access && (uData->access < actor->access)))
2421 del_channel_user(uData, 1);
2429 max_access = UL_OWNER;
2431 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration));
2435 static CHANSERV_FUNC(cmd_trim)
2437 unsigned long duration;
2438 unsigned short min_level, max_level;
2442 duration = ParseInterval(argv[2]);
2445 reply("CSMSG_CANNOT_TRIM");
2449 if(!irccasecmp(argv[1], "bans"))
2451 cmd_trim_bans(user, channel, duration);
2454 else if(!irccasecmp(argv[1], "users"))
2456 cmd_trim_users(user, channel, 0, 0, duration);
2459 else if(parse_level_range(&min_level, &max_level, argv[1]))
2461 cmd_trim_users(user, channel, min_level, max_level, duration);
2464 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2466 cmd_trim_users(user, channel, min_level, min_level, duration);
2471 reply("CSMSG_INVALID_TRIM", argv[1]);
2476 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2477 to the user. cmd_all takes advantage of this. */
2478 static CHANSERV_FUNC(cmd_up)
2480 struct mod_chanmode change;
2481 struct userData *uData;
2484 mod_chanmode_init(&change);
2486 change.args[0].member = GetUserMode(channel, user);
2487 if(!change.args[0].member)
2490 reply("MSG_CHANNEL_ABSENT", channel->name);
2494 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2498 reply("CSMSG_GODMODE_UP", argv[0]);
2501 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2503 change.args[0].mode = MODE_CHANOP;
2504 errmsg = "CSMSG_ALREADY_OPPED";
2508 change.args[0].mode = MODE_VOICE;
2509 errmsg = "CSMSG_ALREADY_VOICED";
2511 change.args[0].mode &= ~change.args[0].member->modes;
2512 if(!change.args[0].mode)
2515 reply(errmsg, channel->name);
2518 modcmd_chanmode_announce(&change);
2522 static CHANSERV_FUNC(cmd_down)
2524 struct mod_chanmode change;
2526 mod_chanmode_init(&change);
2528 change.args[0].member = GetUserMode(channel, user);
2529 if(!change.args[0].member)
2532 reply("MSG_CHANNEL_ABSENT", channel->name);
2536 if(!change.args[0].member->modes)
2539 reply("CSMSG_ALREADY_DOWN", channel->name);
2543 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2544 modcmd_chanmode_announce(&change);
2548 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)
2550 struct userData *cList;
2552 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2554 if(IsSuspended(cList->channel)
2555 || IsUserSuspended(cList)
2556 || !GetUserMode(cList->channel->channel, user))
2559 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2565 static CHANSERV_FUNC(cmd_upall)
2567 return cmd_all(CSFUNC_ARGS, cmd_up);
2570 static CHANSERV_FUNC(cmd_downall)
2572 return cmd_all(CSFUNC_ARGS, cmd_down);
2575 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2576 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2579 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)
2581 unsigned int ii, valid;
2582 struct userNode *victim;
2583 struct mod_chanmode *change;
2585 change = mod_chanmode_alloc(argc - 1);
2587 for(ii=valid=0; ++ii < argc; )
2589 if(!(victim = GetUserH(argv[ii])))
2591 change->args[valid].mode = mode;
2592 change->args[valid].member = GetUserMode(channel, victim);
2593 if(!change->args[valid].member)
2595 if(validate && !validate(user, channel, victim))
2600 change->argc = valid;
2601 if(valid < (argc-1))
2602 reply("CSMSG_PROCESS_FAILED");
2605 modcmd_chanmode_announce(change);
2606 reply(action, channel->name);
2608 mod_chanmode_free(change);
2612 static CHANSERV_FUNC(cmd_op)
2614 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2617 static CHANSERV_FUNC(cmd_deop)
2619 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2622 static CHANSERV_FUNC(cmd_voice)
2624 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2627 static CHANSERV_FUNC(cmd_devoice)
2629 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2633 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2639 for(ii=0; ii<channel->members.used; ii++)
2641 struct modeNode *mn = channel->members.list[ii];
2643 if(IsService(mn->user))
2646 if(!user_matches_glob(mn->user, ban, 1))
2649 if(protect_user(mn->user, user, channel->channel_info))
2653 victims[(*victimCount)++] = mn;
2659 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2661 struct userNode *victim;
2662 struct modeNode **victims;
2663 unsigned int offset, n, victimCount, duration = 0;
2664 char *reason = "Bye.", *ban, *name;
2665 char interval[INTERVALLEN];
2667 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2668 REQUIRE_PARAMS(offset);
2671 reason = unsplit_string(argv + offset, argc - offset, NULL);
2672 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2674 /* Truncate the reason to a length of TOPICLEN, as
2675 the ircd does; however, leave room for an ellipsis
2676 and the kicker's nick. */
2677 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2681 if((victim = GetUserH(argv[1])))
2683 victims = alloca(sizeof(victims[0]));
2684 victims[0] = GetUserMode(channel, victim);
2685 /* XXX: The comparison with ACTION_KICK is just because all
2686 * other actions can work on users outside the channel, and we
2687 * want to allow those (e.g. unbans) in that case. If we add
2688 * some other ejection action for in-channel users, change
2690 victimCount = victims[0] ? 1 : 0;
2692 if(IsService(victim))
2694 reply("MSG_SERVICE_IMMUNE", victim->nick);
2698 if((action == ACTION_KICK) && !victimCount)
2700 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2704 if(protect_user(victim, user, channel->channel_info))
2706 reply("CSMSG_USER_PROTECTED", victim->nick);
2710 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2711 name = victim->nick;
2715 if(!is_ircmask(argv[1]))
2717 reply("MSG_NICK_UNKNOWN", argv[1]);
2721 victims = alloca(sizeof(victims[0]) * channel->members.used);
2723 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2725 reply("CSMSG_MASK_PROTECTED", argv[1]);
2729 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2731 reply("CSMSG_LAME_MASK", argv[1]);
2735 if((action == ACTION_KICK) && (victimCount == 0))
2737 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2741 name = ban = strdup(argv[1]);
2744 /* Truncate the ban in place if necessary; we must ensure
2745 that 'ban' is a valid ban mask before sanitizing it. */
2746 sanitize_ircmask(ban);
2748 if(action & ACTION_ADD_BAN)
2750 struct banData *bData, *next;
2752 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2754 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2759 if(action & ACTION_ADD_TIMED_BAN)
2761 duration = ParseInterval(argv[2]);
2765 reply("CSMSG_DURATION_TOO_LOW");
2769 else if(duration > (86400 * 365 * 2))
2771 reply("CSMSG_DURATION_TOO_HIGH");
2777 for(bData = channel->channel_info->bans; bData; bData = next)
2779 if(match_ircglobs(bData->mask, ban))
2781 int exact = !irccasecmp(bData->mask, ban);
2783 /* The ban is redundant; there is already a ban
2784 with the same effect in place. */
2788 free(bData->reason);
2789 bData->reason = strdup(reason);
2790 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2792 reply("CSMSG_REASON_CHANGE", ban);
2796 if(exact && bData->expires)
2800 /* If the ban matches an existing one exactly,
2801 extend the expiration time if the provided
2802 duration is longer. */
2803 if(duration && ((time_t)(now + duration) > bData->expires))
2805 bData->expires = now + duration;
2816 /* Delete the expiration timeq entry and
2817 requeue if necessary. */
2818 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2821 timeq_add(bData->expires, expire_ban, bData);
2825 /* automated kickban */
2828 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration));
2830 reply("CSMSG_BAN_ADDED", name, channel->name);
2836 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2843 if(match_ircglobs(ban, bData->mask))
2845 /* The ban we are adding makes previously existing
2846 bans redundant; silently remove them. */
2847 del_channel_ban(bData);
2851 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);
2853 name = ban = strdup(bData->mask);
2857 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2859 extern const char *hidden_host_suffix;
2860 const char *old_name = chanserv_conf.old_ban_names->list[n];
2862 unsigned int l1, l2;
2865 l2 = strlen(old_name);
2868 if(irccasecmp(ban + l1 - l2, old_name))
2870 new_mask = malloc(MAXLEN);
2871 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2873 name = ban = new_mask;
2878 if(action & ACTION_BAN)
2880 unsigned int exists;
2881 struct mod_chanmode *change;
2883 if(channel->banlist.used >= MAXBANS)
2886 reply("CSMSG_BANLIST_FULL", channel->name);
2891 exists = ChannelBanExists(channel, ban);
2892 change = mod_chanmode_alloc(victimCount + 1);
2893 for(n = 0; n < victimCount; ++n)
2895 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2896 change->args[n].member = victims[n];
2900 change->args[n].mode = MODE_BAN;
2901 change->args[n++].hostmask = ban;
2905 modcmd_chanmode_announce(change);
2907 mod_chanmode_announce(chanserv, channel, change);
2908 mod_chanmode_free(change);
2910 if(exists && (action == ACTION_BAN))
2913 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2919 if(action & ACTION_KICK)
2921 char kick_reason[MAXLEN];
2922 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2924 for(n = 0; n < victimCount; n++)
2925 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2930 /* No response, since it was automated. */
2932 else if(action & ACTION_ADD_BAN)
2935 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration));
2937 reply("CSMSG_BAN_ADDED", name, channel->name);
2939 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2940 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2941 else if(action & ACTION_BAN)
2942 reply("CSMSG_BAN_DONE", name, channel->name);
2943 else if(action & ACTION_KICK && victimCount)
2944 reply("CSMSG_KICK_DONE", name, channel->name);
2950 static CHANSERV_FUNC(cmd_kickban)
2952 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2955 static CHANSERV_FUNC(cmd_kick)
2957 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2960 static CHANSERV_FUNC(cmd_ban)
2962 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2965 static CHANSERV_FUNC(cmd_addban)
2967 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2970 static CHANSERV_FUNC(cmd_addtimedban)
2972 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2975 static struct mod_chanmode *
2976 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2978 struct mod_chanmode *change;
2979 unsigned char *match;
2980 unsigned int ii, count;
2982 match = alloca(bans->used);
2985 for(ii = count = 0; ii < bans->used; ++ii)
2987 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2994 for(ii = count = 0; ii < bans->used; ++ii)
2996 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3003 change = mod_chanmode_alloc(count);
3004 for(ii = count = 0; ii < bans->used; ++ii)
3008 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3009 change->args[count++].hostmask = bans->list[ii]->ban;
3015 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3017 struct userNode *actee;
3023 /* may want to allow a comma delimited list of users... */
3024 if(!(actee = GetUserH(argv[1])))
3026 if(!is_ircmask(argv[1]))
3028 reply("MSG_NICK_UNKNOWN", argv[1]);
3032 mask = strdup(argv[1]);
3035 /* We don't sanitize the mask here because ircu
3037 if(action & ACTION_UNBAN)
3039 struct mod_chanmode *change;
3040 change = find_matching_bans(&channel->banlist, actee, mask);
3043 modcmd_chanmode_announce(change);
3044 mod_chanmode_free(change);
3049 if(action & ACTION_DEL_BAN)
3051 struct banData *ban, *next;
3053 ban = channel->channel_info->bans;
3057 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3060 for( ; ban && !match_ircglobs(mask, ban->mask);
3065 del_channel_ban(ban);
3072 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3074 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3080 static CHANSERV_FUNC(cmd_unban)
3082 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3085 static CHANSERV_FUNC(cmd_delban)
3087 /* it doesn't necessarily have to remove the channel ban - may want
3088 to make that an option. */
3089 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3092 static CHANSERV_FUNC(cmd_unbanme)
3094 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3095 long flags = ACTION_UNBAN;
3097 /* remove permanent bans if the user has the proper access. */
3098 if(uData->access >= UL_MASTER)
3099 flags |= ACTION_DEL_BAN;
3101 argv[1] = user->nick;
3102 return unban_user(user, channel, 2, argv, cmd, flags);
3105 static CHANSERV_FUNC(cmd_unbanall)
3107 struct mod_chanmode *change;
3110 if(!channel->banlist.used)
3112 reply("CSMSG_NO_BANS", channel->name);
3116 change = mod_chanmode_alloc(channel->banlist.used);
3117 for(ii=0; ii<channel->banlist.used; ii++)
3119 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3120 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3122 modcmd_chanmode_announce(change);
3123 mod_chanmode_free(change);
3124 reply("CSMSG_BANS_REMOVED", channel->name);
3128 static CHANSERV_FUNC(cmd_open)
3130 struct mod_chanmode *change;
3132 change = find_matching_bans(&channel->banlist, user, NULL);
3134 change = mod_chanmode_alloc(0);
3135 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3136 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3137 && channel->channel_info->modes.modes_set)
3138 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3139 modcmd_chanmode_announce(change);
3140 reply("CSMSG_CHANNEL_OPENED", channel->name);
3141 mod_chanmode_free(change);
3145 static CHANSERV_FUNC(cmd_myaccess)
3147 struct handle_info *target_handle;
3148 struct userData *uData;
3149 const char *chanName;
3152 target_handle = user->handle_info;
3153 else if(!IsHelping(user))
3155 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3158 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3161 if(!target_handle->channels)
3163 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3167 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3168 for(uData = target_handle->channels; uData; uData = uData->u_next)
3170 struct chanData *cData = uData->channel;
3172 if(uData->access > UL_OWNER)
3174 if(IsProtected(cData)
3175 && (target_handle != user->handle_info)
3176 && !GetTrueChannelAccess(cData, user->handle_info))
3178 chanName = cData->channel->name;
3180 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3182 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3188 static CHANSERV_FUNC(cmd_access)
3190 struct userNode *target;
3191 struct handle_info *target_handle;
3192 struct userData *uData;
3194 char prefix[MAXLEN];
3199 target_handle = target->handle_info;
3201 else if((target = GetUserH(argv[1])))
3203 target_handle = target->handle_info;
3205 else if(argv[1][0] == '*')
3207 if(!(target_handle = get_handle_info(argv[1]+1)))
3209 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3215 reply("MSG_NICK_UNKNOWN", argv[1]);
3219 assert(target || target_handle);
3221 if(target == chanserv)
3223 reply("CSMSG_IS_CHANSERV");
3231 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3236 reply("MSG_USER_AUTHENTICATE", target->nick);
3239 reply("MSG_AUTHENTICATE");
3245 const char *epithet = NULL, *type = NULL;
3248 epithet = chanserv_conf.irc_operator_epithet;
3251 else if(IsNetworkHelper(target))
3253 epithet = chanserv_conf.network_helper_epithet;
3254 type = "network helper";
3256 else if(IsSupportHelper(target))
3258 epithet = chanserv_conf.support_helper_epithet;
3259 type = "support helper";
3263 if(target_handle->epithet)
3264 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3266 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3268 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3272 sprintf(prefix, "%s", target_handle->handle);
3275 if(!channel->channel_info)
3277 reply("CSMSG_NOT_REGISTERED", channel->name);
3281 helping = HANDLE_FLAGGED(target_handle, HELPING)
3282 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3283 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3285 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3286 /* To prevent possible information leaks, only show infolines
3287 * if the requestor is in the channel or it's their own
3289 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3291 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3293 /* Likewise, only say it's suspended if the user has active
3294 * access in that channel or it's their own entry. */
3295 if(IsUserSuspended(uData)
3296 && (GetChannelUser(channel->channel_info, user->handle_info)
3297 || (user->handle_info == uData->handle)))
3299 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3304 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3311 zoot_list(struct listData *list)
3313 struct userData *uData;
3314 unsigned int start, curr, highest, lowest;
3315 struct helpfile_table tmp_table;
3316 const char **temp, *msg;
3318 if(list->table.length == 1)
3321 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3323 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3324 msg = user_find_message(list->user, "MSG_NONE");
3325 send_message_type(4, list->user, list->bot, " %s", msg);
3327 tmp_table.width = list->table.width;
3328 tmp_table.flags = list->table.flags;
3329 list->table.contents[0][0] = " ";
3330 highest = list->highest;
3331 if(list->lowest != 0)
3332 lowest = list->lowest;
3333 else if(highest < 100)
3336 lowest = highest - 100;
3337 for(start = curr = 1; curr < list->table.length; )
3339 uData = list->users[curr-1];
3340 list->table.contents[curr++][0] = " ";
3341 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3344 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3346 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3347 temp = list->table.contents[--start];
3348 list->table.contents[start] = list->table.contents[0];
3349 tmp_table.contents = list->table.contents + start;
3350 tmp_table.length = curr - start;
3351 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3352 list->table.contents[start] = temp;
3354 highest = lowest - 1;
3355 lowest = (highest < 100) ? 0 : (highest - 99);
3361 def_list(struct listData *list)
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 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3369 if(list->table.length == 1)
3371 msg = user_find_message(list->user, "MSG_NONE");
3372 send_message_type(4, list->user, list->bot, " %s", msg);
3377 userData_access_comp(const void *arg_a, const void *arg_b)
3379 const struct userData *a = *(struct userData**)arg_a;
3380 const struct userData *b = *(struct userData**)arg_b;
3382 if(a->access != b->access)
3383 res = b->access - a->access;
3385 res = irccasecmp(a->handle->handle, b->handle->handle);
3390 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3392 void (*send_list)(struct listData *);
3393 struct userData *uData;
3394 struct listData lData;
3395 unsigned int matches;
3399 lData.bot = cmd->parent->bot;
3400 lData.channel = channel;
3401 lData.lowest = lowest;
3402 lData.highest = highest;
3403 lData.search = (argc > 1) ? argv[1] : NULL;
3404 send_list = zoot_list;
3406 if(user->handle_info)
3408 switch(user->handle_info->userlist_style)
3410 case HI_STYLE_DEF: send_list = def_list; break;
3411 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3415 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3417 for(uData = channel->channel_info->users; uData; uData = uData->next)
3419 if((uData->access < lowest)
3420 || (uData->access > highest)
3421 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3423 lData.users[matches++] = uData;
3425 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3427 lData.table.length = matches+1;
3428 lData.table.width = 4;
3429 lData.table.flags = TABLE_NO_FREE;
3430 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3431 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3432 lData.table.contents[0] = ary;
3435 ary[2] = "Last Seen";
3437 for(matches = 1; matches < lData.table.length; ++matches)
3439 struct userData *uData = lData.users[matches-1];
3440 char seen[INTERVALLEN];
3442 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3443 lData.table.contents[matches] = ary;
3444 ary[0] = strtab(uData->access);
3445 ary[1] = uData->handle->handle;
3448 else if(!uData->seen)
3451 ary[2] = intervalString(seen, now - uData->seen);
3452 ary[2] = strdup(ary[2]);
3453 if(IsUserSuspended(uData))
3454 ary[3] = "Suspended";
3455 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3456 ary[3] = "Vacation";
3461 for(matches = 1; matches < lData.table.length; ++matches)
3463 free((char*)lData.table.contents[matches][2]);
3464 free(lData.table.contents[matches]);
3466 free(lData.table.contents[0]);
3467 free(lData.table.contents);
3471 static CHANSERV_FUNC(cmd_users)
3473 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3476 static CHANSERV_FUNC(cmd_wlist)
3478 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3481 static CHANSERV_FUNC(cmd_clist)
3483 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3486 static CHANSERV_FUNC(cmd_mlist)
3488 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3491 static CHANSERV_FUNC(cmd_olist)
3493 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3496 static CHANSERV_FUNC(cmd_plist)
3498 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3501 static CHANSERV_FUNC(cmd_bans)
3503 struct helpfile_table tbl;
3504 unsigned int matches = 0, timed = 0, ii;
3505 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3506 const char *msg_never, *triggered, *expires;
3507 struct banData *ban, **bans;
3514 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3516 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3518 if(search && !match_ircglobs(search, ban->mask))
3520 bans[matches++] = ban;
3525 tbl.length = matches + 1;
3526 tbl.width = 4 + timed;
3528 tbl.flags = TABLE_NO_FREE;
3529 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3530 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3531 tbl.contents[0][0] = "Mask";
3532 tbl.contents[0][1] = "Set By";
3533 tbl.contents[0][2] = "Triggered";
3536 tbl.contents[0][3] = "Expires";
3537 tbl.contents[0][4] = "Reason";
3540 tbl.contents[0][3] = "Reason";
3543 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3545 free(tbl.contents[0]);
3550 msg_never = user_find_message(user, "MSG_NEVER");
3551 for(ii = 0; ii < matches; )
3557 else if(ban->expires)
3558 expires = intervalString(e_buffer, ban->expires - now);
3560 expires = msg_never;
3563 triggered = intervalString(t_buffer, now - ban->triggered);
3565 triggered = msg_never;
3567 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3568 tbl.contents[ii][0] = ban->mask;
3569 tbl.contents[ii][1] = ban->owner;
3570 tbl.contents[ii][2] = strdup(triggered);
3573 tbl.contents[ii][3] = strdup(expires);
3574 tbl.contents[ii][4] = ban->reason;
3577 tbl.contents[ii][3] = ban->reason;
3579 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3580 reply("MSG_MATCH_COUNT", matches);
3581 for(ii = 1; ii < tbl.length; ++ii)
3583 free((char*)tbl.contents[ii][2]);
3585 free((char*)tbl.contents[ii][3]);
3586 free(tbl.contents[ii]);
3588 free(tbl.contents[0]);
3594 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3596 struct chanData *cData = channel->channel_info;
3597 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3599 if(cData->topic_mask)
3600 return !match_ircglob(new_topic, cData->topic_mask);
3601 else if(cData->topic)
3602 return irccasecmp(new_topic, cData->topic);
3607 static CHANSERV_FUNC(cmd_topic)
3609 struct chanData *cData;
3612 cData = channel->channel_info;
3617 SetChannelTopic(channel, chanserv, cData->topic, 1);
3618 reply("CSMSG_TOPIC_SET", cData->topic);
3622 reply("CSMSG_NO_TOPIC", channel->name);
3626 topic = unsplit_string(argv + 1, argc - 1, NULL);
3627 /* If they say "!topic *", use an empty topic. */
3628 if((topic[0] == '*') && (topic[1] == 0))
3630 if(bad_topic(channel, user, topic))
3632 char *topic_mask = cData->topic_mask;
3635 char new_topic[TOPICLEN+1], tchar;
3636 int pos=0, starpos=-1, dpos=0, len;
3638 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3645 len = strlen(topic);
3646 if((dpos + len) > TOPICLEN)
3647 len = TOPICLEN + 1 - dpos;
3648 memcpy(new_topic+dpos, topic, len);
3652 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3653 default: new_topic[dpos++] = tchar; break;
3656 if((dpos > TOPICLEN) || tchar)
3659 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3660 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3663 new_topic[dpos] = 0;
3664 SetChannelTopic(channel, chanserv, new_topic, 1);
3666 reply("CSMSG_TOPIC_LOCKED", channel->name);
3671 SetChannelTopic(channel, chanserv, topic, 1);
3673 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3675 /* Grab the topic and save it as the default topic. */
3677 cData->topic = strdup(channel->topic);
3683 static CHANSERV_FUNC(cmd_mode)
3685 struct mod_chanmode *change;
3689 change = &channel->channel_info->modes;
3690 if(change->modes_set || change->modes_clear) {
3691 modcmd_chanmode_announce(change);
3692 reply("CSMSG_DEFAULTED_MODES", channel->name);
3694 reply("CSMSG_NO_MODES", channel->name);
3698 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3701 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3705 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3706 && mode_lock_violated(&channel->channel_info->modes, change))
3709 mod_chanmode_format(&channel->channel_info->modes, modes);
3710 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3714 modcmd_chanmode_announce(change);
3715 mod_chanmode_free(change);
3716 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3720 static CHANSERV_FUNC(cmd_invite)
3722 struct userData *uData;
3723 struct userNode *invite;
3725 uData = GetChannelUser(channel->channel_info, user->handle_info);
3729 if(!(invite = GetUserH(argv[1])))
3731 reply("MSG_NICK_UNKNOWN", argv[1]);
3738 if(GetUserMode(channel, invite))
3740 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3746 char *reason = (argc > 2) ? unsplit_string(argv + 2, argc - 2, NULL) : "";
3747 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name, (argc > 2) ? ": " : ".", reason);
3749 irc_invite(chanserv, invite, channel);
3751 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3756 static CHANSERV_FUNC(cmd_inviteme)
3758 if(GetUserMode(channel, user))
3760 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3763 if(channel->channel_info
3764 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3766 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3769 irc_invite(cmd->parent->bot, user, channel);
3774 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3777 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3779 /* We display things based on two dimensions:
3780 * - Issue time: present or absent
3781 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3782 * (in order of precedence, so something both expired and revoked
3783 * only counts as revoked)
3785 combo = (suspended->issued ? 4 : 0)
3786 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3788 case 0: /* no issue time, indefinite expiration */
3789 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3791 case 1: /* no issue time, expires in future */
3792 intervalString(buf1, suspended->expires-now);
3793 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3795 case 2: /* no issue time, expired */
3796 intervalString(buf1, now-suspended->expires);
3797 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3799 case 3: /* no issue time, revoked */
3800 intervalString(buf1, now-suspended->revoked);
3801 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3803 case 4: /* issue time set, indefinite expiration */
3804 intervalString(buf1, now-suspended->issued);
3805 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3807 case 5: /* issue time set, expires in future */
3808 intervalString(buf1, now-suspended->issued);
3809 intervalString(buf2, suspended->expires-now);
3810 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3812 case 6: /* issue time set, expired */
3813 intervalString(buf1, now-suspended->issued);
3814 intervalString(buf2, now-suspended->expires);
3815 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3817 case 7: /* issue time set, revoked */
3818 intervalString(buf1, now-suspended->issued);
3819 intervalString(buf2, now-suspended->revoked);
3820 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3823 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3828 static CHANSERV_FUNC(cmd_info)
3830 char modes[MAXLEN], buffer[INTERVALLEN];
3831 struct userData *uData, *owner;
3832 struct chanData *cData;
3833 struct do_not_register *dnr;
3838 cData = channel->channel_info;
3839 reply("CSMSG_CHANNEL_INFO", channel->name);
3841 uData = GetChannelUser(cData, user->handle_info);
3842 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3844 mod_chanmode_format(&cData->modes, modes);
3845 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3846 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3849 for(it = dict_first(cData->notes); it; it = iter_next(it))
3853 note = iter_data(it);
3854 if(!note_type_visible_to_user(cData, note->type, user))
3857 padding = PADLEN - 1 - strlen(iter_key(it));
3858 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3861 reply("CSMSG_CHANNEL_MAX", cData->max);
3862 for(owner = cData->users; owner; owner = owner->next)
3863 if(owner->access == UL_OWNER)
3864 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3865 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3866 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3867 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited));
3868 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered));
3870 privileged = IsStaff(user);
3871 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3872 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3874 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3875 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3877 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3879 struct suspended *suspended;
3880 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3881 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3882 show_suspension_info(cmd, user, suspended);
3884 else if(IsSuspended(cData))
3886 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3887 show_suspension_info(cmd, user, cData->suspended);
3892 static CHANSERV_FUNC(cmd_netinfo)
3894 extern time_t boot_time;
3895 extern unsigned long burst_length;
3896 char interval[INTERVALLEN];
3898 reply("CSMSG_NETWORK_INFO");
3899 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3900 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3901 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3902 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3903 reply("CSMSG_NETWORK_BANS", banCount);
3904 reply("CSMSG_CHANNEL_USERS", userCount);
3905 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time));
3906 reply("CSMSG_BURST_LENGTH",intervalString(interval, burst_length));
3911 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3913 struct helpfile_table table;
3915 struct userNode *user;
3920 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3921 table.contents = alloca(list->used*sizeof(*table.contents));
3922 for(nn=0; nn<list->used; nn++)
3924 user = list->list[nn];
3925 if(user->modes & skip_flags)
3929 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3932 nick = alloca(strlen(user->nick)+3);
3933 sprintf(nick, "(%s)", user->nick);
3937 table.contents[table.length][0] = nick;
3940 table_send(chanserv, to->nick, 0, NULL, table);
3943 static CHANSERV_FUNC(cmd_ircops)
3945 reply("CSMSG_STAFF_OPERS");
3946 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3950 static CHANSERV_FUNC(cmd_helpers)
3952 reply("CSMSG_STAFF_HELPERS");
3953 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3957 static CHANSERV_FUNC(cmd_staff)
3959 reply("CSMSG_NETWORK_STAFF");
3960 cmd_ircops(CSFUNC_ARGS);
3961 cmd_helpers(CSFUNC_ARGS);
3965 static CHANSERV_FUNC(cmd_peek)
3967 struct modeNode *mn;
3968 char modes[MODELEN];
3970 struct helpfile_table table;
3972 irc_make_chanmode(channel, modes);
3974 reply("CSMSG_PEEK_INFO", channel->name);
3975 reply("CSMSG_PEEK_TOPIC", channel->topic);
3976 reply("CSMSG_PEEK_MODES", modes);
3977 reply("CSMSG_PEEK_USERS", channel->members.used);
3981 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3982 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3983 for(n = 0; n < channel->members.used; n++)
3985 mn = channel->members.list[n];
3986 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3988 table.contents[table.length] = alloca(sizeof(**table.contents));
3989 table.contents[table.length][0] = mn->user->nick;
3994 reply("CSMSG_PEEK_OPS");
3995 table_send(chanserv, user->nick, 0, NULL, table);
3998 reply("CSMSG_PEEK_NO_OPS");
4002 static MODCMD_FUNC(cmd_wipeinfo)
4004 struct handle_info *victim;
4005 struct userData *ud, *actor;
4008 actor = GetChannelUser(channel->channel_info, user->handle_info);
4009 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4011 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4013 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4016 if((ud->access >= actor->access) && (ud != actor))
4018 reply("MSG_USER_OUTRANKED", victim->handle);
4024 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4028 static CHANSERV_FUNC(cmd_resync)
4030 struct mod_chanmode *changes;
4031 struct chanData *cData = channel->channel_info;
4032 unsigned int ii, used;
4034 changes = mod_chanmode_alloc(channel->members.used * 2);
4035 for(ii = used = 0; ii < channel->members.used; ++ii)
4037 struct modeNode *mn = channel->members.list[ii];
4038 struct userData *uData;
4040 if(IsService(mn->user))
4043 uData = GetChannelAccess(cData, mn->user->handle_info);
4044 if(!cData->lvlOpts[lvlGiveOps]
4045 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4047 if(!(mn->modes & MODE_CHANOP))
4049 changes->args[used].mode = MODE_CHANOP;
4050 changes->args[used++].member = mn;
4053 else if(!cData->lvlOpts[lvlGiveVoice]
4054 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4056 if(mn->modes & MODE_CHANOP)
4058 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4059 changes->args[used++].member = mn;
4061 if(!(mn->modes & MODE_VOICE))
4063 changes->args[used].mode = MODE_VOICE;
4064 changes->args[used++].member = mn;
4071 changes->args[used].mode = MODE_REMOVE | mn->modes;
4072 changes->args[used++].member = mn;
4076 changes->argc = used;
4077 modcmd_chanmode_announce(changes);
4078 mod_chanmode_free(changes);
4079 reply("CSMSG_RESYNCED_USERS", channel->name);
4083 static CHANSERV_FUNC(cmd_seen)
4085 struct userData *uData;
4086 struct handle_info *handle;
4087 char seen[INTERVALLEN];
4091 if(!irccasecmp(argv[1], chanserv->nick))
4093 reply("CSMSG_IS_CHANSERV");
4097 if(!(handle = get_handle_info(argv[1])))
4099 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4103 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4105 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4110 reply("CSMSG_USER_PRESENT", handle->handle);
4111 else if(uData->seen)
4112 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen));
4114 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4116 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4117 reply("CSMSG_USER_VACATION", handle->handle);
4122 static MODCMD_FUNC(cmd_names)
4124 struct userNode *targ;
4125 struct userData *targData;
4126 unsigned int ii, pos;
4129 for(ii=pos=0; ii<channel->members.used; ++ii)
4131 targ = channel->members.list[ii]->user;
4132 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4135 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4138 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4142 if(IsUserSuspended(targData))
4144 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4147 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4148 reply("CSMSG_END_NAMES", channel->name);
4153 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4155 switch(ntype->visible_type)
4157 case NOTE_VIS_ALL: return 1;
4158 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4159 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4164 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4166 struct userData *uData;
4168 switch(ntype->set_access_type)
4170 case NOTE_SET_CHANNEL_ACCESS:
4171 if(!user->handle_info)
4173 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4175 return uData->access >= ntype->set_access.min_ulevel;
4176 case NOTE_SET_CHANNEL_SETTER:
4177 return check_user_level(channel, user, lvlSetters, 1, 0);
4178 case NOTE_SET_PRIVILEGED: default:
4179 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4183 static CHANSERV_FUNC(cmd_note)
4185 struct chanData *cData;
4187 struct note_type *ntype;
4189 cData = channel->channel_info;
4192 reply("CSMSG_NOT_REGISTERED", channel->name);
4196 /* If no arguments, show all visible notes for the channel. */
4202 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4204 note = iter_data(it);
4205 if(!note_type_visible_to_user(cData, note->type, user))
4208 reply("CSMSG_NOTELIST_HEADER", channel->name);
4209 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4212 reply("CSMSG_NOTELIST_END", channel->name);
4214 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4216 /* If one argument, show the named note. */
4219 if((note = dict_find(cData->notes, argv[1], NULL))
4220 && note_type_visible_to_user(cData, note->type, user))
4222 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4224 else if((ntype = dict_find(note_types, argv[1], NULL))
4225 && note_type_visible_to_user(NULL, ntype, user))
4227 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4232 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4236 /* Assume they're trying to set a note. */
4240 ntype = dict_find(note_types, argv[1], NULL);
4243 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4246 else if(note_type_settable_by_user(channel, ntype, user))
4248 note_text = unsplit_string(argv+2, argc-2, NULL);
4249 if((note = dict_find(cData->notes, argv[1], NULL)))
4250 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4251 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4252 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4254 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4256 /* The note is viewable to staff only, so return 0
4257 to keep the invocation from getting logged (or
4258 regular users can see it in !events). */
4264 reply("CSMSG_NO_ACCESS");
4271 static CHANSERV_FUNC(cmd_delnote)
4276 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4277 || !note_type_settable_by_user(channel, note->type, user))
4279 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4282 dict_remove(channel->channel_info->notes, note->type->name);
4283 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4287 static CHANSERV_FUNC(cmd_events)
4289 struct logSearch discrim;
4290 struct logReport report;
4291 unsigned int matches, limit;
4293 limit = (argc > 1) ? atoi(argv[1]) : 10;
4294 if(limit < 1 || limit > 200) limit = 10;
4296 memset(&discrim, 0, sizeof(discrim));
4297 discrim.masks.bot = chanserv;
4298 discrim.masks.channel_name = channel->name;
4299 if(argc > 2) discrim.masks.command = argv[2];
4300 discrim.limit = limit;
4301 discrim.max_time = INT_MAX;
4302 discrim.severities = 1 << LOG_COMMAND;
4303 report.reporter = chanserv;
4305 reply("CSMSG_EVENT_SEARCH_RESULTS");
4306 matches = log_entry_search(&discrim, log_report_entry, &report);
4308 reply("MSG_MATCH_COUNT", matches);
4310 reply("MSG_NO_MATCHES");
4314 static CHANSERV_FUNC(cmd_say)
4320 msg = unsplit_string(argv + 1, argc - 1, NULL);
4321 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4323 else if(GetUserH(argv[1]))
4326 msg = unsplit_string(argv + 2, argc - 2, NULL);
4327 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4331 reply("MSG_NOT_TARGET_NAME");
4337 static CHANSERV_FUNC(cmd_emote)
4343 /* CTCP is so annoying. */
4344 msg = unsplit_string(argv + 1, argc - 1, NULL);
4345 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4347 else if(GetUserH(argv[1]))
4349 msg = unsplit_string(argv + 2, argc - 2, NULL);
4350 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4354 reply("MSG_NOT_TARGET_NAME");
4360 struct channelList *
4361 chanserv_support_channels(void)
4363 return &chanserv_conf.support_channels;
4366 static CHANSERV_FUNC(cmd_expire)
4368 int channel_count = registered_channels;
4369 expire_channels(NULL);
4370 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4375 chanserv_expire_suspension(void *data)
4377 struct suspended *suspended = data;
4378 struct chanNode *channel;
4379 struct mod_chanmode change;
4381 if(!suspended->expires || (now < suspended->expires))
4382 suspended->revoked = now;
4383 channel = suspended->cData->channel;
4384 suspended->cData->channel = channel;
4385 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4386 mod_chanmode_init(&change);
4388 change.args[0].mode = MODE_CHANOP;
4389 change.args[0].member = AddChannelUser(chanserv, channel);
4390 mod_chanmode_announce(chanserv, channel, &change);
4393 static CHANSERV_FUNC(cmd_csuspend)
4395 struct suspended *suspended;
4396 char reason[MAXLEN];
4397 time_t expiry, duration;
4398 struct userData *uData;
4402 if(IsProtected(channel->channel_info))
4404 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4408 if(argv[1][0] == '!')
4410 else if(IsSuspended(channel->channel_info))
4412 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4413 show_suspension_info(cmd, user, channel->channel_info->suspended);
4417 if(!strcmp(argv[1], "0"))
4419 else if((duration = ParseInterval(argv[1])))
4420 expiry = now + duration;
4423 reply("MSG_INVALID_DURATION", argv[1]);
4427 unsplit_string(argv + 2, argc - 2, reason);
4429 suspended = calloc(1, sizeof(*suspended));
4430 suspended->revoked = 0;
4431 suspended->issued = now;
4432 suspended->suspender = strdup(user->handle_info->handle);
4433 suspended->expires = expiry;
4434 suspended->reason = strdup(reason);
4435 suspended->cData = channel->channel_info;
4436 suspended->previous = suspended->cData->suspended;
4437 suspended->cData->suspended = suspended;
4439 if(suspended->expires)
4440 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4442 if(IsSuspended(channel->channel_info))
4444 suspended->previous->revoked = now;
4445 if(suspended->previous->expires)
4446 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4447 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4448 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4452 /* Mark all users in channel as absent. */
4453 for(uData = channel->channel_info->users; uData; uData = uData->next)
4462 /* Mark the channel as suspended, then part. */
4463 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4464 DelChannelUser(chanserv, channel, suspended->reason, 0);
4465 reply("CSMSG_SUSPENDED", channel->name);
4466 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4467 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4472 static CHANSERV_FUNC(cmd_cunsuspend)
4474 struct suspended *suspended;
4475 char message[MAXLEN];
4477 if(!IsSuspended(channel->channel_info))
4479 reply("CSMSG_NOT_SUSPENDED", channel->name);
4483 suspended = channel->channel_info->suspended;
4485 /* Expire the suspension and join ChanServ to the channel. */
4486 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4487 chanserv_expire_suspension(suspended);
4488 reply("CSMSG_UNSUSPENDED", channel->name);
4489 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4490 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4494 typedef struct chanservSearch
4502 unsigned long flags;
4506 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4509 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4514 search = malloc(sizeof(struct chanservSearch));
4515 memset(search, 0, sizeof(*search));
4518 for(i = 0; i < argc; i++)
4520 /* Assume all criteria require arguments. */
4523 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4527 if(!irccasecmp(argv[i], "name"))
4528 search->name = argv[++i];
4529 else if(!irccasecmp(argv[i], "registrar"))
4530 search->registrar = argv[++i];
4531 else if(!irccasecmp(argv[i], "unvisited"))
4532 search->unvisited = ParseInterval(argv[++i]);
4533 else if(!irccasecmp(argv[i], "registered"))
4534 search->registered = ParseInterval(argv[++i]);
4535 else if(!irccasecmp(argv[i], "flags"))
4538 if(!irccasecmp(argv[i], "nodelete"))
4539 search->flags |= CHANNEL_NODELETE;
4540 else if(!irccasecmp(argv[i], "suspended"))
4541 search->flags |= CHANNEL_SUSPENDED;
4544 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4548 else if(!irccasecmp(argv[i], "limit"))
4549 search->limit = strtoul(argv[++i], NULL, 10);
4552 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4557 if(search->name && !strcmp(search->name, "*"))
4559 if(search->registrar && !strcmp(search->registrar, "*"))
4560 search->registrar = 0;
4569 chanserv_channel_match(struct chanData *channel, search_t search)
4571 const char *name = channel->channel->name;
4572 if((search->name && !match_ircglob(name, search->name)) ||
4573 (search->registrar && !channel->registrar) ||
4574 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4575 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4576 (search->registered && (now - channel->registered) > search->registered) ||
4577 (search->flags && ((search->flags & channel->flags) != search->flags)))
4584 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4586 struct chanData *channel;
4587 unsigned int matches = 0;
4589 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4591 if(!chanserv_channel_match(channel, search))
4601 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4606 search_print(struct chanData *channel, void *data)
4608 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4611 static CHANSERV_FUNC(cmd_search)
4614 unsigned int matches;
4615 channel_search_func action;
4619 if(!irccasecmp(argv[1], "count"))
4620 action = search_count;
4621 else if(!irccasecmp(argv[1], "print"))
4622 action = search_print;
4625 reply("CSMSG_ACTION_INVALID", argv[1]);
4629 search = chanserv_search_create(user, argc - 2, argv + 2);
4633 if(action == search_count)
4634 search->limit = INT_MAX;
4636 if(action == search_print)
4637 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4639 matches = chanserv_channel_search(search, action, user);
4642 reply("MSG_MATCH_COUNT", matches);
4644 reply("MSG_NO_MATCHES");
4650 static CHANSERV_FUNC(cmd_unvisited)
4652 struct chanData *cData;
4653 time_t interval = chanserv_conf.channel_expire_delay;
4654 char buffer[INTERVALLEN];
4655 unsigned int limit = 25, matches = 0;
4659 interval = ParseInterval(argv[1]);
4661 limit = atoi(argv[2]);
4664 intervalString(buffer, interval);
4665 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4667 for(cData = channelList; cData && matches < limit; cData = cData->next)
4669 if((now - cData->visited) < interval)
4672 intervalString(buffer, now - cData->visited);
4673 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4680 static MODCMD_FUNC(chan_opt_defaulttopic)
4686 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4688 reply("CSMSG_TOPIC_LOCKED", channel->name);
4692 topic = unsplit_string(argv+1, argc-1, NULL);
4694 free(channel->channel_info->topic);
4695 if(topic[0] == '*' && topic[1] == 0)
4697 topic = channel->channel_info->topic = NULL;
4701 topic = channel->channel_info->topic = strdup(topic);
4702 if(channel->channel_info->topic_mask
4703 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4704 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4706 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4709 if(channel->channel_info->topic)
4710 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4712 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4716 static MODCMD_FUNC(chan_opt_topicmask)
4720 struct chanData *cData = channel->channel_info;
4723 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4725 reply("CSMSG_TOPIC_LOCKED", channel->name);
4729 mask = unsplit_string(argv+1, argc-1, NULL);
4731 if(cData->topic_mask)
4732 free(cData->topic_mask);
4733 if(mask[0] == '*' && mask[1] == 0)
4735 cData->topic_mask = 0;
4739 cData->topic_mask = strdup(mask);
4741 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4742 else if(!match_ircglob(cData->topic, cData->topic_mask))
4743 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4747 if(channel->channel_info->topic_mask)
4748 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4750 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4754 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4758 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4762 if(greeting[0] == '*' && greeting[1] == 0)
4766 unsigned int length = strlen(greeting);
4767 if(length > chanserv_conf.greeting_length)
4769 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4772 *data = strdup(greeting);
4781 reply(name, user_find_message(user, "MSG_NONE"));
4785 static MODCMD_FUNC(chan_opt_greeting)
4787 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4790 static MODCMD_FUNC(chan_opt_usergreeting)
4792 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4795 static MODCMD_FUNC(chan_opt_modes)
4797 struct mod_chanmode *new_modes;
4798 char modes[MODELEN];
4802 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4804 reply("CSMSG_NO_ACCESS");
4807 if(argv[1][0] == '*' && argv[1][1] == 0)
4809 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4811 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4813 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4816 else if(new_modes->argc > 1)
4818 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4819 mod_chanmode_free(new_modes);
4824 channel->channel_info->modes = *new_modes;
4825 modcmd_chanmode_announce(new_modes);
4826 mod_chanmode_free(new_modes);
4830 mod_chanmode_format(&channel->channel_info->modes, modes);
4832 reply("CSMSG_SET_MODES", modes);
4834 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4838 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4840 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4842 struct chanData *cData = channel->channel_info;
4847 /* Set flag according to value. */
4848 if(enabled_string(argv[1]))
4850 cData->flags |= mask;
4853 else if(disabled_string(argv[1]))
4855 cData->flags &= ~mask;
4860 reply("MSG_INVALID_BINARY", argv[1]);
4866 /* Find current option value. */
4867 value = (cData->flags & mask) ? 1 : 0;
4871 reply(name, user_find_message(user, "MSG_ON"));
4873 reply(name, user_find_message(user, "MSG_OFF"));
4877 static MODCMD_FUNC(chan_opt_nodelete)
4879 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4881 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4885 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4888 static MODCMD_FUNC(chan_opt_dynlimit)
4890 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4893 static MODCMD_FUNC(chan_opt_defaults)
4895 struct userData *uData;
4896 struct chanData *cData;
4897 const char *confirm;
4898 enum levelOption lvlOpt;
4899 enum charOption chOpt;
4901 cData = channel->channel_info;
4902 uData = GetChannelUser(cData, user->handle_info);
4903 if(!uData || (uData->access < UL_OWNER))
4905 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4908 confirm = make_confirmation_string(uData);
4909 if((argc < 2) || strcmp(argv[1], confirm))
4911 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4914 cData->flags = CHANNEL_DEFAULT_FLAGS;
4915 cData->modes = chanserv_conf.default_modes;
4916 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4917 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4918 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4919 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4920 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4925 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4927 struct chanData *cData = channel->channel_info;
4928 struct userData *uData;
4929 unsigned short value;
4933 if(!check_user_level(channel, user, option, 1, 1))
4935 reply("CSMSG_CANNOT_SET");
4938 value = user_level_from_name(argv[1], UL_OWNER+1);
4939 if(!value && strcmp(argv[1], "0"))
4941 reply("CSMSG_INVALID_ACCESS", argv[1]);
4944 uData = GetChannelUser(cData, user->handle_info);
4945 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4947 reply("CSMSG_BAD_SETLEVEL");
4953 if(value > cData->lvlOpts[lvlGiveOps])
4955 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4960 if(value < cData->lvlOpts[lvlGiveVoice])
4962 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4967 /* This test only applies to owners, since non-owners
4968 * trying to set an option to above their level get caught
4969 * by the CSMSG_BAD_SETLEVEL test above.
4971 if(value > uData->access)
4973 reply("CSMSG_BAD_SETTERS");
4980 cData->lvlOpts[option] = value;
4982 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4986 static MODCMD_FUNC(chan_opt_enfops)
4988 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4991 static MODCMD_FUNC(chan_opt_giveops)
4993 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
4996 static MODCMD_FUNC(chan_opt_enfmodes)
4998 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5001 static MODCMD_FUNC(chan_opt_enftopic)
5003 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5006 static MODCMD_FUNC(chan_opt_pubcmd)
5008 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5011 static MODCMD_FUNC(chan_opt_setters)
5013 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5016 static MODCMD_FUNC(chan_opt_ctcpusers)
5018 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5021 static MODCMD_FUNC(chan_opt_userinfo)
5023 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5026 static MODCMD_FUNC(chan_opt_givevoice)
5028 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5031 static MODCMD_FUNC(chan_opt_topicsnarf)
5033 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5036 static MODCMD_FUNC(chan_opt_inviteme)
5038 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5042 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5044 struct chanData *cData = channel->channel_info;
5045 int count = charOptions[option].count, index;
5049 index = atoi(argv[1]);
5051 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5053 reply("CSMSG_INVALID_NUMERIC", index);
5054 /* Show possible values. */
5055 for(index = 0; index < count; index++)
5056 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5060 cData->chOpts[option] = charOptions[option].values[index].value;
5064 /* Find current option value. */
5067 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5071 /* Somehow, the option value is corrupt; reset it to the default. */
5072 cData->chOpts[option] = charOptions[option].default_value;
5077 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5081 static MODCMD_FUNC(chan_opt_protect)
5083 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5086 static MODCMD_FUNC(chan_opt_toys)
5088 return channel_multiple_option(chToys, CSFUNC_ARGS);
5091 static MODCMD_FUNC(chan_opt_ctcpreaction)
5093 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5096 static MODCMD_FUNC(chan_opt_topicrefresh)
5098 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5101 static struct svccmd_list set_shows_list;
5104 handle_svccmd_unbind(struct svccmd *target) {
5106 for(ii=0; ii<set_shows_list.used; ++ii)
5107 if(target == set_shows_list.list[ii])
5108 set_shows_list.used = 0;
5111 static CHANSERV_FUNC(cmd_set)
5113 struct svccmd *subcmd;
5117 /* Check if we need to (re-)initialize set_shows_list. */
5118 if(!set_shows_list.used)
5120 if(!set_shows_list.size)
5122 set_shows_list.size = chanserv_conf.set_shows->used;
5123 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5125 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5127 const char *name = chanserv_conf.set_shows->list[ii];
5128 sprintf(buf, "%s %s", argv[0], name);
5129 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5132 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5135 svccmd_list_append(&set_shows_list, subcmd);
5141 reply("CSMSG_CHANNEL_OPTIONS");
5142 for(ii = 0; ii < set_shows_list.used; ii++)
5144 subcmd = set_shows_list.list[ii];
5145 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5150 sprintf(buf, "%s %s", argv[0], argv[1]);
5151 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5154 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5157 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5159 reply("CSMSG_NO_ACCESS");
5163 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5167 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5169 struct userData *uData;
5171 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5174 reply("CSMSG_NOT_USER", channel->name);
5180 /* Just show current option value. */
5182 else if(enabled_string(argv[1]))
5184 uData->flags |= mask;
5186 else if(disabled_string(argv[1]))
5188 uData->flags &= ~mask;
5192 reply("MSG_INVALID_BINARY", argv[1]);
5196 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5200 static MODCMD_FUNC(user_opt_noautoop)
5202 struct userData *uData;
5204 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5207 reply("CSMSG_NOT_USER", channel->name);
5210 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5211 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5213 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5216 static MODCMD_FUNC(user_opt_autoinvite)
5218 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5221 static MODCMD_FUNC(user_opt_info)
5223 struct userData *uData;
5226 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5230 /* If they got past the command restrictions (which require access)
5231 * but fail this test, we have some fool with security override on.
5233 reply("CSMSG_NOT_USER", channel->name);
5239 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5242 if(infoline[0] == '*' && infoline[1] == 0)
5245 uData->info = strdup(infoline);
5248 reply("CSMSG_USET_INFO", uData->info);
5250 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5254 struct svccmd_list uset_shows_list;
5256 static CHANSERV_FUNC(cmd_uset)
5258 struct svccmd *subcmd;
5262 /* Check if we need to (re-)initialize uset_shows_list. */
5263 if(!uset_shows_list.used)
5267 "NoAutoOp", "AutoInvite", "Info"
5270 if(!uset_shows_list.size)
5272 uset_shows_list.size = ArrayLength(options);
5273 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5275 for(ii = 0; ii < ArrayLength(options); ii++)
5277 const char *name = options[ii];
5278 sprintf(buf, "%s %s", argv[0], name);
5279 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5282 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5285 svccmd_list_append(&uset_shows_list, subcmd);
5291 /* Do this so options are presented in a consistent order. */
5292 reply("CSMSG_USER_OPTIONS");
5293 for(ii = 0; ii < uset_shows_list.used; ii++)
5294 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5298 sprintf(buf, "%s %s", argv[0], argv[1]);
5299 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5302 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5306 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5309 static CHANSERV_FUNC(cmd_giveownership)
5311 struct handle_info *new_owner_hi;
5312 struct userData *new_owner, *curr_user;
5313 struct chanData *cData = channel->channel_info;
5314 struct do_not_register *dnr;
5316 unsigned short co_access;
5317 char reason[MAXLEN];
5320 curr_user = GetChannelAccess(cData, user->handle_info);
5321 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5322 if(!curr_user || (curr_user->access != UL_OWNER))
5324 struct userData *owner = NULL;
5325 for(curr_user = channel->channel_info->users;
5327 curr_user = curr_user->next)
5329 if(curr_user->access != UL_OWNER)
5333 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5340 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5342 if(new_owner_hi == user->handle_info)
5344 reply("CSMSG_NO_TRANSFER_SELF");
5347 new_owner = GetChannelAccess(cData, new_owner_hi);
5350 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5353 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5355 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5358 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5359 if(!IsHelping(user))
5360 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5362 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5365 if(new_owner->access >= UL_COOWNER)
5366 co_access = new_owner->access;
5368 co_access = UL_COOWNER;
5369 new_owner->access = UL_OWNER;
5371 curr_user->access = co_access;
5372 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5373 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5374 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5378 static CHANSERV_FUNC(cmd_suspend)
5380 struct handle_info *hi;
5381 struct userData *self, *target;
5384 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5385 self = GetChannelUser(channel->channel_info, user->handle_info);
5386 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5388 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5391 if(target->access >= self->access)
5393 reply("MSG_USER_OUTRANKED", hi->handle);
5396 if(target->flags & USER_SUSPENDED)
5398 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5403 target->present = 0;
5406 target->flags |= USER_SUSPENDED;
5407 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5411 static CHANSERV_FUNC(cmd_unsuspend)
5413 struct handle_info *hi;
5414 struct userData *self, *target;
5417 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5418 self = GetChannelUser(channel->channel_info, user->handle_info);
5419 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5421 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5424 if(target->access >= self->access)
5426 reply("MSG_USER_OUTRANKED", hi->handle);
5429 if(!(target->flags & USER_SUSPENDED))
5431 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5434 target->flags &= ~USER_SUSPENDED;
5435 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5439 static MODCMD_FUNC(cmd_deleteme)
5441 struct handle_info *hi;
5442 struct userData *target;
5443 const char *confirm_string;
5444 unsigned short access;
5447 hi = user->handle_info;
5448 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5450 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5453 if(target->access == UL_OWNER)
5455 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5458 confirm_string = make_confirmation_string(target);
5459 if((argc < 2) || strcmp(argv[1], confirm_string))
5461 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5464 access = target->access;
5465 channel_name = strdup(channel->name);
5466 del_channel_user(target, 1);
5467 reply("CSMSG_DELETED_YOU", access, channel_name);
5473 chanserv_refresh_topics(UNUSED_ARG(void *data))
5475 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5476 struct chanData *cData;
5479 for(cData = channelList; cData; cData = cData->next)
5481 if(IsSuspended(cData))
5483 opt = cData->chOpts[chTopicRefresh];
5486 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5489 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5490 cData->last_refresh = refresh_num;
5492 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5495 static CHANSERV_FUNC(cmd_unf)
5499 char response[MAXLEN];
5500 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5501 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5502 irc_privmsg(cmd->parent->bot, channel->name, response);
5505 reply("CSMSG_UNF_RESPONSE");
5509 static CHANSERV_FUNC(cmd_ping)
5513 char response[MAXLEN];
5514 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5515 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5516 irc_privmsg(cmd->parent->bot, channel->name, response);
5519 reply("CSMSG_PING_RESPONSE");
5523 static CHANSERV_FUNC(cmd_wut)
5527 char response[MAXLEN];
5528 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5529 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5530 irc_privmsg(cmd->parent->bot, channel->name, response);
5533 reply("CSMSG_WUT_RESPONSE");
5537 static CHANSERV_FUNC(cmd_8ball)
5539 unsigned int i, j, accum;
5544 for(i=1; i<argc; i++)
5545 for(j=0; argv[i][j]; j++)
5546 accum = (accum << 5) - accum + toupper(argv[i][j]);
5547 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5550 char response[MAXLEN];
5551 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5552 irc_privmsg(cmd->parent->bot, channel->name, response);
5555 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5559 static CHANSERV_FUNC(cmd_d)
5561 unsigned long sides, count, modifier, ii, total;
5562 char response[MAXLEN], *sep;
5566 if((count = strtoul(argv[1], &sep, 10)) < 1)
5576 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5577 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5581 else if((sep[0] == '-') && isdigit(sep[1]))
5582 modifier = strtoul(sep, NULL, 10);
5583 else if((sep[0] == '+') && isdigit(sep[1]))
5584 modifier = strtoul(sep+1, NULL, 10);
5591 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5596 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5599 for(total = ii = 0; ii < count; ++ii)
5600 total += (rand() % sides) + 1;
5603 if((count > 1) || modifier)
5605 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5606 sprintf(response, fmt, total, count, sides, modifier);
5610 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5611 sprintf(response, fmt, total, sides);
5614 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5616 send_message_type(4, user, cmd->parent->bot, "%s", response);
5620 static CHANSERV_FUNC(cmd_huggle)
5622 /* CTCP must be via PRIVMSG, never notice */
5624 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5626 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5631 chanserv_adjust_limit(void *data)
5633 struct mod_chanmode change;
5634 struct chanData *cData = data;
5635 struct chanNode *channel = cData->channel;
5638 if(IsSuspended(cData))
5641 cData->limitAdjusted = now;
5642 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5643 if(cData->modes.modes_set & MODE_LIMIT)
5645 if(limit > cData->modes.new_limit)
5646 limit = cData->modes.new_limit;
5647 else if(limit == cData->modes.new_limit)
5651 mod_chanmode_init(&change);
5652 change.modes_set = MODE_LIMIT;
5653 change.new_limit = limit;
5654 mod_chanmode_announce(chanserv, channel, &change);
5658 handle_new_channel(struct chanNode *channel)
5660 struct chanData *cData;
5662 if(!(cData = channel->channel_info))
5665 if(cData->modes.modes_set || cData->modes.modes_clear)
5666 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5668 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5669 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5672 /* Welcome to my worst nightmare. Warning: Read (or modify)
5673 the code below at your own risk. */
5675 handle_join(struct modeNode *mNode)
5677 struct mod_chanmode change;
5678 struct userNode *user = mNode->user;
5679 struct chanNode *channel = mNode->channel;
5680 struct chanData *cData;
5681 struct userData *uData = NULL;
5682 struct banData *bData;
5683 struct handle_info *handle;
5684 unsigned int modes = 0, info = 0;
5687 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5690 cData = channel->channel_info;
5691 if(channel->members.used > cData->max)
5692 cData->max = channel->members.used;
5694 /* Check for bans. If they're joining through a ban, one of two
5696 * 1: Join during a netburst, by riding the break. Kick them
5697 * unless they have ops or voice in the channel.
5698 * 2: They're allowed to join through the ban (an invite in
5699 * ircu2.10, or a +e on Hybrid, or something).
5700 * If they're not joining through a ban, and the banlist is not
5701 * full, see if they're on the banlist for the channel. If so,
5704 if(user->uplink->burst && !mNode->modes)
5707 for(ii = 0; ii < channel->banlist.used; ii++)
5709 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5711 /* Riding a netburst. Naughty. */
5712 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5718 mod_chanmode_init(&change);
5720 if(channel->banlist.used < MAXBANS)
5722 /* Not joining through a ban. */
5723 for(bData = cData->bans;
5724 bData && !user_matches_glob(user, bData->mask, 1);
5725 bData = bData->next);
5729 char kick_reason[MAXLEN];
5730 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5732 bData->triggered = now;
5733 if(bData != cData->bans)
5735 /* Shuffle the ban to the head of the list. */
5737 bData->next->prev = bData->prev;
5739 bData->prev->next = bData->next;
5742 bData->next = cData->bans;
5745 cData->bans->prev = bData;
5746 cData->bans = bData;
5749 change.args[0].mode = MODE_BAN;
5750 change.args[0].hostmask = bData->mask;
5751 mod_chanmode_announce(chanserv, channel, &change);
5752 KickChannelUser(user, channel, chanserv, kick_reason);
5757 /* ChanServ will not modify the limits in join-flooded channels.
5758 It will also skip DynLimit processing when the user (or srvx)
5759 is bursting in, because there are likely more incoming. */
5760 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5761 && !user->uplink->burst
5762 && !channel->join_flooded
5763 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5765 /* The user count has begun "bumping" into the channel limit,
5766 so set a timer to raise the limit a bit. Any previous
5767 timers are removed so three incoming users within the delay
5768 results in one limit change, not three. */
5770 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5771 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5774 if(channel->join_flooded)
5776 /* don't automatically give ops or voice during a join flood */
5778 else if(cData->lvlOpts[lvlGiveOps] == 0)
5779 modes |= MODE_CHANOP;
5780 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5781 modes |= MODE_VOICE;
5783 greeting = cData->greeting;
5784 if(user->handle_info)
5786 handle = user->handle_info;
5788 if(IsHelper(user) && !IsHelping(user))
5791 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5793 if(channel == chanserv_conf.support_channels.list[ii])
5795 HANDLE_SET_FLAG(user->handle_info, HELPING);
5801 uData = GetTrueChannelAccess(cData, handle);
5802 if(uData && !IsUserSuspended(uData))
5804 /* Ops and above were handled by the above case. */
5805 if(IsUserAutoOp(uData))
5807 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5808 modes |= MODE_CHANOP;
5809 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5810 modes |= MODE_VOICE;
5812 if(uData->access >= UL_PRESENT)
5813 cData->visited = now;
5814 if(cData->user_greeting)
5815 greeting = cData->user_greeting;
5817 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5818 && ((now - uData->seen) >= chanserv_conf.info_delay)
5825 if(!user->uplink->burst)
5829 if(modes & MODE_CHANOP)
5830 modes &= ~MODE_VOICE;
5831 change.args[0].mode = modes;
5832 change.args[0].member = mNode;
5833 mod_chanmode_announce(chanserv, channel, &change);
5835 if(greeting && !user->uplink->burst)
5836 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5838 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5844 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5846 struct mod_chanmode change;
5847 struct userData *channel;
5848 unsigned int ii, jj;
5850 if(!user->handle_info)
5853 mod_chanmode_init(&change);
5855 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5857 struct chanNode *cn;
5858 struct modeNode *mn;
5859 if(IsUserSuspended(channel)
5860 || IsSuspended(channel->channel)
5861 || !(cn = channel->channel->channel))
5864 mn = GetUserMode(cn, user);
5867 if(!IsUserSuspended(channel)
5868 && IsUserAutoInvite(channel)
5869 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5870 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5872 irc_invite(chanserv, user, cn);
5876 if(channel->access >= UL_PRESENT)
5877 channel->channel->visited = now;
5879 if(IsUserAutoOp(channel))
5881 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5882 change.args[0].mode = MODE_CHANOP;
5883 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5884 change.args[0].mode = MODE_VOICE;
5886 change.args[0].mode = 0;
5887 change.args[0].member = mn;
5888 if(change.args[0].mode)
5889 mod_chanmode_announce(chanserv, cn, &change);
5892 channel->seen = now;
5893 channel->present = 1;
5896 for(ii = 0; ii < user->channels.used; ++ii)
5898 struct chanNode *channel = user->channels.list[ii]->channel;
5899 struct banData *ban;
5901 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5902 || !channel->channel_info)
5904 for(jj = 0; jj < channel->banlist.used; ++jj)
5905 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5907 if(jj < channel->banlist.used)
5909 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5911 char kick_reason[MAXLEN];
5912 if(!user_matches_glob(user, ban->mask, 1))
5914 change.args[0].mode = MODE_BAN;
5915 change.args[0].hostmask = ban->mask;
5916 mod_chanmode_announce(chanserv, channel, &change);
5917 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5918 KickChannelUser(user, channel, chanserv, kick_reason);
5919 ban->triggered = now;
5924 if(IsSupportHelper(user))
5926 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5928 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5930 HANDLE_SET_FLAG(user->handle_info, HELPING);
5938 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5940 struct chanData *cData;
5941 struct userData *uData;
5943 cData = channel->channel_info;
5944 if(!cData || IsSuspended(cData) || IsLocal(user))
5947 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5949 /* Allow for a bit of padding so that the limit doesn't
5950 track the user count exactly, which could get annoying. */
5951 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5953 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5954 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5958 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5960 scan_user_presence(uData, user);
5964 if(IsHelping(user) && IsSupportHelper(user))
5966 unsigned int ii, jj;
5967 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5969 for(jj = 0; jj < user->channels.used; ++jj)
5970 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5972 if(jj < user->channels.used)
5975 if(ii == chanserv_conf.support_channels.used)
5976 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5981 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5983 struct userData *uData;
5985 if(!channel->channel_info || !kicker || IsService(kicker)
5986 || (kicker == victim) || IsSuspended(channel->channel_info)
5987 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5990 if(protect_user(victim, kicker, channel->channel_info))
5992 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
5993 KickChannelUser(kicker, channel, chanserv, reason);
5996 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6001 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6003 struct chanData *cData;
6005 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6008 cData = channel->channel_info;
6009 if(bad_topic(channel, user, channel->topic))
6011 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6012 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6013 SetChannelTopic(channel, chanserv, old_topic, 1);
6014 else if(cData->topic)
6015 SetChannelTopic(channel, chanserv, cData->topic, 1);
6018 /* With topicsnarf, grab the topic and save it as the default topic. */
6019 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6022 cData->topic = strdup(channel->topic);
6028 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6030 struct mod_chanmode *bounce = NULL;
6031 unsigned int bnc, ii;
6034 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6037 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6038 && mode_lock_violated(&channel->channel_info->modes, change))
6040 char correct[MAXLEN];
6041 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6042 mod_chanmode_format(&channel->channel_info->modes, correct);
6043 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6045 for(ii = bnc = 0; ii < change->argc; ++ii)
6047 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6049 const struct userNode *victim = change->args[ii].member->user;
6050 if(!protect_user(victim, user, channel->channel_info))
6053 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6056 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6057 bounce->args[bnc].member = GetUserMode(channel, user);
6058 if(bounce->args[bnc].member)
6062 bounce->args[bnc].mode = MODE_CHANOP;
6063 bounce->args[bnc].member = change->args[ii].member;
6065 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6067 else if(change->args[ii].mode & MODE_CHANOP)
6069 const struct userNode *victim = change->args[ii].member->user;
6070 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6073 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6074 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6075 bounce->args[bnc].member = change->args[ii].member;
6078 else if(change->args[ii].mode & MODE_BAN)
6080 const char *ban = change->args[ii].hostmask;
6081 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6084 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6085 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6086 bounce->args[bnc].hostmask = ban;
6088 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6093 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6094 mod_chanmode_announce(chanserv, channel, bounce);
6095 mod_chanmode_free(bounce);
6100 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6102 struct chanNode *channel;
6103 struct banData *bData;
6104 struct mod_chanmode change;
6105 unsigned int ii, jj;
6106 char kick_reason[MAXLEN];
6108 mod_chanmode_init(&change);
6110 change.args[0].mode = MODE_BAN;
6111 for(ii = 0; ii < user->channels.used; ++ii)
6113 channel = user->channels.list[ii]->channel;
6114 /* Need not check for bans if they're opped or voiced. */
6115 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6117 /* Need not check for bans unless channel registration is active. */
6118 if(!channel->channel_info || IsSuspended(channel->channel_info))
6120 /* Look for a matching ban already on the channel. */
6121 for(jj = 0; jj < channel->banlist.used; ++jj)
6122 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6124 /* Need not act if we found one. */
6125 if(jj < channel->banlist.used)
6127 /* Look for a matching ban in this channel. */
6128 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6130 if(!user_matches_glob(user, bData->mask, 1))
6132 change.args[0].hostmask = bData->mask;
6133 mod_chanmode_announce(chanserv, channel, &change);
6134 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6135 KickChannelUser(user, channel, chanserv, kick_reason);
6136 bData->triggered = now;
6137 break; /* we don't need to check any more bans in the channel */
6142 static void handle_rename(struct handle_info *handle, const char *old_handle)
6144 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6148 dict_remove2(handle_dnrs, old_handle, 1);
6149 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6150 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6155 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6157 struct userNode *h_user;
6159 if(handle->channels)
6161 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6162 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6164 while(handle->channels)
6165 del_channel_user(handle->channels, 1);
6170 handle_server_link(UNUSED_ARG(struct server *server))
6172 struct chanData *cData;
6174 for(cData = channelList; cData; cData = cData->next)
6176 if(!IsSuspended(cData))
6177 cData->may_opchan = 1;
6178 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6179 && !cData->channel->join_flooded
6180 && ((cData->channel->limit - cData->channel->members.used)
6181 < chanserv_conf.adjust_threshold))
6183 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6184 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6190 chanserv_conf_read(void)
6194 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6195 struct mod_chanmode *change;
6196 struct string_list *strlist;
6197 struct chanNode *chan;
6200 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6202 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6205 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6206 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6207 chanserv_conf.support_channels.used = 0;
6208 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6210 for(ii = 0; ii < strlist->used; ++ii)
6212 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6215 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6217 channelList_append(&chanserv_conf.support_channels, chan);
6220 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6223 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6226 chan = AddChannel(str, now, str2, NULL);
6228 channelList_append(&chanserv_conf.support_channels, chan);
6230 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6231 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6232 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6233 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6234 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6235 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6236 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6237 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6238 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6239 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6240 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6241 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6242 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6243 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6244 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6245 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6246 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6247 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6248 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6249 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6250 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6252 NickChange(chanserv, str, 0);
6253 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6254 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6255 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6256 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6257 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6258 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6259 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6260 chanserv_conf.max_owned = str ? atoi(str) : 5;
6261 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6262 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6263 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6264 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6265 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6266 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6267 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6270 safestrncpy(mode_line, str, sizeof(mode_line));
6271 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6272 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6274 chanserv_conf.default_modes = *change;
6275 mod_chanmode_free(change);
6277 free_string_list(chanserv_conf.set_shows);
6278 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6280 strlist = string_list_copy(strlist);
6283 static const char *list[] = {
6284 /* free form text */
6285 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6286 /* options based on user level */
6287 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6288 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6289 /* multiple choice options */
6290 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6291 /* binary options */
6292 "DynLimit", "NoDelete",
6297 strlist = alloc_string_list(ArrayLength(list)-1);
6298 for(ii=0; list[ii]; ii++)
6299 string_list_append(strlist, strdup(list[ii]));
6301 chanserv_conf.set_shows = strlist;
6302 /* We don't look things up now, in case the list refers to options
6303 * defined by modules initialized after this point. Just mark the
6304 * function list as invalid, so it will be initialized.
6306 set_shows_list.used = 0;
6307 free_string_list(chanserv_conf.eightball);
6308 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6311 strlist = string_list_copy(strlist);
6315 strlist = alloc_string_list(4);
6316 string_list_append(strlist, strdup("Yes."));
6317 string_list_append(strlist, strdup("No."));
6318 string_list_append(strlist, strdup("Maybe so."));
6320 chanserv_conf.eightball = strlist;
6321 free_string_list(chanserv_conf.old_ban_names);
6322 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6324 strlist = string_list_copy(strlist);
6326 strlist = alloc_string_list(2);
6327 chanserv_conf.old_ban_names = strlist;
6331 chanserv_note_type_read(const char *key, struct record_data *rd)
6334 struct note_type *ntype;
6337 if(!(obj = GET_RECORD_OBJECT(rd)))
6339 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6342 if(!(ntype = chanserv_create_note_type(key)))
6344 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6348 /* Figure out set access */
6349 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6351 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6352 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6354 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6356 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6357 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6359 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6361 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6365 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6366 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6367 ntype->set_access.min_opserv = 0;
6370 /* Figure out visibility */
6371 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6372 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6373 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6374 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6375 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6376 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6377 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6378 ntype->visible_type = NOTE_VIS_ALL;
6380 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6382 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6383 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6387 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6389 struct handle_info *handle;
6390 struct userData *uData;
6391 char *seen, *inf, *flags;
6393 unsigned short access;
6395 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6397 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6401 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6402 if(access > UL_OWNER)
6404 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6408 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6409 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6410 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6411 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6412 handle = get_handle_info(key);
6415 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6419 uData = add_channel_user(chan, handle, access, last_seen, inf);
6420 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6424 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6426 struct banData *bData;
6427 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6428 time_t set_time, triggered_time, expires_time;
6430 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6432 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6436 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6437 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6438 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6439 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6440 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6441 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6443 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6444 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6446 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6448 expires_time = set_time + atoi(s_duration);
6452 if(expires_time && (expires_time < now))
6455 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6458 static struct suspended *
6459 chanserv_read_suspended(dict_t obj)
6461 struct suspended *suspended = calloc(1, sizeof(*suspended));
6465 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6466 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6467 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6468 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6469 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6470 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6471 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6472 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6473 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6474 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6479 chanserv_channel_read(const char *key, struct record_data *hir)
6481 struct suspended *suspended;
6482 struct mod_chanmode *modes;
6483 struct chanNode *cNode;
6484 struct chanData *cData;
6485 struct dict *channel, *obj;
6486 char *str, *argv[10];
6490 channel = hir->d.object;
6492 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6495 cNode = AddChannel(key, now, NULL, NULL);
6498 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6501 cData = register_channel(cNode, str);
6504 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6508 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6510 enum levelOption lvlOpt;
6511 enum charOption chOpt;
6513 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6514 cData->flags = atoi(str);
6516 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6518 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6520 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6521 else if(levelOptions[lvlOpt].old_flag)
6523 if(cData->flags & levelOptions[lvlOpt].old_flag)
6524 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6526 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6530 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6532 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6534 cData->chOpts[chOpt] = str[0];
6537 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6539 enum levelOption lvlOpt;
6540 enum charOption chOpt;
6543 cData->flags = base64toint(str, 5);
6544 count = strlen(str += 5);
6545 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6548 if(levelOptions[lvlOpt].old_flag)
6550 if(cData->flags & levelOptions[lvlOpt].old_flag)
6551 lvl = levelOptions[lvlOpt].flag_value;
6553 lvl = levelOptions[lvlOpt].default_value;
6555 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6557 case 'c': lvl = UL_COOWNER; break;
6558 case 'm': lvl = UL_MASTER; break;
6559 case 'n': lvl = UL_OWNER+1; break;
6560 case 'o': lvl = UL_OP; break;
6561 case 'p': lvl = UL_PEON; break;
6562 case 'w': lvl = UL_OWNER; break;
6563 default: lvl = 0; break;
6565 cData->lvlOpts[lvlOpt] = lvl;
6567 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6568 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6571 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6573 suspended = chanserv_read_suspended(obj);
6574 cData->suspended = suspended;
6575 suspended->cData = cData;
6576 /* We could use suspended->expires and suspended->revoked to
6577 * set the CHANNEL_SUSPENDED flag, but we don't. */
6579 else if(cData->flags & CHANNEL_SUSPENDED)
6581 suspended = calloc(1, sizeof(*suspended));
6582 suspended->issued = 0;
6583 suspended->revoked = 0;
6584 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6585 suspended->expires = str ? atoi(str) : 0;
6586 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6587 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6588 suspended->reason = strdup(str ? str : "No reason");
6589 suspended->previous = NULL;
6590 cData->suspended = suspended;
6591 suspended->cData = cData;
6596 if((cData->flags & CHANNEL_SUSPENDED)
6597 && suspended->expires
6598 && (suspended->expires <= now))
6600 cData->flags &= ~CHANNEL_SUSPENDED;
6603 if(!(cData->flags & CHANNEL_SUSPENDED))
6605 struct mod_chanmode change;
6606 mod_chanmode_init(&change);
6608 change.args[0].mode = MODE_CHANOP;
6609 change.args[0].member = AddChannelUser(chanserv, cNode);
6610 mod_chanmode_announce(chanserv, cNode, &change);
6612 else if(suspended->expires > now)
6614 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6617 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6618 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6619 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6620 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6621 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6622 cData->max = str ? atoi(str) : 0;
6623 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6624 cData->greeting = str ? strdup(str) : NULL;
6625 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6626 cData->user_greeting = str ? strdup(str) : NULL;
6627 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6628 cData->topic_mask = str ? strdup(str) : NULL;
6629 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6630 cData->topic = str ? strdup(str) : NULL;
6632 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6633 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6634 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6635 cData->modes = *modes;
6636 if(cData->modes.argc > 1)
6637 cData->modes.argc = 1;
6638 if(!IsSuspended(cData))
6639 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6640 mod_chanmode_free(modes);
6643 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6644 for(it = dict_first(obj); it; it = iter_next(it))
6645 user_read_helper(iter_key(it), iter_data(it), cData);
6647 if(!cData->users && !IsProtected(cData))
6649 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6650 unregister_channel(cData, "has empty user list.");
6654 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6655 for(it = dict_first(obj); it; it = iter_next(it))
6656 ban_read_helper(iter_key(it), iter_data(it), cData);
6658 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6659 for(it = dict_first(obj); it; it = iter_next(it))
6661 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6662 struct record_data *rd = iter_data(it);
6663 const char *note, *setter;
6665 if(rd->type != RECDB_OBJECT)
6667 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6671 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6673 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6675 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6679 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6680 if(!setter) setter = "<unknown>";
6681 chanserv_add_channel_note(cData, ntype, setter, note);
6689 chanserv_dnr_read(const char *key, struct record_data *hir)
6691 const char *setter, *reason, *str;
6692 struct do_not_register *dnr;
6694 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6697 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6700 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6703 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6706 dnr = chanserv_add_dnr(key, setter, reason);
6709 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6711 dnr->set = atoi(str);
6717 chanserv_saxdb_read(struct dict *database)
6719 struct dict *section;
6722 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6723 for(it = dict_first(section); it; it = iter_next(it))
6724 chanserv_note_type_read(iter_key(it), iter_data(it));
6726 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6727 for(it = dict_first(section); it; it = iter_next(it))
6728 chanserv_channel_read(iter_key(it), iter_data(it));
6730 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6731 for(it = dict_first(section); it; it = iter_next(it))
6732 chanserv_dnr_read(iter_key(it), iter_data(it));
6738 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6740 int high_present = 0;
6741 saxdb_start_record(ctx, KEY_USERS, 1);
6742 for(; uData; uData = uData->next)
6744 if((uData->access >= UL_PRESENT) && uData->present)
6746 saxdb_start_record(ctx, uData->handle->handle, 0);
6747 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6748 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6750 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6752 saxdb_write_string(ctx, KEY_INFO, uData->info);
6753 saxdb_end_record(ctx);
6755 saxdb_end_record(ctx);
6756 return high_present;
6760 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6764 saxdb_start_record(ctx, KEY_BANS, 1);
6765 for(; bData; bData = bData->next)
6767 saxdb_start_record(ctx, bData->mask, 0);
6768 saxdb_write_int(ctx, KEY_SET, bData->set);
6769 if(bData->triggered)
6770 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6772 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6774 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6776 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6777 saxdb_end_record(ctx);
6779 saxdb_end_record(ctx);
6783 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6785 saxdb_start_record(ctx, name, 0);
6786 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6787 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6789 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6791 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6793 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6795 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6796 saxdb_end_record(ctx);
6800 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6804 enum levelOption lvlOpt;
6805 enum charOption chOpt;
6807 saxdb_start_record(ctx, channel->channel->name, 1);
6809 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6810 saxdb_write_int(ctx, KEY_MAX, channel->max);
6812 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6813 if(channel->registrar)
6814 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6815 if(channel->greeting)
6816 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6817 if(channel->user_greeting)
6818 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6819 if(channel->topic_mask)
6820 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6821 if(channel->suspended)
6822 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6824 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6825 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6826 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6827 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6828 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6830 buf[0] = channel->chOpts[chOpt];
6832 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6834 saxdb_end_record(ctx);
6836 if(channel->modes.modes_set || channel->modes.modes_clear)
6838 mod_chanmode_format(&channel->modes, buf);
6839 saxdb_write_string(ctx, KEY_MODES, buf);
6842 high_present = chanserv_write_users(ctx, channel->users);
6843 chanserv_write_bans(ctx, channel->bans);
6845 if(dict_size(channel->notes))
6849 saxdb_start_record(ctx, KEY_NOTES, 1);
6850 for(it = dict_first(channel->notes); it; it = iter_next(it))
6852 struct note *note = iter_data(it);
6853 saxdb_start_record(ctx, iter_key(it), 0);
6854 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6855 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6856 saxdb_end_record(ctx);
6858 saxdb_end_record(ctx);
6861 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6862 saxdb_end_record(ctx);
6866 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6870 saxdb_start_record(ctx, ntype->name, 0);
6871 switch(ntype->set_access_type)
6873 case NOTE_SET_CHANNEL_ACCESS:
6874 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6876 case NOTE_SET_CHANNEL_SETTER:
6877 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6879 case NOTE_SET_PRIVILEGED: default:
6880 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6883 switch(ntype->visible_type)
6885 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6886 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6887 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6889 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6890 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6891 saxdb_end_record(ctx);
6895 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6897 struct do_not_register *dnr;
6900 for(it = dict_first(dnrs); it; it = iter_next(it))
6902 dnr = iter_data(it);
6903 saxdb_start_record(ctx, dnr->chan_name, 0);
6905 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6906 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6907 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6908 saxdb_end_record(ctx);
6913 chanserv_saxdb_write(struct saxdb_context *ctx)
6916 struct chanData *channel;
6919 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6920 for(it = dict_first(note_types); it; it = iter_next(it))
6921 chanserv_write_note_type(ctx, iter_data(it));
6922 saxdb_end_record(ctx);
6925 saxdb_start_record(ctx, KEY_DNR, 1);
6926 write_dnrs_helper(ctx, handle_dnrs);
6927 write_dnrs_helper(ctx, plain_dnrs);
6928 write_dnrs_helper(ctx, mask_dnrs);
6929 saxdb_end_record(ctx);
6932 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6933 for(channel = channelList; channel; channel = channel->next)
6934 chanserv_write_channel(ctx, channel);
6935 saxdb_end_record(ctx);
6941 chanserv_db_cleanup(void) {
6943 unreg_part_func(handle_part);
6945 unregister_channel(channelList, "terminating.");
6946 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6947 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6948 free(chanserv_conf.support_channels.list);
6949 dict_delete(handle_dnrs);
6950 dict_delete(plain_dnrs);
6951 dict_delete(mask_dnrs);
6952 dict_delete(note_types);
6953 free_string_list(chanserv_conf.eightball);
6954 free_string_list(chanserv_conf.old_ban_names);
6955 free_string_list(chanserv_conf.set_shows);
6956 free(set_shows_list.list);
6957 free(uset_shows_list.list);
6960 struct userData *helper = helperList;
6961 helperList = helperList->next;
6966 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6967 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6968 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6971 init_chanserv(const char *nick)
6973 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6974 conf_register_reload(chanserv_conf_read);
6976 reg_server_link_func(handle_server_link);
6978 reg_new_channel_func(handle_new_channel);
6979 reg_join_func(handle_join);
6980 reg_part_func(handle_part);
6981 reg_kick_func(handle_kick);
6982 reg_topic_func(handle_topic);
6983 reg_mode_change_func(handle_mode);
6984 reg_nick_change_func(handle_nick_change);
6986 reg_auth_func(handle_auth);
6987 reg_handle_rename_func(handle_rename);
6988 reg_unreg_func(handle_unreg);
6990 handle_dnrs = dict_new();
6991 dict_set_free_data(handle_dnrs, free);
6992 plain_dnrs = dict_new();
6993 dict_set_free_data(plain_dnrs, free);
6994 mask_dnrs = dict_new();
6995 dict_set_free_data(mask_dnrs, free);
6997 reg_svccmd_unbind_func(handle_svccmd_unbind);
6998 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
6999 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7000 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7001 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7002 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7003 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7004 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7005 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7006 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7008 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7009 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7011 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7012 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7013 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7014 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7015 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7017 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7018 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7019 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7020 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7021 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7023 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7024 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7025 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7026 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7028 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7029 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7030 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7031 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7032 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7033 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7034 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7035 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7037 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7038 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7039 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7040 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7041 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7042 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7043 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7044 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7045 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7046 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7047 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7048 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7049 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7050 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7052 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7053 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7054 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7055 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7056 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7058 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7059 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7061 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7062 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7063 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7064 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7065 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7066 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7067 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7068 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7069 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7070 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7071 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7073 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7074 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7076 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7077 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7078 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7079 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7081 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7082 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7083 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7084 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7085 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7087 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7088 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7089 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7090 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7091 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7092 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7094 /* Channel options */
7095 DEFINE_CHANNEL_OPTION(defaulttopic);
7096 DEFINE_CHANNEL_OPTION(topicmask);
7097 DEFINE_CHANNEL_OPTION(greeting);
7098 DEFINE_CHANNEL_OPTION(usergreeting);
7099 DEFINE_CHANNEL_OPTION(modes);
7100 DEFINE_CHANNEL_OPTION(enfops);
7101 DEFINE_CHANNEL_OPTION(giveops);
7102 DEFINE_CHANNEL_OPTION(protect);
7103 DEFINE_CHANNEL_OPTION(enfmodes);
7104 DEFINE_CHANNEL_OPTION(enftopic);
7105 DEFINE_CHANNEL_OPTION(pubcmd);
7106 DEFINE_CHANNEL_OPTION(givevoice);
7107 DEFINE_CHANNEL_OPTION(userinfo);
7108 DEFINE_CHANNEL_OPTION(dynlimit);
7109 DEFINE_CHANNEL_OPTION(topicsnarf);
7110 DEFINE_CHANNEL_OPTION(nodelete);
7111 DEFINE_CHANNEL_OPTION(toys);
7112 DEFINE_CHANNEL_OPTION(setters);
7113 DEFINE_CHANNEL_OPTION(topicrefresh);
7114 DEFINE_CHANNEL_OPTION(ctcpusers);
7115 DEFINE_CHANNEL_OPTION(ctcpreaction);
7116 DEFINE_CHANNEL_OPTION(inviteme);
7117 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7119 /* Alias set topic to set defaulttopic for compatibility. */
7120 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7123 DEFINE_USER_OPTION(noautoop);
7124 DEFINE_USER_OPTION(autoinvite);
7125 DEFINE_USER_OPTION(info);
7127 /* Alias uset autovoice to uset autoop. */
7128 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7130 note_types = dict_new();
7131 dict_set_free_data(note_types, chanserv_deref_note_type);
7134 chanserv = AddService(nick, "Channel Services");
7135 service_register(chanserv, '!');
7136 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7138 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7140 if(chanserv_conf.channel_expire_frequency)
7141 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7143 if(chanserv_conf.refresh_period)
7145 time_t next_refresh;
7146 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7147 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7150 reg_exit_func(chanserv_db_cleanup);
7151 message_register_table(msgtab);