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_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "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" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
424 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
427 { "CSMSG_USER_OPTIONS", "User Options:" },
428 { "CSMSG_USER_PROTECTED", "That user is protected." },
431 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
432 { "CSMSG_PING_RESPONSE", "Pong!" },
433 { "CSMSG_WUT_RESPONSE", "wut" },
434 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
435 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
436 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
437 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
438 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
439 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
440 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
443 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
447 /* eject_user and unban_user flags */
448 #define ACTION_KICK 0x0001
449 #define ACTION_BAN 0x0002
450 #define ACTION_ADD_BAN 0x0004
451 #define ACTION_ADD_TIMED_BAN 0x0008
452 #define ACTION_UNBAN 0x0010
453 #define ACTION_DEL_BAN 0x0020
455 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
456 #define MODELEN 40 + KEYLEN
460 #define CSFUNC_ARGS user, channel, argc, argv, cmd
462 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
463 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
464 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
465 reply("MSG_MISSING_PARAMS", argv[0]); \
469 DECLARE_LIST(dnrList, struct do_not_register *);
470 DEFINE_LIST(dnrList, struct do_not_register *);
472 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
474 struct userNode *chanserv;
477 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
478 static struct log_type *CS_LOG;
482 struct channelList support_channels;
483 struct mod_chanmode default_modes;
485 unsigned long db_backup_frequency;
486 unsigned long channel_expire_frequency;
489 unsigned int adjust_delay;
490 long channel_expire_delay;
491 unsigned int nodelete_level;
493 unsigned int adjust_threshold;
494 int join_flood_threshold;
496 unsigned int greeting_length;
497 unsigned int refresh_period;
498 unsigned int giveownership_period;
500 unsigned int max_owned;
501 unsigned int max_chan_users;
502 unsigned int max_chan_bans;
503 unsigned int max_userinfo_length;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->ownerTransfer = now;
1084 channel->flags = CHANNEL_DEFAULT_FLAGS;
1085 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1086 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1088 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1090 channel->prev = NULL;
1091 channel->next = channelList;
1094 channelList->prev = channel;
1095 channelList = channel;
1096 registered_channels++;
1098 channel->channel = cNode;
1100 cNode->channel_info = channel;
1105 static struct userData*
1106 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1108 struct userData *ud;
1110 if(access > UL_OWNER)
1113 ud = calloc(1, sizeof(*ud));
1114 ud->channel = channel;
1115 ud->handle = handle;
1117 ud->access = access;
1118 ud->info = info ? strdup(info) : NULL;
1121 ud->next = channel->users;
1123 channel->users->prev = ud;
1124 channel->users = ud;
1126 channel->userCount++;
1130 ud->u_next = ud->handle->channels;
1132 ud->u_next->u_prev = ud;
1133 ud->handle->channels = ud;
1138 static void unregister_channel(struct chanData *channel, const char *reason);
1141 del_channel_user(struct userData *user, int do_gc)
1143 struct chanData *channel = user->channel;
1145 channel->userCount--;
1149 user->prev->next = user->next;
1151 channel->users = user->next;
1153 user->next->prev = user->prev;
1156 user->u_prev->u_next = user->u_next;
1158 user->handle->channels = user->u_next;
1160 user->u_next->u_prev = user->u_prev;
1164 if(do_gc && !channel->users && !IsProtected(channel))
1165 unregister_channel(channel, "lost all users.");
1168 static void expire_ban(void *data);
1170 static struct banData*
1171 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1174 unsigned int ii, l1, l2;
1179 bd = malloc(sizeof(struct banData));
1181 bd->channel = channel;
1183 bd->triggered = triggered;
1184 bd->expires = expires;
1186 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1188 extern const char *hidden_host_suffix;
1189 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1193 l2 = strlen(old_name);
1196 if(irccasecmp(mask + l1 - l2, old_name))
1198 new_mask = alloca(MAXLEN);
1199 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1202 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1204 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1205 bd->reason = reason ? strdup(reason) : NULL;
1208 timeq_add(expires, expire_ban, bd);
1211 bd->next = channel->bans;
1213 channel->bans->prev = bd;
1215 channel->banCount++;
1222 del_channel_ban(struct banData *ban)
1224 ban->channel->banCount--;
1228 ban->prev->next = ban->next;
1230 ban->channel->bans = ban->next;
1233 ban->next->prev = ban->prev;
1236 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1245 expire_ban(void *data)
1247 struct banData *bd = data;
1248 if(!IsSuspended(bd->channel))
1250 struct banList bans;
1251 struct mod_chanmode change;
1253 bans = bd->channel->channel->banlist;
1254 mod_chanmode_init(&change);
1255 for(ii=0; ii<bans.used; ii++)
1257 if(!strcmp(bans.list[ii]->ban, bd->mask))
1260 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1261 change.args[0].u.hostmask = bd->mask;
1262 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1268 del_channel_ban(bd);
1271 static void chanserv_expire_suspension(void *data);
1274 unregister_channel(struct chanData *channel, const char *reason)
1276 struct mod_chanmode change;
1277 char msgbuf[MAXLEN];
1279 /* After channel unregistration, the following must be cleaned
1281 - Channel information.
1284 - Channel suspension data.
1285 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1291 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 mod_chanmode_init(&change);
1296 change.modes_clear |= MODE_REGISTERED;
1297 mod_chanmode_announce(chanserv, channel->channel, &change);
1300 while(channel->users)
1301 del_channel_user(channel->users, 0);
1303 while(channel->bans)
1304 del_channel_ban(channel->bans);
1306 free(channel->topic);
1307 free(channel->registrar);
1308 free(channel->greeting);
1309 free(channel->user_greeting);
1310 free(channel->topic_mask);
1313 channel->prev->next = channel->next;
1315 channelList = channel->next;
1318 channel->next->prev = channel->prev;
1320 if(channel->suspended)
1322 struct chanNode *cNode = channel->channel;
1323 struct suspended *suspended, *next_suspended;
1325 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1327 next_suspended = suspended->previous;
1328 free(suspended->suspender);
1329 free(suspended->reason);
1330 if(suspended->expires)
1331 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1336 cNode->channel_info = NULL;
1338 channel->channel->channel_info = NULL;
1340 dict_delete(channel->notes);
1341 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1342 if(!IsSuspended(channel))
1343 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1345 UnlockChannel(channel->channel);
1347 registered_channels--;
1351 expire_channels(UNUSED_ARG(void *data))
1353 struct chanData *channel, *next;
1354 struct userData *user;
1355 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1357 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1358 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1360 for(channel = channelList; channel; channel = next)
1362 next = channel->next;
1364 /* See if the channel can be expired. */
1365 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1366 || IsProtected(channel))
1369 /* Make sure there are no high-ranking users still in the channel. */
1370 for(user=channel->users; user; user=user->next)
1371 if(user->present && (user->access >= UL_PRESENT))
1376 /* Unregister the channel */
1377 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1378 unregister_channel(channel, "registration expired.");
1381 if(chanserv_conf.channel_expire_frequency)
1382 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1386 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1388 char protect = channel->chOpts[chProtect];
1389 struct userData *cs_victim, *cs_aggressor;
1391 /* Don't protect if no one is to be protected, someone is attacking
1392 himself, or if the aggressor is an IRC Operator. */
1393 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1396 /* Don't protect if the victim isn't authenticated (because they
1397 can't be a channel user), unless we are to protect non-users
1399 cs_victim = GetChannelAccess(channel, victim->handle_info);
1400 if(protect != 'a' && !cs_victim)
1403 /* Protect if the aggressor isn't a user because at this point,
1404 the aggressor can only be less than or equal to the victim. */
1405 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1409 /* If the aggressor was a user, then the victim can't be helped. */
1416 if(cs_victim->access > cs_aggressor->access)
1421 if(cs_victim->access >= cs_aggressor->access)
1430 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1432 struct chanData *cData = channel->channel_info;
1433 struct userData *cs_victim;
1435 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1436 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1437 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1439 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1447 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1449 if(IsService(victim))
1451 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1455 if(protect_user(victim, user, channel->channel_info))
1457 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1464 static struct do_not_register *
1465 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1467 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1468 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1469 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1470 strcpy(dnr->reason, reason);
1472 if(dnr->chan_name[0] == '*')
1473 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1474 else if(strpbrk(dnr->chan_name, "*?"))
1475 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1477 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1481 static struct dnrList
1482 chanserv_find_dnrs(const char *chan_name, const char *handle)
1484 struct dnrList list;
1486 struct do_not_register *dnr;
1488 dnrList_init(&list);
1489 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1490 dnrList_append(&list, dnr);
1491 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1492 dnrList_append(&list, dnr);
1494 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1495 if(match_ircglob(chan_name, iter_key(it)))
1496 dnrList_append(&list, iter_data(it));
1501 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1503 struct dnrList list;
1504 struct do_not_register *dnr;
1506 char buf[INTERVALLEN];
1508 list = chanserv_find_dnrs(chan_name, handle);
1509 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1511 dnr = list.list[ii];
1514 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1515 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1518 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1521 reply("CSMSG_MORE_DNRS", list.used - ii);
1526 struct do_not_register *
1527 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1529 struct do_not_register *dnr;
1532 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1536 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1538 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1539 if(match_ircglob(chan_name, iter_key(it)))
1540 return iter_data(it);
1545 static CHANSERV_FUNC(cmd_noregister)
1548 struct do_not_register *dnr;
1549 char buf[INTERVALLEN];
1550 unsigned int matches;
1556 reply("CSMSG_DNR_SEARCH_RESULTS");
1558 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1560 dnr = iter_data(it);
1562 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1564 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1569 dnr = iter_data(it);
1571 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1573 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1576 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1578 dnr = iter_data(it);
1580 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1582 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587 reply("MSG_MATCH_COUNT", matches);
1589 reply("MSG_NO_MATCHES");
1595 if(!IsChannelName(target) && (*target != '*'))
1597 reply("CSMSG_NOT_DNR", target);
1603 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1604 if((*target == '*') && !get_handle_info(target + 1))
1606 reply("MSG_HANDLE_UNKNOWN", target + 1);
1609 chanserv_add_dnr(target, user->handle_info->handle, reason);
1610 reply("CSMSG_NOREGISTER_CHANNEL", target);
1614 reply("CSMSG_DNR_SEARCH_RESULTS");
1616 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1618 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1620 reply("MSG_NO_MATCHES");
1624 static CHANSERV_FUNC(cmd_allowregister)
1626 const char *chan_name = argv[1];
1628 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1630 dict_remove(handle_dnrs, chan_name+1);
1631 reply("CSMSG_DNR_REMOVED", chan_name);
1633 else if(dict_find(plain_dnrs, chan_name, NULL))
1635 dict_remove(plain_dnrs, chan_name);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(mask_dnrs, chan_name, NULL))
1640 dict_remove(mask_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1645 reply("CSMSG_NO_SUCH_DNR", chan_name);
1652 chanserv_get_owned_count(struct handle_info *hi)
1654 struct userData *cList;
1657 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1658 if(cList->access == UL_OWNER)
1663 static CHANSERV_FUNC(cmd_register)
1665 struct mod_chanmode *change;
1666 struct handle_info *handle;
1667 struct chanData *cData;
1668 struct modeNode *mn;
1669 char reason[MAXLEN];
1671 unsigned int new_channel, force=0;
1672 struct do_not_register *dnr;
1676 if(channel->channel_info)
1678 reply("CSMSG_ALREADY_REGGED", channel->name);
1682 if(channel->bad_channel)
1684 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1688 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1690 reply("CSMSG_MUST_BE_OPPED", channel->name);
1695 chan_name = channel->name;
1699 if((argc < 2) || !IsChannelName(argv[1]))
1701 reply("MSG_NOT_CHANNEL_NAME");
1705 if(opserv_bad_channel(argv[1]))
1707 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1712 chan_name = argv[1];
1715 if(argc >= (new_channel+2))
1717 if(!IsHelping(user))
1719 reply("CSMSG_PROXY_FORBIDDEN");
1723 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1725 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1726 dnr = chanserv_is_dnr(chan_name, handle);
1730 handle = user->handle_info;
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 if(!IsHelping(user))
1736 reply("CSMSG_DNR_CHANNEL", chan_name);
1738 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1742 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1744 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1749 channel = AddChannel(argv[1], now, NULL, NULL);
1751 cData = register_channel(channel, user->handle_info->handle);
1752 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1753 cData->modes = chanserv_conf.default_modes;
1755 cData->modes.modes_set |= MODE_REGISTERED;
1756 change = mod_chanmode_dup(&cData->modes, 1);
1757 change->args[change->argc].mode = MODE_CHANOP;
1758 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1760 mod_chanmode_announce(chanserv, channel, change);
1761 mod_chanmode_free(change);
1763 /* Initialize the channel's max user record. */
1764 cData->max = channel->members.used;
1766 if(handle != user->handle_info)
1767 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1769 reply("CSMSG_REG_SUCCESS", channel->name);
1771 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1772 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1777 make_confirmation_string(struct userData *uData)
1779 static char strbuf[16];
1784 for(src = uData->handle->handle; *src; )
1785 accum = accum * 31 + toupper(*src++);
1787 for(src = uData->channel->channel->name; *src; )
1788 accum = accum * 31 + toupper(*src++);
1789 sprintf(strbuf, "%08x", accum);
1793 static CHANSERV_FUNC(cmd_unregister)
1796 char reason[MAXLEN];
1797 struct chanData *cData;
1798 struct userData *uData;
1800 cData = channel->channel_info;
1803 reply("CSMSG_NOT_REGISTERED", channel->name);
1807 uData = GetChannelUser(cData, user->handle_info);
1808 if(!uData || (uData->access < UL_OWNER))
1810 reply("CSMSG_NO_ACCESS");
1814 if(IsProtected(cData))
1816 reply("CSMSG_UNREG_NODELETE", channel->name);
1820 if(!IsHelping(user))
1822 const char *confirm_string;
1823 if(IsSuspended(cData))
1825 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1828 confirm_string = make_confirmation_string(uData);
1829 if((argc < 2) || strcmp(argv[1], confirm_string))
1831 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1836 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1837 name = strdup(channel->name);
1838 unregister_channel(cData, reason);
1839 reply("CSMSG_UNREG_SUCCESS", name);
1844 static CHANSERV_FUNC(cmd_move)
1846 struct mod_chanmode change;
1847 struct chanNode *target;
1848 struct modeNode *mn;
1849 struct userData *uData;
1850 char reason[MAXLEN];
1851 struct do_not_register *dnr;
1855 if(IsProtected(channel->channel_info))
1857 reply("CSMSG_MOVE_NODELETE", channel->name);
1861 if(!IsChannelName(argv[1]))
1863 reply("MSG_NOT_CHANNEL_NAME");
1867 if(opserv_bad_channel(argv[1]))
1869 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1873 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1875 for(uData = channel->channel_info->users; uData; uData = uData->next)
1877 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1879 if(!IsHelping(user))
1880 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1882 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1888 mod_chanmode_init(&change);
1889 if(!(target = GetChannel(argv[1])))
1891 target = AddChannel(argv[1], now, NULL, NULL);
1892 if(!IsSuspended(channel->channel_info))
1893 AddChannelUser(chanserv, target);
1895 else if(target->channel_info)
1897 reply("CSMSG_ALREADY_REGGED", target->name);
1900 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1901 && !IsHelping(user))
1903 reply("CSMSG_MUST_BE_OPPED", target->name);
1906 else if(!IsSuspended(channel->channel_info))
1909 change.args[0].mode = MODE_CHANOP;
1910 change.args[0].u.member = AddChannelUser(chanserv, target);
1911 mod_chanmode_announce(chanserv, target, &change);
1916 /* Clear MODE_REGISTERED from old channel, add it to new. */
1918 change.modes_clear = MODE_REGISTERED;
1919 mod_chanmode_announce(chanserv, channel, &change);
1920 change.modes_clear = 0;
1921 change.modes_set = MODE_REGISTERED;
1922 mod_chanmode_announce(chanserv, target, &change);
1925 /* Move the channel_info to the target channel; it
1926 shouldn't be necessary to clear timeq callbacks
1927 for the old channel. */
1928 target->channel_info = channel->channel_info;
1929 target->channel_info->channel = target;
1930 channel->channel_info = NULL;
1932 reply("CSMSG_MOVE_SUCCESS", target->name);
1934 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1935 if(!IsSuspended(target->channel_info))
1937 char reason2[MAXLEN];
1938 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1939 DelChannelUser(chanserv, channel, reason2, 0);
1941 UnlockChannel(channel);
1942 LockChannel(target);
1943 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1948 merge_users(struct chanData *source, struct chanData *target)
1950 struct userData *suData, *tuData, *next;
1956 /* Insert the source's users into the scratch area. */
1957 for(suData = source->users; suData; suData = suData->next)
1958 dict_insert(merge, suData->handle->handle, suData);
1960 /* Iterate through the target's users, looking for
1961 users common to both channels. The lower access is
1962 removed from either the scratch area or target user
1964 for(tuData = target->users; tuData; tuData = next)
1966 struct userData *choice;
1968 next = tuData->next;
1970 /* If a source user exists with the same handle as a target
1971 channel's user, resolve the conflict by removing one. */
1972 suData = dict_find(merge, tuData->handle->handle, NULL);
1976 /* Pick the data we want to keep. */
1977 /* If the access is the same, use the later seen time. */
1978 if(suData->access == tuData->access)
1979 choice = (suData->seen > tuData->seen) ? suData : tuData;
1980 else /* Otherwise, keep the higher access level. */
1981 choice = (suData->access > tuData->access) ? suData : tuData;
1983 /* Remove the user that wasn't picked. */
1984 if(choice == tuData)
1986 dict_remove(merge, suData->handle->handle);
1987 del_channel_user(suData, 0);
1990 del_channel_user(tuData, 0);
1993 /* Move the remaining users to the target channel. */
1994 for(it = dict_first(merge); it; it = iter_next(it))
1996 suData = iter_data(it);
1998 /* Insert the user into the target channel's linked list. */
1999 suData->prev = NULL;
2000 suData->next = target->users;
2001 suData->channel = target;
2004 target->users->prev = suData;
2005 target->users = suData;
2007 /* Update the user counts for the target channel; the
2008 source counts are left alone. */
2009 target->userCount++;
2012 /* Possible to assert (source->users == NULL) here. */
2013 source->users = NULL;
2018 merge_bans(struct chanData *source, struct chanData *target)
2020 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2022 /* Hold on to the original head of the target ban list
2023 to avoid comparing source bans with source bans. */
2024 tFront = target->bans;
2026 /* Perform a totally expensive O(n*m) merge, ick. */
2027 for(sbData = source->bans; sbData; sbData = sNext)
2029 /* Flag to track whether the ban's been moved
2030 to the destination yet. */
2033 /* Possible to assert (sbData->prev == NULL) here. */
2034 sNext = sbData->next;
2036 for(tbData = tFront; tbData; tbData = tNext)
2038 tNext = tbData->next;
2040 /* Perform two comparisons between each source
2041 and target ban, conflicts are resolved by
2042 keeping the broader ban and copying the later
2043 expiration and triggered time. */
2044 if(match_ircglobs(tbData->mask, sbData->mask))
2046 /* There is a broader ban in the target channel that
2047 overrides one in the source channel; remove the
2048 source ban and break. */
2049 if(sbData->expires > tbData->expires)
2050 tbData->expires = sbData->expires;
2051 if(sbData->triggered > tbData->triggered)
2052 tbData->triggered = sbData->triggered;
2053 del_channel_ban(sbData);
2056 else if(match_ircglobs(sbData->mask, tbData->mask))
2058 /* There is a broader ban in the source channel that
2059 overrides one in the target channel; remove the
2060 target ban, fall through and move the source over. */
2061 if(tbData->expires > sbData->expires)
2062 sbData->expires = tbData->expires;
2063 if(tbData->triggered > sbData->triggered)
2064 sbData->triggered = tbData->triggered;
2065 if(tbData == tFront)
2067 del_channel_ban(tbData);
2070 /* Source bans can override multiple target bans, so
2071 we allow a source to run through this loop multiple
2072 times, but we can only move it once. */
2077 /* Remove the source ban from the source ban list. */
2079 sbData->next->prev = sbData->prev;
2081 /* Modify the source ban's associated channel. */
2082 sbData->channel = target;
2084 /* Insert the ban into the target channel's linked list. */
2085 sbData->prev = NULL;
2086 sbData->next = target->bans;
2089 target->bans->prev = sbData;
2090 target->bans = sbData;
2092 /* Update the user counts for the target channel. */
2097 /* Possible to assert (source->bans == NULL) here. */
2098 source->bans = NULL;
2102 merge_data(struct chanData *source, struct chanData *target)
2104 if(source->visited > target->visited)
2105 target->visited = source->visited;
2109 merge_channel(struct chanData *source, struct chanData *target)
2111 merge_users(source, target);
2112 merge_bans(source, target);
2113 merge_data(source, target);
2116 static CHANSERV_FUNC(cmd_merge)
2118 struct userData *target_user;
2119 struct chanNode *target;
2120 char reason[MAXLEN];
2124 /* Make sure the target channel exists and is registered to the user
2125 performing the command. */
2126 if(!(target = GetChannel(argv[1])))
2128 reply("MSG_INVALID_CHANNEL");
2132 if(!target->channel_info)
2134 reply("CSMSG_NOT_REGISTERED", target->name);
2138 if(IsProtected(channel->channel_info))
2140 reply("CSMSG_MERGE_NODELETE");
2144 if(IsSuspended(target->channel_info))
2146 reply("CSMSG_MERGE_SUSPENDED");
2150 if(channel == target)
2152 reply("CSMSG_MERGE_SELF");
2156 target_user = GetChannelUser(target->channel_info, user->handle_info);
2157 if(!target_user || (target_user->access < UL_OWNER))
2159 reply("CSMSG_MERGE_NOT_OWNER");
2163 /* Merge the channel structures and associated data. */
2164 merge_channel(channel->channel_info, target->channel_info);
2165 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2166 unregister_channel(channel->channel_info, reason);
2167 reply("CSMSG_MERGE_SUCCESS", target->name);
2171 static CHANSERV_FUNC(cmd_opchan)
2173 struct mod_chanmode change;
2174 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2176 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2179 channel->channel_info->may_opchan = 0;
2180 mod_chanmode_init(&change);
2182 change.args[0].mode = MODE_CHANOP;
2183 change.args[0].u.member = GetUserMode(channel, chanserv);
2184 mod_chanmode_announce(chanserv, channel, &change);
2185 reply("CSMSG_OPCHAN_DONE", channel->name);
2189 static CHANSERV_FUNC(cmd_adduser)
2191 struct userData *actee;
2192 struct userData *actor;
2193 struct handle_info *handle;
2194 unsigned short access;
2198 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2200 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2204 access = user_level_from_name(argv[2], UL_OWNER);
2207 reply("CSMSG_INVALID_ACCESS", argv[2]);
2211 actor = GetChannelUser(channel->channel_info, user->handle_info);
2212 if(actor->access <= access)
2214 reply("CSMSG_NO_BUMP_ACCESS");
2218 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2221 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2223 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2227 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2228 scan_user_presence(actee, NULL);
2229 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2233 static CHANSERV_FUNC(cmd_clvl)
2235 struct handle_info *handle;
2236 struct userData *victim;
2237 struct userData *actor;
2238 unsigned short new_access;
2239 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2243 actor = GetChannelUser(channel->channel_info, user->handle_info);
2245 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2248 if(handle == user->handle_info && !privileged)
2250 reply("CSMSG_NO_SELF_CLVL");
2254 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2256 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2260 if(actor->access <= victim->access && !privileged)
2262 reply("MSG_USER_OUTRANKED", handle->handle);
2266 new_access = user_level_from_name(argv[2], UL_OWNER);
2270 reply("CSMSG_INVALID_ACCESS", argv[2]);
2274 if(new_access >= actor->access && !privileged)
2276 reply("CSMSG_NO_BUMP_ACCESS");
2280 victim->access = new_access;
2281 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2285 static CHANSERV_FUNC(cmd_deluser)
2287 struct handle_info *handle;
2288 struct userData *victim;
2289 struct userData *actor;
2290 unsigned short access;
2295 actor = GetChannelUser(channel->channel_info, user->handle_info);
2297 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2300 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2302 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2308 access = user_level_from_name(argv[1], UL_OWNER);
2311 reply("CSMSG_INVALID_ACCESS", argv[1]);
2314 if(access != victim->access)
2316 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2322 access = victim->access;
2325 if((actor->access <= victim->access) && !IsHelping(user))
2327 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2331 chan_name = strdup(channel->name);
2332 del_channel_user(victim, 1);
2333 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2339 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2341 struct userData *actor, *uData, *next;
2343 actor = GetChannelUser(channel->channel_info, user->handle_info);
2345 if(min_access > max_access)
2347 reply("CSMSG_BAD_RANGE", min_access, max_access);
2351 if((actor->access <= max_access) && !IsHelping(user))
2353 reply("CSMSG_NO_ACCESS");
2357 for(uData = channel->channel_info->users; uData; uData = next)
2361 if((uData->access >= min_access)
2362 && (uData->access <= max_access)
2363 && match_ircglob(uData->handle->handle, mask))
2364 del_channel_user(uData, 1);
2367 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2371 static CHANSERV_FUNC(cmd_mdelowner)
2373 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2376 static CHANSERV_FUNC(cmd_mdelcoowner)
2378 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2381 static CHANSERV_FUNC(cmd_mdelmaster)
2383 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2386 static CHANSERV_FUNC(cmd_mdelop)
2388 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2391 static CHANSERV_FUNC(cmd_mdelpeon)
2393 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2397 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2399 struct banData *bData, *next;
2400 char interval[INTERVALLEN];
2405 limit = now - duration;
2406 for(bData = channel->channel_info->bans; bData; bData = next)
2410 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2413 del_channel_ban(bData);
2417 intervalString(interval, duration, user->handle_info);
2418 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2423 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2425 struct userData *actor, *uData, *next;
2426 char interval[INTERVALLEN];
2430 actor = GetChannelUser(channel->channel_info, user->handle_info);
2431 if(min_access > max_access)
2433 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2437 if((actor->access <= max_access) && !IsHelping(user))
2439 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2444 limit = now - duration;
2445 for(uData = channel->channel_info->users; uData; uData = next)
2449 if((uData->seen > limit) || uData->present)
2452 if(((uData->access >= min_access) && (uData->access <= max_access))
2453 || (!max_access && (uData->access < actor->access)))
2455 del_channel_user(uData, 1);
2463 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2465 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2469 static CHANSERV_FUNC(cmd_trim)
2471 unsigned long duration;
2472 unsigned short min_level, max_level;
2476 duration = ParseInterval(argv[2]);
2479 reply("CSMSG_CANNOT_TRIM");
2483 if(!irccasecmp(argv[1], "bans"))
2485 cmd_trim_bans(user, channel, duration);
2488 else if(!irccasecmp(argv[1], "users"))
2490 cmd_trim_users(user, channel, 0, 0, duration);
2493 else if(parse_level_range(&min_level, &max_level, argv[1]))
2495 cmd_trim_users(user, channel, min_level, max_level, duration);
2498 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2500 cmd_trim_users(user, channel, min_level, min_level, duration);
2505 reply("CSMSG_INVALID_TRIM", argv[1]);
2510 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2511 to the user. cmd_all takes advantage of this. */
2512 static CHANSERV_FUNC(cmd_up)
2514 struct mod_chanmode change;
2515 struct userData *uData;
2518 mod_chanmode_init(&change);
2520 change.args[0].u.member = GetUserMode(channel, user);
2521 if(!change.args[0].u.member)
2524 reply("MSG_CHANNEL_ABSENT", channel->name);
2528 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2532 reply("CSMSG_GODMODE_UP", argv[0]);
2535 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2537 change.args[0].mode = MODE_CHANOP;
2538 errmsg = "CSMSG_ALREADY_OPPED";
2540 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2542 change.args[0].mode = MODE_VOICE;
2543 errmsg = "CSMSG_ALREADY_VOICED";
2548 reply("CSMSG_NO_ACCESS");
2551 change.args[0].mode &= ~change.args[0].u.member->modes;
2552 if(!change.args[0].mode)
2555 reply(errmsg, channel->name);
2558 modcmd_chanmode_announce(&change);
2562 static CHANSERV_FUNC(cmd_down)
2564 struct mod_chanmode change;
2566 mod_chanmode_init(&change);
2568 change.args[0].u.member = GetUserMode(channel, user);
2569 if(!change.args[0].u.member)
2572 reply("MSG_CHANNEL_ABSENT", channel->name);
2576 if(!change.args[0].u.member->modes)
2579 reply("CSMSG_ALREADY_DOWN", channel->name);
2583 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2584 modcmd_chanmode_announce(&change);
2588 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)
2590 struct userData *cList;
2592 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2594 if(IsSuspended(cList->channel)
2595 || IsUserSuspended(cList)
2596 || !GetUserMode(cList->channel->channel, user))
2599 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2605 static CHANSERV_FUNC(cmd_upall)
2607 return cmd_all(CSFUNC_ARGS, cmd_up);
2610 static CHANSERV_FUNC(cmd_downall)
2612 return cmd_all(CSFUNC_ARGS, cmd_down);
2615 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2616 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2619 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)
2621 unsigned int ii, valid;
2622 struct userNode *victim;
2623 struct mod_chanmode *change;
2625 change = mod_chanmode_alloc(argc - 1);
2627 for(ii=valid=0; ++ii < argc; )
2629 if(!(victim = GetUserH(argv[ii])))
2631 change->args[valid].mode = mode;
2632 change->args[valid].u.member = GetUserMode(channel, victim);
2633 if(!change->args[valid].u.member)
2635 if(validate && !validate(user, channel, victim))
2640 change->argc = valid;
2641 if(valid < (argc-1))
2642 reply("CSMSG_PROCESS_FAILED");
2645 modcmd_chanmode_announce(change);
2646 reply(action, channel->name);
2648 mod_chanmode_free(change);
2652 static CHANSERV_FUNC(cmd_op)
2654 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2657 static CHANSERV_FUNC(cmd_deop)
2659 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2662 static CHANSERV_FUNC(cmd_voice)
2664 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2667 static CHANSERV_FUNC(cmd_devoice)
2669 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2673 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2679 for(ii=0; ii<channel->members.used; ii++)
2681 struct modeNode *mn = channel->members.list[ii];
2683 if(IsService(mn->user))
2686 if(!user_matches_glob(mn->user, ban, 1))
2689 if(protect_user(mn->user, user, channel->channel_info))
2693 victims[(*victimCount)++] = mn;
2699 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2701 struct userNode *victim;
2702 struct modeNode **victims;
2703 unsigned int offset, n, victimCount, duration = 0;
2704 char *reason = "Bye.", *ban, *name;
2705 char interval[INTERVALLEN];
2707 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2708 REQUIRE_PARAMS(offset);
2711 reason = unsplit_string(argv + offset, argc - offset, NULL);
2712 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2714 /* Truncate the reason to a length of TOPICLEN, as
2715 the ircd does; however, leave room for an ellipsis
2716 and the kicker's nick. */
2717 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2721 if((victim = GetUserH(argv[1])))
2723 victims = alloca(sizeof(victims[0]));
2724 victims[0] = GetUserMode(channel, victim);
2725 /* XXX: The comparison with ACTION_KICK is just because all
2726 * other actions can work on users outside the channel, and we
2727 * want to allow those (e.g. unbans) in that case. If we add
2728 * some other ejection action for in-channel users, change
2730 victimCount = victims[0] ? 1 : 0;
2732 if(IsService(victim))
2734 reply("MSG_SERVICE_IMMUNE", victim->nick);
2738 if((action == ACTION_KICK) && !victimCount)
2740 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2744 if(protect_user(victim, user, channel->channel_info))
2746 reply("CSMSG_USER_PROTECTED", victim->nick);
2750 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2751 name = victim->nick;
2755 if(!is_ircmask(argv[1]))
2757 reply("MSG_NICK_UNKNOWN", argv[1]);
2761 victims = alloca(sizeof(victims[0]) * channel->members.used);
2763 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2765 reply("CSMSG_MASK_PROTECTED", argv[1]);
2769 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2771 reply("CSMSG_LAME_MASK", argv[1]);
2775 if((action == ACTION_KICK) && (victimCount == 0))
2777 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2781 name = ban = strdup(argv[1]);
2784 /* Truncate the ban in place if necessary; we must ensure
2785 that 'ban' is a valid ban mask before sanitizing it. */
2786 sanitize_ircmask(ban);
2788 if(action & ACTION_ADD_BAN)
2790 struct banData *bData, *next;
2792 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2794 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2799 if(action & ACTION_ADD_TIMED_BAN)
2801 duration = ParseInterval(argv[2]);
2805 reply("CSMSG_DURATION_TOO_LOW");
2809 else if(duration > (86400 * 365 * 2))
2811 reply("CSMSG_DURATION_TOO_HIGH");
2817 for(bData = channel->channel_info->bans; bData; bData = next)
2819 if(match_ircglobs(bData->mask, ban))
2821 int exact = !irccasecmp(bData->mask, ban);
2823 /* The ban is redundant; there is already a ban
2824 with the same effect in place. */
2828 free(bData->reason);
2829 bData->reason = strdup(reason);
2830 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2832 reply("CSMSG_REASON_CHANGE", ban);
2836 if(exact && bData->expires)
2840 /* If the ban matches an existing one exactly,
2841 extend the expiration time if the provided
2842 duration is longer. */
2843 if(duration && ((time_t)(now + duration) > bData->expires))
2845 bData->expires = now + duration;
2856 /* Delete the expiration timeq entry and
2857 requeue if necessary. */
2858 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2861 timeq_add(bData->expires, expire_ban, bData);
2865 /* automated kickban */
2868 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2870 reply("CSMSG_BAN_ADDED", name, channel->name);
2876 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2883 if(match_ircglobs(ban, bData->mask))
2885 /* The ban we are adding makes previously existing
2886 bans redundant; silently remove them. */
2887 del_channel_ban(bData);
2891 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);
2893 name = ban = strdup(bData->mask);
2897 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2899 extern const char *hidden_host_suffix;
2900 const char *old_name = chanserv_conf.old_ban_names->list[n];
2902 unsigned int l1, l2;
2905 l2 = strlen(old_name);
2908 if(irccasecmp(ban + l1 - l2, old_name))
2910 new_mask = malloc(MAXLEN);
2911 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2913 name = ban = new_mask;
2918 if(action & ACTION_BAN)
2920 unsigned int exists;
2921 struct mod_chanmode *change;
2923 if(channel->banlist.used >= MAXBANS)
2926 reply("CSMSG_BANLIST_FULL", channel->name);
2931 exists = ChannelBanExists(channel, ban);
2932 change = mod_chanmode_alloc(victimCount + 1);
2933 for(n = 0; n < victimCount; ++n)
2935 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2936 change->args[n].u.member = victims[n];
2940 change->args[n].mode = MODE_BAN;
2941 change->args[n++].u.hostmask = ban;
2945 modcmd_chanmode_announce(change);
2947 mod_chanmode_announce(chanserv, channel, change);
2948 mod_chanmode_free(change);
2950 if(exists && (action == ACTION_BAN))
2953 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2959 if(action & ACTION_KICK)
2961 char kick_reason[MAXLEN];
2962 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2964 for(n = 0; n < victimCount; n++)
2965 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2970 /* No response, since it was automated. */
2972 else if(action & ACTION_ADD_BAN)
2975 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2977 reply("CSMSG_BAN_ADDED", name, channel->name);
2979 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2980 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2981 else if(action & ACTION_BAN)
2982 reply("CSMSG_BAN_DONE", name, channel->name);
2983 else if(action & ACTION_KICK && victimCount)
2984 reply("CSMSG_KICK_DONE", name, channel->name);
2990 static CHANSERV_FUNC(cmd_kickban)
2992 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2995 static CHANSERV_FUNC(cmd_kick)
2997 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3000 static CHANSERV_FUNC(cmd_ban)
3002 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3005 static CHANSERV_FUNC(cmd_addban)
3007 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3010 static CHANSERV_FUNC(cmd_addtimedban)
3012 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3015 static struct mod_chanmode *
3016 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3018 struct mod_chanmode *change;
3019 unsigned char *match;
3020 unsigned int ii, count;
3022 match = alloca(bans->used);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3027 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3034 for(ii = count = 0; ii < bans->used; ++ii)
3036 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3043 change = mod_chanmode_alloc(count);
3044 for(ii = count = 0; ii < bans->used; ++ii)
3048 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3049 change->args[count++].u.hostmask = bans->list[ii]->ban;
3055 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3057 struct userNode *actee;
3063 /* may want to allow a comma delimited list of users... */
3064 if(!(actee = GetUserH(argv[1])))
3066 if(!is_ircmask(argv[1]))
3068 reply("MSG_NICK_UNKNOWN", argv[1]);
3072 mask = strdup(argv[1]);
3075 /* We don't sanitize the mask here because ircu
3077 if(action & ACTION_UNBAN)
3079 struct mod_chanmode *change;
3080 change = find_matching_bans(&channel->banlist, actee, mask);
3083 modcmd_chanmode_announce(change);
3084 mod_chanmode_free(change);
3089 if(action & ACTION_DEL_BAN)
3091 struct banData *ban, *next;
3093 ban = channel->channel_info->bans;
3097 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3100 for( ; ban && !match_ircglobs(mask, ban->mask);
3105 del_channel_ban(ban);
3112 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3114 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3120 static CHANSERV_FUNC(cmd_unban)
3122 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3125 static CHANSERV_FUNC(cmd_delban)
3127 /* it doesn't necessarily have to remove the channel ban - may want
3128 to make that an option. */
3129 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3132 static CHANSERV_FUNC(cmd_unbanme)
3134 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3135 long flags = ACTION_UNBAN;
3137 /* remove permanent bans if the user has the proper access. */
3138 if(uData->access >= UL_MASTER)
3139 flags |= ACTION_DEL_BAN;
3141 argv[1] = user->nick;
3142 return unban_user(user, channel, 2, argv, cmd, flags);
3145 static CHANSERV_FUNC(cmd_unbanall)
3147 struct mod_chanmode *change;
3150 if(!channel->banlist.used)
3152 reply("CSMSG_NO_BANS", channel->name);
3156 change = mod_chanmode_alloc(channel->banlist.used);
3157 for(ii=0; ii<channel->banlist.used; ii++)
3159 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3160 change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
3162 modcmd_chanmode_announce(change);
3163 mod_chanmode_free(change);
3164 reply("CSMSG_BANS_REMOVED", channel->name);
3168 static CHANSERV_FUNC(cmd_open)
3170 struct mod_chanmode *change;
3172 change = find_matching_bans(&channel->banlist, user, NULL);
3174 change = mod_chanmode_alloc(0);
3175 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3176 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3177 && channel->channel_info->modes.modes_set)
3178 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3179 modcmd_chanmode_announce(change);
3180 reply("CSMSG_CHANNEL_OPENED", channel->name);
3181 mod_chanmode_free(change);
3185 static CHANSERV_FUNC(cmd_myaccess)
3187 static struct string_buffer sbuf;
3188 struct handle_info *target_handle;
3189 struct userData *uData;
3192 target_handle = user->handle_info;
3193 else if(!IsHelping(user))
3195 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3198 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3201 if(!target_handle->channels)
3203 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3207 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3208 for(uData = target_handle->channels; uData; uData = uData->u_next)
3210 struct chanData *cData = uData->channel;
3212 if(uData->access > UL_OWNER)
3214 if(IsProtected(cData)
3215 && (target_handle != user->handle_info)
3216 && !GetTrueChannelAccess(cData, user->handle_info))
3219 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3220 if(uData->flags != USER_AUTO_OP)
3221 string_buffer_append(&sbuf, ',');
3222 if(IsUserSuspended(uData))
3223 string_buffer_append(&sbuf, 's');
3224 if(IsUserAutoOp(uData))
3226 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3227 string_buffer_append(&sbuf, 'o');
3228 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3229 string_buffer_append(&sbuf, 'v');
3231 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3232 string_buffer_append(&sbuf, 'i');
3234 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3236 string_buffer_append_string(&sbuf, ")]");
3237 string_buffer_append(&sbuf, '\0');
3238 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3244 static CHANSERV_FUNC(cmd_access)
3246 struct userNode *target;
3247 struct handle_info *target_handle;
3248 struct userData *uData;
3250 char prefix[MAXLEN];
3255 target_handle = target->handle_info;
3257 else if((target = GetUserH(argv[1])))
3259 target_handle = target->handle_info;
3261 else if(argv[1][0] == '*')
3263 if(!(target_handle = get_handle_info(argv[1]+1)))
3265 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3271 reply("MSG_NICK_UNKNOWN", argv[1]);
3275 assert(target || target_handle);
3277 if(target == chanserv)
3279 reply("CSMSG_IS_CHANSERV");
3287 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3292 reply("MSG_USER_AUTHENTICATE", target->nick);
3295 reply("MSG_AUTHENTICATE");
3301 const char *epithet = NULL, *type = NULL;
3304 epithet = chanserv_conf.irc_operator_epithet;
3307 else if(IsNetworkHelper(target))
3309 epithet = chanserv_conf.network_helper_epithet;
3310 type = "network helper";
3312 else if(IsSupportHelper(target))
3314 epithet = chanserv_conf.support_helper_epithet;
3315 type = "support helper";
3319 if(target_handle->epithet)
3320 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3322 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3324 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3328 sprintf(prefix, "%s", target_handle->handle);
3331 if(!channel->channel_info)
3333 reply("CSMSG_NOT_REGISTERED", channel->name);
3337 helping = HANDLE_FLAGGED(target_handle, HELPING)
3338 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3339 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3341 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3342 /* To prevent possible information leaks, only show infolines
3343 * if the requestor is in the channel or it's their own
3345 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3347 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3349 /* Likewise, only say it's suspended if the user has active
3350 * access in that channel or it's their own entry. */
3351 if(IsUserSuspended(uData)
3352 && (GetChannelUser(channel->channel_info, user->handle_info)
3353 || (user->handle_info == uData->handle)))
3355 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3360 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3367 zoot_list(struct listData *list)
3369 struct userData *uData;
3370 unsigned int start, curr, highest, lowest;
3371 struct helpfile_table tmp_table;
3372 const char **temp, *msg;
3374 if(list->table.length == 1)
3377 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3379 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3380 msg = user_find_message(list->user, "MSG_NONE");
3381 send_message_type(4, list->user, list->bot, " %s", msg);
3383 tmp_table.width = list->table.width;
3384 tmp_table.flags = list->table.flags;
3385 list->table.contents[0][0] = " ";
3386 highest = list->highest;
3387 if(list->lowest != 0)
3388 lowest = list->lowest;
3389 else if(highest < 100)
3392 lowest = highest - 100;
3393 for(start = curr = 1; curr < list->table.length; )
3395 uData = list->users[curr-1];
3396 list->table.contents[curr++][0] = " ";
3397 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3400 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3402 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3403 temp = list->table.contents[--start];
3404 list->table.contents[start] = list->table.contents[0];
3405 tmp_table.contents = list->table.contents + start;
3406 tmp_table.length = curr - start;
3407 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3408 list->table.contents[start] = temp;
3410 highest = lowest - 1;
3411 lowest = (highest < 100) ? 0 : (highest - 99);
3417 def_list(struct listData *list)
3421 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3423 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3424 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3425 if(list->table.length == 1)
3427 msg = user_find_message(list->user, "MSG_NONE");
3428 send_message_type(4, list->user, list->bot, " %s", msg);
3433 userData_access_comp(const void *arg_a, const void *arg_b)
3435 const struct userData *a = *(struct userData**)arg_a;
3436 const struct userData *b = *(struct userData**)arg_b;
3438 if(a->access != b->access)
3439 res = b->access - a->access;
3441 res = irccasecmp(a->handle->handle, b->handle->handle);
3446 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3448 void (*send_list)(struct listData *);
3449 struct userData *uData;
3450 struct listData lData;
3451 unsigned int matches;
3455 lData.bot = cmd->parent->bot;
3456 lData.channel = channel;
3457 lData.lowest = lowest;
3458 lData.highest = highest;
3459 lData.search = (argc > 1) ? argv[1] : NULL;
3460 send_list = def_list;
3461 (void)zoot_list; /* since it doesn't show user levels */
3463 if(user->handle_info)
3465 switch(user->handle_info->userlist_style)
3467 case HI_STYLE_DEF: send_list = def_list; break;
3468 case HI_STYLE_ZOOT: send_list = def_list; break;
3472 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3474 for(uData = channel->channel_info->users; uData; uData = uData->next)
3476 if((uData->access < lowest)
3477 || (uData->access > highest)
3478 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3480 lData.users[matches++] = uData;
3482 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3484 lData.table.length = matches+1;
3485 lData.table.width = 4;
3486 lData.table.flags = TABLE_NO_FREE;
3487 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3488 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3489 lData.table.contents[0] = ary;
3492 ary[2] = "Last Seen";
3494 for(matches = 1; matches < lData.table.length; ++matches)
3496 struct userData *uData = lData.users[matches-1];
3497 char seen[INTERVALLEN];
3499 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3500 lData.table.contents[matches] = ary;
3501 ary[0] = strtab(uData->access);
3502 ary[1] = uData->handle->handle;
3505 else if(!uData->seen)
3508 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3509 ary[2] = strdup(ary[2]);
3510 if(IsUserSuspended(uData))
3511 ary[3] = "Suspended";
3512 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3513 ary[3] = "Vacation";
3518 for(matches = 1; matches < lData.table.length; ++matches)
3520 free((char*)lData.table.contents[matches][2]);
3521 free(lData.table.contents[matches]);
3523 free(lData.table.contents[0]);
3524 free(lData.table.contents);
3528 static CHANSERV_FUNC(cmd_users)
3530 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3533 static CHANSERV_FUNC(cmd_wlist)
3535 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3538 static CHANSERV_FUNC(cmd_clist)
3540 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3543 static CHANSERV_FUNC(cmd_mlist)
3545 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3548 static CHANSERV_FUNC(cmd_olist)
3550 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3553 static CHANSERV_FUNC(cmd_plist)
3555 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3558 static CHANSERV_FUNC(cmd_bans)
3560 struct helpfile_table tbl;
3561 unsigned int matches = 0, timed = 0, ii;
3562 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3563 const char *msg_never, *triggered, *expires;
3564 struct banData *ban, **bans;
3571 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3573 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3575 if(search && !match_ircglobs(search, ban->mask))
3577 bans[matches++] = ban;
3582 tbl.length = matches + 1;
3583 tbl.width = 4 + timed;
3585 tbl.flags = TABLE_NO_FREE;
3586 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3587 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3588 tbl.contents[0][0] = "Mask";
3589 tbl.contents[0][1] = "Set By";
3590 tbl.contents[0][2] = "Triggered";
3593 tbl.contents[0][3] = "Expires";
3594 tbl.contents[0][4] = "Reason";
3597 tbl.contents[0][3] = "Reason";
3600 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3602 free(tbl.contents[0]);
3607 msg_never = user_find_message(user, "MSG_NEVER");
3608 for(ii = 0; ii < matches; )
3614 else if(ban->expires)
3615 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3617 expires = msg_never;
3620 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3622 triggered = msg_never;
3624 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3625 tbl.contents[ii][0] = ban->mask;
3626 tbl.contents[ii][1] = ban->owner;
3627 tbl.contents[ii][2] = strdup(triggered);
3630 tbl.contents[ii][3] = strdup(expires);
3631 tbl.contents[ii][4] = ban->reason;
3634 tbl.contents[ii][3] = ban->reason;
3636 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3637 reply("MSG_MATCH_COUNT", matches);
3638 for(ii = 1; ii < tbl.length; ++ii)
3640 free((char*)tbl.contents[ii][2]);
3642 free((char*)tbl.contents[ii][3]);
3643 free(tbl.contents[ii]);
3645 free(tbl.contents[0]);
3651 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3653 struct chanData *cData = channel->channel_info;
3654 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3656 if(cData->topic_mask)
3657 return !match_ircglob(new_topic, cData->topic_mask);
3658 else if(cData->topic)
3659 return irccasecmp(new_topic, cData->topic);
3664 static CHANSERV_FUNC(cmd_topic)
3666 struct chanData *cData;
3669 cData = channel->channel_info;
3674 SetChannelTopic(channel, chanserv, cData->topic, 1);
3675 reply("CSMSG_TOPIC_SET", cData->topic);
3679 reply("CSMSG_NO_TOPIC", channel->name);
3683 topic = unsplit_string(argv + 1, argc - 1, NULL);
3684 /* If they say "!topic *", use an empty topic. */
3685 if((topic[0] == '*') && (topic[1] == 0))
3687 if(bad_topic(channel, user, topic))
3689 char *topic_mask = cData->topic_mask;
3692 char new_topic[TOPICLEN+1], tchar;
3693 int pos=0, starpos=-1, dpos=0, len;
3695 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3702 len = strlen(topic);
3703 if((dpos + len) > TOPICLEN)
3704 len = TOPICLEN + 1 - dpos;
3705 memcpy(new_topic+dpos, topic, len);
3709 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3710 default: new_topic[dpos++] = tchar; break;
3713 if((dpos > TOPICLEN) || tchar)
3716 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3717 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3720 new_topic[dpos] = 0;
3721 SetChannelTopic(channel, chanserv, new_topic, 1);
3723 reply("CSMSG_TOPIC_LOCKED", channel->name);
3728 SetChannelTopic(channel, chanserv, topic, 1);
3730 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3732 /* Grab the topic and save it as the default topic. */
3734 cData->topic = strdup(channel->topic);
3740 static CHANSERV_FUNC(cmd_mode)
3742 struct mod_chanmode *change;
3746 change = &channel->channel_info->modes;
3747 if(change->modes_set || change->modes_clear) {
3748 modcmd_chanmode_announce(change);
3749 reply("CSMSG_DEFAULTED_MODES", channel->name);
3751 reply("CSMSG_NO_MODES", channel->name);
3755 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3758 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3762 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3763 && mode_lock_violated(&channel->channel_info->modes, change))
3766 mod_chanmode_format(&channel->channel_info->modes, modes);
3767 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3771 modcmd_chanmode_announce(change);
3772 mod_chanmode_free(change);
3773 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3777 static CHANSERV_FUNC(cmd_invite)
3779 struct userData *uData;
3780 struct userNode *invite;
3782 uData = GetChannelUser(channel->channel_info, user->handle_info);
3786 if(!(invite = GetUserH(argv[1])))
3788 reply("MSG_NICK_UNKNOWN", argv[1]);
3795 if(GetUserMode(channel, invite))
3797 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3805 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3806 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3809 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3811 irc_invite(chanserv, invite, channel);
3813 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3818 static CHANSERV_FUNC(cmd_inviteme)
3820 if(GetUserMode(channel, user))
3822 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3825 if(channel->channel_info
3826 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3828 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3831 irc_invite(cmd->parent->bot, user, channel);
3836 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3839 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3841 /* We display things based on two dimensions:
3842 * - Issue time: present or absent
3843 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3844 * (in order of precedence, so something both expired and revoked
3845 * only counts as revoked)
3847 combo = (suspended->issued ? 4 : 0)
3848 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3850 case 0: /* no issue time, indefinite expiration */
3851 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3853 case 1: /* no issue time, expires in future */
3854 intervalString(buf1, suspended->expires-now, user->handle_info);
3855 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3857 case 2: /* no issue time, expired */
3858 intervalString(buf1, now-suspended->expires, user->handle_info);
3859 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3861 case 3: /* no issue time, revoked */
3862 intervalString(buf1, now-suspended->revoked, user->handle_info);
3863 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3865 case 4: /* issue time set, indefinite expiration */
3866 intervalString(buf1, now-suspended->issued, user->handle_info);
3867 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3869 case 5: /* issue time set, expires in future */
3870 intervalString(buf1, now-suspended->issued, user->handle_info);
3871 intervalString(buf2, suspended->expires-now, user->handle_info);
3872 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3874 case 6: /* issue time set, expired */
3875 intervalString(buf1, now-suspended->issued, user->handle_info);
3876 intervalString(buf2, now-suspended->expires, user->handle_info);
3877 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3879 case 7: /* issue time set, revoked */
3880 intervalString(buf1, now-suspended->issued, user->handle_info);
3881 intervalString(buf2, now-suspended->revoked, user->handle_info);
3882 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3885 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3890 static CHANSERV_FUNC(cmd_info)
3892 char modes[MAXLEN], buffer[INTERVALLEN];
3893 struct userData *uData, *owner;
3894 struct chanData *cData;
3895 struct do_not_register *dnr;
3900 cData = channel->channel_info;
3901 reply("CSMSG_CHANNEL_INFO", channel->name);
3903 uData = GetChannelUser(cData, user->handle_info);
3904 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3906 mod_chanmode_format(&cData->modes, modes);
3907 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3908 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3911 for(it = dict_first(cData->notes); it; it = iter_next(it))
3915 note = iter_data(it);
3916 if(!note_type_visible_to_user(cData, note->type, user))
3919 padding = PADLEN - 1 - strlen(iter_key(it));
3920 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3923 reply("CSMSG_CHANNEL_MAX", cData->max);
3924 for(owner = cData->users; owner; owner = owner->next)
3925 if(owner->access == UL_OWNER)
3926 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3927 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3928 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3929 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3930 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3932 privileged = IsStaff(user);
3933 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3934 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3936 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3937 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3939 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3941 struct suspended *suspended;
3942 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3943 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3944 show_suspension_info(cmd, user, suspended);
3946 else if(IsSuspended(cData))
3948 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3949 show_suspension_info(cmd, user, cData->suspended);
3954 static CHANSERV_FUNC(cmd_netinfo)
3956 extern time_t boot_time;
3957 extern unsigned long burst_length;
3958 char interval[INTERVALLEN];
3960 reply("CSMSG_NETWORK_INFO");
3961 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3962 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3963 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3964 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3965 reply("CSMSG_NETWORK_BANS", banCount);
3966 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3967 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3968 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3973 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3975 struct helpfile_table table;
3977 struct userNode *user;
3982 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3983 table.contents = alloca(list->used*sizeof(*table.contents));
3984 for(nn=0; nn<list->used; nn++)
3986 user = list->list[nn];
3987 if(user->modes & skip_flags)
3991 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3994 nick = alloca(strlen(user->nick)+3);
3995 sprintf(nick, "(%s)", user->nick);
3999 table.contents[table.length][0] = nick;
4002 table_send(chanserv, to->nick, 0, NULL, table);
4005 static CHANSERV_FUNC(cmd_ircops)
4007 reply("CSMSG_STAFF_OPERS");
4008 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4012 static CHANSERV_FUNC(cmd_helpers)
4014 reply("CSMSG_STAFF_HELPERS");
4015 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4019 static CHANSERV_FUNC(cmd_staff)
4021 reply("CSMSG_NETWORK_STAFF");
4022 cmd_ircops(CSFUNC_ARGS);
4023 cmd_helpers(CSFUNC_ARGS);
4027 static CHANSERV_FUNC(cmd_peek)
4029 struct modeNode *mn;
4030 char modes[MODELEN];
4032 struct helpfile_table table;
4034 irc_make_chanmode(channel, modes);
4036 reply("CSMSG_PEEK_INFO", channel->name);
4037 reply("CSMSG_PEEK_TOPIC", channel->topic);
4038 reply("CSMSG_PEEK_MODES", modes);
4039 reply("CSMSG_PEEK_USERS", channel->members.used);
4043 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4044 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4045 for(n = 0; n < channel->members.used; n++)
4047 mn = channel->members.list[n];
4048 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4050 table.contents[table.length] = alloca(sizeof(**table.contents));
4051 table.contents[table.length][0] = mn->user->nick;
4056 reply("CSMSG_PEEK_OPS");
4057 table_send(chanserv, user->nick, 0, NULL, table);
4060 reply("CSMSG_PEEK_NO_OPS");
4064 static MODCMD_FUNC(cmd_wipeinfo)
4066 struct handle_info *victim;
4067 struct userData *ud, *actor;
4070 actor = GetChannelUser(channel->channel_info, user->handle_info);
4071 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4073 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4075 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4078 if((ud->access >= actor->access) && (ud != actor))
4080 reply("MSG_USER_OUTRANKED", victim->handle);
4086 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4090 static CHANSERV_FUNC(cmd_resync)
4092 struct mod_chanmode *changes;
4093 struct chanData *cData = channel->channel_info;
4094 unsigned int ii, used;
4096 changes = mod_chanmode_alloc(channel->members.used * 2);
4097 for(ii = used = 0; ii < channel->members.used; ++ii)
4099 struct modeNode *mn = channel->members.list[ii];
4100 struct userData *uData;
4102 if(IsService(mn->user))
4105 uData = GetChannelAccess(cData, mn->user->handle_info);
4106 if(!cData->lvlOpts[lvlGiveOps]
4107 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4109 if(!(mn->modes & MODE_CHANOP))
4111 changes->args[used].mode = MODE_CHANOP;
4112 changes->args[used++].u.member = mn;
4115 else if(!cData->lvlOpts[lvlGiveVoice]
4116 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4118 if(mn->modes & MODE_CHANOP)
4120 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4121 changes->args[used++].u.member = mn;
4123 if(!(mn->modes & MODE_VOICE))
4125 changes->args[used].mode = MODE_VOICE;
4126 changes->args[used++].u.member = mn;
4133 changes->args[used].mode = MODE_REMOVE | mn->modes;
4134 changes->args[used++].u.member = mn;
4138 changes->argc = used;
4139 modcmd_chanmode_announce(changes);
4140 mod_chanmode_free(changes);
4141 reply("CSMSG_RESYNCED_USERS", channel->name);
4145 static CHANSERV_FUNC(cmd_seen)
4147 struct userData *uData;
4148 struct handle_info *handle;
4149 char seen[INTERVALLEN];
4153 if(!irccasecmp(argv[1], chanserv->nick))
4155 reply("CSMSG_IS_CHANSERV");
4159 if(!(handle = get_handle_info(argv[1])))
4161 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4165 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4167 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4172 reply("CSMSG_USER_PRESENT", handle->handle);
4173 else if(uData->seen)
4174 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4176 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4178 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4179 reply("CSMSG_USER_VACATION", handle->handle);
4184 static MODCMD_FUNC(cmd_names)
4186 struct userNode *targ;
4187 struct userData *targData;
4188 unsigned int ii, pos;
4191 for(ii=pos=0; ii<channel->members.used; ++ii)
4193 targ = channel->members.list[ii]->user;
4194 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4197 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4200 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4204 if(IsUserSuspended(targData))
4206 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4209 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4210 reply("CSMSG_END_NAMES", channel->name);
4215 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4217 switch(ntype->visible_type)
4219 case NOTE_VIS_ALL: return 1;
4220 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4221 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4226 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4228 struct userData *uData;
4230 switch(ntype->set_access_type)
4232 case NOTE_SET_CHANNEL_ACCESS:
4233 if(!user->handle_info)
4235 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4237 return uData->access >= ntype->set_access.min_ulevel;
4238 case NOTE_SET_CHANNEL_SETTER:
4239 return check_user_level(channel, user, lvlSetters, 1, 0);
4240 case NOTE_SET_PRIVILEGED: default:
4241 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4245 static CHANSERV_FUNC(cmd_note)
4247 struct chanData *cData;
4249 struct note_type *ntype;
4251 cData = channel->channel_info;
4254 reply("CSMSG_NOT_REGISTERED", channel->name);
4258 /* If no arguments, show all visible notes for the channel. */
4264 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4266 note = iter_data(it);
4267 if(!note_type_visible_to_user(cData, note->type, user))
4270 reply("CSMSG_NOTELIST_HEADER", channel->name);
4271 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4274 reply("CSMSG_NOTELIST_END", channel->name);
4276 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4278 /* If one argument, show the named note. */
4281 if((note = dict_find(cData->notes, argv[1], NULL))
4282 && note_type_visible_to_user(cData, note->type, user))
4284 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4286 else if((ntype = dict_find(note_types, argv[1], NULL))
4287 && note_type_visible_to_user(NULL, ntype, user))
4289 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4294 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4298 /* Assume they're trying to set a note. */
4302 ntype = dict_find(note_types, argv[1], NULL);
4305 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4308 else if(note_type_settable_by_user(channel, ntype, user))
4310 note_text = unsplit_string(argv+2, argc-2, NULL);
4311 if((note = dict_find(cData->notes, argv[1], NULL)))
4312 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4313 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4314 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4316 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4318 /* The note is viewable to staff only, so return 0
4319 to keep the invocation from getting logged (or
4320 regular users can see it in !events). */
4326 reply("CSMSG_NO_ACCESS");
4333 static CHANSERV_FUNC(cmd_delnote)
4338 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4339 || !note_type_settable_by_user(channel, note->type, user))
4341 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4344 dict_remove(channel->channel_info->notes, note->type->name);
4345 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4349 static CHANSERV_FUNC(cmd_events)
4351 struct logSearch discrim;
4352 struct logReport report;
4353 unsigned int matches, limit;
4355 limit = (argc > 1) ? atoi(argv[1]) : 10;
4356 if(limit < 1 || limit > 200)
4359 memset(&discrim, 0, sizeof(discrim));
4360 discrim.masks.bot = chanserv;
4361 discrim.masks.channel_name = channel->name;
4363 discrim.masks.command = argv[2];
4364 discrim.limit = limit;
4365 discrim.max_time = INT_MAX;
4366 discrim.severities = 1 << LOG_COMMAND;
4367 report.reporter = chanserv;
4369 reply("CSMSG_EVENT_SEARCH_RESULTS");
4370 matches = log_entry_search(&discrim, log_report_entry, &report);
4372 reply("MSG_MATCH_COUNT", matches);
4374 reply("MSG_NO_MATCHES");
4378 static CHANSERV_FUNC(cmd_say)
4384 msg = unsplit_string(argv + 1, argc - 1, NULL);
4385 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4387 else if(GetUserH(argv[1]))
4390 msg = unsplit_string(argv + 2, argc - 2, NULL);
4391 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4395 reply("MSG_NOT_TARGET_NAME");
4401 static CHANSERV_FUNC(cmd_emote)
4407 /* CTCP is so annoying. */
4408 msg = unsplit_string(argv + 1, argc - 1, NULL);
4409 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4411 else if(GetUserH(argv[1]))
4413 msg = unsplit_string(argv + 2, argc - 2, NULL);
4414 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4418 reply("MSG_NOT_TARGET_NAME");
4424 struct channelList *
4425 chanserv_support_channels(void)
4427 return &chanserv_conf.support_channels;
4430 static CHANSERV_FUNC(cmd_expire)
4432 int channel_count = registered_channels;
4433 expire_channels(NULL);
4434 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4439 chanserv_expire_suspension(void *data)
4441 struct suspended *suspended = data;
4442 struct chanNode *channel;
4443 struct mod_chanmode change;
4445 if(!suspended->expires || (now < suspended->expires))
4446 suspended->revoked = now;
4447 channel = suspended->cData->channel;
4448 suspended->cData->channel = channel;
4449 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4450 mod_chanmode_init(&change);
4452 change.args[0].mode = MODE_CHANOP;
4453 change.args[0].u.member = AddChannelUser(chanserv, channel);
4454 mod_chanmode_announce(chanserv, channel, &change);
4457 static CHANSERV_FUNC(cmd_csuspend)
4459 struct suspended *suspended;
4460 char reason[MAXLEN];
4461 time_t expiry, duration;
4462 struct userData *uData;
4466 if(IsProtected(channel->channel_info))
4468 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4472 if(argv[1][0] == '!')
4474 else if(IsSuspended(channel->channel_info))
4476 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4477 show_suspension_info(cmd, user, channel->channel_info->suspended);
4481 if(!strcmp(argv[1], "0"))
4483 else if((duration = ParseInterval(argv[1])))
4484 expiry = now + duration;
4487 reply("MSG_INVALID_DURATION", argv[1]);
4491 unsplit_string(argv + 2, argc - 2, reason);
4493 suspended = calloc(1, sizeof(*suspended));
4494 suspended->revoked = 0;
4495 suspended->issued = now;
4496 suspended->suspender = strdup(user->handle_info->handle);
4497 suspended->expires = expiry;
4498 suspended->reason = strdup(reason);
4499 suspended->cData = channel->channel_info;
4500 suspended->previous = suspended->cData->suspended;
4501 suspended->cData->suspended = suspended;
4503 if(suspended->expires)
4504 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4506 if(IsSuspended(channel->channel_info))
4508 suspended->previous->revoked = now;
4509 if(suspended->previous->expires)
4510 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4511 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4512 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4516 /* Mark all users in channel as absent. */
4517 for(uData = channel->channel_info->users; uData; uData = uData->next)
4526 /* Mark the channel as suspended, then part. */
4527 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4528 DelChannelUser(chanserv, channel, suspended->reason, 0);
4529 reply("CSMSG_SUSPENDED", channel->name);
4530 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4531 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4536 static CHANSERV_FUNC(cmd_cunsuspend)
4538 struct suspended *suspended;
4539 char message[MAXLEN];
4541 if(!IsSuspended(channel->channel_info))
4543 reply("CSMSG_NOT_SUSPENDED", channel->name);
4547 suspended = channel->channel_info->suspended;
4549 /* Expire the suspension and join ChanServ to the channel. */
4550 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4551 chanserv_expire_suspension(suspended);
4552 reply("CSMSG_UNSUSPENDED", channel->name);
4553 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4554 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4558 typedef struct chanservSearch
4566 unsigned long flags;
4570 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4573 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4578 search = malloc(sizeof(struct chanservSearch));
4579 memset(search, 0, sizeof(*search));
4582 for(i = 0; i < argc; i++)
4584 /* Assume all criteria require arguments. */
4587 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4591 if(!irccasecmp(argv[i], "name"))
4592 search->name = argv[++i];
4593 else if(!irccasecmp(argv[i], "registrar"))
4594 search->registrar = argv[++i];
4595 else if(!irccasecmp(argv[i], "unvisited"))
4596 search->unvisited = ParseInterval(argv[++i]);
4597 else if(!irccasecmp(argv[i], "registered"))
4598 search->registered = ParseInterval(argv[++i]);
4599 else if(!irccasecmp(argv[i], "flags"))
4602 if(!irccasecmp(argv[i], "nodelete"))
4603 search->flags |= CHANNEL_NODELETE;
4604 else if(!irccasecmp(argv[i], "suspended"))
4605 search->flags |= CHANNEL_SUSPENDED;
4608 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4612 else if(!irccasecmp(argv[i], "limit"))
4613 search->limit = strtoul(argv[++i], NULL, 10);
4616 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4621 if(search->name && !strcmp(search->name, "*"))
4623 if(search->registrar && !strcmp(search->registrar, "*"))
4624 search->registrar = 0;
4633 chanserv_channel_match(struct chanData *channel, search_t search)
4635 const char *name = channel->channel->name;
4636 if((search->name && !match_ircglob(name, search->name)) ||
4637 (search->registrar && !channel->registrar) ||
4638 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4639 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4640 (search->registered && (now - channel->registered) > search->registered) ||
4641 (search->flags && ((search->flags & channel->flags) != search->flags)))
4648 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4650 struct chanData *channel;
4651 unsigned int matches = 0;
4653 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4655 if(!chanserv_channel_match(channel, search))
4665 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4670 search_print(struct chanData *channel, void *data)
4672 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4675 static CHANSERV_FUNC(cmd_search)
4678 unsigned int matches;
4679 channel_search_func action;
4683 if(!irccasecmp(argv[1], "count"))
4684 action = search_count;
4685 else if(!irccasecmp(argv[1], "print"))
4686 action = search_print;
4689 reply("CSMSG_ACTION_INVALID", argv[1]);
4693 search = chanserv_search_create(user, argc - 2, argv + 2);
4697 if(action == search_count)
4698 search->limit = INT_MAX;
4700 if(action == search_print)
4701 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4703 matches = chanserv_channel_search(search, action, user);
4706 reply("MSG_MATCH_COUNT", matches);
4708 reply("MSG_NO_MATCHES");
4714 static CHANSERV_FUNC(cmd_unvisited)
4716 struct chanData *cData;
4717 time_t interval = chanserv_conf.channel_expire_delay;
4718 char buffer[INTERVALLEN];
4719 unsigned int limit = 25, matches = 0;
4723 interval = ParseInterval(argv[1]);
4725 limit = atoi(argv[2]);
4728 intervalString(buffer, interval, user->handle_info);
4729 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4731 for(cData = channelList; cData && matches < limit; cData = cData->next)
4733 if((now - cData->visited) < interval)
4736 intervalString(buffer, now - cData->visited, user->handle_info);
4737 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4744 static MODCMD_FUNC(chan_opt_defaulttopic)
4750 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4752 reply("CSMSG_TOPIC_LOCKED", channel->name);
4756 topic = unsplit_string(argv+1, argc-1, NULL);
4758 free(channel->channel_info->topic);
4759 if(topic[0] == '*' && topic[1] == 0)
4761 topic = channel->channel_info->topic = NULL;
4765 topic = channel->channel_info->topic = strdup(topic);
4766 if(channel->channel_info->topic_mask
4767 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4768 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4770 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4773 if(channel->channel_info->topic)
4774 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4776 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4780 static MODCMD_FUNC(chan_opt_topicmask)
4784 struct chanData *cData = channel->channel_info;
4787 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4789 reply("CSMSG_TOPIC_LOCKED", channel->name);
4793 mask = unsplit_string(argv+1, argc-1, NULL);
4795 if(cData->topic_mask)
4796 free(cData->topic_mask);
4797 if(mask[0] == '*' && mask[1] == 0)
4799 cData->topic_mask = 0;
4803 cData->topic_mask = strdup(mask);
4805 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4806 else if(!match_ircglob(cData->topic, cData->topic_mask))
4807 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4811 if(channel->channel_info->topic_mask)
4812 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4814 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4818 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4822 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4826 if(greeting[0] == '*' && greeting[1] == 0)
4830 unsigned int length = strlen(greeting);
4831 if(length > chanserv_conf.greeting_length)
4833 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4836 *data = strdup(greeting);
4845 reply(name, user_find_message(user, "MSG_NONE"));
4849 static MODCMD_FUNC(chan_opt_greeting)
4851 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4854 static MODCMD_FUNC(chan_opt_usergreeting)
4856 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4859 static MODCMD_FUNC(chan_opt_modes)
4861 struct mod_chanmode *new_modes;
4862 char modes[MODELEN];
4866 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4868 reply("CSMSG_NO_ACCESS");
4871 if(argv[1][0] == '*' && argv[1][1] == 0)
4873 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4875 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4877 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4880 else if(new_modes->argc > 1)
4882 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4883 mod_chanmode_free(new_modes);
4888 channel->channel_info->modes = *new_modes;
4889 modcmd_chanmode_announce(new_modes);
4890 mod_chanmode_free(new_modes);
4894 mod_chanmode_format(&channel->channel_info->modes, modes);
4896 reply("CSMSG_SET_MODES", modes);
4898 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4902 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4904 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4906 struct chanData *cData = channel->channel_info;
4911 /* Set flag according to value. */
4912 if(enabled_string(argv[1]))
4914 cData->flags |= mask;
4917 else if(disabled_string(argv[1]))
4919 cData->flags &= ~mask;
4924 reply("MSG_INVALID_BINARY", argv[1]);
4930 /* Find current option value. */
4931 value = (cData->flags & mask) ? 1 : 0;
4935 reply(name, user_find_message(user, "MSG_ON"));
4937 reply(name, user_find_message(user, "MSG_OFF"));
4941 static MODCMD_FUNC(chan_opt_nodelete)
4943 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4945 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4949 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4952 static MODCMD_FUNC(chan_opt_dynlimit)
4954 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4957 static MODCMD_FUNC(chan_opt_offchannel)
4959 struct chanData *cData = channel->channel_info;
4964 /* Set flag according to value. */
4965 if(enabled_string(argv[1]))
4967 if(!IsOffChannel(cData))
4968 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4969 cData->flags |= CHANNEL_OFFCHANNEL;
4972 else if(disabled_string(argv[1]))
4974 if(IsOffChannel(cData))
4976 struct mod_chanmode change;
4977 mod_chanmode_init(&change);
4979 change.args[0].mode = MODE_CHANOP;
4980 change.args[0].u.member = AddChannelUser(chanserv, channel);
4981 mod_chanmode_announce(chanserv, channel, &change);
4983 cData->flags &= ~CHANNEL_OFFCHANNEL;
4988 reply("MSG_INVALID_BINARY", argv[1]);
4994 /* Find current option value. */
4995 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4999 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5001 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5005 static MODCMD_FUNC(chan_opt_defaults)
5007 struct userData *uData;
5008 struct chanData *cData;
5009 const char *confirm;
5010 enum levelOption lvlOpt;
5011 enum charOption chOpt;
5013 cData = channel->channel_info;
5014 uData = GetChannelUser(cData, user->handle_info);
5015 if(!uData || (uData->access < UL_OWNER))
5017 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5020 confirm = make_confirmation_string(uData);
5021 if((argc < 2) || strcmp(argv[1], confirm))
5023 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5026 cData->flags = CHANNEL_DEFAULT_FLAGS;
5027 cData->modes = chanserv_conf.default_modes;
5028 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5029 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5030 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5031 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5032 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5037 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5039 struct chanData *cData = channel->channel_info;
5040 struct userData *uData;
5041 unsigned short value;
5045 if(!check_user_level(channel, user, option, 1, 1))
5047 reply("CSMSG_CANNOT_SET");
5050 value = user_level_from_name(argv[1], UL_OWNER+1);
5051 if(!value && strcmp(argv[1], "0"))
5053 reply("CSMSG_INVALID_ACCESS", argv[1]);
5056 uData = GetChannelUser(cData, user->handle_info);
5057 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5059 reply("CSMSG_BAD_SETLEVEL");
5065 if(value > cData->lvlOpts[lvlGiveOps])
5067 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5072 if(value < cData->lvlOpts[lvlGiveVoice])
5074 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5079 /* This test only applies to owners, since non-owners
5080 * trying to set an option to above their level get caught
5081 * by the CSMSG_BAD_SETLEVEL test above.
5083 if(value > uData->access)
5085 reply("CSMSG_BAD_SETTERS");
5092 cData->lvlOpts[option] = value;
5094 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5098 static MODCMD_FUNC(chan_opt_enfops)
5100 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5103 static MODCMD_FUNC(chan_opt_giveops)
5105 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5108 static MODCMD_FUNC(chan_opt_enfmodes)
5110 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5113 static MODCMD_FUNC(chan_opt_enftopic)
5115 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5118 static MODCMD_FUNC(chan_opt_pubcmd)
5120 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5123 static MODCMD_FUNC(chan_opt_setters)
5125 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5128 static MODCMD_FUNC(chan_opt_ctcpusers)
5130 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5133 static MODCMD_FUNC(chan_opt_userinfo)
5135 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5138 static MODCMD_FUNC(chan_opt_givevoice)
5140 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5143 static MODCMD_FUNC(chan_opt_topicsnarf)
5145 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5148 static MODCMD_FUNC(chan_opt_inviteme)
5150 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5154 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5156 struct chanData *cData = channel->channel_info;
5157 int count = charOptions[option].count, index;
5161 index = atoi(argv[1]);
5163 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5165 reply("CSMSG_INVALID_NUMERIC", index);
5166 /* Show possible values. */
5167 for(index = 0; index < count; index++)
5168 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5172 cData->chOpts[option] = charOptions[option].values[index].value;
5176 /* Find current option value. */
5179 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5183 /* Somehow, the option value is corrupt; reset it to the default. */
5184 cData->chOpts[option] = charOptions[option].default_value;
5189 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5193 static MODCMD_FUNC(chan_opt_protect)
5195 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5198 static MODCMD_FUNC(chan_opt_toys)
5200 return channel_multiple_option(chToys, CSFUNC_ARGS);
5203 static MODCMD_FUNC(chan_opt_ctcpreaction)
5205 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5208 static MODCMD_FUNC(chan_opt_topicrefresh)
5210 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5213 static struct svccmd_list set_shows_list;
5216 handle_svccmd_unbind(struct svccmd *target) {
5218 for(ii=0; ii<set_shows_list.used; ++ii)
5219 if(target == set_shows_list.list[ii])
5220 set_shows_list.used = 0;
5223 static CHANSERV_FUNC(cmd_set)
5225 struct svccmd *subcmd;
5229 /* Check if we need to (re-)initialize set_shows_list. */
5230 if(!set_shows_list.used)
5232 if(!set_shows_list.size)
5234 set_shows_list.size = chanserv_conf.set_shows->used;
5235 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5237 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5239 const char *name = chanserv_conf.set_shows->list[ii];
5240 sprintf(buf, "%s %s", argv[0], name);
5241 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5244 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5247 svccmd_list_append(&set_shows_list, subcmd);
5253 reply("CSMSG_CHANNEL_OPTIONS");
5254 for(ii = 0; ii < set_shows_list.used; ii++)
5256 subcmd = set_shows_list.list[ii];
5257 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5262 sprintf(buf, "%s %s", argv[0], argv[1]);
5263 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5266 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5269 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5271 reply("CSMSG_NO_ACCESS");
5275 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5279 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5281 struct userData *uData;
5283 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5286 reply("CSMSG_NOT_USER", channel->name);
5292 /* Just show current option value. */
5294 else if(enabled_string(argv[1]))
5296 uData->flags |= mask;
5298 else if(disabled_string(argv[1]))
5300 uData->flags &= ~mask;
5304 reply("MSG_INVALID_BINARY", argv[1]);
5308 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5312 static MODCMD_FUNC(user_opt_noautoop)
5314 struct userData *uData;
5316 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5319 reply("CSMSG_NOT_USER", channel->name);
5322 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5323 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5325 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5328 static MODCMD_FUNC(user_opt_autoinvite)
5330 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5333 static MODCMD_FUNC(user_opt_info)
5335 struct userData *uData;
5338 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5342 /* If they got past the command restrictions (which require access)
5343 * but fail this test, we have some fool with security override on.
5345 reply("CSMSG_NOT_USER", channel->name);
5352 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5353 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5355 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5358 bp = strcspn(infoline, "\001");
5361 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5366 if(infoline[0] == '*' && infoline[1] == 0)
5369 uData->info = strdup(infoline);
5372 reply("CSMSG_USET_INFO", uData->info);
5374 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5378 struct svccmd_list uset_shows_list;
5380 static CHANSERV_FUNC(cmd_uset)
5382 struct svccmd *subcmd;
5386 /* Check if we need to (re-)initialize uset_shows_list. */
5387 if(!uset_shows_list.used)
5391 "NoAutoOp", "AutoInvite", "Info"
5394 if(!uset_shows_list.size)
5396 uset_shows_list.size = ArrayLength(options);
5397 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5399 for(ii = 0; ii < ArrayLength(options); ii++)
5401 const char *name = options[ii];
5402 sprintf(buf, "%s %s", argv[0], name);
5403 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5406 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5409 svccmd_list_append(&uset_shows_list, subcmd);
5415 /* Do this so options are presented in a consistent order. */
5416 reply("CSMSG_USER_OPTIONS");
5417 for(ii = 0; ii < uset_shows_list.used; ii++)
5418 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5422 sprintf(buf, "%s %s", argv[0], argv[1]);
5423 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5426 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5430 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5433 static CHANSERV_FUNC(cmd_giveownership)
5435 struct handle_info *new_owner_hi;
5436 struct userData *new_owner, *curr_user;
5437 struct chanData *cData = channel->channel_info;
5438 struct do_not_register *dnr;
5440 unsigned short co_access;
5441 char reason[MAXLEN];
5444 curr_user = GetChannelAccess(cData, user->handle_info);
5445 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5446 if(!curr_user || (curr_user->access != UL_OWNER))
5448 struct userData *owner = NULL;
5449 for(curr_user = channel->channel_info->users;
5451 curr_user = curr_user->next)
5453 if(curr_user->access != UL_OWNER)
5457 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5464 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5466 char delay[INTERVALLEN];
5467 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5468 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5471 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5473 if(new_owner_hi == user->handle_info)
5475 reply("CSMSG_NO_TRANSFER_SELF");
5478 new_owner = GetChannelAccess(cData, new_owner_hi);
5481 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5484 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5486 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5489 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5490 if(!IsHelping(user))
5491 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5493 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5496 if(new_owner->access >= UL_COOWNER)
5497 co_access = new_owner->access;
5499 co_access = UL_COOWNER;
5500 new_owner->access = UL_OWNER;
5502 curr_user->access = co_access;
5503 cData->ownerTransfer = now;
5504 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5505 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5506 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5510 static CHANSERV_FUNC(cmd_suspend)
5512 struct handle_info *hi;
5513 struct userData *self, *target;
5516 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5517 self = GetChannelUser(channel->channel_info, user->handle_info);
5518 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5520 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5523 if(target->access >= self->access)
5525 reply("MSG_USER_OUTRANKED", hi->handle);
5528 if(target->flags & USER_SUSPENDED)
5530 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5535 target->present = 0;
5538 target->flags |= USER_SUSPENDED;
5539 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5543 static CHANSERV_FUNC(cmd_unsuspend)
5545 struct handle_info *hi;
5546 struct userData *self, *target;
5549 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5550 self = GetChannelUser(channel->channel_info, user->handle_info);
5551 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5553 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5556 if(target->access >= self->access)
5558 reply("MSG_USER_OUTRANKED", hi->handle);
5561 if(!(target->flags & USER_SUSPENDED))
5563 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5566 target->flags &= ~USER_SUSPENDED;
5567 scan_user_presence(target, NULL);
5568 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5572 static MODCMD_FUNC(cmd_deleteme)
5574 struct handle_info *hi;
5575 struct userData *target;
5576 const char *confirm_string;
5577 unsigned short access;
5580 hi = user->handle_info;
5581 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5583 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5586 if(target->access == UL_OWNER)
5588 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5591 confirm_string = make_confirmation_string(target);
5592 if((argc < 2) || strcmp(argv[1], confirm_string))
5594 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5597 access = target->access;
5598 channel_name = strdup(channel->name);
5599 del_channel_user(target, 1);
5600 reply("CSMSG_DELETED_YOU", access, channel_name);
5606 chanserv_refresh_topics(UNUSED_ARG(void *data))
5608 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5609 struct chanData *cData;
5612 for(cData = channelList; cData; cData = cData->next)
5614 if(IsSuspended(cData))
5616 opt = cData->chOpts[chTopicRefresh];
5619 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5622 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5623 cData->last_refresh = refresh_num;
5625 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5628 static CHANSERV_FUNC(cmd_unf)
5632 char response[MAXLEN];
5633 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5634 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5635 irc_privmsg(cmd->parent->bot, channel->name, response);
5638 reply("CSMSG_UNF_RESPONSE");
5642 static CHANSERV_FUNC(cmd_ping)
5646 char response[MAXLEN];
5647 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5648 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5649 irc_privmsg(cmd->parent->bot, channel->name, response);
5652 reply("CSMSG_PING_RESPONSE");
5656 static CHANSERV_FUNC(cmd_wut)
5660 char response[MAXLEN];
5661 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5662 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5663 irc_privmsg(cmd->parent->bot, channel->name, response);
5666 reply("CSMSG_WUT_RESPONSE");
5670 static CHANSERV_FUNC(cmd_8ball)
5672 unsigned int i, j, accum;
5677 for(i=1; i<argc; i++)
5678 for(j=0; argv[i][j]; j++)
5679 accum = (accum << 5) - accum + toupper(argv[i][j]);
5680 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5683 char response[MAXLEN];
5684 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5685 irc_privmsg(cmd->parent->bot, channel->name, response);
5688 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5692 static CHANSERV_FUNC(cmd_d)
5694 unsigned long sides, count, modifier, ii, total;
5695 char response[MAXLEN], *sep;
5699 if((count = strtoul(argv[1], &sep, 10)) < 1)
5709 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5710 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5714 else if((sep[0] == '-') && isdigit(sep[1]))
5715 modifier = strtoul(sep, NULL, 10);
5716 else if((sep[0] == '+') && isdigit(sep[1]))
5717 modifier = strtoul(sep+1, NULL, 10);
5724 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5729 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5732 for(total = ii = 0; ii < count; ++ii)
5733 total += (rand() % sides) + 1;
5736 if((count > 1) || modifier)
5738 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5739 sprintf(response, fmt, total, count, sides, modifier);
5743 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5744 sprintf(response, fmt, total, sides);
5747 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5749 send_message_type(4, user, cmd->parent->bot, "%s", response);
5753 static CHANSERV_FUNC(cmd_huggle)
5755 /* CTCP must be via PRIVMSG, never notice */
5757 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5759 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5764 chanserv_adjust_limit(void *data)
5766 struct mod_chanmode change;
5767 struct chanData *cData = data;
5768 struct chanNode *channel = cData->channel;
5771 if(IsSuspended(cData))
5774 cData->limitAdjusted = now;
5775 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5776 if(cData->modes.modes_set & MODE_LIMIT)
5778 if(limit > cData->modes.new_limit)
5779 limit = cData->modes.new_limit;
5780 else if(limit == cData->modes.new_limit)
5784 mod_chanmode_init(&change);
5785 change.modes_set = MODE_LIMIT;
5786 change.new_limit = limit;
5787 mod_chanmode_announce(chanserv, channel, &change);
5791 handle_new_channel(struct chanNode *channel)
5793 struct chanData *cData;
5795 if(!(cData = channel->channel_info))
5798 if(cData->modes.modes_set || cData->modes.modes_clear)
5799 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5801 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5802 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5805 /* Welcome to my worst nightmare. Warning: Read (or modify)
5806 the code below at your own risk. */
5808 handle_join(struct modeNode *mNode)
5810 struct mod_chanmode change;
5811 struct userNode *user = mNode->user;
5812 struct chanNode *channel = mNode->channel;
5813 struct chanData *cData;
5814 struct userData *uData = NULL;
5815 struct banData *bData;
5816 struct handle_info *handle;
5817 unsigned int modes = 0, info = 0;
5820 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5823 cData = channel->channel_info;
5824 if(channel->members.used > cData->max)
5825 cData->max = channel->members.used;
5827 /* Check for bans. If they're joining through a ban, one of two
5829 * 1: Join during a netburst, by riding the break. Kick them
5830 * unless they have ops or voice in the channel.
5831 * 2: They're allowed to join through the ban (an invite in
5832 * ircu2.10, or a +e on Hybrid, or something).
5833 * If they're not joining through a ban, and the banlist is not
5834 * full, see if they're on the banlist for the channel. If so,
5837 if(user->uplink->burst && !mNode->modes)
5840 for(ii = 0; ii < channel->banlist.used; ii++)
5842 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5844 /* Riding a netburst. Naughty. */
5845 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5851 mod_chanmode_init(&change);
5853 if(channel->banlist.used < MAXBANS)
5855 /* Not joining through a ban. */
5856 for(bData = cData->bans;
5857 bData && !user_matches_glob(user, bData->mask, 1);
5858 bData = bData->next);
5862 char kick_reason[MAXLEN];
5863 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5865 bData->triggered = now;
5866 if(bData != cData->bans)
5868 /* Shuffle the ban to the head of the list. */
5870 bData->next->prev = bData->prev;
5872 bData->prev->next = bData->next;
5875 bData->next = cData->bans;
5878 cData->bans->prev = bData;
5879 cData->bans = bData;
5882 change.args[0].mode = MODE_BAN;
5883 change.args[0].u.hostmask = bData->mask;
5884 mod_chanmode_announce(chanserv, channel, &change);
5885 KickChannelUser(user, channel, chanserv, kick_reason);
5890 /* ChanServ will not modify the limits in join-flooded channels.
5891 It will also skip DynLimit processing when the user (or srvx)
5892 is bursting in, because there are likely more incoming. */
5893 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5894 && !user->uplink->burst
5895 && !channel->join_flooded
5896 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5898 /* The user count has begun "bumping" into the channel limit,
5899 so set a timer to raise the limit a bit. Any previous
5900 timers are removed so three incoming users within the delay
5901 results in one limit change, not three. */
5903 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5904 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5907 if(channel->join_flooded)
5909 /* don't automatically give ops or voice during a join flood */
5911 else if(cData->lvlOpts[lvlGiveOps] == 0)
5912 modes |= MODE_CHANOP;
5913 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5914 modes |= MODE_VOICE;
5916 greeting = cData->greeting;
5917 if(user->handle_info)
5919 handle = user->handle_info;
5921 if(IsHelper(user) && !IsHelping(user))
5924 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5926 if(channel == chanserv_conf.support_channels.list[ii])
5928 HANDLE_SET_FLAG(user->handle_info, HELPING);
5934 uData = GetTrueChannelAccess(cData, handle);
5935 if(uData && !IsUserSuspended(uData))
5937 /* Ops and above were handled by the above case. */
5938 if(IsUserAutoOp(uData))
5940 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5941 modes |= MODE_CHANOP;
5942 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5943 modes |= MODE_VOICE;
5945 if(uData->access >= UL_PRESENT)
5946 cData->visited = now;
5947 if(cData->user_greeting)
5948 greeting = cData->user_greeting;
5950 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5951 && ((now - uData->seen) >= chanserv_conf.info_delay)
5958 if(!user->uplink->burst)
5962 if(modes & MODE_CHANOP)
5963 modes &= ~MODE_VOICE;
5964 change.args[0].mode = modes;
5965 change.args[0].u.member = mNode;
5966 mod_chanmode_announce(chanserv, channel, &change);
5968 if(greeting && !user->uplink->burst)
5969 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5971 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5977 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5979 struct mod_chanmode change;
5980 struct userData *channel;
5981 unsigned int ii, jj;
5983 if(!user->handle_info)
5986 mod_chanmode_init(&change);
5988 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5990 struct chanNode *cn;
5991 struct modeNode *mn;
5992 if(IsUserSuspended(channel)
5993 || IsSuspended(channel->channel)
5994 || !(cn = channel->channel->channel))
5997 mn = GetUserMode(cn, user);
6000 if(!IsUserSuspended(channel)
6001 && IsUserAutoInvite(channel)
6002 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6004 && !user->uplink->burst)
6005 irc_invite(chanserv, user, cn);
6009 if(channel->access >= UL_PRESENT)
6010 channel->channel->visited = now;
6012 if(IsUserAutoOp(channel))
6014 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6015 change.args[0].mode = MODE_CHANOP;
6016 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6017 change.args[0].mode = MODE_VOICE;
6019 change.args[0].mode = 0;
6020 change.args[0].u.member = mn;
6021 if(change.args[0].mode)
6022 mod_chanmode_announce(chanserv, cn, &change);
6025 channel->seen = now;
6026 channel->present = 1;
6029 for(ii = 0; ii < user->channels.used; ++ii)
6031 struct chanNode *channel = user->channels.list[ii]->channel;
6032 struct banData *ban;
6034 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6035 || !channel->channel_info
6036 || IsSuspended(channel->channel_info))
6038 for(jj = 0; jj < channel->banlist.used; ++jj)
6039 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6041 if(jj < channel->banlist.used)
6043 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6045 char kick_reason[MAXLEN];
6046 if(!user_matches_glob(user, ban->mask, 1))
6048 change.args[0].mode = MODE_BAN;
6049 change.args[0].u.hostmask = ban->mask;
6050 mod_chanmode_announce(chanserv, channel, &change);
6051 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6052 KickChannelUser(user, channel, chanserv, kick_reason);
6053 ban->triggered = now;
6058 if(IsSupportHelper(user))
6060 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6062 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6064 HANDLE_SET_FLAG(user->handle_info, HELPING);
6072 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6074 struct chanData *cData;
6075 struct userData *uData;
6077 cData = mn->channel->channel_info;
6078 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6081 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6083 /* Allow for a bit of padding so that the limit doesn't
6084 track the user count exactly, which could get annoying. */
6085 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6087 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6088 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6092 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6094 scan_user_presence(uData, mn->user);
6098 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6100 unsigned int ii, jj;
6101 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6103 for(jj = 0; jj < mn->user->channels.used; ++jj)
6104 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6106 if(jj < mn->user->channels.used)
6109 if(ii == chanserv_conf.support_channels.used)
6110 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6115 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6117 struct userData *uData;
6119 if(!channel->channel_info || !kicker || IsService(kicker)
6120 || (kicker == victim) || IsSuspended(channel->channel_info)
6121 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6124 if(protect_user(victim, kicker, channel->channel_info))
6126 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6127 KickChannelUser(kicker, channel, chanserv, reason);
6130 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6135 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6137 struct chanData *cData;
6139 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6142 cData = channel->channel_info;
6143 if(bad_topic(channel, user, channel->topic))
6145 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6146 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6147 SetChannelTopic(channel, chanserv, old_topic, 1);
6148 else if(cData->topic)
6149 SetChannelTopic(channel, chanserv, cData->topic, 1);
6152 /* With topicsnarf, grab the topic and save it as the default topic. */
6153 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6156 cData->topic = strdup(channel->topic);
6162 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6164 struct mod_chanmode *bounce = NULL;
6165 unsigned int bnc, ii;
6168 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6171 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6172 && mode_lock_violated(&channel->channel_info->modes, change))
6174 char correct[MAXLEN];
6175 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6176 mod_chanmode_format(&channel->channel_info->modes, correct);
6177 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6179 for(ii = bnc = 0; ii < change->argc; ++ii)
6181 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6183 const struct userNode *victim = change->args[ii].u.member->user;
6184 if(!protect_user(victim, user, channel->channel_info))
6187 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6190 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6191 bounce->args[bnc].u.member = GetUserMode(channel, user);
6192 if(bounce->args[bnc].u.member)
6196 bounce->args[bnc].mode = MODE_CHANOP;
6197 bounce->args[bnc].u.member = change->args[ii].u.member;
6199 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6201 else if(change->args[ii].mode & MODE_CHANOP)
6203 const struct userNode *victim = change->args[ii].u.member->user;
6204 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6207 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6208 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6209 bounce->args[bnc].u.member = change->args[ii].u.member;
6212 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6214 const char *ban = change->args[ii].u.hostmask;
6215 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6218 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6219 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6220 bounce->args[bnc].u.hostmask = ban;
6222 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6227 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6228 mod_chanmode_announce(chanserv, channel, bounce);
6229 mod_chanmode_free(bounce);
6234 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6236 struct chanNode *channel;
6237 struct banData *bData;
6238 struct mod_chanmode change;
6239 unsigned int ii, jj;
6240 char kick_reason[MAXLEN];
6242 mod_chanmode_init(&change);
6244 change.args[0].mode = MODE_BAN;
6245 for(ii = 0; ii < user->channels.used; ++ii)
6247 channel = user->channels.list[ii]->channel;
6248 /* Need not check for bans if they're opped or voiced. */
6249 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6251 /* Need not check for bans unless channel registration is active. */
6252 if(!channel->channel_info || IsSuspended(channel->channel_info))
6254 /* Look for a matching ban already on the channel. */
6255 for(jj = 0; jj < channel->banlist.used; ++jj)
6256 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6258 /* Need not act if we found one. */
6259 if(jj < channel->banlist.used)
6261 /* Look for a matching ban in this channel. */
6262 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6264 if(!user_matches_glob(user, bData->mask, 1))
6266 change.args[0].u.hostmask = bData->mask;
6267 mod_chanmode_announce(chanserv, channel, &change);
6268 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6269 KickChannelUser(user, channel, chanserv, kick_reason);
6270 bData->triggered = now;
6271 break; /* we don't need to check any more bans in the channel */
6276 static void handle_rename(struct handle_info *handle, const char *old_handle)
6278 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6282 dict_remove2(handle_dnrs, old_handle, 1);
6283 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6284 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6289 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6291 struct userNode *h_user;
6293 if(handle->channels)
6295 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6296 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6298 while(handle->channels)
6299 del_channel_user(handle->channels, 1);
6304 handle_server_link(UNUSED_ARG(struct server *server))
6306 struct chanData *cData;
6308 for(cData = channelList; cData; cData = cData->next)
6310 if(!IsSuspended(cData))
6311 cData->may_opchan = 1;
6312 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6313 && !cData->channel->join_flooded
6314 && ((cData->channel->limit - cData->channel->members.used)
6315 < chanserv_conf.adjust_threshold))
6317 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6318 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6324 chanserv_conf_read(void)
6328 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6329 struct mod_chanmode *change;
6330 struct string_list *strlist;
6331 struct chanNode *chan;
6334 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6336 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6339 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6340 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6341 chanserv_conf.support_channels.used = 0;
6342 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6344 for(ii = 0; ii < strlist->used; ++ii)
6346 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6349 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6351 channelList_append(&chanserv_conf.support_channels, chan);
6354 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6357 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6360 chan = AddChannel(str, now, str2, NULL);
6362 channelList_append(&chanserv_conf.support_channels, chan);
6364 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6365 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6366 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6367 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6368 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6369 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6370 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6371 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6372 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6373 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6374 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6375 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6376 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6377 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6378 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6379 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6380 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6381 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6382 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6383 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6384 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6385 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6386 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6388 NickChange(chanserv, str, 0);
6389 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6390 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6391 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6392 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6393 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6394 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6395 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6396 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6397 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6398 chanserv_conf.max_owned = str ? atoi(str) : 5;
6399 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6400 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6401 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6402 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6403 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6404 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6405 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6408 safestrncpy(mode_line, str, sizeof(mode_line));
6409 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6410 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6412 chanserv_conf.default_modes = *change;
6413 mod_chanmode_free(change);
6415 free_string_list(chanserv_conf.set_shows);
6416 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6418 strlist = string_list_copy(strlist);
6421 static const char *list[] = {
6422 /* free form text */
6423 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6424 /* options based on user level */
6425 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6426 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6427 /* multiple choice options */
6428 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6429 /* binary options */
6430 "DynLimit", "NoDelete",
6435 strlist = alloc_string_list(ArrayLength(list)-1);
6436 for(ii=0; list[ii]; ii++)
6437 string_list_append(strlist, strdup(list[ii]));
6439 chanserv_conf.set_shows = strlist;
6440 /* We don't look things up now, in case the list refers to options
6441 * defined by modules initialized after this point. Just mark the
6442 * function list as invalid, so it will be initialized.
6444 set_shows_list.used = 0;
6445 free_string_list(chanserv_conf.eightball);
6446 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6449 strlist = string_list_copy(strlist);
6453 strlist = alloc_string_list(4);
6454 string_list_append(strlist, strdup("Yes."));
6455 string_list_append(strlist, strdup("No."));
6456 string_list_append(strlist, strdup("Maybe so."));
6458 chanserv_conf.eightball = strlist;
6459 free_string_list(chanserv_conf.old_ban_names);
6460 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6462 strlist = string_list_copy(strlist);
6464 strlist = alloc_string_list(2);
6465 chanserv_conf.old_ban_names = strlist;
6466 /* the variable itself is actually declared in proto-common.c; this is equally
6468 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6469 off_channel = str ? atoi(str) : 0;
6473 chanserv_note_type_read(const char *key, struct record_data *rd)
6476 struct note_type *ntype;
6479 if(!(obj = GET_RECORD_OBJECT(rd)))
6481 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6484 if(!(ntype = chanserv_create_note_type(key)))
6486 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6490 /* Figure out set access */
6491 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6493 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6494 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6496 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6498 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6499 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6501 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6503 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6507 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6508 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6509 ntype->set_access.min_opserv = 0;
6512 /* Figure out visibility */
6513 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6514 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6515 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6516 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6517 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6518 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6519 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6520 ntype->visible_type = NOTE_VIS_ALL;
6522 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6524 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6525 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6529 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6531 struct handle_info *handle;
6532 struct userData *uData;
6533 char *seen, *inf, *flags;
6535 unsigned short access;
6537 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6539 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6543 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6544 if(access > UL_OWNER)
6546 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6550 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6551 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6552 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6553 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6554 handle = get_handle_info(key);
6557 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6561 uData = add_channel_user(chan, handle, access, last_seen, inf);
6562 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6566 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6568 struct banData *bData;
6569 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6570 time_t set_time, triggered_time, expires_time;
6572 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6574 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6578 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6579 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6580 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6581 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6582 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6583 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6585 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6586 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6588 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6590 expires_time = set_time + atoi(s_duration);
6594 if(expires_time && (expires_time < now))
6597 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6600 static struct suspended *
6601 chanserv_read_suspended(dict_t obj)
6603 struct suspended *suspended = calloc(1, sizeof(*suspended));
6607 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6608 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6609 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6610 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6611 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6612 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6613 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6614 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6615 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6616 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6621 chanserv_channel_read(const char *key, struct record_data *hir)
6623 struct suspended *suspended;
6624 struct mod_chanmode *modes;
6625 struct chanNode *cNode;
6626 struct chanData *cData;
6627 struct dict *channel, *obj;
6628 char *str, *argv[10];
6632 channel = hir->d.object;
6634 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6637 cNode = AddChannel(key, now, NULL, NULL);
6640 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6643 cData = register_channel(cNode, str);
6646 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6650 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6652 enum levelOption lvlOpt;
6653 enum charOption chOpt;
6655 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6656 cData->flags = atoi(str);
6658 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6660 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6662 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6663 else if(levelOptions[lvlOpt].old_flag)
6665 if(cData->flags & levelOptions[lvlOpt].old_flag)
6666 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6668 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6672 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6674 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6676 cData->chOpts[chOpt] = str[0];
6679 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6681 enum levelOption lvlOpt;
6682 enum charOption chOpt;
6685 cData->flags = base64toint(str, 5);
6686 count = strlen(str += 5);
6687 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6690 if(levelOptions[lvlOpt].old_flag)
6692 if(cData->flags & levelOptions[lvlOpt].old_flag)
6693 lvl = levelOptions[lvlOpt].flag_value;
6695 lvl = levelOptions[lvlOpt].default_value;
6697 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6699 case 'c': lvl = UL_COOWNER; break;
6700 case 'm': lvl = UL_MASTER; break;
6701 case 'n': lvl = UL_OWNER+1; break;
6702 case 'o': lvl = UL_OP; break;
6703 case 'p': lvl = UL_PEON; break;
6704 case 'w': lvl = UL_OWNER; break;
6705 default: lvl = 0; break;
6707 cData->lvlOpts[lvlOpt] = lvl;
6709 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6710 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6713 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6715 suspended = chanserv_read_suspended(obj);
6716 cData->suspended = suspended;
6717 suspended->cData = cData;
6718 /* We could use suspended->expires and suspended->revoked to
6719 * set the CHANNEL_SUSPENDED flag, but we don't. */
6721 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6723 suspended = calloc(1, sizeof(*suspended));
6724 suspended->issued = 0;
6725 suspended->revoked = 0;
6726 suspended->suspender = strdup(str);
6727 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6728 suspended->expires = str ? atoi(str) : 0;
6729 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6730 suspended->reason = strdup(str ? str : "No reason");
6731 suspended->previous = NULL;
6732 cData->suspended = suspended;
6733 suspended->cData = cData;
6737 cData->flags &= ~CHANNEL_SUSPENDED;
6738 suspended = NULL; /* to squelch a warning */
6741 if(IsSuspended(cData)) {
6742 if(suspended->expires > now)
6743 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6744 else if(suspended->expires)
6745 cData->flags &= ~CHANNEL_SUSPENDED;
6748 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6749 struct mod_chanmode change;
6750 mod_chanmode_init(&change);
6752 change.args[0].mode = MODE_CHANOP;
6753 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6754 mod_chanmode_announce(chanserv, cNode, &change);
6757 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6758 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6759 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6760 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6761 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6762 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6763 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6764 cData->max = str ? atoi(str) : 0;
6765 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6766 cData->greeting = str ? strdup(str) : NULL;
6767 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6768 cData->user_greeting = str ? strdup(str) : NULL;
6769 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6770 cData->topic_mask = str ? strdup(str) : NULL;
6771 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6772 cData->topic = str ? strdup(str) : NULL;
6774 if(!IsSuspended(cData)
6775 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6776 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6777 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6778 cData->modes = *modes;
6780 cData->modes.modes_set |= MODE_REGISTERED;
6781 if(cData->modes.argc > 1)
6782 cData->modes.argc = 1;
6783 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6784 mod_chanmode_free(modes);
6787 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6788 for(it = dict_first(obj); it; it = iter_next(it))
6789 user_read_helper(iter_key(it), iter_data(it), cData);
6791 if(!cData->users && !IsProtected(cData))
6793 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6794 unregister_channel(cData, "has empty user list.");
6798 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6799 for(it = dict_first(obj); it; it = iter_next(it))
6800 ban_read_helper(iter_key(it), iter_data(it), cData);
6802 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6803 for(it = dict_first(obj); it; it = iter_next(it))
6805 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6806 struct record_data *rd = iter_data(it);
6807 const char *note, *setter;
6809 if(rd->type != RECDB_OBJECT)
6811 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6815 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6817 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6819 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6823 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6824 if(!setter) setter = "<unknown>";
6825 chanserv_add_channel_note(cData, ntype, setter, note);
6833 chanserv_dnr_read(const char *key, struct record_data *hir)
6835 const char *setter, *reason, *str;
6836 struct do_not_register *dnr;
6838 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6841 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6844 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6847 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6850 dnr = chanserv_add_dnr(key, setter, reason);
6853 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6855 dnr->set = atoi(str);
6861 chanserv_saxdb_read(struct dict *database)
6863 struct dict *section;
6866 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6867 for(it = dict_first(section); it; it = iter_next(it))
6868 chanserv_note_type_read(iter_key(it), iter_data(it));
6870 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6871 for(it = dict_first(section); it; it = iter_next(it))
6872 chanserv_channel_read(iter_key(it), iter_data(it));
6874 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6875 for(it = dict_first(section); it; it = iter_next(it))
6876 chanserv_dnr_read(iter_key(it), iter_data(it));
6882 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6884 int high_present = 0;
6885 saxdb_start_record(ctx, KEY_USERS, 1);
6886 for(; uData; uData = uData->next)
6888 if((uData->access >= UL_PRESENT) && uData->present)
6890 saxdb_start_record(ctx, uData->handle->handle, 0);
6891 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6892 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6894 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6896 saxdb_write_string(ctx, KEY_INFO, uData->info);
6897 saxdb_end_record(ctx);
6899 saxdb_end_record(ctx);
6900 return high_present;
6904 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6908 saxdb_start_record(ctx, KEY_BANS, 1);
6909 for(; bData; bData = bData->next)
6911 saxdb_start_record(ctx, bData->mask, 0);
6912 saxdb_write_int(ctx, KEY_SET, bData->set);
6913 if(bData->triggered)
6914 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6916 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6918 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6920 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6921 saxdb_end_record(ctx);
6923 saxdb_end_record(ctx);
6927 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6929 saxdb_start_record(ctx, name, 0);
6930 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6931 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6933 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6935 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6937 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6939 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6940 saxdb_end_record(ctx);
6944 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6948 enum levelOption lvlOpt;
6949 enum charOption chOpt;
6951 saxdb_start_record(ctx, channel->channel->name, 1);
6953 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6954 saxdb_write_int(ctx, KEY_MAX, channel->max);
6956 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6957 if(channel->registrar)
6958 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6959 if(channel->greeting)
6960 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6961 if(channel->user_greeting)
6962 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6963 if(channel->topic_mask)
6964 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6965 if(channel->suspended)
6966 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6968 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6969 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6970 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6971 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6972 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6974 buf[0] = channel->chOpts[chOpt];
6976 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6978 saxdb_end_record(ctx);
6980 if(channel->modes.modes_set || channel->modes.modes_clear)
6982 mod_chanmode_format(&channel->modes, buf);
6983 saxdb_write_string(ctx, KEY_MODES, buf);
6986 high_present = chanserv_write_users(ctx, channel->users);
6987 chanserv_write_bans(ctx, channel->bans);
6989 if(dict_size(channel->notes))
6993 saxdb_start_record(ctx, KEY_NOTES, 1);
6994 for(it = dict_first(channel->notes); it; it = iter_next(it))
6996 struct note *note = iter_data(it);
6997 saxdb_start_record(ctx, iter_key(it), 0);
6998 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6999 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7000 saxdb_end_record(ctx);
7002 saxdb_end_record(ctx);
7005 if(channel->ownerTransfer)
7006 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7007 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7008 saxdb_end_record(ctx);
7012 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7016 saxdb_start_record(ctx, ntype->name, 0);
7017 switch(ntype->set_access_type)
7019 case NOTE_SET_CHANNEL_ACCESS:
7020 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7022 case NOTE_SET_CHANNEL_SETTER:
7023 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7025 case NOTE_SET_PRIVILEGED: default:
7026 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7029 switch(ntype->visible_type)
7031 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7032 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7033 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7035 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7036 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7037 saxdb_end_record(ctx);
7041 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7043 struct do_not_register *dnr;
7046 for(it = dict_first(dnrs); it; it = iter_next(it))
7048 dnr = iter_data(it);
7049 saxdb_start_record(ctx, dnr->chan_name, 0);
7051 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7052 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7053 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7054 saxdb_end_record(ctx);
7059 chanserv_saxdb_write(struct saxdb_context *ctx)
7062 struct chanData *channel;
7065 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7066 for(it = dict_first(note_types); it; it = iter_next(it))
7067 chanserv_write_note_type(ctx, iter_data(it));
7068 saxdb_end_record(ctx);
7071 saxdb_start_record(ctx, KEY_DNR, 1);
7072 write_dnrs_helper(ctx, handle_dnrs);
7073 write_dnrs_helper(ctx, plain_dnrs);
7074 write_dnrs_helper(ctx, mask_dnrs);
7075 saxdb_end_record(ctx);
7078 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7079 for(channel = channelList; channel; channel = channel->next)
7080 chanserv_write_channel(ctx, channel);
7081 saxdb_end_record(ctx);
7087 chanserv_db_cleanup(void) {
7089 unreg_part_func(handle_part);
7091 unregister_channel(channelList, "terminating.");
7092 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7093 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7094 free(chanserv_conf.support_channels.list);
7095 dict_delete(handle_dnrs);
7096 dict_delete(plain_dnrs);
7097 dict_delete(mask_dnrs);
7098 dict_delete(note_types);
7099 free_string_list(chanserv_conf.eightball);
7100 free_string_list(chanserv_conf.old_ban_names);
7101 free_string_list(chanserv_conf.set_shows);
7102 free(set_shows_list.list);
7103 free(uset_shows_list.list);
7106 struct userData *helper = helperList;
7107 helperList = helperList->next;
7112 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7113 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7114 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7117 init_chanserv(const char *nick)
7119 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7120 conf_register_reload(chanserv_conf_read);
7122 reg_server_link_func(handle_server_link);
7124 reg_new_channel_func(handle_new_channel);
7125 reg_join_func(handle_join);
7126 reg_part_func(handle_part);
7127 reg_kick_func(handle_kick);
7128 reg_topic_func(handle_topic);
7129 reg_mode_change_func(handle_mode);
7130 reg_nick_change_func(handle_nick_change);
7132 reg_auth_func(handle_auth);
7133 reg_handle_rename_func(handle_rename);
7134 reg_unreg_func(handle_unreg);
7136 handle_dnrs = dict_new();
7137 dict_set_free_data(handle_dnrs, free);
7138 plain_dnrs = dict_new();
7139 dict_set_free_data(plain_dnrs, free);
7140 mask_dnrs = dict_new();
7141 dict_set_free_data(mask_dnrs, free);
7143 reg_svccmd_unbind_func(handle_svccmd_unbind);
7144 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7145 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7146 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7147 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7148 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7149 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7150 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7151 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7152 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7154 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7155 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7157 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7158 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7159 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7160 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7161 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7163 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7164 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7165 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7166 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7167 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7169 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7170 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7171 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7172 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7174 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7175 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7176 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7177 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7178 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7179 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7180 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7181 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7183 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7184 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7185 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7186 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7187 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7188 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7189 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7190 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7191 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7192 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7193 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7194 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7195 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7196 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7198 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7199 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7200 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7201 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7202 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7204 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7205 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7207 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7208 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7209 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7210 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7211 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7212 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7213 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7214 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7215 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7216 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7217 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7219 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7220 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7222 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7223 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7224 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7225 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7227 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7228 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7229 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7230 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7231 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7233 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7234 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7235 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7236 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7237 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7238 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7240 /* Channel options */
7241 DEFINE_CHANNEL_OPTION(defaulttopic);
7242 DEFINE_CHANNEL_OPTION(topicmask);
7243 DEFINE_CHANNEL_OPTION(greeting);
7244 DEFINE_CHANNEL_OPTION(usergreeting);
7245 DEFINE_CHANNEL_OPTION(modes);
7246 DEFINE_CHANNEL_OPTION(enfops);
7247 DEFINE_CHANNEL_OPTION(giveops);
7248 DEFINE_CHANNEL_OPTION(protect);
7249 DEFINE_CHANNEL_OPTION(enfmodes);
7250 DEFINE_CHANNEL_OPTION(enftopic);
7251 DEFINE_CHANNEL_OPTION(pubcmd);
7252 DEFINE_CHANNEL_OPTION(givevoice);
7253 DEFINE_CHANNEL_OPTION(userinfo);
7254 DEFINE_CHANNEL_OPTION(dynlimit);
7255 DEFINE_CHANNEL_OPTION(topicsnarf);
7256 DEFINE_CHANNEL_OPTION(nodelete);
7257 DEFINE_CHANNEL_OPTION(toys);
7258 DEFINE_CHANNEL_OPTION(setters);
7259 DEFINE_CHANNEL_OPTION(topicrefresh);
7260 DEFINE_CHANNEL_OPTION(ctcpusers);
7261 DEFINE_CHANNEL_OPTION(ctcpreaction);
7262 DEFINE_CHANNEL_OPTION(inviteme);
7264 DEFINE_CHANNEL_OPTION(offchannel);
7265 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7267 /* Alias set topic to set defaulttopic for compatibility. */
7268 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7271 DEFINE_USER_OPTION(noautoop);
7272 DEFINE_USER_OPTION(autoinvite);
7273 DEFINE_USER_OPTION(info);
7275 /* Alias uset autovoice to uset autoop. */
7276 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7278 note_types = dict_new();
7279 dict_set_free_data(note_types, chanserv_deref_note_type);
7282 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7283 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7284 service_register(chanserv)->trigger = '!';
7285 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7287 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7289 if(chanserv_conf.channel_expire_frequency)
7290 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7292 if(chanserv_conf.refresh_period)
7294 time_t next_refresh;
7295 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7296 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7299 reg_exit_func(chanserv_db_cleanup);
7300 message_register_table(msgtab);