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 irc_invite(chanserv, user, cn);
5922 if(channel->access >= UL_PRESENT)
5923 channel->channel->visited = now;
5925 if(IsUserAutoOp(channel))
5927 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5928 change.args[0].mode = MODE_CHANOP;
5929 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5930 change.args[0].mode = MODE_VOICE;
5932 change.args[0].mode = 0;
5933 change.args[0].member = mn;
5934 if(change.args[0].mode)
5935 mod_chanmode_announce(chanserv, cn, &change);
5938 channel->seen = now;
5939 channel->present = 1;
5942 for(ii = 0; ii < user->channels.used; ++ii)
5944 struct chanNode *channel = user->channels.list[ii]->channel;
5945 struct banData *ban;
5947 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5948 || !channel->channel_info)
5950 for(jj = 0; jj < channel->banlist.used; ++jj)
5951 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5953 if(jj < channel->banlist.used)
5955 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5957 char kick_reason[MAXLEN];
5958 if(!user_matches_glob(user, ban->mask, 1))
5960 change.args[0].mode = MODE_BAN;
5961 change.args[0].hostmask = ban->mask;
5962 mod_chanmode_announce(chanserv, channel, &change);
5963 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5964 KickChannelUser(user, channel, chanserv, kick_reason);
5965 ban->triggered = now;
5970 if(IsSupportHelper(user))
5972 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5974 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5976 HANDLE_SET_FLAG(user->handle_info, HELPING);
5984 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5986 struct chanData *cData;
5987 struct userData *uData;
5989 cData = channel->channel_info;
5990 if(!cData || IsSuspended(cData) || IsLocal(user))
5993 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5995 /* Allow for a bit of padding so that the limit doesn't
5996 track the user count exactly, which could get annoying. */
5997 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5999 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6000 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6004 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
6006 scan_user_presence(uData, user);
6010 if(IsHelping(user) && IsSupportHelper(user))
6012 unsigned int ii, jj;
6013 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6015 for(jj = 0; jj < user->channels.used; ++jj)
6016 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6018 if(jj < user->channels.used)
6021 if(ii == chanserv_conf.support_channels.used)
6022 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
6027 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6029 struct userData *uData;
6031 if(!channel->channel_info || !kicker || IsService(kicker)
6032 || (kicker == victim) || IsSuspended(channel->channel_info)
6033 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6036 if(protect_user(victim, kicker, channel->channel_info))
6038 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6039 KickChannelUser(kicker, channel, chanserv, reason);
6042 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6047 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6049 struct chanData *cData;
6051 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6054 cData = channel->channel_info;
6055 if(bad_topic(channel, user, channel->topic))
6057 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6058 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6059 SetChannelTopic(channel, chanserv, old_topic, 1);
6060 else if(cData->topic)
6061 SetChannelTopic(channel, chanserv, cData->topic, 1);
6064 /* With topicsnarf, grab the topic and save it as the default topic. */
6065 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6068 cData->topic = strdup(channel->topic);
6074 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6076 struct mod_chanmode *bounce = NULL;
6077 unsigned int bnc, ii;
6080 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6083 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6084 && mode_lock_violated(&channel->channel_info->modes, change))
6086 char correct[MAXLEN];
6087 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6088 mod_chanmode_format(&channel->channel_info->modes, correct);
6089 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6091 for(ii = bnc = 0; ii < change->argc; ++ii)
6093 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6095 const struct userNode *victim = change->args[ii].member->user;
6096 if(!protect_user(victim, user, channel->channel_info))
6099 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6102 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6103 bounce->args[bnc].member = GetUserMode(channel, user);
6104 if(bounce->args[bnc].member)
6108 bounce->args[bnc].mode = MODE_CHANOP;
6109 bounce->args[bnc].member = change->args[ii].member;
6111 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6113 else if(change->args[ii].mode & MODE_CHANOP)
6115 const struct userNode *victim = change->args[ii].member->user;
6116 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6119 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6120 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6121 bounce->args[bnc].member = change->args[ii].member;
6124 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6126 const char *ban = change->args[ii].hostmask;
6127 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6130 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6131 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6132 bounce->args[bnc].hostmask = ban;
6134 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6139 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6140 mod_chanmode_announce(chanserv, channel, bounce);
6141 mod_chanmode_free(bounce);
6146 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6148 struct chanNode *channel;
6149 struct banData *bData;
6150 struct mod_chanmode change;
6151 unsigned int ii, jj;
6152 char kick_reason[MAXLEN];
6154 mod_chanmode_init(&change);
6156 change.args[0].mode = MODE_BAN;
6157 for(ii = 0; ii < user->channels.used; ++ii)
6159 channel = user->channels.list[ii]->channel;
6160 /* Need not check for bans if they're opped or voiced. */
6161 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6163 /* Need not check for bans unless channel registration is active. */
6164 if(!channel->channel_info || IsSuspended(channel->channel_info))
6166 /* Look for a matching ban already on the channel. */
6167 for(jj = 0; jj < channel->banlist.used; ++jj)
6168 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6170 /* Need not act if we found one. */
6171 if(jj < channel->banlist.used)
6173 /* Look for a matching ban in this channel. */
6174 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6176 if(!user_matches_glob(user, bData->mask, 1))
6178 change.args[0].hostmask = bData->mask;
6179 mod_chanmode_announce(chanserv, channel, &change);
6180 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6181 KickChannelUser(user, channel, chanserv, kick_reason);
6182 bData->triggered = now;
6183 break; /* we don't need to check any more bans in the channel */
6188 static void handle_rename(struct handle_info *handle, const char *old_handle)
6190 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6194 dict_remove2(handle_dnrs, old_handle, 1);
6195 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6196 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6201 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6203 struct userNode *h_user;
6205 if(handle->channels)
6207 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6208 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6210 while(handle->channels)
6211 del_channel_user(handle->channels, 1);
6216 handle_server_link(UNUSED_ARG(struct server *server))
6218 struct chanData *cData;
6220 for(cData = channelList; cData; cData = cData->next)
6222 if(!IsSuspended(cData))
6223 cData->may_opchan = 1;
6224 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6225 && !cData->channel->join_flooded
6226 && ((cData->channel->limit - cData->channel->members.used)
6227 < chanserv_conf.adjust_threshold))
6229 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6230 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6236 chanserv_conf_read(void)
6240 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6241 struct mod_chanmode *change;
6242 struct string_list *strlist;
6243 struct chanNode *chan;
6246 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6248 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6251 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6252 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6253 chanserv_conf.support_channels.used = 0;
6254 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6256 for(ii = 0; ii < strlist->used; ++ii)
6258 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6261 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6263 channelList_append(&chanserv_conf.support_channels, chan);
6266 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6269 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6272 chan = AddChannel(str, now, str2, NULL);
6274 channelList_append(&chanserv_conf.support_channels, chan);
6276 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6277 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6278 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6279 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6280 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6281 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6282 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6283 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6284 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6285 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6286 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6287 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6288 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6289 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6290 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6291 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6292 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6293 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6294 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6295 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6296 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6297 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6298 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6300 NickChange(chanserv, str, 0);
6301 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6302 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6303 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6304 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6305 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6306 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6307 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6308 chanserv_conf.max_owned = str ? atoi(str) : 5;
6309 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6310 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6311 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6312 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6313 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6314 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6315 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6318 safestrncpy(mode_line, str, sizeof(mode_line));
6319 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6320 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6322 chanserv_conf.default_modes = *change;
6323 mod_chanmode_free(change);
6325 free_string_list(chanserv_conf.set_shows);
6326 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6328 strlist = string_list_copy(strlist);
6331 static const char *list[] = {
6332 /* free form text */
6333 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6334 /* options based on user level */
6335 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6336 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6337 /* multiple choice options */
6338 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6339 /* binary options */
6340 "DynLimit", "NoDelete",
6345 strlist = alloc_string_list(ArrayLength(list)-1);
6346 for(ii=0; list[ii]; ii++)
6347 string_list_append(strlist, strdup(list[ii]));
6349 chanserv_conf.set_shows = strlist;
6350 /* We don't look things up now, in case the list refers to options
6351 * defined by modules initialized after this point. Just mark the
6352 * function list as invalid, so it will be initialized.
6354 set_shows_list.used = 0;
6355 free_string_list(chanserv_conf.eightball);
6356 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6359 strlist = string_list_copy(strlist);
6363 strlist = alloc_string_list(4);
6364 string_list_append(strlist, strdup("Yes."));
6365 string_list_append(strlist, strdup("No."));
6366 string_list_append(strlist, strdup("Maybe so."));
6368 chanserv_conf.eightball = strlist;
6369 free_string_list(chanserv_conf.old_ban_names);
6370 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6372 strlist = string_list_copy(strlist);
6374 strlist = alloc_string_list(2);
6375 chanserv_conf.old_ban_names = strlist;
6379 chanserv_note_type_read(const char *key, struct record_data *rd)
6382 struct note_type *ntype;
6385 if(!(obj = GET_RECORD_OBJECT(rd)))
6387 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6390 if(!(ntype = chanserv_create_note_type(key)))
6392 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6396 /* Figure out set access */
6397 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6399 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6400 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6402 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6404 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6405 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6407 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6409 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6413 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6414 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6415 ntype->set_access.min_opserv = 0;
6418 /* Figure out visibility */
6419 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6420 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6421 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6422 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6423 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6424 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6425 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6426 ntype->visible_type = NOTE_VIS_ALL;
6428 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6430 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6431 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6435 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6437 struct handle_info *handle;
6438 struct userData *uData;
6439 char *seen, *inf, *flags;
6441 unsigned short access;
6443 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6445 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6449 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6450 if(access > UL_OWNER)
6452 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6456 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6457 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6458 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6459 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6460 handle = get_handle_info(key);
6463 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6467 uData = add_channel_user(chan, handle, access, last_seen, inf);
6468 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6472 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6474 struct banData *bData;
6475 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6476 time_t set_time, triggered_time, expires_time;
6478 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6480 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6484 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6485 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6486 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6487 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6488 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6489 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6491 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6492 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6494 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6496 expires_time = set_time + atoi(s_duration);
6500 if(expires_time && (expires_time < now))
6503 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6506 static struct suspended *
6507 chanserv_read_suspended(dict_t obj)
6509 struct suspended *suspended = calloc(1, sizeof(*suspended));
6513 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6514 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6515 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6516 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6517 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6518 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6519 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6520 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6521 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6522 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6527 chanserv_channel_read(const char *key, struct record_data *hir)
6529 struct suspended *suspended;
6530 struct mod_chanmode *modes;
6531 struct chanNode *cNode;
6532 struct chanData *cData;
6533 struct dict *channel, *obj;
6534 char *str, *argv[10];
6538 channel = hir->d.object;
6540 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6543 cNode = AddChannel(key, now, NULL, NULL);
6546 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6549 cData = register_channel(cNode, str);
6552 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6556 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6558 enum levelOption lvlOpt;
6559 enum charOption chOpt;
6561 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6562 cData->flags = atoi(str);
6564 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6566 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6568 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6569 else if(levelOptions[lvlOpt].old_flag)
6571 if(cData->flags & levelOptions[lvlOpt].old_flag)
6572 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6574 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6578 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6580 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6582 cData->chOpts[chOpt] = str[0];
6585 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6587 enum levelOption lvlOpt;
6588 enum charOption chOpt;
6591 cData->flags = base64toint(str, 5);
6592 count = strlen(str += 5);
6593 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6596 if(levelOptions[lvlOpt].old_flag)
6598 if(cData->flags & levelOptions[lvlOpt].old_flag)
6599 lvl = levelOptions[lvlOpt].flag_value;
6601 lvl = levelOptions[lvlOpt].default_value;
6603 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6605 case 'c': lvl = UL_COOWNER; break;
6606 case 'm': lvl = UL_MASTER; break;
6607 case 'n': lvl = UL_OWNER+1; break;
6608 case 'o': lvl = UL_OP; break;
6609 case 'p': lvl = UL_PEON; break;
6610 case 'w': lvl = UL_OWNER; break;
6611 default: lvl = 0; break;
6613 cData->lvlOpts[lvlOpt] = lvl;
6615 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6616 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6619 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6621 suspended = chanserv_read_suspended(obj);
6622 cData->suspended = suspended;
6623 suspended->cData = cData;
6624 /* We could use suspended->expires and suspended->revoked to
6625 * set the CHANNEL_SUSPENDED flag, but we don't. */
6627 else if(cData->flags & CHANNEL_SUSPENDED)
6629 suspended = calloc(1, sizeof(*suspended));
6630 suspended->issued = 0;
6631 suspended->revoked = 0;
6632 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6633 suspended->expires = str ? atoi(str) : 0;
6634 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6635 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6636 suspended->reason = strdup(str ? str : "No reason");
6637 suspended->previous = NULL;
6638 cData->suspended = suspended;
6639 suspended->cData = cData;
6644 if((cData->flags & CHANNEL_SUSPENDED)
6645 && suspended->expires
6646 && (suspended->expires <= now))
6648 cData->flags &= ~CHANNEL_SUSPENDED;
6651 if(!(cData->flags & CHANNEL_SUSPENDED))
6653 struct mod_chanmode change;
6654 mod_chanmode_init(&change);
6656 change.args[0].mode = MODE_CHANOP;
6657 change.args[0].member = AddChannelUser(chanserv, cNode);
6658 mod_chanmode_announce(chanserv, cNode, &change);
6660 else if(suspended->expires > now)
6662 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6665 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6666 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6667 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6668 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6669 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6670 cData->max = str ? atoi(str) : 0;
6671 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6672 cData->greeting = str ? strdup(str) : NULL;
6673 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6674 cData->user_greeting = str ? strdup(str) : NULL;
6675 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6676 cData->topic_mask = str ? strdup(str) : NULL;
6677 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6678 cData->topic = str ? strdup(str) : NULL;
6680 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6681 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6682 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6683 cData->modes = *modes;
6684 if(cData->modes.argc > 1)
6685 cData->modes.argc = 1;
6686 if(!IsSuspended(cData))
6687 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6688 mod_chanmode_free(modes);
6691 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6692 for(it = dict_first(obj); it; it = iter_next(it))
6693 user_read_helper(iter_key(it), iter_data(it), cData);
6695 if(!cData->users && !IsProtected(cData))
6697 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6698 unregister_channel(cData, "has empty user list.");
6702 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6703 for(it = dict_first(obj); it; it = iter_next(it))
6704 ban_read_helper(iter_key(it), iter_data(it), cData);
6706 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6707 for(it = dict_first(obj); it; it = iter_next(it))
6709 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6710 struct record_data *rd = iter_data(it);
6711 const char *note, *setter;
6713 if(rd->type != RECDB_OBJECT)
6715 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6719 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6721 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6723 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6727 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6728 if(!setter) setter = "<unknown>";
6729 chanserv_add_channel_note(cData, ntype, setter, note);
6737 chanserv_dnr_read(const char *key, struct record_data *hir)
6739 const char *setter, *reason, *str;
6740 struct do_not_register *dnr;
6742 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6745 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6748 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6751 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6754 dnr = chanserv_add_dnr(key, setter, reason);
6757 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6759 dnr->set = atoi(str);
6765 chanserv_saxdb_read(struct dict *database)
6767 struct dict *section;
6770 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6771 for(it = dict_first(section); it; it = iter_next(it))
6772 chanserv_note_type_read(iter_key(it), iter_data(it));
6774 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6775 for(it = dict_first(section); it; it = iter_next(it))
6776 chanserv_channel_read(iter_key(it), iter_data(it));
6778 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6779 for(it = dict_first(section); it; it = iter_next(it))
6780 chanserv_dnr_read(iter_key(it), iter_data(it));
6786 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6788 int high_present = 0;
6789 saxdb_start_record(ctx, KEY_USERS, 1);
6790 for(; uData; uData = uData->next)
6792 if((uData->access >= UL_PRESENT) && uData->present)
6794 saxdb_start_record(ctx, uData->handle->handle, 0);
6795 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6796 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6798 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6800 saxdb_write_string(ctx, KEY_INFO, uData->info);
6801 saxdb_end_record(ctx);
6803 saxdb_end_record(ctx);
6804 return high_present;
6808 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6812 saxdb_start_record(ctx, KEY_BANS, 1);
6813 for(; bData; bData = bData->next)
6815 saxdb_start_record(ctx, bData->mask, 0);
6816 saxdb_write_int(ctx, KEY_SET, bData->set);
6817 if(bData->triggered)
6818 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6820 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6822 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6824 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6825 saxdb_end_record(ctx);
6827 saxdb_end_record(ctx);
6831 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6833 saxdb_start_record(ctx, name, 0);
6834 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6835 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6837 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6839 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6841 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6843 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6844 saxdb_end_record(ctx);
6848 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6852 enum levelOption lvlOpt;
6853 enum charOption chOpt;
6855 saxdb_start_record(ctx, channel->channel->name, 1);
6857 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6858 saxdb_write_int(ctx, KEY_MAX, channel->max);
6860 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6861 if(channel->registrar)
6862 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6863 if(channel->greeting)
6864 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6865 if(channel->user_greeting)
6866 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6867 if(channel->topic_mask)
6868 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6869 if(channel->suspended)
6870 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6872 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6873 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6874 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6875 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6876 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6878 buf[0] = channel->chOpts[chOpt];
6880 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6882 saxdb_end_record(ctx);
6884 if(channel->modes.modes_set || channel->modes.modes_clear)
6886 mod_chanmode_format(&channel->modes, buf);
6887 saxdb_write_string(ctx, KEY_MODES, buf);
6890 high_present = chanserv_write_users(ctx, channel->users);
6891 chanserv_write_bans(ctx, channel->bans);
6893 if(dict_size(channel->notes))
6897 saxdb_start_record(ctx, KEY_NOTES, 1);
6898 for(it = dict_first(channel->notes); it; it = iter_next(it))
6900 struct note *note = iter_data(it);
6901 saxdb_start_record(ctx, iter_key(it), 0);
6902 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6903 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6904 saxdb_end_record(ctx);
6906 saxdb_end_record(ctx);
6909 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6910 saxdb_end_record(ctx);
6914 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6918 saxdb_start_record(ctx, ntype->name, 0);
6919 switch(ntype->set_access_type)
6921 case NOTE_SET_CHANNEL_ACCESS:
6922 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6924 case NOTE_SET_CHANNEL_SETTER:
6925 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6927 case NOTE_SET_PRIVILEGED: default:
6928 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6931 switch(ntype->visible_type)
6933 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6934 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6935 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6937 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6938 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6939 saxdb_end_record(ctx);
6943 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6945 struct do_not_register *dnr;
6948 for(it = dict_first(dnrs); it; it = iter_next(it))
6950 dnr = iter_data(it);
6951 saxdb_start_record(ctx, dnr->chan_name, 0);
6953 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6954 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6955 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6956 saxdb_end_record(ctx);
6961 chanserv_saxdb_write(struct saxdb_context *ctx)
6964 struct chanData *channel;
6967 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6968 for(it = dict_first(note_types); it; it = iter_next(it))
6969 chanserv_write_note_type(ctx, iter_data(it));
6970 saxdb_end_record(ctx);
6973 saxdb_start_record(ctx, KEY_DNR, 1);
6974 write_dnrs_helper(ctx, handle_dnrs);
6975 write_dnrs_helper(ctx, plain_dnrs);
6976 write_dnrs_helper(ctx, mask_dnrs);
6977 saxdb_end_record(ctx);
6980 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6981 for(channel = channelList; channel; channel = channel->next)
6982 chanserv_write_channel(ctx, channel);
6983 saxdb_end_record(ctx);
6989 chanserv_db_cleanup(void) {
6991 unreg_part_func(handle_part);
6993 unregister_channel(channelList, "terminating.");
6994 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6995 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6996 free(chanserv_conf.support_channels.list);
6997 dict_delete(handle_dnrs);
6998 dict_delete(plain_dnrs);
6999 dict_delete(mask_dnrs);
7000 dict_delete(note_types);
7001 free_string_list(chanserv_conf.eightball);
7002 free_string_list(chanserv_conf.old_ban_names);
7003 free_string_list(chanserv_conf.set_shows);
7004 free(set_shows_list.list);
7005 free(uset_shows_list.list);
7008 struct userData *helper = helperList;
7009 helperList = helperList->next;
7014 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7015 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7016 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7019 init_chanserv(const char *nick)
7021 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7022 conf_register_reload(chanserv_conf_read);
7024 reg_server_link_func(handle_server_link);
7026 reg_new_channel_func(handle_new_channel);
7027 reg_join_func(handle_join);
7028 reg_part_func(handle_part);
7029 reg_kick_func(handle_kick);
7030 reg_topic_func(handle_topic);
7031 reg_mode_change_func(handle_mode);
7032 reg_nick_change_func(handle_nick_change);
7034 reg_auth_func(handle_auth);
7035 reg_handle_rename_func(handle_rename);
7036 reg_unreg_func(handle_unreg);
7038 handle_dnrs = dict_new();
7039 dict_set_free_data(handle_dnrs, free);
7040 plain_dnrs = dict_new();
7041 dict_set_free_data(plain_dnrs, free);
7042 mask_dnrs = dict_new();
7043 dict_set_free_data(mask_dnrs, free);
7045 reg_svccmd_unbind_func(handle_svccmd_unbind);
7046 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7047 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7048 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7049 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7050 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7051 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7052 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7053 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7054 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7056 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7057 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7059 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7060 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7061 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7062 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7063 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7065 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7066 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7067 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7068 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7069 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7071 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7072 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7073 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7074 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7076 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7077 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7078 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7079 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7080 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7081 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7082 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7083 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7085 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7086 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7087 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7088 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7089 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7090 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7091 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7092 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7093 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7094 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7095 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7096 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7097 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7098 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7100 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7101 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7102 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7103 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7104 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7106 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7107 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7109 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7110 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7111 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7112 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7113 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7114 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7115 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7116 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7117 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7118 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7119 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7121 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7122 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7124 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7125 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7126 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7127 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7129 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7130 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7131 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7132 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7133 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7135 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7136 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7137 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7138 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7139 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7140 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7142 /* Channel options */
7143 DEFINE_CHANNEL_OPTION(defaulttopic);
7144 DEFINE_CHANNEL_OPTION(topicmask);
7145 DEFINE_CHANNEL_OPTION(greeting);
7146 DEFINE_CHANNEL_OPTION(usergreeting);
7147 DEFINE_CHANNEL_OPTION(modes);
7148 DEFINE_CHANNEL_OPTION(enfops);
7149 DEFINE_CHANNEL_OPTION(giveops);
7150 DEFINE_CHANNEL_OPTION(protect);
7151 DEFINE_CHANNEL_OPTION(enfmodes);
7152 DEFINE_CHANNEL_OPTION(enftopic);
7153 DEFINE_CHANNEL_OPTION(pubcmd);
7154 DEFINE_CHANNEL_OPTION(givevoice);
7155 DEFINE_CHANNEL_OPTION(userinfo);
7156 DEFINE_CHANNEL_OPTION(dynlimit);
7157 DEFINE_CHANNEL_OPTION(topicsnarf);
7158 DEFINE_CHANNEL_OPTION(nodelete);
7159 DEFINE_CHANNEL_OPTION(toys);
7160 DEFINE_CHANNEL_OPTION(setters);
7161 DEFINE_CHANNEL_OPTION(topicrefresh);
7162 DEFINE_CHANNEL_OPTION(ctcpusers);
7163 DEFINE_CHANNEL_OPTION(ctcpreaction);
7164 DEFINE_CHANNEL_OPTION(inviteme);
7165 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7167 /* Alias set topic to set defaulttopic for compatibility. */
7168 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7171 DEFINE_USER_OPTION(noautoop);
7172 DEFINE_USER_OPTION(autoinvite);
7173 DEFINE_USER_OPTION(info);
7175 /* Alias uset autovoice to uset autoop. */
7176 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7178 note_types = dict_new();
7179 dict_set_free_data(note_types, chanserv_deref_note_type);
7182 chanserv = AddService(nick, "Channel Services", NULL);
7183 service_register(chanserv)->trigger = '!';
7184 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7186 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7188 if(chanserv_conf.channel_expire_frequency)
7189 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7191 if(chanserv_conf.refresh_period)
7193 time_t next_refresh;
7194 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7195 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7198 reg_exit_func(chanserv_db_cleanup);
7199 message_register_table(msgtab);