1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
46 #define KEY_SWITCH_TIMEOUT "switch_timeout"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (0)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
199 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
202 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
203 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
204 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
205 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
206 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
207 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
208 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
209 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
210 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
211 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
212 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
213 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
214 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
215 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
216 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
217 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
219 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
221 /* Channel management */
222 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
223 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
224 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
226 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
227 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
228 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
229 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
230 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
231 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
232 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
234 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
235 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
236 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
237 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
238 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
239 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
240 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
241 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
242 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
243 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
244 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
245 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
246 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
247 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
248 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
249 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
250 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
251 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
252 { "CSMSG_SET_MODES", "$bModes $b %s" },
253 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
254 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
255 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
256 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
257 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
258 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
259 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
260 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
261 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
262 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
263 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
264 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
265 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
266 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
267 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
268 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
269 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
270 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
271 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
272 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
273 { "CSMSG_USET_INFO", "$bInfo $b %s" },
275 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
276 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
277 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
278 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
279 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
280 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
281 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
282 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
283 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
284 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
285 { "CSMSG_PROTECT_NONE", "No users will be protected." },
286 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
287 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
288 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
289 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
290 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
291 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
292 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
293 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
294 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
295 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
299 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
300 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
301 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
302 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
303 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
304 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
305 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
306 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
308 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
309 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
310 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
312 /* Channel userlist */
313 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
314 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
315 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
316 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
318 /* Channel note list */
319 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
320 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
321 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
322 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
323 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
324 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
325 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
326 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
327 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
328 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
329 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
330 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
331 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
332 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
333 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
335 /* Channel [un]suspension */
336 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
337 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
338 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
339 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
340 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
341 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
342 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
344 /* Access information */
345 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
346 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
347 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
348 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
349 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
350 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
351 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
352 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
353 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
354 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
355 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
357 /* Seen information */
358 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
359 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
360 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
361 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
363 /* Names information */
364 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
365 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
367 /* Channel information */
368 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
369 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
370 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
371 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
372 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
373 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
374 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
375 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
376 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
377 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
378 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
379 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
380 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
387 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
388 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
390 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
391 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
392 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
393 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
394 { "CSMSG_PEEK_OPS", "$bOps:$b" },
395 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
397 /* Network information */
398 { "CSMSG_NETWORK_INFO", "Network Information:" },
399 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
400 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
401 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
402 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
403 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
404 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
405 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
406 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
409 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
410 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
411 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
413 /* Channel searches */
414 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
415 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
416 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
417 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
419 /* Channel configuration */
420 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
421 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
422 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
425 { "CSMSG_USER_OPTIONS", "User Options:" },
426 { "CSMSG_USER_PROTECTED", "That user is protected." },
429 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
430 { "CSMSG_PING_RESPONSE", "Pong!" },
431 { "CSMSG_WUT_RESPONSE", "wut" },
432 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
433 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
434 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
435 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
436 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
437 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
438 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
441 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
445 /* eject_user and unban_user flags */
446 #define ACTION_KICK 0x0001
447 #define ACTION_BAN 0x0002
448 #define ACTION_ADD_BAN 0x0004
449 #define ACTION_ADD_TIMED_BAN 0x0008
450 #define ACTION_UNBAN 0x0010
451 #define ACTION_DEL_BAN 0x0020
453 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
454 #define MODELEN 40 + KEYLEN
458 #define CSFUNC_ARGS user, channel, argc, argv, cmd
460 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
461 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
462 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
463 reply("MSG_MISSING_PARAMS", argv[0]); \
467 DECLARE_LIST(dnrList, struct do_not_register *);
468 DEFINE_LIST(dnrList, struct do_not_register *);
470 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
472 struct userNode *chanserv;
474 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
475 static struct log_type *CS_LOG;
479 struct channelList support_channels;
480 struct mod_chanmode default_modes;
482 unsigned long db_backup_frequency;
483 unsigned long channel_expire_frequency;
486 unsigned int adjust_delay;
487 long channel_expire_delay;
488 unsigned int nodelete_level;
490 unsigned int adjust_threshold;
491 int join_flood_threshold;
493 unsigned int greeting_length;
494 unsigned int refresh_period;
496 unsigned int max_owned;
497 unsigned int max_chan_users;
498 unsigned int max_chan_bans;
499 unsigned int max_userinfo_length;
501 struct string_list *set_shows;
502 struct string_list *eightball;
503 struct string_list *old_ban_names;
505 const char *ctcp_short_ban_duration;
506 const char *ctcp_long_ban_duration;
508 const char *irc_operator_epithet;
509 const char *network_helper_epithet;
510 const char *support_helper_epithet;
515 struct userNode *user;
516 struct userNode *bot;
517 struct chanNode *channel;
519 unsigned short lowest;
520 unsigned short highest;
521 struct userData **users;
522 struct helpfile_table table;
525 enum note_access_type
527 NOTE_SET_CHANNEL_ACCESS,
528 NOTE_SET_CHANNEL_SETTER,
532 enum note_visible_type
535 NOTE_VIS_CHANNEL_USERS,
541 enum note_access_type set_access_type;
543 unsigned int min_opserv;
544 unsigned short min_ulevel;
546 enum note_visible_type visible_type;
547 unsigned int max_length;
554 struct note_type *type;
555 char setter[NICKSERV_HANDLE_LEN+1];
559 static unsigned int registered_channels;
560 static unsigned int banCount;
562 static const struct {
565 unsigned short level;
568 { "peon", "Peon", UL_PEON, '+' },
569 { "op", "Op", UL_OP, '@' },
570 { "master", "Master", UL_MASTER, '%' },
571 { "coowner", "Coowner", UL_COOWNER, '*' },
572 { "owner", "Owner", UL_OWNER, '!' },
573 { "helper", "BUG:", UL_HELPER, 'X' }
576 static const struct {
579 unsigned short default_value;
580 unsigned int old_idx;
581 unsigned int old_flag;
582 unsigned short flag_value;
584 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
585 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
586 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
587 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
588 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
589 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
590 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
591 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
592 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
593 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
594 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
597 struct charOptionValues {
600 } protectValues[] = {
601 { 'a', "CSMSG_PROTECT_ALL" },
602 { 'e', "CSMSG_PROTECT_EQUAL" },
603 { 'l', "CSMSG_PROTECT_LOWER" },
604 { 'n', "CSMSG_PROTECT_NONE" }
606 { 'd', "CSMSG_TOYS_DISABLED" },
607 { 'n', "CSMSG_TOYS_PRIVATE" },
608 { 'p', "CSMSG_TOYS_PUBLIC" }
609 }, topicRefreshValues[] = {
610 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
611 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
612 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
613 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
614 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
615 }, ctcpReactionValues[] = {
616 { 'k', "CSMSG_CTCPREACTION_KICK" },
617 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
618 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
619 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
622 static const struct {
626 unsigned int old_idx;
628 struct charOptionValues *values;
630 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
631 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
632 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
633 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
636 struct userData *helperList;
637 struct chanData *channelList;
638 static struct module *chanserv_module;
639 static unsigned int userCount;
641 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
642 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
643 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
646 user_level_from_name(const char *name, unsigned short clamp_level)
648 unsigned int level = 0, ii;
650 level = strtoul(name, NULL, 10);
651 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
652 if(!irccasecmp(name, accessLevels[ii].name))
653 level = accessLevels[ii].level;
654 if(level > clamp_level)
660 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
663 *minl = strtoul(arg, &sep, 10);
671 *maxl = strtoul(sep+1, &sep, 10);
679 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
681 struct userData *uData, **head;
683 if(!channel || !handle)
686 if(override && HANDLE_FLAGGED(handle, HELPING)
687 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
689 for(uData = helperList;
690 uData && uData->handle != handle;
691 uData = uData->next);
695 uData = calloc(1, sizeof(struct userData));
696 uData->handle = handle;
698 uData->access = UL_HELPER;
704 uData->next = helperList;
706 helperList->prev = uData;
714 for(uData = channel->users; uData; uData = uData->next)
715 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
718 head = &(channel->users);
721 if(uData && (uData != *head))
723 /* Shuffle the user to the head of whatever list he was in. */
725 uData->next->prev = uData->prev;
727 uData->prev->next = uData->next;
733 (**head).prev = uData;
740 /* Returns non-zero if user has at least the minimum access.
741 * exempt_owner is set when handling !set, so the owner can set things
744 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
746 struct userData *uData;
747 struct chanData *cData = channel->channel_info;
748 unsigned short minimum = cData->lvlOpts[opt];
751 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
754 if(minimum <= uData->access)
756 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
761 /* Scan for other users authenticated to the same handle
762 still in the channel. If so, keep them listed as present.
764 user is optional, if not null, it skips checking that userNode
765 (for the handle_part function) */
767 scan_user_presence(struct userData *uData, struct userNode *user)
771 if(IsSuspended(uData->channel)
772 || IsUserSuspended(uData)
773 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
785 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
787 unsigned int eflags, argc;
789 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
791 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
792 if(!channel->channel_info
793 || IsSuspended(channel->channel_info)
795 || !ircncasecmp(text, "ACTION ", 7))
797 /* Figure out the minimum level needed to CTCP the channel */
798 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
800 /* We need to enforce against them; do so. */
803 argv[1] = user->nick;
805 if(GetUserMode(channel, user))
806 eflags |= ACTION_KICK;
807 switch(channel->channel_info->chOpts[chCTCPReaction]) {
808 default: case 'k': /* just do the kick */ break;
810 eflags |= ACTION_BAN;
813 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
814 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
821 argv[argc++] = bad_ctcp_reason;
822 eject_user(chanserv, channel, argc, argv, NULL, eflags);
826 chanserv_create_note_type(const char *name)
828 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
829 strcpy(ntype->name, name);
831 dict_insert(note_types, ntype->name, ntype);
836 chanserv_deref_note_type(void *data)
838 struct note_type *ntype = data;
840 if(--ntype->refs > 0)
846 chanserv_flush_note_type(struct note_type *ntype)
848 struct chanData *cData;
849 for(cData = channelList; cData; cData = cData->next)
850 dict_remove(cData->notes, ntype->name);
854 chanserv_truncate_notes(struct note_type *ntype)
856 struct chanData *cData;
858 unsigned int size = sizeof(*note) + ntype->max_length;
860 for(cData = channelList; cData; cData = cData->next) {
861 note = dict_find(cData->notes, ntype->name, NULL);
864 if(strlen(note->note) <= ntype->max_length)
866 dict_remove2(cData->notes, ntype->name, 1);
867 note = realloc(note, size);
868 note->note[ntype->max_length] = 0;
869 dict_insert(cData->notes, ntype->name, note);
873 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
876 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
879 unsigned int len = strlen(text);
881 if(len > type->max_length) len = type->max_length;
882 note = calloc(1, sizeof(*note) + len);
884 strncpy(note->setter, setter, sizeof(note->setter)-1);
885 memcpy(note->note, text, len);
887 dict_insert(channel->notes, type->name, note);
893 chanserv_free_note(void *data)
895 struct note *note = data;
897 chanserv_deref_note_type(note->type);
898 assert(note->type->refs > 0); /* must use delnote to remove the type */
902 static MODCMD_FUNC(cmd_createnote) {
903 struct note_type *ntype;
904 unsigned int arg = 1, existed = 0, max_length;
906 if((ntype = dict_find(note_types, argv[1], NULL)))
909 ntype = chanserv_create_note_type(argv[arg]);
910 if(!irccasecmp(argv[++arg], "privileged"))
913 ntype->set_access_type = NOTE_SET_PRIVILEGED;
914 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
916 else if(!irccasecmp(argv[arg], "channel"))
918 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
921 reply("CSMSG_INVALID_ACCESS", argv[arg]);
924 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
925 ntype->set_access.min_ulevel = ulvl;
927 else if(!irccasecmp(argv[arg], "setter"))
929 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
933 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
937 if(!irccasecmp(argv[++arg], "privileged"))
938 ntype->visible_type = NOTE_VIS_PRIVILEGED;
939 else if(!irccasecmp(argv[arg], "channel_users"))
940 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
941 else if(!irccasecmp(argv[arg], "all"))
942 ntype->visible_type = NOTE_VIS_ALL;
944 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
948 if((arg+1) >= argc) {
949 reply("MSG_MISSING_PARAMS", argv[0]);
952 max_length = strtoul(argv[++arg], NULL, 0);
953 if(max_length < 20 || max_length > 450)
955 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
958 if(existed && (max_length < ntype->max_length))
960 ntype->max_length = max_length;
961 chanserv_truncate_notes(ntype);
963 ntype->max_length = max_length;
966 reply("CSMSG_NOTE_MODIFIED", ntype->name);
968 reply("CSMSG_NOTE_CREATED", ntype->name);
973 dict_remove(note_types, ntype->name);
977 static MODCMD_FUNC(cmd_removenote) {
978 struct note_type *ntype;
981 ntype = dict_find(note_types, argv[1], NULL);
982 force = (argc > 2) && !irccasecmp(argv[2], "force");
985 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
992 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
995 chanserv_flush_note_type(ntype);
997 dict_remove(note_types, argv[1]);
998 reply("CSMSG_NOTE_DELETED", argv[1]);
1003 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1007 if(orig->modes_set & change->modes_clear)
1009 if(orig->modes_clear & change->modes_set)
1011 if((orig->modes_set & MODE_KEY)
1012 && strcmp(orig->new_key, change->new_key))
1014 if((orig->modes_set & MODE_LIMIT)
1015 && (orig->new_limit != change->new_limit))
1020 static char max_length_text[MAXLEN+1][16];
1022 static struct helpfile_expansion
1023 chanserv_expand_variable(const char *variable)
1025 struct helpfile_expansion exp;
1027 if(!irccasecmp(variable, "notes"))
1030 exp.type = HF_TABLE;
1031 exp.value.table.length = 1;
1032 exp.value.table.width = 3;
1033 exp.value.table.flags = 0;
1034 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1035 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1036 exp.value.table.contents[0][0] = "Note Type";
1037 exp.value.table.contents[0][1] = "Visibility";
1038 exp.value.table.contents[0][2] = "Max Length";
1039 for(it=dict_first(note_types); it; it=iter_next(it))
1041 struct note_type *ntype = iter_data(it);
1044 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1045 row = exp.value.table.length++;
1046 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1047 exp.value.table.contents[row][0] = ntype->name;
1048 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1049 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1051 if(!max_length_text[ntype->max_length][0])
1052 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1053 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1058 exp.type = HF_STRING;
1059 exp.value.str = NULL;
1063 static struct chanData*
1064 register_channel(struct chanNode *cNode, char *registrar)
1066 struct chanData *channel;
1067 enum levelOption lvlOpt;
1068 enum charOption chOpt;
1070 channel = calloc(1, sizeof(struct chanData));
1072 channel->notes = dict_new();
1073 dict_set_free_data(channel->notes, chanserv_free_note);
1075 channel->registrar = strdup(registrar);
1076 channel->registered = now;
1077 channel->visited = now;
1078 channel->limitAdjusted = now;
1079 channel->flags = CHANNEL_DEFAULT_FLAGS;
1080 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1081 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1082 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1083 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1085 channel->prev = NULL;
1086 channel->next = channelList;
1089 channelList->prev = channel;
1090 channelList = channel;
1091 registered_channels++;
1093 channel->channel = cNode;
1095 cNode->channel_info = channel;
1100 static struct userData*
1101 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1103 struct userData *ud;
1105 if(access > UL_OWNER)
1108 ud = calloc(1, sizeof(*ud));
1109 ud->channel = channel;
1110 ud->handle = handle;
1112 ud->access = access;
1113 ud->info = info ? strdup(info) : NULL;
1116 ud->next = channel->users;
1118 channel->users->prev = ud;
1119 channel->users = ud;
1121 channel->userCount++;
1125 ud->u_next = ud->handle->channels;
1127 ud->u_next->u_prev = ud;
1128 ud->handle->channels = ud;
1133 static void unregister_channel(struct chanData *channel, const char *reason);
1136 del_channel_user(struct userData *user, int do_gc)
1138 struct chanData *channel = user->channel;
1140 channel->userCount--;
1144 user->prev->next = user->next;
1146 channel->users = user->next;
1148 user->next->prev = user->prev;
1151 user->u_prev->u_next = user->u_next;
1153 user->handle->channels = user->u_next;
1155 user->u_next->u_prev = user->u_prev;
1159 if(do_gc && !channel->users && !IsProtected(channel))
1160 unregister_channel(channel, "lost all users.");
1163 static void expire_ban(void *data);
1165 static struct banData*
1166 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1169 unsigned int ii, l1, l2;
1174 bd = malloc(sizeof(struct banData));
1176 bd->channel = channel;
1178 bd->triggered = triggered;
1179 bd->expires = expires;
1181 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1183 extern const char *hidden_host_suffix;
1184 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1188 l2 = strlen(old_name);
1191 if(irccasecmp(mask + l1 - l2, old_name))
1193 new_mask = alloca(MAXLEN);
1194 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1197 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1199 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1200 bd->reason = reason ? strdup(reason) : NULL;
1203 timeq_add(expires, expire_ban, bd);
1206 bd->next = channel->bans;
1208 channel->bans->prev = bd;
1210 channel->banCount++;
1217 del_channel_ban(struct banData *ban)
1219 ban->channel->banCount--;
1223 ban->prev->next = ban->next;
1225 ban->channel->bans = ban->next;
1228 ban->next->prev = ban->prev;
1231 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1240 expire_ban(void *data)
1242 struct banData *bd = data;
1243 if(!IsSuspended(bd->channel))
1245 struct banList bans;
1246 struct mod_chanmode change;
1248 bans = bd->channel->channel->banlist;
1249 mod_chanmode_init(&change);
1250 for(ii=0; ii<bans.used; ii++)
1252 if(!strcmp(bans.list[ii]->ban, bd->mask))
1255 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1256 change.args[0].hostmask = bd->mask;
1257 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1263 del_channel_ban(bd);
1266 static void chanserv_expire_suspension(void *data);
1269 unregister_channel(struct chanData *channel, const char *reason)
1271 char msgbuf[MAXLEN];
1273 /* After channel unregistration, the following must be cleaned
1275 - Channel information.
1278 - Channel suspension data.
1279 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1285 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1287 while(channel->users)
1288 del_channel_user(channel->users, 0);
1290 while(channel->bans)
1291 del_channel_ban(channel->bans);
1293 if(channel->topic) free(channel->topic);
1294 if(channel->registrar) free(channel->registrar);
1295 if(channel->greeting) free(channel->greeting);
1296 if(channel->user_greeting) free(channel->user_greeting);
1297 if(channel->topic_mask) free(channel->topic_mask);
1299 if(channel->prev) channel->prev->next = channel->next;
1300 else channelList = channel->next;
1302 if(channel->next) channel->next->prev = channel->prev;
1304 if(channel->suspended)
1306 struct chanNode *cNode = channel->channel;
1307 struct suspended *suspended, *next_suspended;
1309 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1311 next_suspended = suspended->previous;
1312 free(suspended->suspender);
1313 free(suspended->reason);
1314 if(suspended->expires)
1315 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1320 cNode->channel_info = NULL;
1322 channel->channel->channel_info = NULL;
1325 dict_delete(channel->notes);
1326 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1327 if(!IsSuspended(channel))
1328 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1329 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1330 UnlockChannel(channel->channel);
1332 registered_channels--;
1336 expire_channels(UNUSED_ARG(void *data))
1338 struct chanData *channel, *next;
1339 struct userData *user;
1340 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1342 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1343 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1345 for(channel = channelList; channel; channel = next)
1347 next = channel->next;
1349 /* See if the channel can be expired. */
1350 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1351 || IsProtected(channel))
1354 /* Make sure there are no high-ranking users still in the channel. */
1355 for(user=channel->users; user; user=user->next)
1356 if(user->present && (user->access >= UL_PRESENT))
1361 /* Unregister the channel */
1362 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1363 unregister_channel(channel, "registration expired.");
1366 if(chanserv_conf.channel_expire_frequency)
1367 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1371 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1373 char protect = channel->chOpts[chProtect];
1374 struct userData *cs_victim, *cs_aggressor;
1376 /* Don't protect if no one is to be protected, someone is attacking
1377 himself, or if the aggressor is an IRC Operator. */
1378 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1381 /* Don't protect if the victim isn't authenticated (because they
1382 can't be a channel user), unless we are to protect non-users
1384 cs_victim = GetChannelAccess(channel, victim->handle_info);
1385 if(protect != 'a' && !cs_victim)
1388 /* Protect if the aggressor isn't a user because at this point,
1389 the aggressor can only be less than or equal to the victim. */
1390 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1394 /* If the aggressor was a user, then the victim can't be helped. */
1401 if(cs_victim->access > cs_aggressor->access)
1406 if(cs_victim->access >= cs_aggressor->access)
1415 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1417 struct chanData *cData = channel->channel_info;
1418 struct userData *cs_victim;
1420 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1421 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1422 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1424 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1432 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1434 if(IsService(victim))
1436 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1440 if(protect_user(victim, user, channel->channel_info))
1442 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1449 static struct do_not_register *
1450 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1452 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1453 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1454 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1455 strcpy(dnr->reason, reason);
1457 if(dnr->chan_name[0] == '*')
1458 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1459 else if(strpbrk(dnr->chan_name, "*?"))
1460 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1462 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1466 static struct dnrList
1467 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1469 struct dnrList list;
1471 struct do_not_register *dnr;
1473 dnrList_init(&list);
1474 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1475 dnrList_append(&list, dnr);
1476 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1477 dnrList_append(&list, dnr);
1479 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1480 if(match_ircglob(chan_name, iter_key(it)))
1481 dnrList_append(&list, iter_data(it));
1486 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1488 struct dnrList list;
1489 struct do_not_register *dnr;
1491 char buf[INTERVALLEN];
1493 list = chanserv_find_dnrs(chan_name, handle);
1494 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1496 dnr = list.list[ii];
1499 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1500 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1503 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1506 reply("CSMSG_MORE_DNRS", list.used - ii);
1511 struct do_not_register *
1512 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1514 struct do_not_register *dnr;
1517 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1521 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1523 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1524 if(match_ircglob(chan_name, iter_key(it)))
1525 return iter_data(it);
1530 static CHANSERV_FUNC(cmd_noregister)
1533 struct do_not_register *dnr;
1534 char buf[INTERVALLEN];
1535 unsigned int matches;
1541 reply("CSMSG_DNR_SEARCH_RESULTS");
1543 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1545 dnr = iter_data(it);
1547 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1549 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1552 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1554 dnr = iter_data(it);
1556 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1558 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1561 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1563 dnr = iter_data(it);
1565 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1567 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1572 reply("MSG_MATCH_COUNT", matches);
1574 reply("MSG_NO_MATCHES");
1580 if(!IsChannelName(target) && (*target != '*'))
1582 reply("CSMSG_NOT_DNR", target);
1588 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1589 if((*target == '*') && !get_handle_info(target + 1))
1591 reply("MSG_HANDLE_UNKNOWN", target + 1);
1594 chanserv_add_dnr(target, user->handle_info->handle, reason);
1595 reply("CSMSG_NOREGISTER_CHANNEL", target);
1599 reply("CSMSG_DNR_SEARCH_RESULTS");
1601 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1603 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1605 reply("MSG_NO_MATCHES");
1609 static CHANSERV_FUNC(cmd_allowregister)
1611 const char *chan_name = argv[1];
1613 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1615 dict_remove(handle_dnrs, chan_name+1);
1616 reply("CSMSG_DNR_REMOVED", chan_name);
1618 else if(dict_find(plain_dnrs, chan_name, NULL))
1620 dict_remove(plain_dnrs, chan_name);
1621 reply("CSMSG_DNR_REMOVED", chan_name);
1623 else if(dict_find(mask_dnrs, chan_name, NULL))
1625 dict_remove(mask_dnrs, chan_name);
1626 reply("CSMSG_DNR_REMOVED", chan_name);
1630 reply("CSMSG_NO_SUCH_DNR", chan_name);
1637 chanserv_get_owned_count(struct handle_info *hi)
1639 struct userData *cList;
1642 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1643 if(cList->access == UL_OWNER)
1648 static CHANSERV_FUNC(cmd_register)
1650 struct mod_chanmode *change;
1651 struct handle_info *handle;
1652 struct chanData *cData;
1653 struct modeNode *mn;
1654 char reason[MAXLEN];
1656 unsigned int new_channel, force=0;
1657 struct do_not_register *dnr;
1661 if(channel->channel_info)
1663 reply("CSMSG_ALREADY_REGGED", channel->name);
1667 if(channel->bad_channel)
1669 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1673 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1675 reply("CSMSG_MUST_BE_OPPED", channel->name);
1680 chan_name = channel->name;
1684 if((argc < 2) || !IsChannelName(argv[1]))
1686 reply("MSG_NOT_CHANNEL_NAME");
1690 if(opserv_bad_channel(argv[1]))
1692 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1697 chan_name = argv[1];
1700 if(argc >= (new_channel+2))
1702 if(!IsHelping(user))
1704 reply("CSMSG_PROXY_FORBIDDEN");
1708 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1710 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1711 dnr = chanserv_is_dnr(chan_name, handle);
1715 handle = user->handle_info;
1716 dnr = chanserv_is_dnr(chan_name, handle);
1720 if(!IsHelping(user))
1721 reply("CSMSG_DNR_CHANNEL", chan_name);
1723 chanserv_show_dnrs(user, cmd, chan_name, handle);
1727 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1729 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1734 channel = AddChannel(argv[1], now, NULL, NULL);
1736 cData = register_channel(channel, user->handle_info->handle);
1737 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1738 cData->modes = chanserv_conf.default_modes;
1739 change = mod_chanmode_dup(&cData->modes, 1);
1740 change->args[change->argc].mode = MODE_CHANOP;
1741 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1743 mod_chanmode_announce(chanserv, channel, change);
1744 mod_chanmode_free(change);
1746 /* Initialize the channel's max user record. */
1747 cData->max = channel->members.used;
1749 if(handle != user->handle_info)
1750 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1752 reply("CSMSG_REG_SUCCESS", channel->name);
1754 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1755 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1760 make_confirmation_string(struct userData *uData)
1762 static char strbuf[16];
1767 for(src = uData->handle->handle; *src; )
1768 accum = accum * 31 + toupper(*src++);
1770 for(src = uData->channel->channel->name; *src; )
1771 accum = accum * 31 + toupper(*src++);
1772 sprintf(strbuf, "%08x", accum);
1776 static CHANSERV_FUNC(cmd_unregister)
1779 char reason[MAXLEN];
1780 struct chanData *cData;
1781 struct userData *uData;
1783 cData = channel->channel_info;
1786 reply("CSMSG_NOT_REGISTERED", channel->name);
1790 uData = GetChannelUser(cData, user->handle_info);
1791 if(!uData || (uData->access < UL_OWNER))
1793 reply("CSMSG_NO_ACCESS");
1797 if(IsProtected(cData))
1799 reply("CSMSG_UNREG_NODELETE", channel->name);
1803 if(!IsHelping(user))
1805 const char *confirm_string;
1806 if(IsSuspended(cData))
1808 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1811 confirm_string = make_confirmation_string(uData);
1812 if((argc < 2) || strcmp(argv[1], confirm_string))
1814 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1819 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1820 name = strdup(channel->name);
1821 unregister_channel(cData, reason);
1822 reply("CSMSG_UNREG_SUCCESS", name);
1827 static CHANSERV_FUNC(cmd_move)
1829 struct chanNode *target;
1830 struct modeNode *mn;
1831 struct userData *uData;
1832 char reason[MAXLEN];
1833 struct do_not_register *dnr;
1837 if(IsProtected(channel->channel_info))
1839 reply("CSMSG_MOVE_NODELETE", channel->name);
1843 if(!IsChannelName(argv[1]))
1845 reply("MSG_NOT_CHANNEL_NAME");
1849 if(opserv_bad_channel(argv[1]))
1851 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1855 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1857 for(uData = channel->channel_info->users; uData; uData = uData->next)
1859 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1861 if(!IsHelping(user))
1862 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1864 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1870 if(!(target = GetChannel(argv[1])))
1872 target = AddChannel(argv[1], now, NULL, NULL);
1873 if(!IsSuspended(channel->channel_info))
1874 AddChannelUser(chanserv, target);
1876 else if(target->channel_info)
1878 reply("CSMSG_ALREADY_REGGED", target->name);
1881 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1882 && !IsHelping(user))
1884 reply("CSMSG_MUST_BE_OPPED", target->name);
1887 else if(!IsSuspended(channel->channel_info))
1889 struct mod_chanmode change;
1890 mod_chanmode_init(&change);
1892 change.args[0].mode = MODE_CHANOP;
1893 change.args[0].member = AddChannelUser(chanserv, target);
1894 mod_chanmode_announce(chanserv, target, &change);
1897 /* Move the channel_info to the target channel; it
1898 shouldn't be necessary to clear timeq callbacks
1899 for the old channel. */
1900 target->channel_info = channel->channel_info;
1901 target->channel_info->channel = target;
1902 channel->channel_info = NULL;
1904 reply("CSMSG_MOVE_SUCCESS", target->name);
1906 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1907 if(!IsSuspended(target->channel_info))
1909 char reason2[MAXLEN];
1910 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1911 DelChannelUser(chanserv, channel, reason2, 0);
1913 UnlockChannel(channel);
1914 LockChannel(target);
1915 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1920 merge_users(struct chanData *source, struct chanData *target)
1922 struct userData *suData, *tuData, *next;
1928 /* Insert the source's users into the scratch area. */
1929 for(suData = source->users; suData; suData = suData->next)
1930 dict_insert(merge, suData->handle->handle, suData);
1932 /* Iterate through the target's users, looking for
1933 users common to both channels. The lower access is
1934 removed from either the scratch area or target user
1936 for(tuData = target->users; tuData; tuData = next)
1938 struct userData *choice;
1940 next = tuData->next;
1942 /* If a source user exists with the same handle as a target
1943 channel's user, resolve the conflict by removing one. */
1944 suData = dict_find(merge, tuData->handle->handle, NULL);
1948 /* Pick the data we want to keep. */
1949 /* If the access is the same, use the later seen time. */
1950 if(suData->access == tuData->access)
1951 choice = (suData->seen > tuData->seen) ? suData : tuData;
1952 else /* Otherwise, keep the higher access level. */
1953 choice = (suData->access > tuData->access) ? suData : tuData;
1955 /* Remove the user that wasn't picked. */
1956 if(choice == tuData)
1958 dict_remove(merge, suData->handle->handle);
1959 del_channel_user(suData, 0);
1962 del_channel_user(tuData, 0);
1965 /* Move the remaining users to the target channel. */
1966 for(it = dict_first(merge); it; it = iter_next(it))
1968 suData = iter_data(it);
1970 /* Insert the user into the target channel's linked list. */
1971 suData->prev = NULL;
1972 suData->next = target->users;
1973 suData->channel = target;
1976 target->users->prev = suData;
1977 target->users = suData;
1979 /* Update the user counts for the target channel; the
1980 source counts are left alone. */
1981 target->userCount++;
1984 /* Possible to assert (source->users == NULL) here. */
1985 source->users = NULL;
1990 merge_bans(struct chanData *source, struct chanData *target)
1992 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1994 /* Hold on to the original head of the target ban list
1995 to avoid comparing source bans with source bans. */
1996 tFront = target->bans;
1998 /* Perform a totally expensive O(n*m) merge, ick. */
1999 for(sbData = source->bans; sbData; sbData = sNext)
2001 /* Flag to track whether the ban's been moved
2002 to the destination yet. */
2005 /* Possible to assert (sbData->prev == NULL) here. */
2006 sNext = sbData->next;
2008 for(tbData = tFront; tbData; tbData = tNext)
2010 tNext = tbData->next;
2012 /* Perform two comparisons between each source
2013 and target ban, conflicts are resolved by
2014 keeping the broader ban and copying the later
2015 expiration and triggered time. */
2016 if(match_ircglobs(tbData->mask, sbData->mask))
2018 /* There is a broader ban in the target channel that
2019 overrides one in the source channel; remove the
2020 source ban and break. */
2021 if(sbData->expires > tbData->expires)
2022 tbData->expires = sbData->expires;
2023 if(sbData->triggered > tbData->triggered)
2024 tbData->triggered = sbData->triggered;
2025 del_channel_ban(sbData);
2028 else if(match_ircglobs(sbData->mask, tbData->mask))
2030 /* There is a broader ban in the source channel that
2031 overrides one in the target channel; remove the
2032 target ban, fall through and move the source over. */
2033 if(tbData->expires > sbData->expires)
2034 sbData->expires = tbData->expires;
2035 if(tbData->triggered > sbData->triggered)
2036 sbData->triggered = tbData->triggered;
2037 if(tbData == tFront)
2039 del_channel_ban(tbData);
2042 /* Source bans can override multiple target bans, so
2043 we allow a source to run through this loop multiple
2044 times, but we can only move it once. */
2049 /* Remove the source ban from the source ban list. */
2051 sbData->next->prev = sbData->prev;
2053 /* Modify the source ban's associated channel. */
2054 sbData->channel = target;
2056 /* Insert the ban into the target channel's linked list. */
2057 sbData->prev = NULL;
2058 sbData->next = target->bans;
2061 target->bans->prev = sbData;
2062 target->bans = sbData;
2064 /* Update the user counts for the target channel. */
2069 /* Possible to assert (source->bans == NULL) here. */
2070 source->bans = NULL;
2074 merge_data(struct chanData *source, struct chanData *target)
2076 if(source->visited > target->visited)
2077 target->visited = source->visited;
2081 merge_channel(struct chanData *source, struct chanData *target)
2083 merge_users(source, target);
2084 merge_bans(source, target);
2085 merge_data(source, target);
2088 static CHANSERV_FUNC(cmd_merge)
2090 struct userData *target_user;
2091 struct chanNode *target;
2092 char reason[MAXLEN];
2096 /* Make sure the target channel exists and is registered to the user
2097 performing the command. */
2098 if(!(target = GetChannel(argv[1])))
2100 reply("MSG_INVALID_CHANNEL");
2104 if(!target->channel_info)
2106 reply("CSMSG_NOT_REGISTERED", target->name);
2110 if(IsProtected(channel->channel_info))
2112 reply("CSMSG_MERGE_NODELETE");
2116 if(IsSuspended(target->channel_info))
2118 reply("CSMSG_MERGE_SUSPENDED");
2122 if(channel == target)
2124 reply("CSMSG_MERGE_SELF");
2128 target_user = GetChannelUser(target->channel_info, user->handle_info);
2129 if(!target_user || (target_user->access < UL_OWNER))
2131 reply("CSMSG_MERGE_NOT_OWNER");
2135 /* Merge the channel structures and associated data. */
2136 merge_channel(channel->channel_info, target->channel_info);
2137 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2138 unregister_channel(channel->channel_info, reason);
2139 reply("CSMSG_MERGE_SUCCESS", target->name);
2143 static CHANSERV_FUNC(cmd_opchan)
2145 struct mod_chanmode change;
2146 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2148 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2151 channel->channel_info->may_opchan = 0;
2152 mod_chanmode_init(&change);
2154 change.args[0].mode = MODE_CHANOP;
2155 change.args[0].member = GetUserMode(channel, chanserv);
2156 mod_chanmode_announce(chanserv, channel, &change);
2157 reply("CSMSG_OPCHAN_DONE", channel->name);
2161 static CHANSERV_FUNC(cmd_adduser)
2163 struct userData *actee;
2164 struct userData *actor;
2165 struct handle_info *handle;
2166 unsigned short access;
2170 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2172 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2176 access = user_level_from_name(argv[2], UL_OWNER);
2179 reply("CSMSG_INVALID_ACCESS", argv[2]);
2183 actor = GetChannelUser(channel->channel_info, user->handle_info);
2184 if(actor->access <= access)
2186 reply("CSMSG_NO_BUMP_ACCESS");
2190 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2193 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2195 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2199 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2200 scan_user_presence(actee, NULL);
2201 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2205 static CHANSERV_FUNC(cmd_clvl)
2207 struct handle_info *handle;
2208 struct userData *victim;
2209 struct userData *actor;
2210 unsigned short new_access;
2211 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2215 actor = GetChannelUser(channel->channel_info, user->handle_info);
2217 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2220 if(handle == user->handle_info && !privileged)
2222 reply("CSMSG_NO_SELF_CLVL");
2226 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2228 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2232 if(actor->access <= victim->access && !privileged)
2234 reply("MSG_USER_OUTRANKED", handle->handle);
2238 new_access = user_level_from_name(argv[2], UL_OWNER);
2242 reply("CSMSG_INVALID_ACCESS", argv[2]);
2246 if(new_access >= actor->access && !privileged)
2248 reply("CSMSG_NO_BUMP_ACCESS");
2252 victim->access = new_access;
2253 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2257 static CHANSERV_FUNC(cmd_deluser)
2259 struct handle_info *handle;
2260 struct userData *victim;
2261 struct userData *actor;
2262 unsigned short access;
2267 actor = GetChannelUser(channel->channel_info, user->handle_info);
2269 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2272 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2274 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2280 access = user_level_from_name(argv[1], UL_OWNER);
2283 reply("CSMSG_INVALID_ACCESS", argv[1]);
2286 if(access != victim->access)
2288 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2294 access = victim->access;
2297 if((actor->access <= victim->access) && !IsHelping(user))
2299 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2303 chan_name = strdup(channel->name);
2304 del_channel_user(victim, 1);
2305 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2311 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2313 struct userData *actor, *uData, *next;
2315 actor = GetChannelUser(channel->channel_info, user->handle_info);
2317 if(min_access > max_access)
2319 reply("CSMSG_BAD_RANGE", min_access, max_access);
2323 if((actor->access <= max_access) && !IsHelping(user))
2325 reply("CSMSG_NO_ACCESS");
2329 for(uData = channel->channel_info->users; uData; uData = next)
2333 if((uData->access >= min_access)
2334 && (uData->access <= max_access)
2335 && match_ircglob(uData->handle->handle, mask))
2336 del_channel_user(uData, 1);
2339 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2343 static CHANSERV_FUNC(cmd_mdelowner)
2345 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2348 static CHANSERV_FUNC(cmd_mdelcoowner)
2350 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2353 static CHANSERV_FUNC(cmd_mdelmaster)
2355 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2358 static CHANSERV_FUNC(cmd_mdelop)
2360 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2363 static CHANSERV_FUNC(cmd_mdelpeon)
2365 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2369 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2371 struct banData *bData, *next;
2372 char interval[INTERVALLEN];
2377 limit = now - duration;
2378 for(bData = channel->channel_info->bans; bData; bData = next)
2382 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2385 del_channel_ban(bData);
2389 intervalString(interval, duration, user->handle_info);
2390 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2395 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2397 struct userData *actor, *uData, *next;
2398 char interval[INTERVALLEN];
2402 actor = GetChannelUser(channel->channel_info, user->handle_info);
2403 if(min_access > max_access)
2405 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2409 if((actor->access <= max_access) && !IsHelping(user))
2411 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2416 limit = now - duration;
2417 for(uData = channel->channel_info->users; uData; uData = next)
2421 if((uData->seen > limit) || uData->present)
2424 if(((uData->access >= min_access) && (uData->access <= max_access))
2425 || (!max_access && (uData->access < actor->access)))
2427 del_channel_user(uData, 1);
2435 max_access = UL_OWNER;
2437 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2441 static CHANSERV_FUNC(cmd_trim)
2443 unsigned long duration;
2444 unsigned short min_level, max_level;
2448 duration = ParseInterval(argv[2]);
2451 reply("CSMSG_CANNOT_TRIM");
2455 if(!irccasecmp(argv[1], "bans"))
2457 cmd_trim_bans(user, channel, duration);
2460 else if(!irccasecmp(argv[1], "users"))
2462 cmd_trim_users(user, channel, 0, 0, duration);
2465 else if(parse_level_range(&min_level, &max_level, argv[1]))
2467 cmd_trim_users(user, channel, min_level, max_level, duration);
2470 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2472 cmd_trim_users(user, channel, min_level, min_level, duration);
2477 reply("CSMSG_INVALID_TRIM", argv[1]);
2482 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2483 to the user. cmd_all takes advantage of this. */
2484 static CHANSERV_FUNC(cmd_up)
2486 struct mod_chanmode change;
2487 struct userData *uData;
2490 mod_chanmode_init(&change);
2492 change.args[0].member = GetUserMode(channel, user);
2493 if(!change.args[0].member)
2496 reply("MSG_CHANNEL_ABSENT", channel->name);
2500 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2504 reply("CSMSG_GODMODE_UP", argv[0]);
2507 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2509 change.args[0].mode = MODE_CHANOP;
2510 errmsg = "CSMSG_ALREADY_OPPED";
2514 change.args[0].mode = MODE_VOICE;
2515 errmsg = "CSMSG_ALREADY_VOICED";
2517 change.args[0].mode &= ~change.args[0].member->modes;
2518 if(!change.args[0].mode)
2521 reply(errmsg, channel->name);
2524 modcmd_chanmode_announce(&change);
2528 static CHANSERV_FUNC(cmd_down)
2530 struct mod_chanmode change;
2532 mod_chanmode_init(&change);
2534 change.args[0].member = GetUserMode(channel, user);
2535 if(!change.args[0].member)
2538 reply("MSG_CHANNEL_ABSENT", channel->name);
2542 if(!change.args[0].member->modes)
2545 reply("CSMSG_ALREADY_DOWN", channel->name);
2549 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2550 modcmd_chanmode_announce(&change);
2554 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)
2556 struct userData *cList;
2558 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2560 if(IsSuspended(cList->channel)
2561 || IsUserSuspended(cList)
2562 || !GetUserMode(cList->channel->channel, user))
2565 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2571 static CHANSERV_FUNC(cmd_upall)
2573 return cmd_all(CSFUNC_ARGS, cmd_up);
2576 static CHANSERV_FUNC(cmd_downall)
2578 return cmd_all(CSFUNC_ARGS, cmd_down);
2581 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2582 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2585 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)
2587 unsigned int ii, valid;
2588 struct userNode *victim;
2589 struct mod_chanmode *change;
2591 change = mod_chanmode_alloc(argc - 1);
2593 for(ii=valid=0; ++ii < argc; )
2595 if(!(victim = GetUserH(argv[ii])))
2597 change->args[valid].mode = mode;
2598 change->args[valid].member = GetUserMode(channel, victim);
2599 if(!change->args[valid].member)
2601 if(validate && !validate(user, channel, victim))
2606 change->argc = valid;
2607 if(valid < (argc-1))
2608 reply("CSMSG_PROCESS_FAILED");
2611 modcmd_chanmode_announce(change);
2612 reply(action, channel->name);
2614 mod_chanmode_free(change);
2618 static CHANSERV_FUNC(cmd_op)
2620 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2623 static CHANSERV_FUNC(cmd_deop)
2625 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2628 static CHANSERV_FUNC(cmd_voice)
2630 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2633 static CHANSERV_FUNC(cmd_devoice)
2635 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2639 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2645 for(ii=0; ii<channel->members.used; ii++)
2647 struct modeNode *mn = channel->members.list[ii];
2649 if(IsService(mn->user))
2652 if(!user_matches_glob(mn->user, ban, 1))
2655 if(protect_user(mn->user, user, channel->channel_info))
2659 victims[(*victimCount)++] = mn;
2665 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2667 struct userNode *victim;
2668 struct modeNode **victims;
2669 unsigned int offset, n, victimCount, duration = 0;
2670 char *reason = "Bye.", *ban, *name;
2671 char interval[INTERVALLEN];
2673 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2674 REQUIRE_PARAMS(offset);
2677 reason = unsplit_string(argv + offset, argc - offset, NULL);
2678 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2680 /* Truncate the reason to a length of TOPICLEN, as
2681 the ircd does; however, leave room for an ellipsis
2682 and the kicker's nick. */
2683 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2687 if((victim = GetUserH(argv[1])))
2689 victims = alloca(sizeof(victims[0]));
2690 victims[0] = GetUserMode(channel, victim);
2691 /* XXX: The comparison with ACTION_KICK is just because all
2692 * other actions can work on users outside the channel, and we
2693 * want to allow those (e.g. unbans) in that case. If we add
2694 * some other ejection action for in-channel users, change
2696 victimCount = victims[0] ? 1 : 0;
2698 if(IsService(victim))
2700 reply("MSG_SERVICE_IMMUNE", victim->nick);
2704 if((action == ACTION_KICK) && !victimCount)
2706 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2710 if(protect_user(victim, user, channel->channel_info))
2712 reply("CSMSG_USER_PROTECTED", victim->nick);
2716 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2717 name = victim->nick;
2721 if(!is_ircmask(argv[1]))
2723 reply("MSG_NICK_UNKNOWN", argv[1]);
2727 victims = alloca(sizeof(victims[0]) * channel->members.used);
2729 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2731 reply("CSMSG_MASK_PROTECTED", argv[1]);
2735 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2737 reply("CSMSG_LAME_MASK", argv[1]);
2741 if((action == ACTION_KICK) && (victimCount == 0))
2743 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2747 name = ban = strdup(argv[1]);
2750 /* Truncate the ban in place if necessary; we must ensure
2751 that 'ban' is a valid ban mask before sanitizing it. */
2752 sanitize_ircmask(ban);
2754 if(action & ACTION_ADD_BAN)
2756 struct banData *bData, *next;
2758 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2760 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2765 if(action & ACTION_ADD_TIMED_BAN)
2767 duration = ParseInterval(argv[2]);
2771 reply("CSMSG_DURATION_TOO_LOW");
2775 else if(duration > (86400 * 365 * 2))
2777 reply("CSMSG_DURATION_TOO_HIGH");
2783 for(bData = channel->channel_info->bans; bData; bData = next)
2785 if(match_ircglobs(bData->mask, ban))
2787 int exact = !irccasecmp(bData->mask, ban);
2789 /* The ban is redundant; there is already a ban
2790 with the same effect in place. */
2794 free(bData->reason);
2795 bData->reason = strdup(reason);
2796 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2798 reply("CSMSG_REASON_CHANGE", ban);
2802 if(exact && bData->expires)
2806 /* If the ban matches an existing one exactly,
2807 extend the expiration time if the provided
2808 duration is longer. */
2809 if(duration && ((time_t)(now + duration) > bData->expires))
2811 bData->expires = now + duration;
2822 /* Delete the expiration timeq entry and
2823 requeue if necessary. */
2824 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2827 timeq_add(bData->expires, expire_ban, bData);
2831 /* automated kickban */
2834 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2836 reply("CSMSG_BAN_ADDED", name, channel->name);
2842 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2849 if(match_ircglobs(ban, bData->mask))
2851 /* The ban we are adding makes previously existing
2852 bans redundant; silently remove them. */
2853 del_channel_ban(bData);
2857 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);
2859 name = ban = strdup(bData->mask);
2863 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2865 extern const char *hidden_host_suffix;
2866 const char *old_name = chanserv_conf.old_ban_names->list[n];
2868 unsigned int l1, l2;
2871 l2 = strlen(old_name);
2874 if(irccasecmp(ban + l1 - l2, old_name))
2876 new_mask = malloc(MAXLEN);
2877 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2879 name = ban = new_mask;
2884 if(action & ACTION_BAN)
2886 unsigned int exists;
2887 struct mod_chanmode *change;
2889 if(channel->banlist.used >= MAXBANS)
2892 reply("CSMSG_BANLIST_FULL", channel->name);
2897 exists = ChannelBanExists(channel, ban);
2898 change = mod_chanmode_alloc(victimCount + 1);
2899 for(n = 0; n < victimCount; ++n)
2901 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2902 change->args[n].member = victims[n];
2906 change->args[n].mode = MODE_BAN;
2907 change->args[n++].hostmask = ban;
2911 modcmd_chanmode_announce(change);
2913 mod_chanmode_announce(chanserv, channel, change);
2914 mod_chanmode_free(change);
2916 if(exists && (action == ACTION_BAN))
2919 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2925 if(action & ACTION_KICK)
2927 char kick_reason[MAXLEN];
2928 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2930 for(n = 0; n < victimCount; n++)
2931 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2936 /* No response, since it was automated. */
2938 else if(action & ACTION_ADD_BAN)
2941 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2943 reply("CSMSG_BAN_ADDED", name, channel->name);
2945 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2946 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2947 else if(action & ACTION_BAN)
2948 reply("CSMSG_BAN_DONE", name, channel->name);
2949 else if(action & ACTION_KICK && victimCount)
2950 reply("CSMSG_KICK_DONE", name, channel->name);
2956 static CHANSERV_FUNC(cmd_kickban)
2958 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2961 static CHANSERV_FUNC(cmd_kick)
2963 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2966 static CHANSERV_FUNC(cmd_ban)
2968 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2971 static CHANSERV_FUNC(cmd_addban)
2973 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2976 static CHANSERV_FUNC(cmd_addtimedban)
2978 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2981 static struct mod_chanmode *
2982 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2984 struct mod_chanmode *change;
2985 unsigned char *match;
2986 unsigned int ii, count;
2988 match = alloca(bans->used);
2991 for(ii = count = 0; ii < bans->used; ++ii)
2993 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3000 for(ii = count = 0; ii < bans->used; ++ii)
3002 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3009 change = mod_chanmode_alloc(count);
3010 for(ii = count = 0; ii < bans->used; ++ii)
3014 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3015 change->args[count++].hostmask = bans->list[ii]->ban;
3021 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3023 struct userNode *actee;
3029 /* may want to allow a comma delimited list of users... */
3030 if(!(actee = GetUserH(argv[1])))
3032 if(!is_ircmask(argv[1]))
3034 reply("MSG_NICK_UNKNOWN", argv[1]);
3038 mask = strdup(argv[1]);
3041 /* We don't sanitize the mask here because ircu
3043 if(action & ACTION_UNBAN)
3045 struct mod_chanmode *change;
3046 change = find_matching_bans(&channel->banlist, actee, mask);
3049 modcmd_chanmode_announce(change);
3050 mod_chanmode_free(change);
3055 if(action & ACTION_DEL_BAN)
3057 struct banData *ban, *next;
3059 ban = channel->channel_info->bans;
3063 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3066 for( ; ban && !match_ircglobs(mask, ban->mask);
3071 del_channel_ban(ban);
3078 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3080 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3086 static CHANSERV_FUNC(cmd_unban)
3088 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3091 static CHANSERV_FUNC(cmd_delban)
3093 /* it doesn't necessarily have to remove the channel ban - may want
3094 to make that an option. */
3095 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3098 static CHANSERV_FUNC(cmd_unbanme)
3100 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3101 long flags = ACTION_UNBAN;
3103 /* remove permanent bans if the user has the proper access. */
3104 if(uData->access >= UL_MASTER)
3105 flags |= ACTION_DEL_BAN;
3107 argv[1] = user->nick;
3108 return unban_user(user, channel, 2, argv, cmd, flags);
3111 static CHANSERV_FUNC(cmd_unbanall)
3113 struct mod_chanmode *change;
3116 if(!channel->banlist.used)
3118 reply("CSMSG_NO_BANS", channel->name);
3122 change = mod_chanmode_alloc(channel->banlist.used);
3123 for(ii=0; ii<channel->banlist.used; ii++)
3125 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3126 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3128 modcmd_chanmode_announce(change);
3129 mod_chanmode_free(change);
3130 reply("CSMSG_BANS_REMOVED", channel->name);
3134 static CHANSERV_FUNC(cmd_open)
3136 struct mod_chanmode *change;
3138 change = find_matching_bans(&channel->banlist, user, NULL);
3140 change = mod_chanmode_alloc(0);
3141 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3142 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3143 && channel->channel_info->modes.modes_set)
3144 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3145 modcmd_chanmode_announce(change);
3146 reply("CSMSG_CHANNEL_OPENED", channel->name);
3147 mod_chanmode_free(change);
3151 static CHANSERV_FUNC(cmd_myaccess)
3153 struct handle_info *target_handle;
3154 struct userData *uData;
3155 const char *chanName;
3158 target_handle = user->handle_info;
3159 else if(!IsHelping(user))
3161 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3164 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3167 if(!target_handle->channels)
3169 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3173 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3174 for(uData = target_handle->channels; uData; uData = uData->u_next)
3176 struct chanData *cData = uData->channel;
3178 if(uData->access > UL_OWNER)
3180 if(IsProtected(cData)
3181 && (target_handle != user->handle_info)
3182 && !GetTrueChannelAccess(cData, user->handle_info))
3184 chanName = cData->channel->name;
3186 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3188 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3194 static CHANSERV_FUNC(cmd_access)
3196 struct userNode *target;
3197 struct handle_info *target_handle;
3198 struct userData *uData;
3200 char prefix[MAXLEN];
3205 target_handle = target->handle_info;
3207 else if((target = GetUserH(argv[1])))
3209 target_handle = target->handle_info;
3211 else if(argv[1][0] == '*')
3213 if(!(target_handle = get_handle_info(argv[1]+1)))
3215 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3221 reply("MSG_NICK_UNKNOWN", argv[1]);
3225 assert(target || target_handle);
3227 if(target == chanserv)
3229 reply("CSMSG_IS_CHANSERV");
3237 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3242 reply("MSG_USER_AUTHENTICATE", target->nick);
3245 reply("MSG_AUTHENTICATE");
3251 const char *epithet = NULL, *type = NULL;
3254 epithet = chanserv_conf.irc_operator_epithet;
3257 else if(IsNetworkHelper(target))
3259 epithet = chanserv_conf.network_helper_epithet;
3260 type = "network helper";
3262 else if(IsSupportHelper(target))
3264 epithet = chanserv_conf.support_helper_epithet;
3265 type = "support helper";
3269 if(target_handle->epithet)
3270 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3272 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3274 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3278 sprintf(prefix, "%s", target_handle->handle);
3281 if(!channel->channel_info)
3283 reply("CSMSG_NOT_REGISTERED", channel->name);
3287 helping = HANDLE_FLAGGED(target_handle, HELPING)
3288 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3289 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3291 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3292 /* To prevent possible information leaks, only show infolines
3293 * if the requestor is in the channel or it's their own
3295 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3297 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3299 /* Likewise, only say it's suspended if the user has active
3300 * access in that channel or it's their own entry. */
3301 if(IsUserSuspended(uData)
3302 && (GetChannelUser(channel->channel_info, user->handle_info)
3303 || (user->handle_info == uData->handle)))
3305 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3310 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3317 zoot_list(struct listData *list)
3319 struct userData *uData;
3320 unsigned int start, curr, highest, lowest;
3321 struct helpfile_table tmp_table;
3322 const char **temp, *msg;
3324 if(list->table.length == 1)
3327 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3329 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3330 msg = user_find_message(list->user, "MSG_NONE");
3331 send_message_type(4, list->user, list->bot, " %s", msg);
3333 tmp_table.width = list->table.width;
3334 tmp_table.flags = list->table.flags;
3335 list->table.contents[0][0] = " ";
3336 highest = list->highest;
3337 if(list->lowest != 0)
3338 lowest = list->lowest;
3339 else if(highest < 100)
3342 lowest = highest - 100;
3343 for(start = curr = 1; curr < list->table.length; )
3345 uData = list->users[curr-1];
3346 list->table.contents[curr++][0] = " ";
3347 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3350 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3352 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3353 temp = list->table.contents[--start];
3354 list->table.contents[start] = list->table.contents[0];
3355 tmp_table.contents = list->table.contents + start;
3356 tmp_table.length = curr - start;
3357 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3358 list->table.contents[start] = temp;
3360 highest = lowest - 1;
3361 lowest = (highest < 100) ? 0 : (highest - 99);
3367 def_list(struct listData *list)
3371 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3373 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3374 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3375 if(list->table.length == 1)
3377 msg = user_find_message(list->user, "MSG_NONE");
3378 send_message_type(4, list->user, list->bot, " %s", msg);
3383 userData_access_comp(const void *arg_a, const void *arg_b)
3385 const struct userData *a = *(struct userData**)arg_a;
3386 const struct userData *b = *(struct userData**)arg_b;
3388 if(a->access != b->access)
3389 res = b->access - a->access;
3391 res = irccasecmp(a->handle->handle, b->handle->handle);
3396 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3398 void (*send_list)(struct listData *);
3399 struct userData *uData;
3400 struct listData lData;
3401 unsigned int matches;
3405 lData.bot = cmd->parent->bot;
3406 lData.channel = channel;
3407 lData.lowest = lowest;
3408 lData.highest = highest;
3409 lData.search = (argc > 1) ? argv[1] : NULL;
3410 send_list = zoot_list;
3412 if(user->handle_info)
3414 switch(user->handle_info->userlist_style)
3416 case HI_STYLE_DEF: send_list = def_list; break;
3417 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3421 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3423 for(uData = channel->channel_info->users; uData; uData = uData->next)
3425 if((uData->access < lowest)
3426 || (uData->access > highest)
3427 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3429 lData.users[matches++] = uData;
3431 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3433 lData.table.length = matches+1;
3434 lData.table.width = 4;
3435 lData.table.flags = TABLE_NO_FREE;
3436 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3437 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3438 lData.table.contents[0] = ary;
3441 ary[2] = "Last Seen";
3443 for(matches = 1; matches < lData.table.length; ++matches)
3445 struct userData *uData = lData.users[matches-1];
3446 char seen[INTERVALLEN];
3448 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3449 lData.table.contents[matches] = ary;
3450 ary[0] = strtab(uData->access);
3451 ary[1] = uData->handle->handle;
3454 else if(!uData->seen)
3457 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3458 ary[2] = strdup(ary[2]);
3459 if(IsUserSuspended(uData))
3460 ary[3] = "Suspended";
3461 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3462 ary[3] = "Vacation";
3467 for(matches = 1; matches < lData.table.length; ++matches)
3469 free((char*)lData.table.contents[matches][2]);
3470 free(lData.table.contents[matches]);
3472 free(lData.table.contents[0]);
3473 free(lData.table.contents);
3477 static CHANSERV_FUNC(cmd_users)
3479 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3482 static CHANSERV_FUNC(cmd_wlist)
3484 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3487 static CHANSERV_FUNC(cmd_clist)
3489 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3492 static CHANSERV_FUNC(cmd_mlist)
3494 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3497 static CHANSERV_FUNC(cmd_olist)
3499 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3502 static CHANSERV_FUNC(cmd_plist)
3504 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3507 static CHANSERV_FUNC(cmd_bans)
3509 struct helpfile_table tbl;
3510 unsigned int matches = 0, timed = 0, ii;
3511 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3512 const char *msg_never, *triggered, *expires;
3513 struct banData *ban, **bans;
3520 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3522 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3524 if(search && !match_ircglobs(search, ban->mask))
3526 bans[matches++] = ban;
3531 tbl.length = matches + 1;
3532 tbl.width = 4 + timed;
3534 tbl.flags = TABLE_NO_FREE;
3535 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3536 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3537 tbl.contents[0][0] = "Mask";
3538 tbl.contents[0][1] = "Set By";
3539 tbl.contents[0][2] = "Triggered";
3542 tbl.contents[0][3] = "Expires";
3543 tbl.contents[0][4] = "Reason";
3546 tbl.contents[0][3] = "Reason";
3549 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3551 free(tbl.contents[0]);
3556 msg_never = user_find_message(user, "MSG_NEVER");
3557 for(ii = 0; ii < matches; )
3563 else if(ban->expires)
3564 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3566 expires = msg_never;
3569 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3571 triggered = msg_never;
3573 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3574 tbl.contents[ii][0] = ban->mask;
3575 tbl.contents[ii][1] = ban->owner;
3576 tbl.contents[ii][2] = strdup(triggered);
3579 tbl.contents[ii][3] = strdup(expires);
3580 tbl.contents[ii][4] = ban->reason;
3583 tbl.contents[ii][3] = ban->reason;
3585 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3586 reply("MSG_MATCH_COUNT", matches);
3587 for(ii = 1; ii < tbl.length; ++ii)
3589 free((char*)tbl.contents[ii][2]);
3591 free((char*)tbl.contents[ii][3]);
3592 free(tbl.contents[ii]);
3594 free(tbl.contents[0]);
3600 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3602 struct chanData *cData = channel->channel_info;
3603 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3605 if(cData->topic_mask)
3606 return !match_ircglob(new_topic, cData->topic_mask);
3607 else if(cData->topic)
3608 return irccasecmp(new_topic, cData->topic);
3613 static CHANSERV_FUNC(cmd_topic)
3615 struct chanData *cData;
3618 cData = channel->channel_info;
3623 SetChannelTopic(channel, chanserv, cData->topic, 1);
3624 reply("CSMSG_TOPIC_SET", cData->topic);
3628 reply("CSMSG_NO_TOPIC", channel->name);
3632 topic = unsplit_string(argv + 1, argc - 1, NULL);
3633 /* If they say "!topic *", use an empty topic. */
3634 if((topic[0] == '*') && (topic[1] == 0))
3636 if(bad_topic(channel, user, topic))
3638 char *topic_mask = cData->topic_mask;
3641 char new_topic[TOPICLEN+1], tchar;
3642 int pos=0, starpos=-1, dpos=0, len;
3644 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3651 len = strlen(topic);
3652 if((dpos + len) > TOPICLEN)
3653 len = TOPICLEN + 1 - dpos;
3654 memcpy(new_topic+dpos, topic, len);
3658 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3659 default: new_topic[dpos++] = tchar; break;
3662 if((dpos > TOPICLEN) || tchar)
3665 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3666 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3669 new_topic[dpos] = 0;
3670 SetChannelTopic(channel, chanserv, new_topic, 1);
3672 reply("CSMSG_TOPIC_LOCKED", channel->name);
3677 SetChannelTopic(channel, chanserv, topic, 1);
3679 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3681 /* Grab the topic and save it as the default topic. */
3683 cData->topic = strdup(channel->topic);
3689 static CHANSERV_FUNC(cmd_mode)
3691 struct mod_chanmode *change;
3695 change = &channel->channel_info->modes;
3696 if(change->modes_set || change->modes_clear) {
3697 modcmd_chanmode_announce(change);
3698 reply("CSMSG_DEFAULTED_MODES", channel->name);
3700 reply("CSMSG_NO_MODES", channel->name);
3704 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3707 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3711 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3712 && mode_lock_violated(&channel->channel_info->modes, change))
3715 mod_chanmode_format(&channel->channel_info->modes, modes);
3716 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3720 modcmd_chanmode_announce(change);
3721 mod_chanmode_free(change);
3722 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3726 static CHANSERV_FUNC(cmd_invite)
3728 struct userData *uData;
3729 struct userNode *invite;
3731 uData = GetChannelUser(channel->channel_info, user->handle_info);
3735 if(!(invite = GetUserH(argv[1])))
3737 reply("MSG_NICK_UNKNOWN", argv[1]);
3744 if(GetUserMode(channel, invite))
3746 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3754 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3755 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3758 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3760 irc_invite(chanserv, invite, channel);
3762 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3767 static CHANSERV_FUNC(cmd_inviteme)
3769 if(GetUserMode(channel, user))
3771 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3774 if(channel->channel_info
3775 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3777 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3780 irc_invite(cmd->parent->bot, user, channel);
3785 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3788 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3790 /* We display things based on two dimensions:
3791 * - Issue time: present or absent
3792 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3793 * (in order of precedence, so something both expired and revoked
3794 * only counts as revoked)
3796 combo = (suspended->issued ? 4 : 0)
3797 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3799 case 0: /* no issue time, indefinite expiration */
3800 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3802 case 1: /* no issue time, expires in future */
3803 intervalString(buf1, suspended->expires-now, user->handle_info);
3804 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3806 case 2: /* no issue time, expired */
3807 intervalString(buf1, now-suspended->expires, user->handle_info);
3808 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3810 case 3: /* no issue time, revoked */
3811 intervalString(buf1, now-suspended->revoked, user->handle_info);
3812 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3814 case 4: /* issue time set, indefinite expiration */
3815 intervalString(buf1, now-suspended->issued, user->handle_info);
3816 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3818 case 5: /* issue time set, expires in future */
3819 intervalString(buf1, now-suspended->issued, user->handle_info);
3820 intervalString(buf2, suspended->expires-now, user->handle_info);
3821 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3823 case 6: /* issue time set, expired */
3824 intervalString(buf1, now-suspended->issued, user->handle_info);
3825 intervalString(buf2, now-suspended->expires, user->handle_info);
3826 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3828 case 7: /* issue time set, revoked */
3829 intervalString(buf1, now-suspended->issued, user->handle_info);
3830 intervalString(buf2, now-suspended->revoked, user->handle_info);
3831 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3834 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3839 static CHANSERV_FUNC(cmd_info)
3841 char modes[MAXLEN], buffer[INTERVALLEN];
3842 struct userData *uData, *owner;
3843 struct chanData *cData;
3844 struct do_not_register *dnr;
3849 cData = channel->channel_info;
3850 reply("CSMSG_CHANNEL_INFO", channel->name);
3852 uData = GetChannelUser(cData, user->handle_info);
3853 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3855 mod_chanmode_format(&cData->modes, modes);
3856 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3857 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3860 for(it = dict_first(cData->notes); it; it = iter_next(it))
3864 note = iter_data(it);
3865 if(!note_type_visible_to_user(cData, note->type, user))
3868 padding = PADLEN - 1 - strlen(iter_key(it));
3869 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3872 reply("CSMSG_CHANNEL_MAX", cData->max);
3873 for(owner = cData->users; owner; owner = owner->next)
3874 if(owner->access == UL_OWNER)
3875 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3876 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3877 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3878 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3879 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3881 privileged = IsStaff(user);
3882 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3883 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3885 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3886 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3888 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3890 struct suspended *suspended;
3891 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3892 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3893 show_suspension_info(cmd, user, suspended);
3895 else if(IsSuspended(cData))
3897 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3898 show_suspension_info(cmd, user, cData->suspended);
3903 static CHANSERV_FUNC(cmd_netinfo)
3905 extern time_t boot_time;
3906 extern unsigned long burst_length;
3907 char interval[INTERVALLEN];
3909 reply("CSMSG_NETWORK_INFO");
3910 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3911 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3912 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3913 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3914 reply("CSMSG_NETWORK_BANS", banCount);
3915 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3916 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3917 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3922 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3924 struct helpfile_table table;
3926 struct userNode *user;
3931 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3932 table.contents = alloca(list->used*sizeof(*table.contents));
3933 for(nn=0; nn<list->used; nn++)
3935 user = list->list[nn];
3936 if(user->modes & skip_flags)
3940 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3943 nick = alloca(strlen(user->nick)+3);
3944 sprintf(nick, "(%s)", user->nick);
3948 table.contents[table.length][0] = nick;
3951 table_send(chanserv, to->nick, 0, NULL, table);
3954 static CHANSERV_FUNC(cmd_ircops)
3956 reply("CSMSG_STAFF_OPERS");
3957 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3961 static CHANSERV_FUNC(cmd_helpers)
3963 reply("CSMSG_STAFF_HELPERS");
3964 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3968 static CHANSERV_FUNC(cmd_staff)
3970 reply("CSMSG_NETWORK_STAFF");
3971 cmd_ircops(CSFUNC_ARGS);
3972 cmd_helpers(CSFUNC_ARGS);
3976 static CHANSERV_FUNC(cmd_peek)
3978 struct modeNode *mn;
3979 char modes[MODELEN];
3981 struct helpfile_table table;
3983 irc_make_chanmode(channel, modes);
3985 reply("CSMSG_PEEK_INFO", channel->name);
3986 reply("CSMSG_PEEK_TOPIC", channel->topic);
3987 reply("CSMSG_PEEK_MODES", modes);
3988 reply("CSMSG_PEEK_USERS", channel->members.used);
3992 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3993 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3994 for(n = 0; n < channel->members.used; n++)
3996 mn = channel->members.list[n];
3997 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3999 table.contents[table.length] = alloca(sizeof(**table.contents));
4000 table.contents[table.length][0] = mn->user->nick;
4005 reply("CSMSG_PEEK_OPS");
4006 table_send(chanserv, user->nick, 0, NULL, table);
4009 reply("CSMSG_PEEK_NO_OPS");
4013 static MODCMD_FUNC(cmd_wipeinfo)
4015 struct handle_info *victim;
4016 struct userData *ud, *actor;
4019 actor = GetChannelUser(channel->channel_info, user->handle_info);
4020 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4022 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4024 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4027 if((ud->access >= actor->access) && (ud != actor))
4029 reply("MSG_USER_OUTRANKED", victim->handle);
4035 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4039 static CHANSERV_FUNC(cmd_resync)
4041 struct mod_chanmode *changes;
4042 struct chanData *cData = channel->channel_info;
4043 unsigned int ii, used;
4045 changes = mod_chanmode_alloc(channel->members.used * 2);
4046 for(ii = used = 0; ii < channel->members.used; ++ii)
4048 struct modeNode *mn = channel->members.list[ii];
4049 struct userData *uData;
4051 if(IsService(mn->user))
4054 uData = GetChannelAccess(cData, mn->user->handle_info);
4055 if(!cData->lvlOpts[lvlGiveOps]
4056 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4058 if(!(mn->modes & MODE_CHANOP))
4060 changes->args[used].mode = MODE_CHANOP;
4061 changes->args[used++].member = mn;
4064 else if(!cData->lvlOpts[lvlGiveVoice]
4065 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4067 if(mn->modes & MODE_CHANOP)
4069 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4070 changes->args[used++].member = mn;
4072 if(!(mn->modes & MODE_VOICE))
4074 changes->args[used].mode = MODE_VOICE;
4075 changes->args[used++].member = mn;
4082 changes->args[used].mode = MODE_REMOVE | mn->modes;
4083 changes->args[used++].member = mn;
4087 changes->argc = used;
4088 modcmd_chanmode_announce(changes);
4089 mod_chanmode_free(changes);
4090 reply("CSMSG_RESYNCED_USERS", channel->name);
4094 static CHANSERV_FUNC(cmd_seen)
4096 struct userData *uData;
4097 struct handle_info *handle;
4098 char seen[INTERVALLEN];
4102 if(!irccasecmp(argv[1], chanserv->nick))
4104 reply("CSMSG_IS_CHANSERV");
4108 if(!(handle = get_handle_info(argv[1])))
4110 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4114 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4116 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4121 reply("CSMSG_USER_PRESENT", handle->handle);
4122 else if(uData->seen)
4123 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4125 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4127 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4128 reply("CSMSG_USER_VACATION", handle->handle);
4133 static MODCMD_FUNC(cmd_names)
4135 struct userNode *targ;
4136 struct userData *targData;
4137 unsigned int ii, pos;
4140 for(ii=pos=0; ii<channel->members.used; ++ii)
4142 targ = channel->members.list[ii]->user;
4143 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4146 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4149 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4153 if(IsUserSuspended(targData))
4155 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4158 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4159 reply("CSMSG_END_NAMES", channel->name);
4164 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4166 switch(ntype->visible_type)
4168 case NOTE_VIS_ALL: return 1;
4169 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4170 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4175 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4177 struct userData *uData;
4179 switch(ntype->set_access_type)
4181 case NOTE_SET_CHANNEL_ACCESS:
4182 if(!user->handle_info)
4184 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4186 return uData->access >= ntype->set_access.min_ulevel;
4187 case NOTE_SET_CHANNEL_SETTER:
4188 return check_user_level(channel, user, lvlSetters, 1, 0);
4189 case NOTE_SET_PRIVILEGED: default:
4190 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4194 static CHANSERV_FUNC(cmd_note)
4196 struct chanData *cData;
4198 struct note_type *ntype;
4200 cData = channel->channel_info;
4203 reply("CSMSG_NOT_REGISTERED", channel->name);
4207 /* If no arguments, show all visible notes for the channel. */
4213 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4215 note = iter_data(it);
4216 if(!note_type_visible_to_user(cData, note->type, user))
4219 reply("CSMSG_NOTELIST_HEADER", channel->name);
4220 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4223 reply("CSMSG_NOTELIST_END", channel->name);
4225 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4227 /* If one argument, show the named note. */
4230 if((note = dict_find(cData->notes, argv[1], NULL))
4231 && note_type_visible_to_user(cData, note->type, user))
4233 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4235 else if((ntype = dict_find(note_types, argv[1], NULL))
4236 && note_type_visible_to_user(NULL, ntype, user))
4238 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4243 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4247 /* Assume they're trying to set a note. */
4251 ntype = dict_find(note_types, argv[1], NULL);
4254 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4257 else if(note_type_settable_by_user(channel, ntype, user))
4259 note_text = unsplit_string(argv+2, argc-2, NULL);
4260 if((note = dict_find(cData->notes, argv[1], NULL)))
4261 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4262 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4263 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4265 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4267 /* The note is viewable to staff only, so return 0
4268 to keep the invocation from getting logged (or
4269 regular users can see it in !events). */
4275 reply("CSMSG_NO_ACCESS");
4282 static CHANSERV_FUNC(cmd_delnote)
4287 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4288 || !note_type_settable_by_user(channel, note->type, user))
4290 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4293 dict_remove(channel->channel_info->notes, note->type->name);
4294 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4298 static CHANSERV_FUNC(cmd_events)
4300 struct logSearch discrim;
4301 struct logReport report;
4302 unsigned int matches, limit;
4304 limit = (argc > 1) ? atoi(argv[1]) : 10;
4305 if(limit < 1 || limit > 200)
4308 memset(&discrim, 0, sizeof(discrim));
4309 discrim.masks.bot = chanserv;
4310 discrim.masks.channel_name = channel->name;
4312 discrim.masks.command = argv[2];
4313 discrim.limit = limit;
4314 discrim.max_time = INT_MAX;
4315 discrim.severities = 1 << LOG_COMMAND;
4316 report.reporter = chanserv;
4318 reply("CSMSG_EVENT_SEARCH_RESULTS");
4319 matches = log_entry_search(&discrim, log_report_entry, &report);
4321 reply("MSG_MATCH_COUNT", matches);
4323 reply("MSG_NO_MATCHES");
4327 static CHANSERV_FUNC(cmd_say)
4333 msg = unsplit_string(argv + 1, argc - 1, NULL);
4334 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4336 else if(GetUserH(argv[1]))
4339 msg = unsplit_string(argv + 2, argc - 2, NULL);
4340 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4344 reply("MSG_NOT_TARGET_NAME");
4350 static CHANSERV_FUNC(cmd_emote)
4356 /* CTCP is so annoying. */
4357 msg = unsplit_string(argv + 1, argc - 1, NULL);
4358 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4360 else if(GetUserH(argv[1]))
4362 msg = unsplit_string(argv + 2, argc - 2, NULL);
4363 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4367 reply("MSG_NOT_TARGET_NAME");
4373 struct channelList *
4374 chanserv_support_channels(void)
4376 return &chanserv_conf.support_channels;
4379 static CHANSERV_FUNC(cmd_expire)
4381 int channel_count = registered_channels;
4382 expire_channels(NULL);
4383 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4388 chanserv_expire_suspension(void *data)
4390 struct suspended *suspended = data;
4391 struct chanNode *channel;
4392 struct mod_chanmode change;
4394 if(!suspended->expires || (now < suspended->expires))
4395 suspended->revoked = now;
4396 channel = suspended->cData->channel;
4397 suspended->cData->channel = channel;
4398 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4399 mod_chanmode_init(&change);
4401 change.args[0].mode = MODE_CHANOP;
4402 change.args[0].member = AddChannelUser(chanserv, channel);
4403 mod_chanmode_announce(chanserv, channel, &change);
4406 static CHANSERV_FUNC(cmd_csuspend)
4408 struct suspended *suspended;
4409 char reason[MAXLEN];
4410 time_t expiry, duration;
4411 struct userData *uData;
4415 if(IsProtected(channel->channel_info))
4417 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4421 if(argv[1][0] == '!')
4423 else if(IsSuspended(channel->channel_info))
4425 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4426 show_suspension_info(cmd, user, channel->channel_info->suspended);
4430 if(!strcmp(argv[1], "0"))
4432 else if((duration = ParseInterval(argv[1])))
4433 expiry = now + duration;
4436 reply("MSG_INVALID_DURATION", argv[1]);
4440 unsplit_string(argv + 2, argc - 2, reason);
4442 suspended = calloc(1, sizeof(*suspended));
4443 suspended->revoked = 0;
4444 suspended->issued = now;
4445 suspended->suspender = strdup(user->handle_info->handle);
4446 suspended->expires = expiry;
4447 suspended->reason = strdup(reason);
4448 suspended->cData = channel->channel_info;
4449 suspended->previous = suspended->cData->suspended;
4450 suspended->cData->suspended = suspended;
4452 if(suspended->expires)
4453 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4455 if(IsSuspended(channel->channel_info))
4457 suspended->previous->revoked = now;
4458 if(suspended->previous->expires)
4459 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4460 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4461 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4465 /* Mark all users in channel as absent. */
4466 for(uData = channel->channel_info->users; uData; uData = uData->next)
4475 /* Mark the channel as suspended, then part. */
4476 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4477 DelChannelUser(chanserv, channel, suspended->reason, 0);
4478 reply("CSMSG_SUSPENDED", channel->name);
4479 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4480 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4485 static CHANSERV_FUNC(cmd_cunsuspend)
4487 struct suspended *suspended;
4488 char message[MAXLEN];
4490 if(!IsSuspended(channel->channel_info))
4492 reply("CSMSG_NOT_SUSPENDED", channel->name);
4496 suspended = channel->channel_info->suspended;
4498 /* Expire the suspension and join ChanServ to the channel. */
4499 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4500 chanserv_expire_suspension(suspended);
4501 reply("CSMSG_UNSUSPENDED", channel->name);
4502 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4503 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4507 typedef struct chanservSearch
4515 unsigned long flags;
4519 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4522 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4527 search = malloc(sizeof(struct chanservSearch));
4528 memset(search, 0, sizeof(*search));
4531 for(i = 0; i < argc; i++)
4533 /* Assume all criteria require arguments. */
4536 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4540 if(!irccasecmp(argv[i], "name"))
4541 search->name = argv[++i];
4542 else if(!irccasecmp(argv[i], "registrar"))
4543 search->registrar = argv[++i];
4544 else if(!irccasecmp(argv[i], "unvisited"))
4545 search->unvisited = ParseInterval(argv[++i]);
4546 else if(!irccasecmp(argv[i], "registered"))
4547 search->registered = ParseInterval(argv[++i]);
4548 else if(!irccasecmp(argv[i], "flags"))
4551 if(!irccasecmp(argv[i], "nodelete"))
4552 search->flags |= CHANNEL_NODELETE;
4553 else if(!irccasecmp(argv[i], "suspended"))
4554 search->flags |= CHANNEL_SUSPENDED;
4557 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4561 else if(!irccasecmp(argv[i], "limit"))
4562 search->limit = strtoul(argv[++i], NULL, 10);
4565 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4570 if(search->name && !strcmp(search->name, "*"))
4572 if(search->registrar && !strcmp(search->registrar, "*"))
4573 search->registrar = 0;
4582 chanserv_channel_match(struct chanData *channel, search_t search)
4584 const char *name = channel->channel->name;
4585 if((search->name && !match_ircglob(name, search->name)) ||
4586 (search->registrar && !channel->registrar) ||
4587 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4588 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4589 (search->registered && (now - channel->registered) > search->registered) ||
4590 (search->flags && ((search->flags & channel->flags) != search->flags)))
4597 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4599 struct chanData *channel;
4600 unsigned int matches = 0;
4602 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4604 if(!chanserv_channel_match(channel, search))
4614 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4619 search_print(struct chanData *channel, void *data)
4621 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4624 static CHANSERV_FUNC(cmd_search)
4627 unsigned int matches;
4628 channel_search_func action;
4632 if(!irccasecmp(argv[1], "count"))
4633 action = search_count;
4634 else if(!irccasecmp(argv[1], "print"))
4635 action = search_print;
4638 reply("CSMSG_ACTION_INVALID", argv[1]);
4642 search = chanserv_search_create(user, argc - 2, argv + 2);
4646 if(action == search_count)
4647 search->limit = INT_MAX;
4649 if(action == search_print)
4650 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4652 matches = chanserv_channel_search(search, action, user);
4655 reply("MSG_MATCH_COUNT", matches);
4657 reply("MSG_NO_MATCHES");
4663 static CHANSERV_FUNC(cmd_unvisited)
4665 struct chanData *cData;
4666 time_t interval = chanserv_conf.channel_expire_delay;
4667 char buffer[INTERVALLEN];
4668 unsigned int limit = 25, matches = 0;
4672 interval = ParseInterval(argv[1]);
4674 limit = atoi(argv[2]);
4677 intervalString(buffer, interval, user->handle_info);
4678 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4680 for(cData = channelList; cData && matches < limit; cData = cData->next)
4682 if((now - cData->visited) < interval)
4685 intervalString(buffer, now - cData->visited, user->handle_info);
4686 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4693 static MODCMD_FUNC(chan_opt_defaulttopic)
4699 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4701 reply("CSMSG_TOPIC_LOCKED", channel->name);
4705 topic = unsplit_string(argv+1, argc-1, NULL);
4707 free(channel->channel_info->topic);
4708 if(topic[0] == '*' && topic[1] == 0)
4710 topic = channel->channel_info->topic = NULL;
4714 topic = channel->channel_info->topic = strdup(topic);
4715 if(channel->channel_info->topic_mask
4716 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4717 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4719 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4722 if(channel->channel_info->topic)
4723 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4725 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4729 static MODCMD_FUNC(chan_opt_topicmask)
4733 struct chanData *cData = channel->channel_info;
4736 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4738 reply("CSMSG_TOPIC_LOCKED", channel->name);
4742 mask = unsplit_string(argv+1, argc-1, NULL);
4744 if(cData->topic_mask)
4745 free(cData->topic_mask);
4746 if(mask[0] == '*' && mask[1] == 0)
4748 cData->topic_mask = 0;
4752 cData->topic_mask = strdup(mask);
4754 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4755 else if(!match_ircglob(cData->topic, cData->topic_mask))
4756 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4760 if(channel->channel_info->topic_mask)
4761 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4763 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4767 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4771 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4775 if(greeting[0] == '*' && greeting[1] == 0)
4779 unsigned int length = strlen(greeting);
4780 if(length > chanserv_conf.greeting_length)
4782 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4785 *data = strdup(greeting);
4794 reply(name, user_find_message(user, "MSG_NONE"));
4798 static MODCMD_FUNC(chan_opt_greeting)
4800 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4803 static MODCMD_FUNC(chan_opt_usergreeting)
4805 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4808 static MODCMD_FUNC(chan_opt_modes)
4810 struct mod_chanmode *new_modes;
4811 char modes[MODELEN];
4815 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4817 reply("CSMSG_NO_ACCESS");
4820 if(argv[1][0] == '*' && argv[1][1] == 0)
4822 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4824 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4826 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4829 else if(new_modes->argc > 1)
4831 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4832 mod_chanmode_free(new_modes);
4837 channel->channel_info->modes = *new_modes;
4838 modcmd_chanmode_announce(new_modes);
4839 mod_chanmode_free(new_modes);
4843 mod_chanmode_format(&channel->channel_info->modes, modes);
4845 reply("CSMSG_SET_MODES", modes);
4847 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4851 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4853 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4855 struct chanData *cData = channel->channel_info;
4860 /* Set flag according to value. */
4861 if(enabled_string(argv[1]))
4863 cData->flags |= mask;
4866 else if(disabled_string(argv[1]))
4868 cData->flags &= ~mask;
4873 reply("MSG_INVALID_BINARY", argv[1]);
4879 /* Find current option value. */
4880 value = (cData->flags & mask) ? 1 : 0;
4884 reply(name, user_find_message(user, "MSG_ON"));
4886 reply(name, user_find_message(user, "MSG_OFF"));
4890 static MODCMD_FUNC(chan_opt_nodelete)
4892 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4894 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4898 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4901 static MODCMD_FUNC(chan_opt_dynlimit)
4903 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4906 static MODCMD_FUNC(chan_opt_defaults)
4908 struct userData *uData;
4909 struct chanData *cData;
4910 const char *confirm;
4911 enum levelOption lvlOpt;
4912 enum charOption chOpt;
4914 cData = channel->channel_info;
4915 uData = GetChannelUser(cData, user->handle_info);
4916 if(!uData || (uData->access < UL_OWNER))
4918 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4921 confirm = make_confirmation_string(uData);
4922 if((argc < 2) || strcmp(argv[1], confirm))
4924 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4927 cData->flags = CHANNEL_DEFAULT_FLAGS;
4928 cData->modes = chanserv_conf.default_modes;
4929 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4930 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4931 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4932 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4933 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4938 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4940 struct chanData *cData = channel->channel_info;
4941 struct userData *uData;
4942 unsigned short value;
4946 if(!check_user_level(channel, user, option, 1, 1))
4948 reply("CSMSG_CANNOT_SET");
4951 value = user_level_from_name(argv[1], UL_OWNER+1);
4952 if(!value && strcmp(argv[1], "0"))
4954 reply("CSMSG_INVALID_ACCESS", argv[1]);
4957 uData = GetChannelUser(cData, user->handle_info);
4958 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4960 reply("CSMSG_BAD_SETLEVEL");
4966 if(value > cData->lvlOpts[lvlGiveOps])
4968 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4973 if(value < cData->lvlOpts[lvlGiveVoice])
4975 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4980 /* This test only applies to owners, since non-owners
4981 * trying to set an option to above their level get caught
4982 * by the CSMSG_BAD_SETLEVEL test above.
4984 if(value > uData->access)
4986 reply("CSMSG_BAD_SETTERS");
4993 cData->lvlOpts[option] = value;
4995 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4999 static MODCMD_FUNC(chan_opt_enfops)
5001 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5004 static MODCMD_FUNC(chan_opt_giveops)
5006 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5009 static MODCMD_FUNC(chan_opt_enfmodes)
5011 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5014 static MODCMD_FUNC(chan_opt_enftopic)
5016 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5019 static MODCMD_FUNC(chan_opt_pubcmd)
5021 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5024 static MODCMD_FUNC(chan_opt_setters)
5026 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5029 static MODCMD_FUNC(chan_opt_ctcpusers)
5031 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5034 static MODCMD_FUNC(chan_opt_userinfo)
5036 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5039 static MODCMD_FUNC(chan_opt_givevoice)
5041 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5044 static MODCMD_FUNC(chan_opt_topicsnarf)
5046 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5049 static MODCMD_FUNC(chan_opt_inviteme)
5051 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5055 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5057 struct chanData *cData = channel->channel_info;
5058 int count = charOptions[option].count, index;
5062 index = atoi(argv[1]);
5064 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5066 reply("CSMSG_INVALID_NUMERIC", index);
5067 /* Show possible values. */
5068 for(index = 0; index < count; index++)
5069 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5073 cData->chOpts[option] = charOptions[option].values[index].value;
5077 /* Find current option value. */
5080 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5084 /* Somehow, the option value is corrupt; reset it to the default. */
5085 cData->chOpts[option] = charOptions[option].default_value;
5090 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5094 static MODCMD_FUNC(chan_opt_protect)
5096 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5099 static MODCMD_FUNC(chan_opt_toys)
5101 return channel_multiple_option(chToys, CSFUNC_ARGS);
5104 static MODCMD_FUNC(chan_opt_ctcpreaction)
5106 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5109 static MODCMD_FUNC(chan_opt_topicrefresh)
5111 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5114 static struct svccmd_list set_shows_list;
5117 handle_svccmd_unbind(struct svccmd *target) {
5119 for(ii=0; ii<set_shows_list.used; ++ii)
5120 if(target == set_shows_list.list[ii])
5121 set_shows_list.used = 0;
5124 static CHANSERV_FUNC(cmd_set)
5126 struct svccmd *subcmd;
5130 /* Check if we need to (re-)initialize set_shows_list. */
5131 if(!set_shows_list.used)
5133 if(!set_shows_list.size)
5135 set_shows_list.size = chanserv_conf.set_shows->used;
5136 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5138 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5140 const char *name = chanserv_conf.set_shows->list[ii];
5141 sprintf(buf, "%s %s", argv[0], name);
5142 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5145 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5148 svccmd_list_append(&set_shows_list, subcmd);
5154 reply("CSMSG_CHANNEL_OPTIONS");
5155 for(ii = 0; ii < set_shows_list.used; ii++)
5157 subcmd = set_shows_list.list[ii];
5158 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5163 sprintf(buf, "%s %s", argv[0], argv[1]);
5164 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5167 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5170 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5172 reply("CSMSG_NO_ACCESS");
5176 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5180 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5182 struct userData *uData;
5184 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5187 reply("CSMSG_NOT_USER", channel->name);
5193 /* Just show current option value. */
5195 else if(enabled_string(argv[1]))
5197 uData->flags |= mask;
5199 else if(disabled_string(argv[1]))
5201 uData->flags &= ~mask;
5205 reply("MSG_INVALID_BINARY", argv[1]);
5209 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5213 static MODCMD_FUNC(user_opt_noautoop)
5215 struct userData *uData;
5217 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5220 reply("CSMSG_NOT_USER", channel->name);
5223 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5224 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5226 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5229 static MODCMD_FUNC(user_opt_autoinvite)
5231 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5234 static MODCMD_FUNC(user_opt_info)
5236 struct userData *uData;
5239 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5243 /* If they got past the command restrictions (which require access)
5244 * but fail this test, we have some fool with security override on.
5246 reply("CSMSG_NOT_USER", channel->name);
5253 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5254 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5256 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5259 bp = strcspn(infoline, "\001");
5262 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5267 if(infoline[0] == '*' && infoline[1] == 0)
5270 uData->info = strdup(infoline);
5273 reply("CSMSG_USET_INFO", uData->info);
5275 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5279 struct svccmd_list uset_shows_list;
5281 static CHANSERV_FUNC(cmd_uset)
5283 struct svccmd *subcmd;
5287 /* Check if we need to (re-)initialize uset_shows_list. */
5288 if(!uset_shows_list.used)
5292 "NoAutoOp", "AutoInvite", "Info"
5295 if(!uset_shows_list.size)
5297 uset_shows_list.size = ArrayLength(options);
5298 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5300 for(ii = 0; ii < ArrayLength(options); ii++)
5302 const char *name = options[ii];
5303 sprintf(buf, "%s %s", argv[0], name);
5304 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5307 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5310 svccmd_list_append(&uset_shows_list, subcmd);
5316 /* Do this so options are presented in a consistent order. */
5317 reply("CSMSG_USER_OPTIONS");
5318 for(ii = 0; ii < uset_shows_list.used; ii++)
5319 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5323 sprintf(buf, "%s %s", argv[0], argv[1]);
5324 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5327 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5331 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5334 static CHANSERV_FUNC(cmd_giveownership)
5336 struct handle_info *new_owner_hi;
5337 struct userData *new_owner, *curr_user;
5338 struct chanData *cData = channel->channel_info;
5339 struct do_not_register *dnr;
5341 unsigned short co_access;
5342 char reason[MAXLEN];
5345 curr_user = GetChannelAccess(cData, user->handle_info);
5346 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5347 if(!curr_user || (curr_user->access != UL_OWNER))
5349 struct userData *owner = NULL;
5350 for(curr_user = channel->channel_info->users;
5352 curr_user = curr_user->next)
5354 if(curr_user->access != UL_OWNER)
5358 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5365 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5367 if(new_owner_hi == user->handle_info)
5369 reply("CSMSG_NO_TRANSFER_SELF");
5372 new_owner = GetChannelAccess(cData, new_owner_hi);
5375 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5378 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5380 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5383 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5384 if(!IsHelping(user))
5385 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5387 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5390 if(new_owner->access >= UL_COOWNER)
5391 co_access = new_owner->access;
5393 co_access = UL_COOWNER;
5394 new_owner->access = UL_OWNER;
5396 curr_user->access = co_access;
5397 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5398 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5399 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5403 static CHANSERV_FUNC(cmd_suspend)
5405 struct handle_info *hi;
5406 struct userData *self, *target;
5409 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5410 self = GetChannelUser(channel->channel_info, user->handle_info);
5411 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5413 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5416 if(target->access >= self->access)
5418 reply("MSG_USER_OUTRANKED", hi->handle);
5421 if(target->flags & USER_SUSPENDED)
5423 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5428 target->present = 0;
5431 target->flags |= USER_SUSPENDED;
5432 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5436 static CHANSERV_FUNC(cmd_unsuspend)
5438 struct handle_info *hi;
5439 struct userData *self, *target;
5442 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5443 self = GetChannelUser(channel->channel_info, user->handle_info);
5444 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5446 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5449 if(target->access >= self->access)
5451 reply("MSG_USER_OUTRANKED", hi->handle);
5454 if(!(target->flags & USER_SUSPENDED))
5456 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5459 target->flags &= ~USER_SUSPENDED;
5460 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5464 static MODCMD_FUNC(cmd_deleteme)
5466 struct handle_info *hi;
5467 struct userData *target;
5468 const char *confirm_string;
5469 unsigned short access;
5472 hi = user->handle_info;
5473 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5475 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5478 if(target->access == UL_OWNER)
5480 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5483 confirm_string = make_confirmation_string(target);
5484 if((argc < 2) || strcmp(argv[1], confirm_string))
5486 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5489 access = target->access;
5490 channel_name = strdup(channel->name);
5491 del_channel_user(target, 1);
5492 reply("CSMSG_DELETED_YOU", access, channel_name);
5498 chanserv_refresh_topics(UNUSED_ARG(void *data))
5500 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5501 struct chanData *cData;
5504 for(cData = channelList; cData; cData = cData->next)
5506 if(IsSuspended(cData))
5508 opt = cData->chOpts[chTopicRefresh];
5511 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5514 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5515 cData->last_refresh = refresh_num;
5517 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5520 static CHANSERV_FUNC(cmd_unf)
5524 char response[MAXLEN];
5525 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5526 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5527 irc_privmsg(cmd->parent->bot, channel->name, response);
5530 reply("CSMSG_UNF_RESPONSE");
5534 static CHANSERV_FUNC(cmd_ping)
5538 char response[MAXLEN];
5539 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5540 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5541 irc_privmsg(cmd->parent->bot, channel->name, response);
5544 reply("CSMSG_PING_RESPONSE");
5548 static CHANSERV_FUNC(cmd_wut)
5552 char response[MAXLEN];
5553 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5554 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5555 irc_privmsg(cmd->parent->bot, channel->name, response);
5558 reply("CSMSG_WUT_RESPONSE");
5562 static CHANSERV_FUNC(cmd_8ball)
5564 unsigned int i, j, accum;
5569 for(i=1; i<argc; i++)
5570 for(j=0; argv[i][j]; j++)
5571 accum = (accum << 5) - accum + toupper(argv[i][j]);
5572 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5575 char response[MAXLEN];
5576 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5577 irc_privmsg(cmd->parent->bot, channel->name, response);
5580 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5584 static CHANSERV_FUNC(cmd_d)
5586 unsigned long sides, count, modifier, ii, total;
5587 char response[MAXLEN], *sep;
5591 if((count = strtoul(argv[1], &sep, 10)) < 1)
5601 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5602 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5606 else if((sep[0] == '-') && isdigit(sep[1]))
5607 modifier = strtoul(sep, NULL, 10);
5608 else if((sep[0] == '+') && isdigit(sep[1]))
5609 modifier = strtoul(sep+1, NULL, 10);
5616 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5621 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5624 for(total = ii = 0; ii < count; ++ii)
5625 total += (rand() % sides) + 1;
5628 if((count > 1) || modifier)
5630 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5631 sprintf(response, fmt, total, count, sides, modifier);
5635 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5636 sprintf(response, fmt, total, sides);
5639 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5641 send_message_type(4, user, cmd->parent->bot, "%s", response);
5645 static CHANSERV_FUNC(cmd_huggle)
5647 /* CTCP must be via PRIVMSG, never notice */
5649 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5651 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5656 chanserv_adjust_limit(void *data)
5658 struct mod_chanmode change;
5659 struct chanData *cData = data;
5660 struct chanNode *channel = cData->channel;
5663 if(IsSuspended(cData))
5666 cData->limitAdjusted = now;
5667 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5668 if(cData->modes.modes_set & MODE_LIMIT)
5670 if(limit > cData->modes.new_limit)
5671 limit = cData->modes.new_limit;
5672 else if(limit == cData->modes.new_limit)
5676 mod_chanmode_init(&change);
5677 change.modes_set = MODE_LIMIT;
5678 change.new_limit = limit;
5679 mod_chanmode_announce(chanserv, channel, &change);
5683 handle_new_channel(struct chanNode *channel)
5685 struct chanData *cData;
5687 if(!(cData = channel->channel_info))
5690 if(cData->modes.modes_set || cData->modes.modes_clear)
5691 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5693 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5694 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5697 /* Welcome to my worst nightmare. Warning: Read (or modify)
5698 the code below at your own risk. */
5700 handle_join(struct modeNode *mNode)
5702 struct mod_chanmode change;
5703 struct userNode *user = mNode->user;
5704 struct chanNode *channel = mNode->channel;
5705 struct chanData *cData;
5706 struct userData *uData = NULL;
5707 struct banData *bData;
5708 struct handle_info *handle;
5709 unsigned int modes = 0, info = 0;
5712 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5715 cData = channel->channel_info;
5716 if(channel->members.used > cData->max)
5717 cData->max = channel->members.used;
5719 /* Check for bans. If they're joining through a ban, one of two
5721 * 1: Join during a netburst, by riding the break. Kick them
5722 * unless they have ops or voice in the channel.
5723 * 2: They're allowed to join through the ban (an invite in
5724 * ircu2.10, or a +e on Hybrid, or something).
5725 * If they're not joining through a ban, and the banlist is not
5726 * full, see if they're on the banlist for the channel. If so,
5729 if(user->uplink->burst && !mNode->modes)
5732 for(ii = 0; ii < channel->banlist.used; ii++)
5734 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5736 /* Riding a netburst. Naughty. */
5737 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5743 mod_chanmode_init(&change);
5745 if(channel->banlist.used < MAXBANS)
5747 /* Not joining through a ban. */
5748 for(bData = cData->bans;
5749 bData && !user_matches_glob(user, bData->mask, 1);
5750 bData = bData->next);
5754 char kick_reason[MAXLEN];
5755 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5757 bData->triggered = now;
5758 if(bData != cData->bans)
5760 /* Shuffle the ban to the head of the list. */
5762 bData->next->prev = bData->prev;
5764 bData->prev->next = bData->next;
5767 bData->next = cData->bans;
5770 cData->bans->prev = bData;
5771 cData->bans = bData;
5774 change.args[0].mode = MODE_BAN;
5775 change.args[0].hostmask = bData->mask;
5776 mod_chanmode_announce(chanserv, channel, &change);
5777 KickChannelUser(user, channel, chanserv, kick_reason);
5782 /* ChanServ will not modify the limits in join-flooded channels.
5783 It will also skip DynLimit processing when the user (or srvx)
5784 is bursting in, because there are likely more incoming. */
5785 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5786 && !user->uplink->burst
5787 && !channel->join_flooded
5788 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5790 /* The user count has begun "bumping" into the channel limit,
5791 so set a timer to raise the limit a bit. Any previous
5792 timers are removed so three incoming users within the delay
5793 results in one limit change, not three. */
5795 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5796 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5799 if(channel->join_flooded)
5801 /* don't automatically give ops or voice during a join flood */
5803 else if(cData->lvlOpts[lvlGiveOps] == 0)
5804 modes |= MODE_CHANOP;
5805 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5806 modes |= MODE_VOICE;
5808 greeting = cData->greeting;
5809 if(user->handle_info)
5811 handle = user->handle_info;
5813 if(IsHelper(user) && !IsHelping(user))
5816 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5818 if(channel == chanserv_conf.support_channels.list[ii])
5820 HANDLE_SET_FLAG(user->handle_info, HELPING);
5826 uData = GetTrueChannelAccess(cData, handle);
5827 if(uData && !IsUserSuspended(uData))
5829 /* Ops and above were handled by the above case. */
5830 if(IsUserAutoOp(uData))
5832 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5833 modes |= MODE_CHANOP;
5834 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5835 modes |= MODE_VOICE;
5837 if(uData->access >= UL_PRESENT)
5838 cData->visited = now;
5839 if(cData->user_greeting)
5840 greeting = cData->user_greeting;
5842 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5843 && ((now - uData->seen) >= chanserv_conf.info_delay)
5850 if(!user->uplink->burst)
5854 if(modes & MODE_CHANOP)
5855 modes &= ~MODE_VOICE;
5856 change.args[0].mode = modes;
5857 change.args[0].member = mNode;
5858 mod_chanmode_announce(chanserv, channel, &change);
5860 if(greeting && !user->uplink->burst)
5861 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5863 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5869 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5871 struct mod_chanmode change;
5872 struct userData *channel;
5873 unsigned int ii, jj;
5875 if(!user->handle_info)
5878 mod_chanmode_init(&change);
5880 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5882 struct chanNode *cn;
5883 struct modeNode *mn;
5884 if(IsUserSuspended(channel)
5885 || IsSuspended(channel->channel)
5886 || !(cn = channel->channel->channel))
5889 mn = GetUserMode(cn, user);
5892 if(!IsUserSuspended(channel)
5893 && IsUserAutoInvite(channel)
5894 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5896 irc_invite(chanserv, user, cn);
5900 if(channel->access >= UL_PRESENT)
5901 channel->channel->visited = now;
5903 if(IsUserAutoOp(channel))
5905 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5906 change.args[0].mode = MODE_CHANOP;
5907 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5908 change.args[0].mode = MODE_VOICE;
5910 change.args[0].mode = 0;
5911 change.args[0].member = mn;
5912 if(change.args[0].mode)
5913 mod_chanmode_announce(chanserv, cn, &change);
5916 channel->seen = now;
5917 channel->present = 1;
5920 for(ii = 0; ii < user->channels.used; ++ii)
5922 struct chanNode *channel = user->channels.list[ii]->channel;
5923 struct banData *ban;
5925 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5926 || !channel->channel_info)
5928 for(jj = 0; jj < channel->banlist.used; ++jj)
5929 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5931 if(jj < channel->banlist.used)
5933 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5935 char kick_reason[MAXLEN];
5936 if(!user_matches_glob(user, ban->mask, 1))
5938 change.args[0].mode = MODE_BAN;
5939 change.args[0].hostmask = ban->mask;
5940 mod_chanmode_announce(chanserv, channel, &change);
5941 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5942 KickChannelUser(user, channel, chanserv, kick_reason);
5943 ban->triggered = now;
5948 if(IsSupportHelper(user))
5950 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5952 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5954 HANDLE_SET_FLAG(user->handle_info, HELPING);
5962 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5964 struct chanData *cData;
5965 struct userData *uData;
5967 cData = channel->channel_info;
5968 if(!cData || IsSuspended(cData) || IsLocal(user))
5971 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5973 /* Allow for a bit of padding so that the limit doesn't
5974 track the user count exactly, which could get annoying. */
5975 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5977 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5978 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5982 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5984 scan_user_presence(uData, user);
5988 if(IsHelping(user) && IsSupportHelper(user))
5990 unsigned int ii, jj;
5991 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5993 for(jj = 0; jj < user->channels.used; ++jj)
5994 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5996 if(jj < user->channels.used)
5999 if(ii == chanserv_conf.support_channels.used)
6000 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6005 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6007 struct userData *uData;
6009 if(!channel->channel_info || !kicker || IsService(kicker)
6010 || (kicker == victim) || IsSuspended(channel->channel_info)
6011 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6014 if(protect_user(victim, kicker, channel->channel_info))
6016 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6017 KickChannelUser(kicker, channel, chanserv, reason);
6020 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6025 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6027 struct chanData *cData;
6029 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6032 cData = channel->channel_info;
6033 if(bad_topic(channel, user, channel->topic))
6035 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6036 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6037 SetChannelTopic(channel, chanserv, old_topic, 1);
6038 else if(cData->topic)
6039 SetChannelTopic(channel, chanserv, cData->topic, 1);
6042 /* With topicsnarf, grab the topic and save it as the default topic. */
6043 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6046 cData->topic = strdup(channel->topic);
6052 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6054 struct mod_chanmode *bounce = NULL;
6055 unsigned int bnc, ii;
6058 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6061 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6062 && mode_lock_violated(&channel->channel_info->modes, change))
6064 char correct[MAXLEN];
6065 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6066 mod_chanmode_format(&channel->channel_info->modes, correct);
6067 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6069 for(ii = bnc = 0; ii < change->argc; ++ii)
6071 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6073 const struct userNode *victim = change->args[ii].member->user;
6074 if(!protect_user(victim, user, channel->channel_info))
6077 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6080 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6081 bounce->args[bnc].member = GetUserMode(channel, user);
6082 if(bounce->args[bnc].member)
6086 bounce->args[bnc].mode = MODE_CHANOP;
6087 bounce->args[bnc].member = change->args[ii].member;
6089 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6091 else if(change->args[ii].mode & MODE_CHANOP)
6093 const struct userNode *victim = change->args[ii].member->user;
6094 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6097 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6098 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6099 bounce->args[bnc].member = change->args[ii].member;
6102 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6104 const char *ban = change->args[ii].hostmask;
6105 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6108 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6109 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6110 bounce->args[bnc].hostmask = ban;
6112 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6117 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6118 mod_chanmode_announce(chanserv, channel, bounce);
6119 mod_chanmode_free(bounce);
6124 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6126 struct chanNode *channel;
6127 struct banData *bData;
6128 struct mod_chanmode change;
6129 unsigned int ii, jj;
6130 char kick_reason[MAXLEN];
6132 mod_chanmode_init(&change);
6134 change.args[0].mode = MODE_BAN;
6135 for(ii = 0; ii < user->channels.used; ++ii)
6137 channel = user->channels.list[ii]->channel;
6138 /* Need not check for bans if they're opped or voiced. */
6139 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6141 /* Need not check for bans unless channel registration is active. */
6142 if(!channel->channel_info || IsSuspended(channel->channel_info))
6144 /* Look for a matching ban already on the channel. */
6145 for(jj = 0; jj < channel->banlist.used; ++jj)
6146 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6148 /* Need not act if we found one. */
6149 if(jj < channel->banlist.used)
6151 /* Look for a matching ban in this channel. */
6152 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6154 if(!user_matches_glob(user, bData->mask, 1))
6156 change.args[0].hostmask = bData->mask;
6157 mod_chanmode_announce(chanserv, channel, &change);
6158 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6159 KickChannelUser(user, channel, chanserv, kick_reason);
6160 bData->triggered = now;
6161 break; /* we don't need to check any more bans in the channel */
6166 static void handle_rename(struct handle_info *handle, const char *old_handle)
6168 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6172 dict_remove2(handle_dnrs, old_handle, 1);
6173 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6174 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6179 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6181 struct userNode *h_user;
6183 if(handle->channels)
6185 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6186 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6188 while(handle->channels)
6189 del_channel_user(handle->channels, 1);
6194 handle_server_link(UNUSED_ARG(struct server *server))
6196 struct chanData *cData;
6198 for(cData = channelList; cData; cData = cData->next)
6200 if(!IsSuspended(cData))
6201 cData->may_opchan = 1;
6202 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6203 && !cData->channel->join_flooded
6204 && ((cData->channel->limit - cData->channel->members.used)
6205 < chanserv_conf.adjust_threshold))
6207 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6208 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6214 chanserv_conf_read(void)
6218 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6219 struct mod_chanmode *change;
6220 struct string_list *strlist;
6221 struct chanNode *chan;
6224 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6226 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6229 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6230 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6231 chanserv_conf.support_channels.used = 0;
6232 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6234 for(ii = 0; ii < strlist->used; ++ii)
6236 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6239 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6241 channelList_append(&chanserv_conf.support_channels, chan);
6244 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6247 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6250 chan = AddChannel(str, now, str2, NULL);
6252 channelList_append(&chanserv_conf.support_channels, chan);
6254 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6255 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6256 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6257 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6258 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6259 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6260 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6261 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6262 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6263 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6264 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6265 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6266 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6267 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6268 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6269 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6270 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6271 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6272 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6273 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6274 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6275 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6276 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6278 NickChange(chanserv, str, 0);
6279 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6280 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6281 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6282 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6283 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6284 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6285 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6286 chanserv_conf.max_owned = str ? atoi(str) : 5;
6287 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6288 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6289 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6290 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6291 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6292 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6293 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6296 safestrncpy(mode_line, str, sizeof(mode_line));
6297 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6298 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6300 chanserv_conf.default_modes = *change;
6301 mod_chanmode_free(change);
6303 free_string_list(chanserv_conf.set_shows);
6304 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6306 strlist = string_list_copy(strlist);
6309 static const char *list[] = {
6310 /* free form text */
6311 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6312 /* options based on user level */
6313 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6314 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6315 /* multiple choice options */
6316 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6317 /* binary options */
6318 "DynLimit", "NoDelete",
6323 strlist = alloc_string_list(ArrayLength(list)-1);
6324 for(ii=0; list[ii]; ii++)
6325 string_list_append(strlist, strdup(list[ii]));
6327 chanserv_conf.set_shows = strlist;
6328 /* We don't look things up now, in case the list refers to options
6329 * defined by modules initialized after this point. Just mark the
6330 * function list as invalid, so it will be initialized.
6332 set_shows_list.used = 0;
6333 free_string_list(chanserv_conf.eightball);
6334 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6337 strlist = string_list_copy(strlist);
6341 strlist = alloc_string_list(4);
6342 string_list_append(strlist, strdup("Yes."));
6343 string_list_append(strlist, strdup("No."));
6344 string_list_append(strlist, strdup("Maybe so."));
6346 chanserv_conf.eightball = strlist;
6347 free_string_list(chanserv_conf.old_ban_names);
6348 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6350 strlist = string_list_copy(strlist);
6352 strlist = alloc_string_list(2);
6353 chanserv_conf.old_ban_names = strlist;
6357 chanserv_note_type_read(const char *key, struct record_data *rd)
6360 struct note_type *ntype;
6363 if(!(obj = GET_RECORD_OBJECT(rd)))
6365 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6368 if(!(ntype = chanserv_create_note_type(key)))
6370 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6374 /* Figure out set access */
6375 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6377 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6378 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6380 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6382 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6383 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6385 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6387 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6391 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6392 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6393 ntype->set_access.min_opserv = 0;
6396 /* Figure out visibility */
6397 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6398 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6399 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6400 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6401 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6402 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6403 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6404 ntype->visible_type = NOTE_VIS_ALL;
6406 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6408 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6409 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6413 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6415 struct handle_info *handle;
6416 struct userData *uData;
6417 char *seen, *inf, *flags;
6419 unsigned short access;
6421 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6423 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6427 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6428 if(access > UL_OWNER)
6430 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6434 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6435 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6436 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6437 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6438 handle = get_handle_info(key);
6441 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6445 uData = add_channel_user(chan, handle, access, last_seen, inf);
6446 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6450 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6452 struct banData *bData;
6453 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6454 time_t set_time, triggered_time, expires_time;
6456 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6458 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6462 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6463 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6464 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6465 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6466 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6467 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6469 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6470 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6472 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6474 expires_time = set_time + atoi(s_duration);
6478 if(expires_time && (expires_time < now))
6481 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6484 static struct suspended *
6485 chanserv_read_suspended(dict_t obj)
6487 struct suspended *suspended = calloc(1, sizeof(*suspended));
6491 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6492 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6493 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6494 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6495 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6496 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6497 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6498 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6499 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6500 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6505 chanserv_channel_read(const char *key, struct record_data *hir)
6507 struct suspended *suspended;
6508 struct mod_chanmode *modes;
6509 struct chanNode *cNode;
6510 struct chanData *cData;
6511 struct dict *channel, *obj;
6512 char *str, *argv[10];
6516 channel = hir->d.object;
6518 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6521 cNode = AddChannel(key, now, NULL, NULL);
6524 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6527 cData = register_channel(cNode, str);
6530 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6534 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6536 enum levelOption lvlOpt;
6537 enum charOption chOpt;
6539 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6540 cData->flags = atoi(str);
6542 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6544 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6546 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6547 else if(levelOptions[lvlOpt].old_flag)
6549 if(cData->flags & levelOptions[lvlOpt].old_flag)
6550 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6552 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6556 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6558 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6560 cData->chOpts[chOpt] = str[0];
6563 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6565 enum levelOption lvlOpt;
6566 enum charOption chOpt;
6569 cData->flags = base64toint(str, 5);
6570 count = strlen(str += 5);
6571 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6574 if(levelOptions[lvlOpt].old_flag)
6576 if(cData->flags & levelOptions[lvlOpt].old_flag)
6577 lvl = levelOptions[lvlOpt].flag_value;
6579 lvl = levelOptions[lvlOpt].default_value;
6581 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6583 case 'c': lvl = UL_COOWNER; break;
6584 case 'm': lvl = UL_MASTER; break;
6585 case 'n': lvl = UL_OWNER+1; break;
6586 case 'o': lvl = UL_OP; break;
6587 case 'p': lvl = UL_PEON; break;
6588 case 'w': lvl = UL_OWNER; break;
6589 default: lvl = 0; break;
6591 cData->lvlOpts[lvlOpt] = lvl;
6593 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6594 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6597 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6599 suspended = chanserv_read_suspended(obj);
6600 cData->suspended = suspended;
6601 suspended->cData = cData;
6602 /* We could use suspended->expires and suspended->revoked to
6603 * set the CHANNEL_SUSPENDED flag, but we don't. */
6605 else if(cData->flags & CHANNEL_SUSPENDED)
6607 suspended = calloc(1, sizeof(*suspended));
6608 suspended->issued = 0;
6609 suspended->revoked = 0;
6610 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6611 suspended->expires = str ? atoi(str) : 0;
6612 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6613 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6614 suspended->reason = strdup(str ? str : "No reason");
6615 suspended->previous = NULL;
6616 cData->suspended = suspended;
6617 suspended->cData = cData;
6622 if((cData->flags & CHANNEL_SUSPENDED)
6623 && suspended->expires
6624 && (suspended->expires <= now))
6626 cData->flags &= ~CHANNEL_SUSPENDED;
6629 if(!(cData->flags & CHANNEL_SUSPENDED))
6631 struct mod_chanmode change;
6632 mod_chanmode_init(&change);
6634 change.args[0].mode = MODE_CHANOP;
6635 change.args[0].member = AddChannelUser(chanserv, cNode);
6636 mod_chanmode_announce(chanserv, cNode, &change);
6638 else if(suspended->expires > now)
6640 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6643 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6644 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6645 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6646 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6647 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6648 cData->max = str ? atoi(str) : 0;
6649 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6650 cData->greeting = str ? strdup(str) : NULL;
6651 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6652 cData->user_greeting = str ? strdup(str) : NULL;
6653 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6654 cData->topic_mask = str ? strdup(str) : NULL;
6655 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6656 cData->topic = str ? strdup(str) : NULL;
6658 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6659 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6660 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6661 cData->modes = *modes;
6662 if(cData->modes.argc > 1)
6663 cData->modes.argc = 1;
6664 if(!IsSuspended(cData))
6665 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6666 mod_chanmode_free(modes);
6669 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6670 for(it = dict_first(obj); it; it = iter_next(it))
6671 user_read_helper(iter_key(it), iter_data(it), cData);
6673 if(!cData->users && !IsProtected(cData))
6675 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6676 unregister_channel(cData, "has empty user list.");
6680 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6681 for(it = dict_first(obj); it; it = iter_next(it))
6682 ban_read_helper(iter_key(it), iter_data(it), cData);
6684 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6685 for(it = dict_first(obj); it; it = iter_next(it))
6687 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6688 struct record_data *rd = iter_data(it);
6689 const char *note, *setter;
6691 if(rd->type != RECDB_OBJECT)
6693 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6697 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6699 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6701 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6705 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6706 if(!setter) setter = "<unknown>";
6707 chanserv_add_channel_note(cData, ntype, setter, note);
6715 chanserv_dnr_read(const char *key, struct record_data *hir)
6717 const char *setter, *reason, *str;
6718 struct do_not_register *dnr;
6720 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6723 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6726 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6729 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6732 dnr = chanserv_add_dnr(key, setter, reason);
6735 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6737 dnr->set = atoi(str);
6743 chanserv_saxdb_read(struct dict *database)
6745 struct dict *section;
6748 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6749 for(it = dict_first(section); it; it = iter_next(it))
6750 chanserv_note_type_read(iter_key(it), iter_data(it));
6752 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6753 for(it = dict_first(section); it; it = iter_next(it))
6754 chanserv_channel_read(iter_key(it), iter_data(it));
6756 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6757 for(it = dict_first(section); it; it = iter_next(it))
6758 chanserv_dnr_read(iter_key(it), iter_data(it));
6764 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6766 int high_present = 0;
6767 saxdb_start_record(ctx, KEY_USERS, 1);
6768 for(; uData; uData = uData->next)
6770 if((uData->access >= UL_PRESENT) && uData->present)
6772 saxdb_start_record(ctx, uData->handle->handle, 0);
6773 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6774 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6776 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6778 saxdb_write_string(ctx, KEY_INFO, uData->info);
6779 saxdb_end_record(ctx);
6781 saxdb_end_record(ctx);
6782 return high_present;
6786 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6790 saxdb_start_record(ctx, KEY_BANS, 1);
6791 for(; bData; bData = bData->next)
6793 saxdb_start_record(ctx, bData->mask, 0);
6794 saxdb_write_int(ctx, KEY_SET, bData->set);
6795 if(bData->triggered)
6796 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6798 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6800 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6802 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6803 saxdb_end_record(ctx);
6805 saxdb_end_record(ctx);
6809 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6811 saxdb_start_record(ctx, name, 0);
6812 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6813 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6815 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6817 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6819 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6821 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6822 saxdb_end_record(ctx);
6826 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6830 enum levelOption lvlOpt;
6831 enum charOption chOpt;
6833 saxdb_start_record(ctx, channel->channel->name, 1);
6835 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6836 saxdb_write_int(ctx, KEY_MAX, channel->max);
6838 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6839 if(channel->registrar)
6840 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6841 if(channel->greeting)
6842 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6843 if(channel->user_greeting)
6844 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6845 if(channel->topic_mask)
6846 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6847 if(channel->suspended)
6848 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6850 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6851 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6852 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6853 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6854 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6856 buf[0] = channel->chOpts[chOpt];
6858 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6860 saxdb_end_record(ctx);
6862 if(channel->modes.modes_set || channel->modes.modes_clear)
6864 mod_chanmode_format(&channel->modes, buf);
6865 saxdb_write_string(ctx, KEY_MODES, buf);
6868 high_present = chanserv_write_users(ctx, channel->users);
6869 chanserv_write_bans(ctx, channel->bans);
6871 if(dict_size(channel->notes))
6875 saxdb_start_record(ctx, KEY_NOTES, 1);
6876 for(it = dict_first(channel->notes); it; it = iter_next(it))
6878 struct note *note = iter_data(it);
6879 saxdb_start_record(ctx, iter_key(it), 0);
6880 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6881 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6882 saxdb_end_record(ctx);
6884 saxdb_end_record(ctx);
6887 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6888 saxdb_end_record(ctx);
6892 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6896 saxdb_start_record(ctx, ntype->name, 0);
6897 switch(ntype->set_access_type)
6899 case NOTE_SET_CHANNEL_ACCESS:
6900 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6902 case NOTE_SET_CHANNEL_SETTER:
6903 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6905 case NOTE_SET_PRIVILEGED: default:
6906 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6909 switch(ntype->visible_type)
6911 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6912 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6913 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6915 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6916 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6917 saxdb_end_record(ctx);
6921 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6923 struct do_not_register *dnr;
6926 for(it = dict_first(dnrs); it; it = iter_next(it))
6928 dnr = iter_data(it);
6929 saxdb_start_record(ctx, dnr->chan_name, 0);
6931 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6932 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6933 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6934 saxdb_end_record(ctx);
6939 chanserv_saxdb_write(struct saxdb_context *ctx)
6942 struct chanData *channel;
6945 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6946 for(it = dict_first(note_types); it; it = iter_next(it))
6947 chanserv_write_note_type(ctx, iter_data(it));
6948 saxdb_end_record(ctx);
6951 saxdb_start_record(ctx, KEY_DNR, 1);
6952 write_dnrs_helper(ctx, handle_dnrs);
6953 write_dnrs_helper(ctx, plain_dnrs);
6954 write_dnrs_helper(ctx, mask_dnrs);
6955 saxdb_end_record(ctx);
6958 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6959 for(channel = channelList; channel; channel = channel->next)
6960 chanserv_write_channel(ctx, channel);
6961 saxdb_end_record(ctx);
6967 chanserv_db_cleanup(void) {
6969 unreg_part_func(handle_part);
6971 unregister_channel(channelList, "terminating.");
6972 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6973 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6974 free(chanserv_conf.support_channels.list);
6975 dict_delete(handle_dnrs);
6976 dict_delete(plain_dnrs);
6977 dict_delete(mask_dnrs);
6978 dict_delete(note_types);
6979 free_string_list(chanserv_conf.eightball);
6980 free_string_list(chanserv_conf.old_ban_names);
6981 free_string_list(chanserv_conf.set_shows);
6982 free(set_shows_list.list);
6983 free(uset_shows_list.list);
6986 struct userData *helper = helperList;
6987 helperList = helperList->next;
6992 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6993 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6994 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6997 init_chanserv(const char *nick)
6999 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7000 conf_register_reload(chanserv_conf_read);
7002 reg_server_link_func(handle_server_link);
7004 reg_new_channel_func(handle_new_channel);
7005 reg_join_func(handle_join);
7006 reg_part_func(handle_part);
7007 reg_kick_func(handle_kick);
7008 reg_topic_func(handle_topic);
7009 reg_mode_change_func(handle_mode);
7010 reg_nick_change_func(handle_nick_change);
7012 reg_auth_func(handle_auth);
7013 reg_handle_rename_func(handle_rename);
7014 reg_unreg_func(handle_unreg);
7016 handle_dnrs = dict_new();
7017 dict_set_free_data(handle_dnrs, free);
7018 plain_dnrs = dict_new();
7019 dict_set_free_data(plain_dnrs, free);
7020 mask_dnrs = dict_new();
7021 dict_set_free_data(mask_dnrs, free);
7023 reg_svccmd_unbind_func(handle_svccmd_unbind);
7024 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7025 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7026 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7027 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7028 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7029 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7030 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7031 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7032 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7034 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7035 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7037 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7038 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7039 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7040 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7041 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7043 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7044 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7045 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7046 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7047 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7049 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7050 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7051 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7052 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7054 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7055 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7056 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7057 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7058 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7059 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7060 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7061 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7063 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7064 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7065 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7066 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7067 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7068 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7069 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7070 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7071 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7072 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7073 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7074 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7075 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7076 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7078 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7079 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7080 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7081 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7082 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7084 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7085 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7087 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7088 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7089 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7090 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7091 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7092 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7093 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7094 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7095 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7096 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7097 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7099 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7100 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7102 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7103 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7104 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7105 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7107 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7108 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7109 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7110 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7111 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7113 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7114 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7115 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7116 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7117 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7118 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7120 /* Channel options */
7121 DEFINE_CHANNEL_OPTION(defaulttopic);
7122 DEFINE_CHANNEL_OPTION(topicmask);
7123 DEFINE_CHANNEL_OPTION(greeting);
7124 DEFINE_CHANNEL_OPTION(usergreeting);
7125 DEFINE_CHANNEL_OPTION(modes);
7126 DEFINE_CHANNEL_OPTION(enfops);
7127 DEFINE_CHANNEL_OPTION(giveops);
7128 DEFINE_CHANNEL_OPTION(protect);
7129 DEFINE_CHANNEL_OPTION(enfmodes);
7130 DEFINE_CHANNEL_OPTION(enftopic);
7131 DEFINE_CHANNEL_OPTION(pubcmd);
7132 DEFINE_CHANNEL_OPTION(givevoice);
7133 DEFINE_CHANNEL_OPTION(userinfo);
7134 DEFINE_CHANNEL_OPTION(dynlimit);
7135 DEFINE_CHANNEL_OPTION(topicsnarf);
7136 DEFINE_CHANNEL_OPTION(nodelete);
7137 DEFINE_CHANNEL_OPTION(toys);
7138 DEFINE_CHANNEL_OPTION(setters);
7139 DEFINE_CHANNEL_OPTION(topicrefresh);
7140 DEFINE_CHANNEL_OPTION(ctcpusers);
7141 DEFINE_CHANNEL_OPTION(ctcpreaction);
7142 DEFINE_CHANNEL_OPTION(inviteme);
7143 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7145 /* Alias set topic to set defaulttopic for compatibility. */
7146 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7149 DEFINE_USER_OPTION(noautoop);
7150 DEFINE_USER_OPTION(autoinvite);
7151 DEFINE_USER_OPTION(info);
7153 /* Alias uset autovoice to uset autoop. */
7154 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7156 note_types = dict_new();
7157 dict_set_free_data(note_types, chanserv_deref_note_type);
7160 chanserv = AddService(nick, "Channel Services", NULL);
7161 service_register(chanserv)->trigger = '!';
7162 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7164 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7166 if(chanserv_conf.channel_expire_frequency)
7167 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7169 if(chanserv_conf.refresh_period)
7171 time_t next_refresh;
7172 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7173 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7176 reg_exit_func(chanserv_db_cleanup);
7177 message_register_table(msgtab);