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";
2512 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2514 change.args[0].mode = MODE_VOICE;
2515 errmsg = "CSMSG_ALREADY_VOICED";
2520 reply("CSMSG_NO_ACCESS");
2523 change.args[0].mode &= ~change.args[0].member->modes;
2524 if(!change.args[0].mode)
2527 reply(errmsg, channel->name);
2530 modcmd_chanmode_announce(&change);
2534 static CHANSERV_FUNC(cmd_down)
2536 struct mod_chanmode change;
2538 mod_chanmode_init(&change);
2540 change.args[0].member = GetUserMode(channel, user);
2541 if(!change.args[0].member)
2544 reply("MSG_CHANNEL_ABSENT", channel->name);
2548 if(!change.args[0].member->modes)
2551 reply("CSMSG_ALREADY_DOWN", channel->name);
2555 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2556 modcmd_chanmode_announce(&change);
2560 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)
2562 struct userData *cList;
2564 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2566 if(IsSuspended(cList->channel)
2567 || IsUserSuspended(cList)
2568 || !GetUserMode(cList->channel->channel, user))
2571 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2577 static CHANSERV_FUNC(cmd_upall)
2579 return cmd_all(CSFUNC_ARGS, cmd_up);
2582 static CHANSERV_FUNC(cmd_downall)
2584 return cmd_all(CSFUNC_ARGS, cmd_down);
2587 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2588 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2591 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)
2593 unsigned int ii, valid;
2594 struct userNode *victim;
2595 struct mod_chanmode *change;
2597 change = mod_chanmode_alloc(argc - 1);
2599 for(ii=valid=0; ++ii < argc; )
2601 if(!(victim = GetUserH(argv[ii])))
2603 change->args[valid].mode = mode;
2604 change->args[valid].member = GetUserMode(channel, victim);
2605 if(!change->args[valid].member)
2607 if(validate && !validate(user, channel, victim))
2612 change->argc = valid;
2613 if(valid < (argc-1))
2614 reply("CSMSG_PROCESS_FAILED");
2617 modcmd_chanmode_announce(change);
2618 reply(action, channel->name);
2620 mod_chanmode_free(change);
2624 static CHANSERV_FUNC(cmd_op)
2626 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2629 static CHANSERV_FUNC(cmd_deop)
2631 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2634 static CHANSERV_FUNC(cmd_voice)
2636 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2639 static CHANSERV_FUNC(cmd_devoice)
2641 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2645 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2651 for(ii=0; ii<channel->members.used; ii++)
2653 struct modeNode *mn = channel->members.list[ii];
2655 if(IsService(mn->user))
2658 if(!user_matches_glob(mn->user, ban, 1))
2661 if(protect_user(mn->user, user, channel->channel_info))
2665 victims[(*victimCount)++] = mn;
2671 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2673 struct userNode *victim;
2674 struct modeNode **victims;
2675 unsigned int offset, n, victimCount, duration = 0;
2676 char *reason = "Bye.", *ban, *name;
2677 char interval[INTERVALLEN];
2679 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2680 REQUIRE_PARAMS(offset);
2683 reason = unsplit_string(argv + offset, argc - offset, NULL);
2684 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2686 /* Truncate the reason to a length of TOPICLEN, as
2687 the ircd does; however, leave room for an ellipsis
2688 and the kicker's nick. */
2689 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2693 if((victim = GetUserH(argv[1])))
2695 victims = alloca(sizeof(victims[0]));
2696 victims[0] = GetUserMode(channel, victim);
2697 /* XXX: The comparison with ACTION_KICK is just because all
2698 * other actions can work on users outside the channel, and we
2699 * want to allow those (e.g. unbans) in that case. If we add
2700 * some other ejection action for in-channel users, change
2702 victimCount = victims[0] ? 1 : 0;
2704 if(IsService(victim))
2706 reply("MSG_SERVICE_IMMUNE", victim->nick);
2710 if((action == ACTION_KICK) && !victimCount)
2712 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2716 if(protect_user(victim, user, channel->channel_info))
2718 reply("CSMSG_USER_PROTECTED", victim->nick);
2722 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2723 name = victim->nick;
2727 if(!is_ircmask(argv[1]))
2729 reply("MSG_NICK_UNKNOWN", argv[1]);
2733 victims = alloca(sizeof(victims[0]) * channel->members.used);
2735 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2737 reply("CSMSG_MASK_PROTECTED", argv[1]);
2741 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2743 reply("CSMSG_LAME_MASK", argv[1]);
2747 if((action == ACTION_KICK) && (victimCount == 0))
2749 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2753 name = ban = strdup(argv[1]);
2756 /* Truncate the ban in place if necessary; we must ensure
2757 that 'ban' is a valid ban mask before sanitizing it. */
2758 sanitize_ircmask(ban);
2760 if(action & ACTION_ADD_BAN)
2762 struct banData *bData, *next;
2764 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2766 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2771 if(action & ACTION_ADD_TIMED_BAN)
2773 duration = ParseInterval(argv[2]);
2777 reply("CSMSG_DURATION_TOO_LOW");
2781 else if(duration > (86400 * 365 * 2))
2783 reply("CSMSG_DURATION_TOO_HIGH");
2789 for(bData = channel->channel_info->bans; bData; bData = next)
2791 if(match_ircglobs(bData->mask, ban))
2793 int exact = !irccasecmp(bData->mask, ban);
2795 /* The ban is redundant; there is already a ban
2796 with the same effect in place. */
2800 free(bData->reason);
2801 bData->reason = strdup(reason);
2802 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2804 reply("CSMSG_REASON_CHANGE", ban);
2808 if(exact && bData->expires)
2812 /* If the ban matches an existing one exactly,
2813 extend the expiration time if the provided
2814 duration is longer. */
2815 if(duration && ((time_t)(now + duration) > bData->expires))
2817 bData->expires = now + duration;
2828 /* Delete the expiration timeq entry and
2829 requeue if necessary. */
2830 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2833 timeq_add(bData->expires, expire_ban, bData);
2837 /* automated kickban */
2840 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2842 reply("CSMSG_BAN_ADDED", name, channel->name);
2848 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2855 if(match_ircglobs(ban, bData->mask))
2857 /* The ban we are adding makes previously existing
2858 bans redundant; silently remove them. */
2859 del_channel_ban(bData);
2863 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);
2865 name = ban = strdup(bData->mask);
2869 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2871 extern const char *hidden_host_suffix;
2872 const char *old_name = chanserv_conf.old_ban_names->list[n];
2874 unsigned int l1, l2;
2877 l2 = strlen(old_name);
2880 if(irccasecmp(ban + l1 - l2, old_name))
2882 new_mask = malloc(MAXLEN);
2883 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2885 name = ban = new_mask;
2890 if(action & ACTION_BAN)
2892 unsigned int exists;
2893 struct mod_chanmode *change;
2895 if(channel->banlist.used >= MAXBANS)
2898 reply("CSMSG_BANLIST_FULL", channel->name);
2903 exists = ChannelBanExists(channel, ban);
2904 change = mod_chanmode_alloc(victimCount + 1);
2905 for(n = 0; n < victimCount; ++n)
2907 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2908 change->args[n].member = victims[n];
2912 change->args[n].mode = MODE_BAN;
2913 change->args[n++].hostmask = ban;
2917 modcmd_chanmode_announce(change);
2919 mod_chanmode_announce(chanserv, channel, change);
2920 mod_chanmode_free(change);
2922 if(exists && (action == ACTION_BAN))
2925 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2931 if(action & ACTION_KICK)
2933 char kick_reason[MAXLEN];
2934 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2936 for(n = 0; n < victimCount; n++)
2937 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2942 /* No response, since it was automated. */
2944 else if(action & ACTION_ADD_BAN)
2947 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2949 reply("CSMSG_BAN_ADDED", name, channel->name);
2951 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2952 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2953 else if(action & ACTION_BAN)
2954 reply("CSMSG_BAN_DONE", name, channel->name);
2955 else if(action & ACTION_KICK && victimCount)
2956 reply("CSMSG_KICK_DONE", name, channel->name);
2962 static CHANSERV_FUNC(cmd_kickban)
2964 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2967 static CHANSERV_FUNC(cmd_kick)
2969 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2972 static CHANSERV_FUNC(cmd_ban)
2974 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2977 static CHANSERV_FUNC(cmd_addban)
2979 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2982 static CHANSERV_FUNC(cmd_addtimedban)
2984 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2987 static struct mod_chanmode *
2988 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2990 struct mod_chanmode *change;
2991 unsigned char *match;
2992 unsigned int ii, count;
2994 match = alloca(bans->used);
2997 for(ii = count = 0; ii < bans->used; ++ii)
2999 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3006 for(ii = count = 0; ii < bans->used; ++ii)
3008 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3015 change = mod_chanmode_alloc(count);
3016 for(ii = count = 0; ii < bans->used; ++ii)
3020 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3021 change->args[count++].hostmask = bans->list[ii]->ban;
3027 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3029 struct userNode *actee;
3035 /* may want to allow a comma delimited list of users... */
3036 if(!(actee = GetUserH(argv[1])))
3038 if(!is_ircmask(argv[1]))
3040 reply("MSG_NICK_UNKNOWN", argv[1]);
3044 mask = strdup(argv[1]);
3047 /* We don't sanitize the mask here because ircu
3049 if(action & ACTION_UNBAN)
3051 struct mod_chanmode *change;
3052 change = find_matching_bans(&channel->banlist, actee, mask);
3055 modcmd_chanmode_announce(change);
3056 mod_chanmode_free(change);
3061 if(action & ACTION_DEL_BAN)
3063 struct banData *ban, *next;
3065 ban = channel->channel_info->bans;
3069 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3072 for( ; ban && !match_ircglobs(mask, ban->mask);
3077 del_channel_ban(ban);
3084 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3086 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3092 static CHANSERV_FUNC(cmd_unban)
3094 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3097 static CHANSERV_FUNC(cmd_delban)
3099 /* it doesn't necessarily have to remove the channel ban - may want
3100 to make that an option. */
3101 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3104 static CHANSERV_FUNC(cmd_unbanme)
3106 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3107 long flags = ACTION_UNBAN;
3109 /* remove permanent bans if the user has the proper access. */
3110 if(uData->access >= UL_MASTER)
3111 flags |= ACTION_DEL_BAN;
3113 argv[1] = user->nick;
3114 return unban_user(user, channel, 2, argv, cmd, flags);
3117 static CHANSERV_FUNC(cmd_unbanall)
3119 struct mod_chanmode *change;
3122 if(!channel->banlist.used)
3124 reply("CSMSG_NO_BANS", channel->name);
3128 change = mod_chanmode_alloc(channel->banlist.used);
3129 for(ii=0; ii<channel->banlist.used; ii++)
3131 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3132 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3134 modcmd_chanmode_announce(change);
3135 mod_chanmode_free(change);
3136 reply("CSMSG_BANS_REMOVED", channel->name);
3140 static CHANSERV_FUNC(cmd_open)
3142 struct mod_chanmode *change;
3144 change = find_matching_bans(&channel->banlist, user, NULL);
3146 change = mod_chanmode_alloc(0);
3147 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3148 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3149 && channel->channel_info->modes.modes_set)
3150 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3151 modcmd_chanmode_announce(change);
3152 reply("CSMSG_CHANNEL_OPENED", channel->name);
3153 mod_chanmode_free(change);
3157 static CHANSERV_FUNC(cmd_myaccess)
3159 static struct string_buffer sbuf;
3160 struct handle_info *target_handle;
3161 struct userData *uData;
3164 target_handle = user->handle_info;
3165 else if(!IsHelping(user))
3167 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3170 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3173 if(!target_handle->channels)
3175 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3179 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3180 for(uData = target_handle->channels; uData; uData = uData->u_next)
3182 struct chanData *cData = uData->channel;
3184 if(uData->access > UL_OWNER)
3186 if(IsProtected(cData)
3187 && (target_handle != user->handle_info)
3188 && !GetTrueChannelAccess(cData, user->handle_info))
3191 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3192 if(uData->flags != USER_AUTO_OP)
3193 string_buffer_append(&sbuf, ',');
3194 if(IsUserSuspended(uData))
3195 string_buffer_append(&sbuf, 's');
3196 if(IsUserAutoOp(uData))
3198 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3199 string_buffer_append(&sbuf, 'o');
3200 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3201 string_buffer_append(&sbuf, 'v');
3203 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3204 string_buffer_append(&sbuf, 'i');
3206 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3208 string_buffer_append_string(&sbuf, ")]");
3209 string_buffer_append(&sbuf, '\0');
3210 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3216 static CHANSERV_FUNC(cmd_access)
3218 struct userNode *target;
3219 struct handle_info *target_handle;
3220 struct userData *uData;
3222 char prefix[MAXLEN];
3227 target_handle = target->handle_info;
3229 else if((target = GetUserH(argv[1])))
3231 target_handle = target->handle_info;
3233 else if(argv[1][0] == '*')
3235 if(!(target_handle = get_handle_info(argv[1]+1)))
3237 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3243 reply("MSG_NICK_UNKNOWN", argv[1]);
3247 assert(target || target_handle);
3249 if(target == chanserv)
3251 reply("CSMSG_IS_CHANSERV");
3259 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3264 reply("MSG_USER_AUTHENTICATE", target->nick);
3267 reply("MSG_AUTHENTICATE");
3273 const char *epithet = NULL, *type = NULL;
3276 epithet = chanserv_conf.irc_operator_epithet;
3279 else if(IsNetworkHelper(target))
3281 epithet = chanserv_conf.network_helper_epithet;
3282 type = "network helper";
3284 else if(IsSupportHelper(target))
3286 epithet = chanserv_conf.support_helper_epithet;
3287 type = "support helper";
3291 if(target_handle->epithet)
3292 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3294 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3296 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3300 sprintf(prefix, "%s", target_handle->handle);
3303 if(!channel->channel_info)
3305 reply("CSMSG_NOT_REGISTERED", channel->name);
3309 helping = HANDLE_FLAGGED(target_handle, HELPING)
3310 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3311 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3313 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3314 /* To prevent possible information leaks, only show infolines
3315 * if the requestor is in the channel or it's their own
3317 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3319 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3321 /* Likewise, only say it's suspended if the user has active
3322 * access in that channel or it's their own entry. */
3323 if(IsUserSuspended(uData)
3324 && (GetChannelUser(channel->channel_info, user->handle_info)
3325 || (user->handle_info == uData->handle)))
3327 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3332 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3339 zoot_list(struct listData *list)
3341 struct userData *uData;
3342 unsigned int start, curr, highest, lowest;
3343 struct helpfile_table tmp_table;
3344 const char **temp, *msg;
3346 if(list->table.length == 1)
3349 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3351 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3352 msg = user_find_message(list->user, "MSG_NONE");
3353 send_message_type(4, list->user, list->bot, " %s", msg);
3355 tmp_table.width = list->table.width;
3356 tmp_table.flags = list->table.flags;
3357 list->table.contents[0][0] = " ";
3358 highest = list->highest;
3359 if(list->lowest != 0)
3360 lowest = list->lowest;
3361 else if(highest < 100)
3364 lowest = highest - 100;
3365 for(start = curr = 1; curr < list->table.length; )
3367 uData = list->users[curr-1];
3368 list->table.contents[curr++][0] = " ";
3369 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3372 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3374 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3375 temp = list->table.contents[--start];
3376 list->table.contents[start] = list->table.contents[0];
3377 tmp_table.contents = list->table.contents + start;
3378 tmp_table.length = curr - start;
3379 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3380 list->table.contents[start] = temp;
3382 highest = lowest - 1;
3383 lowest = (highest < 100) ? 0 : (highest - 99);
3389 def_list(struct listData *list)
3393 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3395 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3396 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3397 if(list->table.length == 1)
3399 msg = user_find_message(list->user, "MSG_NONE");
3400 send_message_type(4, list->user, list->bot, " %s", msg);
3405 userData_access_comp(const void *arg_a, const void *arg_b)
3407 const struct userData *a = *(struct userData**)arg_a;
3408 const struct userData *b = *(struct userData**)arg_b;
3410 if(a->access != b->access)
3411 res = b->access - a->access;
3413 res = irccasecmp(a->handle->handle, b->handle->handle);
3418 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3420 void (*send_list)(struct listData *);
3421 struct userData *uData;
3422 struct listData lData;
3423 unsigned int matches;
3427 lData.bot = cmd->parent->bot;
3428 lData.channel = channel;
3429 lData.lowest = lowest;
3430 lData.highest = highest;
3431 lData.search = (argc > 1) ? argv[1] : NULL;
3432 send_list = zoot_list;
3434 if(user->handle_info)
3436 switch(user->handle_info->userlist_style)
3438 case HI_STYLE_DEF: send_list = def_list; break;
3439 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3443 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3445 for(uData = channel->channel_info->users; uData; uData = uData->next)
3447 if((uData->access < lowest)
3448 || (uData->access > highest)
3449 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3451 lData.users[matches++] = uData;
3453 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3455 lData.table.length = matches+1;
3456 lData.table.width = 4;
3457 lData.table.flags = TABLE_NO_FREE;
3458 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3459 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3460 lData.table.contents[0] = ary;
3463 ary[2] = "Last Seen";
3465 for(matches = 1; matches < lData.table.length; ++matches)
3467 struct userData *uData = lData.users[matches-1];
3468 char seen[INTERVALLEN];
3470 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3471 lData.table.contents[matches] = ary;
3472 ary[0] = strtab(uData->access);
3473 ary[1] = uData->handle->handle;
3476 else if(!uData->seen)
3479 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3480 ary[2] = strdup(ary[2]);
3481 if(IsUserSuspended(uData))
3482 ary[3] = "Suspended";
3483 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3484 ary[3] = "Vacation";
3489 for(matches = 1; matches < lData.table.length; ++matches)
3491 free((char*)lData.table.contents[matches][2]);
3492 free(lData.table.contents[matches]);
3494 free(lData.table.contents[0]);
3495 free(lData.table.contents);
3499 static CHANSERV_FUNC(cmd_users)
3501 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3504 static CHANSERV_FUNC(cmd_wlist)
3506 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3509 static CHANSERV_FUNC(cmd_clist)
3511 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3514 static CHANSERV_FUNC(cmd_mlist)
3516 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3519 static CHANSERV_FUNC(cmd_olist)
3521 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3524 static CHANSERV_FUNC(cmd_plist)
3526 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3529 static CHANSERV_FUNC(cmd_bans)
3531 struct helpfile_table tbl;
3532 unsigned int matches = 0, timed = 0, ii;
3533 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3534 const char *msg_never, *triggered, *expires;
3535 struct banData *ban, **bans;
3542 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3544 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3546 if(search && !match_ircglobs(search, ban->mask))
3548 bans[matches++] = ban;
3553 tbl.length = matches + 1;
3554 tbl.width = 4 + timed;
3556 tbl.flags = TABLE_NO_FREE;
3557 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3558 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3559 tbl.contents[0][0] = "Mask";
3560 tbl.contents[0][1] = "Set By";
3561 tbl.contents[0][2] = "Triggered";
3564 tbl.contents[0][3] = "Expires";
3565 tbl.contents[0][4] = "Reason";
3568 tbl.contents[0][3] = "Reason";
3571 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3573 free(tbl.contents[0]);
3578 msg_never = user_find_message(user, "MSG_NEVER");
3579 for(ii = 0; ii < matches; )
3585 else if(ban->expires)
3586 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3588 expires = msg_never;
3591 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3593 triggered = msg_never;
3595 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3596 tbl.contents[ii][0] = ban->mask;
3597 tbl.contents[ii][1] = ban->owner;
3598 tbl.contents[ii][2] = strdup(triggered);
3601 tbl.contents[ii][3] = strdup(expires);
3602 tbl.contents[ii][4] = ban->reason;
3605 tbl.contents[ii][3] = ban->reason;
3607 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3608 reply("MSG_MATCH_COUNT", matches);
3609 for(ii = 1; ii < tbl.length; ++ii)
3611 free((char*)tbl.contents[ii][2]);
3613 free((char*)tbl.contents[ii][3]);
3614 free(tbl.contents[ii]);
3616 free(tbl.contents[0]);
3622 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3624 struct chanData *cData = channel->channel_info;
3625 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3627 if(cData->topic_mask)
3628 return !match_ircglob(new_topic, cData->topic_mask);
3629 else if(cData->topic)
3630 return irccasecmp(new_topic, cData->topic);
3635 static CHANSERV_FUNC(cmd_topic)
3637 struct chanData *cData;
3640 cData = channel->channel_info;
3645 SetChannelTopic(channel, chanserv, cData->topic, 1);
3646 reply("CSMSG_TOPIC_SET", cData->topic);
3650 reply("CSMSG_NO_TOPIC", channel->name);
3654 topic = unsplit_string(argv + 1, argc - 1, NULL);
3655 /* If they say "!topic *", use an empty topic. */
3656 if((topic[0] == '*') && (topic[1] == 0))
3658 if(bad_topic(channel, user, topic))
3660 char *topic_mask = cData->topic_mask;
3663 char new_topic[TOPICLEN+1], tchar;
3664 int pos=0, starpos=-1, dpos=0, len;
3666 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3673 len = strlen(topic);
3674 if((dpos + len) > TOPICLEN)
3675 len = TOPICLEN + 1 - dpos;
3676 memcpy(new_topic+dpos, topic, len);
3680 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3681 default: new_topic[dpos++] = tchar; break;
3684 if((dpos > TOPICLEN) || tchar)
3687 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3688 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3691 new_topic[dpos] = 0;
3692 SetChannelTopic(channel, chanserv, new_topic, 1);
3694 reply("CSMSG_TOPIC_LOCKED", channel->name);
3699 SetChannelTopic(channel, chanserv, topic, 1);
3701 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3703 /* Grab the topic and save it as the default topic. */
3705 cData->topic = strdup(channel->topic);
3711 static CHANSERV_FUNC(cmd_mode)
3713 struct mod_chanmode *change;
3717 change = &channel->channel_info->modes;
3718 if(change->modes_set || change->modes_clear) {
3719 modcmd_chanmode_announce(change);
3720 reply("CSMSG_DEFAULTED_MODES", channel->name);
3722 reply("CSMSG_NO_MODES", channel->name);
3726 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3729 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3733 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3734 && mode_lock_violated(&channel->channel_info->modes, change))
3737 mod_chanmode_format(&channel->channel_info->modes, modes);
3738 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3742 modcmd_chanmode_announce(change);
3743 mod_chanmode_free(change);
3744 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3748 static CHANSERV_FUNC(cmd_invite)
3750 struct userData *uData;
3751 struct userNode *invite;
3753 uData = GetChannelUser(channel->channel_info, user->handle_info);
3757 if(!(invite = GetUserH(argv[1])))
3759 reply("MSG_NICK_UNKNOWN", argv[1]);
3766 if(GetUserMode(channel, invite))
3768 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3776 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3777 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3780 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3782 irc_invite(chanserv, invite, channel);
3784 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3789 static CHANSERV_FUNC(cmd_inviteme)
3791 if(GetUserMode(channel, user))
3793 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3796 if(channel->channel_info
3797 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3799 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3802 irc_invite(cmd->parent->bot, user, channel);
3807 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3810 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3812 /* We display things based on two dimensions:
3813 * - Issue time: present or absent
3814 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3815 * (in order of precedence, so something both expired and revoked
3816 * only counts as revoked)
3818 combo = (suspended->issued ? 4 : 0)
3819 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3821 case 0: /* no issue time, indefinite expiration */
3822 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3824 case 1: /* no issue time, expires in future */
3825 intervalString(buf1, suspended->expires-now, user->handle_info);
3826 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3828 case 2: /* no issue time, expired */
3829 intervalString(buf1, now-suspended->expires, user->handle_info);
3830 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3832 case 3: /* no issue time, revoked */
3833 intervalString(buf1, now-suspended->revoked, user->handle_info);
3834 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3836 case 4: /* issue time set, indefinite expiration */
3837 intervalString(buf1, now-suspended->issued, user->handle_info);
3838 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3840 case 5: /* issue time set, expires in future */
3841 intervalString(buf1, now-suspended->issued, user->handle_info);
3842 intervalString(buf2, suspended->expires-now, user->handle_info);
3843 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3845 case 6: /* issue time set, expired */
3846 intervalString(buf1, now-suspended->issued, user->handle_info);
3847 intervalString(buf2, now-suspended->expires, user->handle_info);
3848 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3850 case 7: /* issue time set, revoked */
3851 intervalString(buf1, now-suspended->issued, user->handle_info);
3852 intervalString(buf2, now-suspended->revoked, user->handle_info);
3853 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3856 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3861 static CHANSERV_FUNC(cmd_info)
3863 char modes[MAXLEN], buffer[INTERVALLEN];
3864 struct userData *uData, *owner;
3865 struct chanData *cData;
3866 struct do_not_register *dnr;
3871 cData = channel->channel_info;
3872 reply("CSMSG_CHANNEL_INFO", channel->name);
3874 uData = GetChannelUser(cData, user->handle_info);
3875 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3877 mod_chanmode_format(&cData->modes, modes);
3878 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3879 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3882 for(it = dict_first(cData->notes); it; it = iter_next(it))
3886 note = iter_data(it);
3887 if(!note_type_visible_to_user(cData, note->type, user))
3890 padding = PADLEN - 1 - strlen(iter_key(it));
3891 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3894 reply("CSMSG_CHANNEL_MAX", cData->max);
3895 for(owner = cData->users; owner; owner = owner->next)
3896 if(owner->access == UL_OWNER)
3897 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3898 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3899 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3900 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3901 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3903 privileged = IsStaff(user);
3904 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3905 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3907 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3908 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3910 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3912 struct suspended *suspended;
3913 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3914 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3915 show_suspension_info(cmd, user, suspended);
3917 else if(IsSuspended(cData))
3919 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3920 show_suspension_info(cmd, user, cData->suspended);
3925 static CHANSERV_FUNC(cmd_netinfo)
3927 extern time_t boot_time;
3928 extern unsigned long burst_length;
3929 char interval[INTERVALLEN];
3931 reply("CSMSG_NETWORK_INFO");
3932 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3933 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3934 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3935 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3936 reply("CSMSG_NETWORK_BANS", banCount);
3937 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3938 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3939 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3944 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3946 struct helpfile_table table;
3948 struct userNode *user;
3953 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3954 table.contents = alloca(list->used*sizeof(*table.contents));
3955 for(nn=0; nn<list->used; nn++)
3957 user = list->list[nn];
3958 if(user->modes & skip_flags)
3962 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3965 nick = alloca(strlen(user->nick)+3);
3966 sprintf(nick, "(%s)", user->nick);
3970 table.contents[table.length][0] = nick;
3973 table_send(chanserv, to->nick, 0, NULL, table);
3976 static CHANSERV_FUNC(cmd_ircops)
3978 reply("CSMSG_STAFF_OPERS");
3979 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3983 static CHANSERV_FUNC(cmd_helpers)
3985 reply("CSMSG_STAFF_HELPERS");
3986 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3990 static CHANSERV_FUNC(cmd_staff)
3992 reply("CSMSG_NETWORK_STAFF");
3993 cmd_ircops(CSFUNC_ARGS);
3994 cmd_helpers(CSFUNC_ARGS);
3998 static CHANSERV_FUNC(cmd_peek)
4000 struct modeNode *mn;
4001 char modes[MODELEN];
4003 struct helpfile_table table;
4005 irc_make_chanmode(channel, modes);
4007 reply("CSMSG_PEEK_INFO", channel->name);
4008 reply("CSMSG_PEEK_TOPIC", channel->topic);
4009 reply("CSMSG_PEEK_MODES", modes);
4010 reply("CSMSG_PEEK_USERS", channel->members.used);
4014 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4015 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4016 for(n = 0; n < channel->members.used; n++)
4018 mn = channel->members.list[n];
4019 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4021 table.contents[table.length] = alloca(sizeof(**table.contents));
4022 table.contents[table.length][0] = mn->user->nick;
4027 reply("CSMSG_PEEK_OPS");
4028 table_send(chanserv, user->nick, 0, NULL, table);
4031 reply("CSMSG_PEEK_NO_OPS");
4035 static MODCMD_FUNC(cmd_wipeinfo)
4037 struct handle_info *victim;
4038 struct userData *ud, *actor;
4041 actor = GetChannelUser(channel->channel_info, user->handle_info);
4042 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4044 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4046 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4049 if((ud->access >= actor->access) && (ud != actor))
4051 reply("MSG_USER_OUTRANKED", victim->handle);
4057 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4061 static CHANSERV_FUNC(cmd_resync)
4063 struct mod_chanmode *changes;
4064 struct chanData *cData = channel->channel_info;
4065 unsigned int ii, used;
4067 changes = mod_chanmode_alloc(channel->members.used * 2);
4068 for(ii = used = 0; ii < channel->members.used; ++ii)
4070 struct modeNode *mn = channel->members.list[ii];
4071 struct userData *uData;
4073 if(IsService(mn->user))
4076 uData = GetChannelAccess(cData, mn->user->handle_info);
4077 if(!cData->lvlOpts[lvlGiveOps]
4078 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4080 if(!(mn->modes & MODE_CHANOP))
4082 changes->args[used].mode = MODE_CHANOP;
4083 changes->args[used++].member = mn;
4086 else if(!cData->lvlOpts[lvlGiveVoice]
4087 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4089 if(mn->modes & MODE_CHANOP)
4091 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4092 changes->args[used++].member = mn;
4094 if(!(mn->modes & MODE_VOICE))
4096 changes->args[used].mode = MODE_VOICE;
4097 changes->args[used++].member = mn;
4104 changes->args[used].mode = MODE_REMOVE | mn->modes;
4105 changes->args[used++].member = mn;
4109 changes->argc = used;
4110 modcmd_chanmode_announce(changes);
4111 mod_chanmode_free(changes);
4112 reply("CSMSG_RESYNCED_USERS", channel->name);
4116 static CHANSERV_FUNC(cmd_seen)
4118 struct userData *uData;
4119 struct handle_info *handle;
4120 char seen[INTERVALLEN];
4124 if(!irccasecmp(argv[1], chanserv->nick))
4126 reply("CSMSG_IS_CHANSERV");
4130 if(!(handle = get_handle_info(argv[1])))
4132 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4136 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4138 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4143 reply("CSMSG_USER_PRESENT", handle->handle);
4144 else if(uData->seen)
4145 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4147 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4149 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4150 reply("CSMSG_USER_VACATION", handle->handle);
4155 static MODCMD_FUNC(cmd_names)
4157 struct userNode *targ;
4158 struct userData *targData;
4159 unsigned int ii, pos;
4162 for(ii=pos=0; ii<channel->members.used; ++ii)
4164 targ = channel->members.list[ii]->user;
4165 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4168 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4171 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4175 if(IsUserSuspended(targData))
4177 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4180 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4181 reply("CSMSG_END_NAMES", channel->name);
4186 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4188 switch(ntype->visible_type)
4190 case NOTE_VIS_ALL: return 1;
4191 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4192 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4197 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4199 struct userData *uData;
4201 switch(ntype->set_access_type)
4203 case NOTE_SET_CHANNEL_ACCESS:
4204 if(!user->handle_info)
4206 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4208 return uData->access >= ntype->set_access.min_ulevel;
4209 case NOTE_SET_CHANNEL_SETTER:
4210 return check_user_level(channel, user, lvlSetters, 1, 0);
4211 case NOTE_SET_PRIVILEGED: default:
4212 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4216 static CHANSERV_FUNC(cmd_note)
4218 struct chanData *cData;
4220 struct note_type *ntype;
4222 cData = channel->channel_info;
4225 reply("CSMSG_NOT_REGISTERED", channel->name);
4229 /* If no arguments, show all visible notes for the channel. */
4235 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4237 note = iter_data(it);
4238 if(!note_type_visible_to_user(cData, note->type, user))
4241 reply("CSMSG_NOTELIST_HEADER", channel->name);
4242 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4245 reply("CSMSG_NOTELIST_END", channel->name);
4247 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4249 /* If one argument, show the named note. */
4252 if((note = dict_find(cData->notes, argv[1], NULL))
4253 && note_type_visible_to_user(cData, note->type, user))
4255 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4257 else if((ntype = dict_find(note_types, argv[1], NULL))
4258 && note_type_visible_to_user(NULL, ntype, user))
4260 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4265 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4269 /* Assume they're trying to set a note. */
4273 ntype = dict_find(note_types, argv[1], NULL);
4276 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4279 else if(note_type_settable_by_user(channel, ntype, user))
4281 note_text = unsplit_string(argv+2, argc-2, NULL);
4282 if((note = dict_find(cData->notes, argv[1], NULL)))
4283 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4284 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4285 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4287 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4289 /* The note is viewable to staff only, so return 0
4290 to keep the invocation from getting logged (or
4291 regular users can see it in !events). */
4297 reply("CSMSG_NO_ACCESS");
4304 static CHANSERV_FUNC(cmd_delnote)
4309 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4310 || !note_type_settable_by_user(channel, note->type, user))
4312 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4315 dict_remove(channel->channel_info->notes, note->type->name);
4316 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4320 static CHANSERV_FUNC(cmd_events)
4322 struct logSearch discrim;
4323 struct logReport report;
4324 unsigned int matches, limit;
4326 limit = (argc > 1) ? atoi(argv[1]) : 10;
4327 if(limit < 1 || limit > 200)
4330 memset(&discrim, 0, sizeof(discrim));
4331 discrim.masks.bot = chanserv;
4332 discrim.masks.channel_name = channel->name;
4334 discrim.masks.command = argv[2];
4335 discrim.limit = limit;
4336 discrim.max_time = INT_MAX;
4337 discrim.severities = 1 << LOG_COMMAND;
4338 report.reporter = chanserv;
4340 reply("CSMSG_EVENT_SEARCH_RESULTS");
4341 matches = log_entry_search(&discrim, log_report_entry, &report);
4343 reply("MSG_MATCH_COUNT", matches);
4345 reply("MSG_NO_MATCHES");
4349 static CHANSERV_FUNC(cmd_say)
4355 msg = unsplit_string(argv + 1, argc - 1, NULL);
4356 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4358 else if(GetUserH(argv[1]))
4361 msg = unsplit_string(argv + 2, argc - 2, NULL);
4362 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4366 reply("MSG_NOT_TARGET_NAME");
4372 static CHANSERV_FUNC(cmd_emote)
4378 /* CTCP is so annoying. */
4379 msg = unsplit_string(argv + 1, argc - 1, NULL);
4380 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4382 else if(GetUserH(argv[1]))
4384 msg = unsplit_string(argv + 2, argc - 2, NULL);
4385 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4389 reply("MSG_NOT_TARGET_NAME");
4395 struct channelList *
4396 chanserv_support_channels(void)
4398 return &chanserv_conf.support_channels;
4401 static CHANSERV_FUNC(cmd_expire)
4403 int channel_count = registered_channels;
4404 expire_channels(NULL);
4405 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4410 chanserv_expire_suspension(void *data)
4412 struct suspended *suspended = data;
4413 struct chanNode *channel;
4414 struct mod_chanmode change;
4416 if(!suspended->expires || (now < suspended->expires))
4417 suspended->revoked = now;
4418 channel = suspended->cData->channel;
4419 suspended->cData->channel = channel;
4420 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4421 mod_chanmode_init(&change);
4423 change.args[0].mode = MODE_CHANOP;
4424 change.args[0].member = AddChannelUser(chanserv, channel);
4425 mod_chanmode_announce(chanserv, channel, &change);
4428 static CHANSERV_FUNC(cmd_csuspend)
4430 struct suspended *suspended;
4431 char reason[MAXLEN];
4432 time_t expiry, duration;
4433 struct userData *uData;
4437 if(IsProtected(channel->channel_info))
4439 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4443 if(argv[1][0] == '!')
4445 else if(IsSuspended(channel->channel_info))
4447 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4448 show_suspension_info(cmd, user, channel->channel_info->suspended);
4452 if(!strcmp(argv[1], "0"))
4454 else if((duration = ParseInterval(argv[1])))
4455 expiry = now + duration;
4458 reply("MSG_INVALID_DURATION", argv[1]);
4462 unsplit_string(argv + 2, argc - 2, reason);
4464 suspended = calloc(1, sizeof(*suspended));
4465 suspended->revoked = 0;
4466 suspended->issued = now;
4467 suspended->suspender = strdup(user->handle_info->handle);
4468 suspended->expires = expiry;
4469 suspended->reason = strdup(reason);
4470 suspended->cData = channel->channel_info;
4471 suspended->previous = suspended->cData->suspended;
4472 suspended->cData->suspended = suspended;
4474 if(suspended->expires)
4475 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4477 if(IsSuspended(channel->channel_info))
4479 suspended->previous->revoked = now;
4480 if(suspended->previous->expires)
4481 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4482 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4483 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4487 /* Mark all users in channel as absent. */
4488 for(uData = channel->channel_info->users; uData; uData = uData->next)
4497 /* Mark the channel as suspended, then part. */
4498 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4499 DelChannelUser(chanserv, channel, suspended->reason, 0);
4500 reply("CSMSG_SUSPENDED", channel->name);
4501 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4502 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4507 static CHANSERV_FUNC(cmd_cunsuspend)
4509 struct suspended *suspended;
4510 char message[MAXLEN];
4512 if(!IsSuspended(channel->channel_info))
4514 reply("CSMSG_NOT_SUSPENDED", channel->name);
4518 suspended = channel->channel_info->suspended;
4520 /* Expire the suspension and join ChanServ to the channel. */
4521 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4522 chanserv_expire_suspension(suspended);
4523 reply("CSMSG_UNSUSPENDED", channel->name);
4524 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4525 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4529 typedef struct chanservSearch
4537 unsigned long flags;
4541 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4544 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4549 search = malloc(sizeof(struct chanservSearch));
4550 memset(search, 0, sizeof(*search));
4553 for(i = 0; i < argc; i++)
4555 /* Assume all criteria require arguments. */
4558 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4562 if(!irccasecmp(argv[i], "name"))
4563 search->name = argv[++i];
4564 else if(!irccasecmp(argv[i], "registrar"))
4565 search->registrar = argv[++i];
4566 else if(!irccasecmp(argv[i], "unvisited"))
4567 search->unvisited = ParseInterval(argv[++i]);
4568 else if(!irccasecmp(argv[i], "registered"))
4569 search->registered = ParseInterval(argv[++i]);
4570 else if(!irccasecmp(argv[i], "flags"))
4573 if(!irccasecmp(argv[i], "nodelete"))
4574 search->flags |= CHANNEL_NODELETE;
4575 else if(!irccasecmp(argv[i], "suspended"))
4576 search->flags |= CHANNEL_SUSPENDED;
4579 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4583 else if(!irccasecmp(argv[i], "limit"))
4584 search->limit = strtoul(argv[++i], NULL, 10);
4587 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4592 if(search->name && !strcmp(search->name, "*"))
4594 if(search->registrar && !strcmp(search->registrar, "*"))
4595 search->registrar = 0;
4604 chanserv_channel_match(struct chanData *channel, search_t search)
4606 const char *name = channel->channel->name;
4607 if((search->name && !match_ircglob(name, search->name)) ||
4608 (search->registrar && !channel->registrar) ||
4609 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4610 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4611 (search->registered && (now - channel->registered) > search->registered) ||
4612 (search->flags && ((search->flags & channel->flags) != search->flags)))
4619 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4621 struct chanData *channel;
4622 unsigned int matches = 0;
4624 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4626 if(!chanserv_channel_match(channel, search))
4636 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4641 search_print(struct chanData *channel, void *data)
4643 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4646 static CHANSERV_FUNC(cmd_search)
4649 unsigned int matches;
4650 channel_search_func action;
4654 if(!irccasecmp(argv[1], "count"))
4655 action = search_count;
4656 else if(!irccasecmp(argv[1], "print"))
4657 action = search_print;
4660 reply("CSMSG_ACTION_INVALID", argv[1]);
4664 search = chanserv_search_create(user, argc - 2, argv + 2);
4668 if(action == search_count)
4669 search->limit = INT_MAX;
4671 if(action == search_print)
4672 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4674 matches = chanserv_channel_search(search, action, user);
4677 reply("MSG_MATCH_COUNT", matches);
4679 reply("MSG_NO_MATCHES");
4685 static CHANSERV_FUNC(cmd_unvisited)
4687 struct chanData *cData;
4688 time_t interval = chanserv_conf.channel_expire_delay;
4689 char buffer[INTERVALLEN];
4690 unsigned int limit = 25, matches = 0;
4694 interval = ParseInterval(argv[1]);
4696 limit = atoi(argv[2]);
4699 intervalString(buffer, interval, user->handle_info);
4700 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4702 for(cData = channelList; cData && matches < limit; cData = cData->next)
4704 if((now - cData->visited) < interval)
4707 intervalString(buffer, now - cData->visited, user->handle_info);
4708 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4715 static MODCMD_FUNC(chan_opt_defaulttopic)
4721 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4723 reply("CSMSG_TOPIC_LOCKED", channel->name);
4727 topic = unsplit_string(argv+1, argc-1, NULL);
4729 free(channel->channel_info->topic);
4730 if(topic[0] == '*' && topic[1] == 0)
4732 topic = channel->channel_info->topic = NULL;
4736 topic = channel->channel_info->topic = strdup(topic);
4737 if(channel->channel_info->topic_mask
4738 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4739 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4741 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4744 if(channel->channel_info->topic)
4745 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4747 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4751 static MODCMD_FUNC(chan_opt_topicmask)
4755 struct chanData *cData = channel->channel_info;
4758 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4760 reply("CSMSG_TOPIC_LOCKED", channel->name);
4764 mask = unsplit_string(argv+1, argc-1, NULL);
4766 if(cData->topic_mask)
4767 free(cData->topic_mask);
4768 if(mask[0] == '*' && mask[1] == 0)
4770 cData->topic_mask = 0;
4774 cData->topic_mask = strdup(mask);
4776 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4777 else if(!match_ircglob(cData->topic, cData->topic_mask))
4778 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4782 if(channel->channel_info->topic_mask)
4783 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4785 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4789 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4793 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4797 if(greeting[0] == '*' && greeting[1] == 0)
4801 unsigned int length = strlen(greeting);
4802 if(length > chanserv_conf.greeting_length)
4804 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4807 *data = strdup(greeting);
4816 reply(name, user_find_message(user, "MSG_NONE"));
4820 static MODCMD_FUNC(chan_opt_greeting)
4822 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4825 static MODCMD_FUNC(chan_opt_usergreeting)
4827 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4830 static MODCMD_FUNC(chan_opt_modes)
4832 struct mod_chanmode *new_modes;
4833 char modes[MODELEN];
4837 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4839 reply("CSMSG_NO_ACCESS");
4842 if(argv[1][0] == '*' && argv[1][1] == 0)
4844 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4846 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4848 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4851 else if(new_modes->argc > 1)
4853 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4854 mod_chanmode_free(new_modes);
4859 channel->channel_info->modes = *new_modes;
4860 modcmd_chanmode_announce(new_modes);
4861 mod_chanmode_free(new_modes);
4865 mod_chanmode_format(&channel->channel_info->modes, modes);
4867 reply("CSMSG_SET_MODES", modes);
4869 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4873 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4875 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4877 struct chanData *cData = channel->channel_info;
4882 /* Set flag according to value. */
4883 if(enabled_string(argv[1]))
4885 cData->flags |= mask;
4888 else if(disabled_string(argv[1]))
4890 cData->flags &= ~mask;
4895 reply("MSG_INVALID_BINARY", argv[1]);
4901 /* Find current option value. */
4902 value = (cData->flags & mask) ? 1 : 0;
4906 reply(name, user_find_message(user, "MSG_ON"));
4908 reply(name, user_find_message(user, "MSG_OFF"));
4912 static MODCMD_FUNC(chan_opt_nodelete)
4914 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4916 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4920 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4923 static MODCMD_FUNC(chan_opt_dynlimit)
4925 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4928 static MODCMD_FUNC(chan_opt_defaults)
4930 struct userData *uData;
4931 struct chanData *cData;
4932 const char *confirm;
4933 enum levelOption lvlOpt;
4934 enum charOption chOpt;
4936 cData = channel->channel_info;
4937 uData = GetChannelUser(cData, user->handle_info);
4938 if(!uData || (uData->access < UL_OWNER))
4940 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4943 confirm = make_confirmation_string(uData);
4944 if((argc < 2) || strcmp(argv[1], confirm))
4946 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4949 cData->flags = CHANNEL_DEFAULT_FLAGS;
4950 cData->modes = chanserv_conf.default_modes;
4951 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4952 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4953 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4954 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4955 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4960 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4962 struct chanData *cData = channel->channel_info;
4963 struct userData *uData;
4964 unsigned short value;
4968 if(!check_user_level(channel, user, option, 1, 1))
4970 reply("CSMSG_CANNOT_SET");
4973 value = user_level_from_name(argv[1], UL_OWNER+1);
4974 if(!value && strcmp(argv[1], "0"))
4976 reply("CSMSG_INVALID_ACCESS", argv[1]);
4979 uData = GetChannelUser(cData, user->handle_info);
4980 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4982 reply("CSMSG_BAD_SETLEVEL");
4988 if(value > cData->lvlOpts[lvlGiveOps])
4990 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4995 if(value < cData->lvlOpts[lvlGiveVoice])
4997 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5002 /* This test only applies to owners, since non-owners
5003 * trying to set an option to above their level get caught
5004 * by the CSMSG_BAD_SETLEVEL test above.
5006 if(value > uData->access)
5008 reply("CSMSG_BAD_SETTERS");
5015 cData->lvlOpts[option] = value;
5017 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5021 static MODCMD_FUNC(chan_opt_enfops)
5023 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5026 static MODCMD_FUNC(chan_opt_giveops)
5028 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5031 static MODCMD_FUNC(chan_opt_enfmodes)
5033 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5036 static MODCMD_FUNC(chan_opt_enftopic)
5038 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5041 static MODCMD_FUNC(chan_opt_pubcmd)
5043 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5046 static MODCMD_FUNC(chan_opt_setters)
5048 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5051 static MODCMD_FUNC(chan_opt_ctcpusers)
5053 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5056 static MODCMD_FUNC(chan_opt_userinfo)
5058 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5061 static MODCMD_FUNC(chan_opt_givevoice)
5063 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5066 static MODCMD_FUNC(chan_opt_topicsnarf)
5068 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5071 static MODCMD_FUNC(chan_opt_inviteme)
5073 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5077 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5079 struct chanData *cData = channel->channel_info;
5080 int count = charOptions[option].count, index;
5084 index = atoi(argv[1]);
5086 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5088 reply("CSMSG_INVALID_NUMERIC", index);
5089 /* Show possible values. */
5090 for(index = 0; index < count; index++)
5091 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5095 cData->chOpts[option] = charOptions[option].values[index].value;
5099 /* Find current option value. */
5102 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5106 /* Somehow, the option value is corrupt; reset it to the default. */
5107 cData->chOpts[option] = charOptions[option].default_value;
5112 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5116 static MODCMD_FUNC(chan_opt_protect)
5118 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5121 static MODCMD_FUNC(chan_opt_toys)
5123 return channel_multiple_option(chToys, CSFUNC_ARGS);
5126 static MODCMD_FUNC(chan_opt_ctcpreaction)
5128 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5131 static MODCMD_FUNC(chan_opt_topicrefresh)
5133 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5136 static struct svccmd_list set_shows_list;
5139 handle_svccmd_unbind(struct svccmd *target) {
5141 for(ii=0; ii<set_shows_list.used; ++ii)
5142 if(target == set_shows_list.list[ii])
5143 set_shows_list.used = 0;
5146 static CHANSERV_FUNC(cmd_set)
5148 struct svccmd *subcmd;
5152 /* Check if we need to (re-)initialize set_shows_list. */
5153 if(!set_shows_list.used)
5155 if(!set_shows_list.size)
5157 set_shows_list.size = chanserv_conf.set_shows->used;
5158 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5160 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5162 const char *name = chanserv_conf.set_shows->list[ii];
5163 sprintf(buf, "%s %s", argv[0], name);
5164 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5167 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5170 svccmd_list_append(&set_shows_list, subcmd);
5176 reply("CSMSG_CHANNEL_OPTIONS");
5177 for(ii = 0; ii < set_shows_list.used; ii++)
5179 subcmd = set_shows_list.list[ii];
5180 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5185 sprintf(buf, "%s %s", argv[0], argv[1]);
5186 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5189 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5192 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5194 reply("CSMSG_NO_ACCESS");
5198 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5202 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5204 struct userData *uData;
5206 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5209 reply("CSMSG_NOT_USER", channel->name);
5215 /* Just show current option value. */
5217 else if(enabled_string(argv[1]))
5219 uData->flags |= mask;
5221 else if(disabled_string(argv[1]))
5223 uData->flags &= ~mask;
5227 reply("MSG_INVALID_BINARY", argv[1]);
5231 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5235 static MODCMD_FUNC(user_opt_noautoop)
5237 struct userData *uData;
5239 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5242 reply("CSMSG_NOT_USER", channel->name);
5245 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5246 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5248 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5251 static MODCMD_FUNC(user_opt_autoinvite)
5253 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5256 static MODCMD_FUNC(user_opt_info)
5258 struct userData *uData;
5261 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5265 /* If they got past the command restrictions (which require access)
5266 * but fail this test, we have some fool with security override on.
5268 reply("CSMSG_NOT_USER", channel->name);
5275 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5276 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5278 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5281 bp = strcspn(infoline, "\001");
5284 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5289 if(infoline[0] == '*' && infoline[1] == 0)
5292 uData->info = strdup(infoline);
5295 reply("CSMSG_USET_INFO", uData->info);
5297 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5301 struct svccmd_list uset_shows_list;
5303 static CHANSERV_FUNC(cmd_uset)
5305 struct svccmd *subcmd;
5309 /* Check if we need to (re-)initialize uset_shows_list. */
5310 if(!uset_shows_list.used)
5314 "NoAutoOp", "AutoInvite", "Info"
5317 if(!uset_shows_list.size)
5319 uset_shows_list.size = ArrayLength(options);
5320 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5322 for(ii = 0; ii < ArrayLength(options); ii++)
5324 const char *name = options[ii];
5325 sprintf(buf, "%s %s", argv[0], name);
5326 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5329 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5332 svccmd_list_append(&uset_shows_list, subcmd);
5338 /* Do this so options are presented in a consistent order. */
5339 reply("CSMSG_USER_OPTIONS");
5340 for(ii = 0; ii < uset_shows_list.used; ii++)
5341 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5345 sprintf(buf, "%s %s", argv[0], argv[1]);
5346 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5349 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5353 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5356 static CHANSERV_FUNC(cmd_giveownership)
5358 struct handle_info *new_owner_hi;
5359 struct userData *new_owner, *curr_user;
5360 struct chanData *cData = channel->channel_info;
5361 struct do_not_register *dnr;
5363 unsigned short co_access;
5364 char reason[MAXLEN];
5367 curr_user = GetChannelAccess(cData, user->handle_info);
5368 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5369 if(!curr_user || (curr_user->access != UL_OWNER))
5371 struct userData *owner = NULL;
5372 for(curr_user = channel->channel_info->users;
5374 curr_user = curr_user->next)
5376 if(curr_user->access != UL_OWNER)
5380 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5387 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5389 if(new_owner_hi == user->handle_info)
5391 reply("CSMSG_NO_TRANSFER_SELF");
5394 new_owner = GetChannelAccess(cData, new_owner_hi);
5397 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5400 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5402 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5405 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5406 if(!IsHelping(user))
5407 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5409 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5412 if(new_owner->access >= UL_COOWNER)
5413 co_access = new_owner->access;
5415 co_access = UL_COOWNER;
5416 new_owner->access = UL_OWNER;
5418 curr_user->access = co_access;
5419 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5420 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5421 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5425 static CHANSERV_FUNC(cmd_suspend)
5427 struct handle_info *hi;
5428 struct userData *self, *target;
5431 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5432 self = GetChannelUser(channel->channel_info, user->handle_info);
5433 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5435 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5438 if(target->access >= self->access)
5440 reply("MSG_USER_OUTRANKED", hi->handle);
5443 if(target->flags & USER_SUSPENDED)
5445 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5450 target->present = 0;
5453 target->flags |= USER_SUSPENDED;
5454 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5458 static CHANSERV_FUNC(cmd_unsuspend)
5460 struct handle_info *hi;
5461 struct userData *self, *target;
5464 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5465 self = GetChannelUser(channel->channel_info, user->handle_info);
5466 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5468 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5471 if(target->access >= self->access)
5473 reply("MSG_USER_OUTRANKED", hi->handle);
5476 if(!(target->flags & USER_SUSPENDED))
5478 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5481 target->flags &= ~USER_SUSPENDED;
5482 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5486 static MODCMD_FUNC(cmd_deleteme)
5488 struct handle_info *hi;
5489 struct userData *target;
5490 const char *confirm_string;
5491 unsigned short access;
5494 hi = user->handle_info;
5495 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5497 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5500 if(target->access == UL_OWNER)
5502 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5505 confirm_string = make_confirmation_string(target);
5506 if((argc < 2) || strcmp(argv[1], confirm_string))
5508 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5511 access = target->access;
5512 channel_name = strdup(channel->name);
5513 del_channel_user(target, 1);
5514 reply("CSMSG_DELETED_YOU", access, channel_name);
5520 chanserv_refresh_topics(UNUSED_ARG(void *data))
5522 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5523 struct chanData *cData;
5526 for(cData = channelList; cData; cData = cData->next)
5528 if(IsSuspended(cData))
5530 opt = cData->chOpts[chTopicRefresh];
5533 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5536 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5537 cData->last_refresh = refresh_num;
5539 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5542 static CHANSERV_FUNC(cmd_unf)
5546 char response[MAXLEN];
5547 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5548 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5549 irc_privmsg(cmd->parent->bot, channel->name, response);
5552 reply("CSMSG_UNF_RESPONSE");
5556 static CHANSERV_FUNC(cmd_ping)
5560 char response[MAXLEN];
5561 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5562 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5563 irc_privmsg(cmd->parent->bot, channel->name, response);
5566 reply("CSMSG_PING_RESPONSE");
5570 static CHANSERV_FUNC(cmd_wut)
5574 char response[MAXLEN];
5575 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5576 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5577 irc_privmsg(cmd->parent->bot, channel->name, response);
5580 reply("CSMSG_WUT_RESPONSE");
5584 static CHANSERV_FUNC(cmd_8ball)
5586 unsigned int i, j, accum;
5591 for(i=1; i<argc; i++)
5592 for(j=0; argv[i][j]; j++)
5593 accum = (accum << 5) - accum + toupper(argv[i][j]);
5594 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5597 char response[MAXLEN];
5598 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5599 irc_privmsg(cmd->parent->bot, channel->name, response);
5602 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5606 static CHANSERV_FUNC(cmd_d)
5608 unsigned long sides, count, modifier, ii, total;
5609 char response[MAXLEN], *sep;
5613 if((count = strtoul(argv[1], &sep, 10)) < 1)
5623 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5624 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5628 else if((sep[0] == '-') && isdigit(sep[1]))
5629 modifier = strtoul(sep, NULL, 10);
5630 else if((sep[0] == '+') && isdigit(sep[1]))
5631 modifier = strtoul(sep+1, NULL, 10);
5638 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5643 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5646 for(total = ii = 0; ii < count; ++ii)
5647 total += (rand() % sides) + 1;
5650 if((count > 1) || modifier)
5652 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5653 sprintf(response, fmt, total, count, sides, modifier);
5657 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5658 sprintf(response, fmt, total, sides);
5661 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5663 send_message_type(4, user, cmd->parent->bot, "%s", response);
5667 static CHANSERV_FUNC(cmd_huggle)
5669 /* CTCP must be via PRIVMSG, never notice */
5671 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5673 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5678 chanserv_adjust_limit(void *data)
5680 struct mod_chanmode change;
5681 struct chanData *cData = data;
5682 struct chanNode *channel = cData->channel;
5685 if(IsSuspended(cData))
5688 cData->limitAdjusted = now;
5689 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5690 if(cData->modes.modes_set & MODE_LIMIT)
5692 if(limit > cData->modes.new_limit)
5693 limit = cData->modes.new_limit;
5694 else if(limit == cData->modes.new_limit)
5698 mod_chanmode_init(&change);
5699 change.modes_set = MODE_LIMIT;
5700 change.new_limit = limit;
5701 mod_chanmode_announce(chanserv, channel, &change);
5705 handle_new_channel(struct chanNode *channel)
5707 struct chanData *cData;
5709 if(!(cData = channel->channel_info))
5712 if(cData->modes.modes_set || cData->modes.modes_clear)
5713 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5715 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5716 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5719 /* Welcome to my worst nightmare. Warning: Read (or modify)
5720 the code below at your own risk. */
5722 handle_join(struct modeNode *mNode)
5724 struct mod_chanmode change;
5725 struct userNode *user = mNode->user;
5726 struct chanNode *channel = mNode->channel;
5727 struct chanData *cData;
5728 struct userData *uData = NULL;
5729 struct banData *bData;
5730 struct handle_info *handle;
5731 unsigned int modes = 0, info = 0;
5734 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5737 cData = channel->channel_info;
5738 if(channel->members.used > cData->max)
5739 cData->max = channel->members.used;
5741 /* Check for bans. If they're joining through a ban, one of two
5743 * 1: Join during a netburst, by riding the break. Kick them
5744 * unless they have ops or voice in the channel.
5745 * 2: They're allowed to join through the ban (an invite in
5746 * ircu2.10, or a +e on Hybrid, or something).
5747 * If they're not joining through a ban, and the banlist is not
5748 * full, see if they're on the banlist for the channel. If so,
5751 if(user->uplink->burst && !mNode->modes)
5754 for(ii = 0; ii < channel->banlist.used; ii++)
5756 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5758 /* Riding a netburst. Naughty. */
5759 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5765 mod_chanmode_init(&change);
5767 if(channel->banlist.used < MAXBANS)
5769 /* Not joining through a ban. */
5770 for(bData = cData->bans;
5771 bData && !user_matches_glob(user, bData->mask, 1);
5772 bData = bData->next);
5776 char kick_reason[MAXLEN];
5777 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5779 bData->triggered = now;
5780 if(bData != cData->bans)
5782 /* Shuffle the ban to the head of the list. */
5784 bData->next->prev = bData->prev;
5786 bData->prev->next = bData->next;
5789 bData->next = cData->bans;
5792 cData->bans->prev = bData;
5793 cData->bans = bData;
5796 change.args[0].mode = MODE_BAN;
5797 change.args[0].hostmask = bData->mask;
5798 mod_chanmode_announce(chanserv, channel, &change);
5799 KickChannelUser(user, channel, chanserv, kick_reason);
5804 /* ChanServ will not modify the limits in join-flooded channels.
5805 It will also skip DynLimit processing when the user (or srvx)
5806 is bursting in, because there are likely more incoming. */
5807 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5808 && !user->uplink->burst
5809 && !channel->join_flooded
5810 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5812 /* The user count has begun "bumping" into the channel limit,
5813 so set a timer to raise the limit a bit. Any previous
5814 timers are removed so three incoming users within the delay
5815 results in one limit change, not three. */
5817 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5818 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5821 if(channel->join_flooded)
5823 /* don't automatically give ops or voice during a join flood */
5825 else if(cData->lvlOpts[lvlGiveOps] == 0)
5826 modes |= MODE_CHANOP;
5827 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5828 modes |= MODE_VOICE;
5830 greeting = cData->greeting;
5831 if(user->handle_info)
5833 handle = user->handle_info;
5835 if(IsHelper(user) && !IsHelping(user))
5838 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5840 if(channel == chanserv_conf.support_channels.list[ii])
5842 HANDLE_SET_FLAG(user->handle_info, HELPING);
5848 uData = GetTrueChannelAccess(cData, handle);
5849 if(uData && !IsUserSuspended(uData))
5851 /* Ops and above were handled by the above case. */
5852 if(IsUserAutoOp(uData))
5854 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5855 modes |= MODE_CHANOP;
5856 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5857 modes |= MODE_VOICE;
5859 if(uData->access >= UL_PRESENT)
5860 cData->visited = now;
5861 if(cData->user_greeting)
5862 greeting = cData->user_greeting;
5864 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5865 && ((now - uData->seen) >= chanserv_conf.info_delay)
5872 if(!user->uplink->burst)
5876 if(modes & MODE_CHANOP)
5877 modes &= ~MODE_VOICE;
5878 change.args[0].mode = modes;
5879 change.args[0].member = mNode;
5880 mod_chanmode_announce(chanserv, channel, &change);
5882 if(greeting && !user->uplink->burst)
5883 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5885 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5891 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5893 struct mod_chanmode change;
5894 struct userData *channel;
5895 unsigned int ii, jj;
5897 if(!user->handle_info)
5900 mod_chanmode_init(&change);
5902 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5904 struct chanNode *cn;
5905 struct modeNode *mn;
5906 if(IsUserSuspended(channel)
5907 || IsSuspended(channel->channel)
5908 || !(cn = channel->channel->channel))
5911 mn = GetUserMode(cn, user);
5914 if(!IsUserSuspended(channel)
5915 && IsUserAutoInvite(channel)
5916 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5918 && !user->uplink->burst)
5919 irc_invite(chanserv, user, cn);
5923 if(channel->access >= UL_PRESENT)
5924 channel->channel->visited = now;
5926 if(IsUserAutoOp(channel))
5928 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5929 change.args[0].mode = MODE_CHANOP;
5930 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5931 change.args[0].mode = MODE_VOICE;
5933 change.args[0].mode = 0;
5934 change.args[0].member = mn;
5935 if(change.args[0].mode)
5936 mod_chanmode_announce(chanserv, cn, &change);
5939 channel->seen = now;
5940 channel->present = 1;
5943 for(ii = 0; ii < user->channels.used; ++ii)
5945 struct chanNode *channel = user->channels.list[ii]->channel;
5946 struct banData *ban;
5948 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5949 || !channel->channel_info)
5951 for(jj = 0; jj < channel->banlist.used; ++jj)
5952 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5954 if(jj < channel->banlist.used)
5956 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5958 char kick_reason[MAXLEN];
5959 if(!user_matches_glob(user, ban->mask, 1))
5961 change.args[0].mode = MODE_BAN;
5962 change.args[0].hostmask = ban->mask;
5963 mod_chanmode_announce(chanserv, channel, &change);
5964 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5965 KickChannelUser(user, channel, chanserv, kick_reason);
5966 ban->triggered = now;
5971 if(IsSupportHelper(user))
5973 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5975 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5977 HANDLE_SET_FLAG(user->handle_info, HELPING);
5985 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5987 struct chanData *cData;
5988 struct userData *uData;
5990 cData = channel->channel_info;
5991 if(!cData || IsSuspended(cData) || IsLocal(user))
5994 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5996 /* Allow for a bit of padding so that the limit doesn't
5997 track the user count exactly, which could get annoying. */
5998 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
6000 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6001 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6005 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
6007 scan_user_presence(uData, user);
6011 if(IsHelping(user) && IsSupportHelper(user))
6013 unsigned int ii, jj;
6014 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6016 for(jj = 0; jj < user->channels.used; ++jj)
6017 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6019 if(jj < user->channels.used)
6022 if(ii == chanserv_conf.support_channels.used)
6023 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6028 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6030 struct userData *uData;
6032 if(!channel->channel_info || !kicker || IsService(kicker)
6033 || (kicker == victim) || IsSuspended(channel->channel_info)
6034 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6037 if(protect_user(victim, kicker, channel->channel_info))
6039 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6040 KickChannelUser(kicker, channel, chanserv, reason);
6043 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6048 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6050 struct chanData *cData;
6052 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6055 cData = channel->channel_info;
6056 if(bad_topic(channel, user, channel->topic))
6058 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6059 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6060 SetChannelTopic(channel, chanserv, old_topic, 1);
6061 else if(cData->topic)
6062 SetChannelTopic(channel, chanserv, cData->topic, 1);
6065 /* With topicsnarf, grab the topic and save it as the default topic. */
6066 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6069 cData->topic = strdup(channel->topic);
6075 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6077 struct mod_chanmode *bounce = NULL;
6078 unsigned int bnc, ii;
6081 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6084 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6085 && mode_lock_violated(&channel->channel_info->modes, change))
6087 char correct[MAXLEN];
6088 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6089 mod_chanmode_format(&channel->channel_info->modes, correct);
6090 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6092 for(ii = bnc = 0; ii < change->argc; ++ii)
6094 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6096 const struct userNode *victim = change->args[ii].member->user;
6097 if(!protect_user(victim, user, channel->channel_info))
6100 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6103 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6104 bounce->args[bnc].member = GetUserMode(channel, user);
6105 if(bounce->args[bnc].member)
6109 bounce->args[bnc].mode = MODE_CHANOP;
6110 bounce->args[bnc].member = change->args[ii].member;
6112 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6114 else if(change->args[ii].mode & MODE_CHANOP)
6116 const struct userNode *victim = change->args[ii].member->user;
6117 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6120 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6121 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6122 bounce->args[bnc].member = change->args[ii].member;
6125 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6127 const char *ban = change->args[ii].hostmask;
6128 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6131 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6132 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6133 bounce->args[bnc].hostmask = ban;
6135 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6140 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6141 mod_chanmode_announce(chanserv, channel, bounce);
6142 mod_chanmode_free(bounce);
6147 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6149 struct chanNode *channel;
6150 struct banData *bData;
6151 struct mod_chanmode change;
6152 unsigned int ii, jj;
6153 char kick_reason[MAXLEN];
6155 mod_chanmode_init(&change);
6157 change.args[0].mode = MODE_BAN;
6158 for(ii = 0; ii < user->channels.used; ++ii)
6160 channel = user->channels.list[ii]->channel;
6161 /* Need not check for bans if they're opped or voiced. */
6162 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6164 /* Need not check for bans unless channel registration is active. */
6165 if(!channel->channel_info || IsSuspended(channel->channel_info))
6167 /* Look for a matching ban already on the channel. */
6168 for(jj = 0; jj < channel->banlist.used; ++jj)
6169 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6171 /* Need not act if we found one. */
6172 if(jj < channel->banlist.used)
6174 /* Look for a matching ban in this channel. */
6175 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6177 if(!user_matches_glob(user, bData->mask, 1))
6179 change.args[0].hostmask = bData->mask;
6180 mod_chanmode_announce(chanserv, channel, &change);
6181 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6182 KickChannelUser(user, channel, chanserv, kick_reason);
6183 bData->triggered = now;
6184 break; /* we don't need to check any more bans in the channel */
6189 static void handle_rename(struct handle_info *handle, const char *old_handle)
6191 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6195 dict_remove2(handle_dnrs, old_handle, 1);
6196 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6197 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6202 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6204 struct userNode *h_user;
6206 if(handle->channels)
6208 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6209 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6211 while(handle->channels)
6212 del_channel_user(handle->channels, 1);
6217 handle_server_link(UNUSED_ARG(struct server *server))
6219 struct chanData *cData;
6221 for(cData = channelList; cData; cData = cData->next)
6223 if(!IsSuspended(cData))
6224 cData->may_opchan = 1;
6225 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6226 && !cData->channel->join_flooded
6227 && ((cData->channel->limit - cData->channel->members.used)
6228 < chanserv_conf.adjust_threshold))
6230 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6231 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6237 chanserv_conf_read(void)
6241 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6242 struct mod_chanmode *change;
6243 struct string_list *strlist;
6244 struct chanNode *chan;
6247 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6249 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6252 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6253 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6254 chanserv_conf.support_channels.used = 0;
6255 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6257 for(ii = 0; ii < strlist->used; ++ii)
6259 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6262 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6264 channelList_append(&chanserv_conf.support_channels, chan);
6267 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6270 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6273 chan = AddChannel(str, now, str2, NULL);
6275 channelList_append(&chanserv_conf.support_channels, chan);
6277 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6278 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6279 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6280 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6281 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6282 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6283 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6284 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6285 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6286 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6287 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6288 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6289 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6290 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6291 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6292 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6293 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6294 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6295 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6296 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6297 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6298 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6299 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6301 NickChange(chanserv, str, 0);
6302 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6303 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6304 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6305 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6306 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6307 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6308 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6309 chanserv_conf.max_owned = str ? atoi(str) : 5;
6310 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6311 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6312 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6313 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6314 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6315 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6316 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6319 safestrncpy(mode_line, str, sizeof(mode_line));
6320 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6321 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6323 chanserv_conf.default_modes = *change;
6324 mod_chanmode_free(change);
6326 free_string_list(chanserv_conf.set_shows);
6327 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6329 strlist = string_list_copy(strlist);
6332 static const char *list[] = {
6333 /* free form text */
6334 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6335 /* options based on user level */
6336 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6337 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6338 /* multiple choice options */
6339 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6340 /* binary options */
6341 "DynLimit", "NoDelete",
6346 strlist = alloc_string_list(ArrayLength(list)-1);
6347 for(ii=0; list[ii]; ii++)
6348 string_list_append(strlist, strdup(list[ii]));
6350 chanserv_conf.set_shows = strlist;
6351 /* We don't look things up now, in case the list refers to options
6352 * defined by modules initialized after this point. Just mark the
6353 * function list as invalid, so it will be initialized.
6355 set_shows_list.used = 0;
6356 free_string_list(chanserv_conf.eightball);
6357 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6360 strlist = string_list_copy(strlist);
6364 strlist = alloc_string_list(4);
6365 string_list_append(strlist, strdup("Yes."));
6366 string_list_append(strlist, strdup("No."));
6367 string_list_append(strlist, strdup("Maybe so."));
6369 chanserv_conf.eightball = strlist;
6370 free_string_list(chanserv_conf.old_ban_names);
6371 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6373 strlist = string_list_copy(strlist);
6375 strlist = alloc_string_list(2);
6376 chanserv_conf.old_ban_names = strlist;
6380 chanserv_note_type_read(const char *key, struct record_data *rd)
6383 struct note_type *ntype;
6386 if(!(obj = GET_RECORD_OBJECT(rd)))
6388 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6391 if(!(ntype = chanserv_create_note_type(key)))
6393 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6397 /* Figure out set access */
6398 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6400 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6401 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6403 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6405 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6406 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6408 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6410 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6414 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6415 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6416 ntype->set_access.min_opserv = 0;
6419 /* Figure out visibility */
6420 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6421 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6422 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6423 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6424 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6425 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6426 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6427 ntype->visible_type = NOTE_VIS_ALL;
6429 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6431 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6432 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6436 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6438 struct handle_info *handle;
6439 struct userData *uData;
6440 char *seen, *inf, *flags;
6442 unsigned short access;
6444 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6446 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6450 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6451 if(access > UL_OWNER)
6453 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6457 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6458 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6459 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6460 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6461 handle = get_handle_info(key);
6464 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6468 uData = add_channel_user(chan, handle, access, last_seen, inf);
6469 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6473 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6475 struct banData *bData;
6476 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6477 time_t set_time, triggered_time, expires_time;
6479 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6481 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6485 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6486 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6487 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6488 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6489 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6490 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6492 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6493 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6495 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6497 expires_time = set_time + atoi(s_duration);
6501 if(expires_time && (expires_time < now))
6504 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6507 static struct suspended *
6508 chanserv_read_suspended(dict_t obj)
6510 struct suspended *suspended = calloc(1, sizeof(*suspended));
6514 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6515 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6516 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6517 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6518 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6519 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6520 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6521 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6522 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6523 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6528 chanserv_channel_read(const char *key, struct record_data *hir)
6530 struct suspended *suspended;
6531 struct mod_chanmode *modes;
6532 struct chanNode *cNode;
6533 struct chanData *cData;
6534 struct dict *channel, *obj;
6535 char *str, *argv[10];
6539 channel = hir->d.object;
6541 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6544 cNode = AddChannel(key, now, NULL, NULL);
6547 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6550 cData = register_channel(cNode, str);
6553 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6557 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6559 enum levelOption lvlOpt;
6560 enum charOption chOpt;
6562 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6563 cData->flags = atoi(str);
6565 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6567 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6569 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6570 else if(levelOptions[lvlOpt].old_flag)
6572 if(cData->flags & levelOptions[lvlOpt].old_flag)
6573 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6575 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6579 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6581 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6583 cData->chOpts[chOpt] = str[0];
6586 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6588 enum levelOption lvlOpt;
6589 enum charOption chOpt;
6592 cData->flags = base64toint(str, 5);
6593 count = strlen(str += 5);
6594 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6597 if(levelOptions[lvlOpt].old_flag)
6599 if(cData->flags & levelOptions[lvlOpt].old_flag)
6600 lvl = levelOptions[lvlOpt].flag_value;
6602 lvl = levelOptions[lvlOpt].default_value;
6604 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6606 case 'c': lvl = UL_COOWNER; break;
6607 case 'm': lvl = UL_MASTER; break;
6608 case 'n': lvl = UL_OWNER+1; break;
6609 case 'o': lvl = UL_OP; break;
6610 case 'p': lvl = UL_PEON; break;
6611 case 'w': lvl = UL_OWNER; break;
6612 default: lvl = 0; break;
6614 cData->lvlOpts[lvlOpt] = lvl;
6616 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6617 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6620 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6622 suspended = chanserv_read_suspended(obj);
6623 cData->suspended = suspended;
6624 suspended->cData = cData;
6625 /* We could use suspended->expires and suspended->revoked to
6626 * set the CHANNEL_SUSPENDED flag, but we don't. */
6628 else if(cData->flags & CHANNEL_SUSPENDED)
6630 suspended = calloc(1, sizeof(*suspended));
6631 suspended->issued = 0;
6632 suspended->revoked = 0;
6633 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6634 suspended->expires = str ? atoi(str) : 0;
6635 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6636 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6637 suspended->reason = strdup(str ? str : "No reason");
6638 suspended->previous = NULL;
6639 cData->suspended = suspended;
6640 suspended->cData = cData;
6645 if((cData->flags & CHANNEL_SUSPENDED)
6646 && suspended->expires
6647 && (suspended->expires <= now))
6649 cData->flags &= ~CHANNEL_SUSPENDED;
6652 if(!(cData->flags & CHANNEL_SUSPENDED))
6654 struct mod_chanmode change;
6655 mod_chanmode_init(&change);
6657 change.args[0].mode = MODE_CHANOP;
6658 change.args[0].member = AddChannelUser(chanserv, cNode);
6659 mod_chanmode_announce(chanserv, cNode, &change);
6661 else if(suspended->expires > now)
6663 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6666 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6667 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6668 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6669 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6670 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6671 cData->max = str ? atoi(str) : 0;
6672 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6673 cData->greeting = str ? strdup(str) : NULL;
6674 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6675 cData->user_greeting = str ? strdup(str) : NULL;
6676 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6677 cData->topic_mask = str ? strdup(str) : NULL;
6678 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6679 cData->topic = str ? strdup(str) : NULL;
6681 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6682 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6683 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6684 cData->modes = *modes;
6685 if(cData->modes.argc > 1)
6686 cData->modes.argc = 1;
6687 if(!IsSuspended(cData))
6688 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6689 mod_chanmode_free(modes);
6692 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6693 for(it = dict_first(obj); it; it = iter_next(it))
6694 user_read_helper(iter_key(it), iter_data(it), cData);
6696 if(!cData->users && !IsProtected(cData))
6698 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6699 unregister_channel(cData, "has empty user list.");
6703 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6704 for(it = dict_first(obj); it; it = iter_next(it))
6705 ban_read_helper(iter_key(it), iter_data(it), cData);
6707 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6708 for(it = dict_first(obj); it; it = iter_next(it))
6710 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6711 struct record_data *rd = iter_data(it);
6712 const char *note, *setter;
6714 if(rd->type != RECDB_OBJECT)
6716 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6720 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6722 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6724 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6728 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6729 if(!setter) setter = "<unknown>";
6730 chanserv_add_channel_note(cData, ntype, setter, note);
6738 chanserv_dnr_read(const char *key, struct record_data *hir)
6740 const char *setter, *reason, *str;
6741 struct do_not_register *dnr;
6743 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6746 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6749 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6752 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6755 dnr = chanserv_add_dnr(key, setter, reason);
6758 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6760 dnr->set = atoi(str);
6766 chanserv_saxdb_read(struct dict *database)
6768 struct dict *section;
6771 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6772 for(it = dict_first(section); it; it = iter_next(it))
6773 chanserv_note_type_read(iter_key(it), iter_data(it));
6775 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6776 for(it = dict_first(section); it; it = iter_next(it))
6777 chanserv_channel_read(iter_key(it), iter_data(it));
6779 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6780 for(it = dict_first(section); it; it = iter_next(it))
6781 chanserv_dnr_read(iter_key(it), iter_data(it));
6787 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6789 int high_present = 0;
6790 saxdb_start_record(ctx, KEY_USERS, 1);
6791 for(; uData; uData = uData->next)
6793 if((uData->access >= UL_PRESENT) && uData->present)
6795 saxdb_start_record(ctx, uData->handle->handle, 0);
6796 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6797 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6799 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6801 saxdb_write_string(ctx, KEY_INFO, uData->info);
6802 saxdb_end_record(ctx);
6804 saxdb_end_record(ctx);
6805 return high_present;
6809 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6813 saxdb_start_record(ctx, KEY_BANS, 1);
6814 for(; bData; bData = bData->next)
6816 saxdb_start_record(ctx, bData->mask, 0);
6817 saxdb_write_int(ctx, KEY_SET, bData->set);
6818 if(bData->triggered)
6819 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6821 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6823 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6825 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6826 saxdb_end_record(ctx);
6828 saxdb_end_record(ctx);
6832 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6834 saxdb_start_record(ctx, name, 0);
6835 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6836 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6838 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6840 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6842 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6844 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6845 saxdb_end_record(ctx);
6849 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6853 enum levelOption lvlOpt;
6854 enum charOption chOpt;
6856 saxdb_start_record(ctx, channel->channel->name, 1);
6858 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6859 saxdb_write_int(ctx, KEY_MAX, channel->max);
6861 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6862 if(channel->registrar)
6863 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6864 if(channel->greeting)
6865 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6866 if(channel->user_greeting)
6867 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6868 if(channel->topic_mask)
6869 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6870 if(channel->suspended)
6871 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6873 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6874 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6875 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6876 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6877 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6879 buf[0] = channel->chOpts[chOpt];
6881 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6883 saxdb_end_record(ctx);
6885 if(channel->modes.modes_set || channel->modes.modes_clear)
6887 mod_chanmode_format(&channel->modes, buf);
6888 saxdb_write_string(ctx, KEY_MODES, buf);
6891 high_present = chanserv_write_users(ctx, channel->users);
6892 chanserv_write_bans(ctx, channel->bans);
6894 if(dict_size(channel->notes))
6898 saxdb_start_record(ctx, KEY_NOTES, 1);
6899 for(it = dict_first(channel->notes); it; it = iter_next(it))
6901 struct note *note = iter_data(it);
6902 saxdb_start_record(ctx, iter_key(it), 0);
6903 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6904 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6905 saxdb_end_record(ctx);
6907 saxdb_end_record(ctx);
6910 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6911 saxdb_end_record(ctx);
6915 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6919 saxdb_start_record(ctx, ntype->name, 0);
6920 switch(ntype->set_access_type)
6922 case NOTE_SET_CHANNEL_ACCESS:
6923 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6925 case NOTE_SET_CHANNEL_SETTER:
6926 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6928 case NOTE_SET_PRIVILEGED: default:
6929 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6932 switch(ntype->visible_type)
6934 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6935 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6936 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6938 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6939 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6940 saxdb_end_record(ctx);
6944 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6946 struct do_not_register *dnr;
6949 for(it = dict_first(dnrs); it; it = iter_next(it))
6951 dnr = iter_data(it);
6952 saxdb_start_record(ctx, dnr->chan_name, 0);
6954 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6955 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6956 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6957 saxdb_end_record(ctx);
6962 chanserv_saxdb_write(struct saxdb_context *ctx)
6965 struct chanData *channel;
6968 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6969 for(it = dict_first(note_types); it; it = iter_next(it))
6970 chanserv_write_note_type(ctx, iter_data(it));
6971 saxdb_end_record(ctx);
6974 saxdb_start_record(ctx, KEY_DNR, 1);
6975 write_dnrs_helper(ctx, handle_dnrs);
6976 write_dnrs_helper(ctx, plain_dnrs);
6977 write_dnrs_helper(ctx, mask_dnrs);
6978 saxdb_end_record(ctx);
6981 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6982 for(channel = channelList; channel; channel = channel->next)
6983 chanserv_write_channel(ctx, channel);
6984 saxdb_end_record(ctx);
6990 chanserv_db_cleanup(void) {
6992 unreg_part_func(handle_part);
6994 unregister_channel(channelList, "terminating.");
6995 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6996 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6997 free(chanserv_conf.support_channels.list);
6998 dict_delete(handle_dnrs);
6999 dict_delete(plain_dnrs);
7000 dict_delete(mask_dnrs);
7001 dict_delete(note_types);
7002 free_string_list(chanserv_conf.eightball);
7003 free_string_list(chanserv_conf.old_ban_names);
7004 free_string_list(chanserv_conf.set_shows);
7005 free(set_shows_list.list);
7006 free(uset_shows_list.list);
7009 struct userData *helper = helperList;
7010 helperList = helperList->next;
7015 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7016 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7017 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7020 init_chanserv(const char *nick)
7022 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7023 conf_register_reload(chanserv_conf_read);
7025 reg_server_link_func(handle_server_link);
7027 reg_new_channel_func(handle_new_channel);
7028 reg_join_func(handle_join);
7029 reg_part_func(handle_part);
7030 reg_kick_func(handle_kick);
7031 reg_topic_func(handle_topic);
7032 reg_mode_change_func(handle_mode);
7033 reg_nick_change_func(handle_nick_change);
7035 reg_auth_func(handle_auth);
7036 reg_handle_rename_func(handle_rename);
7037 reg_unreg_func(handle_unreg);
7039 handle_dnrs = dict_new();
7040 dict_set_free_data(handle_dnrs, free);
7041 plain_dnrs = dict_new();
7042 dict_set_free_data(plain_dnrs, free);
7043 mask_dnrs = dict_new();
7044 dict_set_free_data(mask_dnrs, free);
7046 reg_svccmd_unbind_func(handle_svccmd_unbind);
7047 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7048 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7049 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7050 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7051 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7052 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7053 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7054 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7055 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7057 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7058 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7060 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7061 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7062 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7063 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7064 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7066 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7067 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7068 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7069 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7070 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7072 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7073 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7074 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7075 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7077 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7078 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7079 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7080 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7081 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7082 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7083 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7084 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7086 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7087 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7088 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7089 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7090 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7091 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7092 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7093 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7094 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7095 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7096 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7097 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7098 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7099 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7101 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7102 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7103 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7104 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7105 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7107 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7108 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7110 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7111 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7112 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7113 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7114 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7115 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7116 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7117 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7118 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7119 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7120 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7122 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7123 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7125 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7126 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7127 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7128 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7130 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7131 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7132 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7133 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7134 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7136 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7137 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7138 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7139 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7140 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7141 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7143 /* Channel options */
7144 DEFINE_CHANNEL_OPTION(defaulttopic);
7145 DEFINE_CHANNEL_OPTION(topicmask);
7146 DEFINE_CHANNEL_OPTION(greeting);
7147 DEFINE_CHANNEL_OPTION(usergreeting);
7148 DEFINE_CHANNEL_OPTION(modes);
7149 DEFINE_CHANNEL_OPTION(enfops);
7150 DEFINE_CHANNEL_OPTION(giveops);
7151 DEFINE_CHANNEL_OPTION(protect);
7152 DEFINE_CHANNEL_OPTION(enfmodes);
7153 DEFINE_CHANNEL_OPTION(enftopic);
7154 DEFINE_CHANNEL_OPTION(pubcmd);
7155 DEFINE_CHANNEL_OPTION(givevoice);
7156 DEFINE_CHANNEL_OPTION(userinfo);
7157 DEFINE_CHANNEL_OPTION(dynlimit);
7158 DEFINE_CHANNEL_OPTION(topicsnarf);
7159 DEFINE_CHANNEL_OPTION(nodelete);
7160 DEFINE_CHANNEL_OPTION(toys);
7161 DEFINE_CHANNEL_OPTION(setters);
7162 DEFINE_CHANNEL_OPTION(topicrefresh);
7163 DEFINE_CHANNEL_OPTION(ctcpusers);
7164 DEFINE_CHANNEL_OPTION(ctcpreaction);
7165 DEFINE_CHANNEL_OPTION(inviteme);
7166 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7168 /* Alias set topic to set defaulttopic for compatibility. */
7169 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7172 DEFINE_USER_OPTION(noautoop);
7173 DEFINE_USER_OPTION(autoinvite);
7174 DEFINE_USER_OPTION(info);
7176 /* Alias uset autovoice to uset autoop. */
7177 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7179 note_types = dict_new();
7180 dict_set_free_data(note_types, chanserv_deref_note_type);
7183 chanserv = AddService(nick, "Channel Services", NULL);
7184 service_register(chanserv)->trigger = '!';
7185 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7187 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7189 if(chanserv_conf.channel_expire_frequency)
7190 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7192 if(chanserv_conf.refresh_period)
7194 time_t next_refresh;
7195 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7196 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7199 reg_exit_func(chanserv_db_cleanup);
7200 message_register_table(msgtab);