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 unsigned int use_registered_mode;
507 struct string_list *set_shows;
508 struct string_list *eightball;
509 struct string_list *old_ban_names;
511 const char *ctcp_short_ban_duration;
512 const char *ctcp_long_ban_duration;
514 const char *irc_operator_epithet;
515 const char *network_helper_epithet;
516 const char *support_helper_epithet;
521 struct userNode *user;
522 struct userNode *bot;
523 struct chanNode *channel;
525 unsigned short lowest;
526 unsigned short highest;
527 struct userData **users;
528 struct helpfile_table table;
531 enum note_access_type
533 NOTE_SET_CHANNEL_ACCESS,
534 NOTE_SET_CHANNEL_SETTER,
538 enum note_visible_type
541 NOTE_VIS_CHANNEL_USERS,
547 enum note_access_type set_access_type;
549 unsigned int min_opserv;
550 unsigned short min_ulevel;
552 enum note_visible_type visible_type;
553 unsigned int max_length;
560 struct note_type *type;
561 char setter[NICKSERV_HANDLE_LEN+1];
565 static unsigned int registered_channels;
566 static unsigned int banCount;
568 static const struct {
571 unsigned short level;
574 { "peon", "Peon", UL_PEON, '+' },
575 { "op", "Op", UL_OP, '@' },
576 { "master", "Master", UL_MASTER, '%' },
577 { "coowner", "Coowner", UL_COOWNER, '*' },
578 { "owner", "Owner", UL_OWNER, '!' },
579 { "helper", "BUG:", UL_HELPER, 'X' }
582 static const struct {
585 unsigned short default_value;
586 unsigned int old_idx;
587 unsigned int old_flag;
588 unsigned short flag_value;
590 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
591 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
592 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
593 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
594 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
595 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
596 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
597 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
598 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
599 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
600 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
603 struct charOptionValues {
606 } protectValues[] = {
607 { 'a', "CSMSG_PROTECT_ALL" },
608 { 'e', "CSMSG_PROTECT_EQUAL" },
609 { 'l', "CSMSG_PROTECT_LOWER" },
610 { 'n', "CSMSG_PROTECT_NONE" }
612 { 'd', "CSMSG_TOYS_DISABLED" },
613 { 'n', "CSMSG_TOYS_PRIVATE" },
614 { 'p', "CSMSG_TOYS_PUBLIC" }
615 }, topicRefreshValues[] = {
616 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
617 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
618 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
619 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
620 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
621 }, ctcpReactionValues[] = {
622 { 'k', "CSMSG_CTCPREACTION_KICK" },
623 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
624 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
625 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
628 static const struct {
632 unsigned int old_idx;
634 struct charOptionValues *values;
636 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
637 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
638 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
639 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
642 struct userData *helperList;
643 struct chanData *channelList;
644 static struct module *chanserv_module;
645 static unsigned int userCount;
647 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
648 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
649 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
652 user_level_from_name(const char *name, unsigned short clamp_level)
654 unsigned int level = 0, ii;
656 level = strtoul(name, NULL, 10);
657 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
658 if(!irccasecmp(name, accessLevels[ii].name))
659 level = accessLevels[ii].level;
660 if(level > clamp_level)
666 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
669 *minl = strtoul(arg, &sep, 10);
677 *maxl = strtoul(sep+1, &sep, 10);
685 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
687 struct userData *uData, **head;
689 if(!channel || !handle)
692 if(override && HANDLE_FLAGGED(handle, HELPING)
693 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
695 for(uData = helperList;
696 uData && uData->handle != handle;
697 uData = uData->next);
701 uData = calloc(1, sizeof(struct userData));
702 uData->handle = handle;
704 uData->access = UL_HELPER;
710 uData->next = helperList;
712 helperList->prev = uData;
720 for(uData = channel->users; uData; uData = uData->next)
721 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
724 head = &(channel->users);
727 if(uData && (uData != *head))
729 /* Shuffle the user to the head of whatever list he was in. */
731 uData->next->prev = uData->prev;
733 uData->prev->next = uData->next;
739 (**head).prev = uData;
746 /* Returns non-zero if user has at least the minimum access.
747 * exempt_owner is set when handling !set, so the owner can set things
750 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
752 struct userData *uData;
753 struct chanData *cData = channel->channel_info;
754 unsigned short minimum = cData->lvlOpts[opt];
757 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
760 if(minimum <= uData->access)
762 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
767 /* Scan for other users authenticated to the same handle
768 still in the channel. If so, keep them listed as present.
770 user is optional, if not null, it skips checking that userNode
771 (for the handle_part function) */
773 scan_user_presence(struct userData *uData, struct userNode *user)
777 if(IsSuspended(uData->channel)
778 || IsUserSuspended(uData)
779 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
791 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
793 unsigned int eflags, argc;
795 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
797 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
798 if(!channel->channel_info
799 || IsSuspended(channel->channel_info)
801 || !ircncasecmp(text, "ACTION ", 7))
803 /* Figure out the minimum level needed to CTCP the channel */
804 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
806 /* We need to enforce against them; do so. */
809 argv[1] = user->nick;
811 if(GetUserMode(channel, user))
812 eflags |= ACTION_KICK;
813 switch(channel->channel_info->chOpts[chCTCPReaction]) {
814 default: case 'k': /* just do the kick */ break;
816 eflags |= ACTION_BAN;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
823 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
824 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
827 argv[argc++] = bad_ctcp_reason;
828 eject_user(chanserv, channel, argc, argv, NULL, eflags);
832 chanserv_create_note_type(const char *name)
834 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
835 strcpy(ntype->name, name);
837 dict_insert(note_types, ntype->name, ntype);
842 chanserv_deref_note_type(void *data)
844 struct note_type *ntype = data;
846 if(--ntype->refs > 0)
852 chanserv_flush_note_type(struct note_type *ntype)
854 struct chanData *cData;
855 for(cData = channelList; cData; cData = cData->next)
856 dict_remove(cData->notes, ntype->name);
860 chanserv_truncate_notes(struct note_type *ntype)
862 struct chanData *cData;
864 unsigned int size = sizeof(*note) + ntype->max_length;
866 for(cData = channelList; cData; cData = cData->next) {
867 note = dict_find(cData->notes, ntype->name, NULL);
870 if(strlen(note->note) <= ntype->max_length)
872 dict_remove2(cData->notes, ntype->name, 1);
873 note = realloc(note, size);
874 note->note[ntype->max_length] = 0;
875 dict_insert(cData->notes, ntype->name, note);
879 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
882 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
885 unsigned int len = strlen(text);
887 if(len > type->max_length) len = type->max_length;
888 note = calloc(1, sizeof(*note) + len);
890 strncpy(note->setter, setter, sizeof(note->setter)-1);
891 memcpy(note->note, text, len);
893 dict_insert(channel->notes, type->name, note);
899 chanserv_free_note(void *data)
901 struct note *note = data;
903 chanserv_deref_note_type(note->type);
904 assert(note->type->refs > 0); /* must use delnote to remove the type */
908 static MODCMD_FUNC(cmd_createnote) {
909 struct note_type *ntype;
910 unsigned int arg = 1, existed = 0, max_length;
912 if((ntype = dict_find(note_types, argv[1], NULL)))
915 ntype = chanserv_create_note_type(argv[arg]);
916 if(!irccasecmp(argv[++arg], "privileged"))
919 ntype->set_access_type = NOTE_SET_PRIVILEGED;
920 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
922 else if(!irccasecmp(argv[arg], "channel"))
924 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
927 reply("CSMSG_INVALID_ACCESS", argv[arg]);
930 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
931 ntype->set_access.min_ulevel = ulvl;
933 else if(!irccasecmp(argv[arg], "setter"))
935 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
939 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
943 if(!irccasecmp(argv[++arg], "privileged"))
944 ntype->visible_type = NOTE_VIS_PRIVILEGED;
945 else if(!irccasecmp(argv[arg], "channel_users"))
946 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
947 else if(!irccasecmp(argv[arg], "all"))
948 ntype->visible_type = NOTE_VIS_ALL;
950 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
954 if((arg+1) >= argc) {
955 reply("MSG_MISSING_PARAMS", argv[0]);
958 max_length = strtoul(argv[++arg], NULL, 0);
959 if(max_length < 20 || max_length > 450)
961 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
964 if(existed && (max_length < ntype->max_length))
966 ntype->max_length = max_length;
967 chanserv_truncate_notes(ntype);
969 ntype->max_length = max_length;
972 reply("CSMSG_NOTE_MODIFIED", ntype->name);
974 reply("CSMSG_NOTE_CREATED", ntype->name);
979 dict_remove(note_types, ntype->name);
983 static MODCMD_FUNC(cmd_removenote) {
984 struct note_type *ntype;
987 ntype = dict_find(note_types, argv[1], NULL);
988 force = (argc > 2) && !irccasecmp(argv[2], "force");
991 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
998 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1001 chanserv_flush_note_type(ntype);
1003 dict_remove(note_types, argv[1]);
1004 reply("CSMSG_NOTE_DELETED", argv[1]);
1009 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1013 if(orig->modes_set & change->modes_clear)
1015 if(orig->modes_clear & change->modes_set)
1017 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1018 && strcmp(orig->new_key, change->new_key))
1020 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1021 && (orig->new_limit != change->new_limit))
1026 static char max_length_text[MAXLEN+1][16];
1028 static struct helpfile_expansion
1029 chanserv_expand_variable(const char *variable)
1031 struct helpfile_expansion exp;
1033 if(!irccasecmp(variable, "notes"))
1036 exp.type = HF_TABLE;
1037 exp.value.table.length = 1;
1038 exp.value.table.width = 3;
1039 exp.value.table.flags = 0;
1040 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1041 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1042 exp.value.table.contents[0][0] = "Note Type";
1043 exp.value.table.contents[0][1] = "Visibility";
1044 exp.value.table.contents[0][2] = "Max Length";
1045 for(it=dict_first(note_types); it; it=iter_next(it))
1047 struct note_type *ntype = iter_data(it);
1050 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1051 row = exp.value.table.length++;
1052 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1053 exp.value.table.contents[row][0] = ntype->name;
1054 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1055 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1057 if(!max_length_text[ntype->max_length][0])
1058 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1059 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1064 exp.type = HF_STRING;
1065 exp.value.str = NULL;
1069 static struct chanData*
1070 register_channel(struct chanNode *cNode, char *registrar)
1072 struct chanData *channel;
1073 enum levelOption lvlOpt;
1074 enum charOption chOpt;
1076 channel = calloc(1, sizeof(struct chanData));
1078 channel->notes = dict_new();
1079 dict_set_free_data(channel->notes, chanserv_free_note);
1081 channel->registrar = strdup(registrar);
1082 channel->registered = now;
1083 channel->visited = now;
1084 channel->limitAdjusted = now;
1085 channel->ownerTransfer = now;
1086 channel->flags = CHANNEL_DEFAULT_FLAGS;
1087 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1088 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1089 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1090 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1092 channel->prev = NULL;
1093 channel->next = channelList;
1096 channelList->prev = channel;
1097 channelList = channel;
1098 registered_channels++;
1100 channel->channel = cNode;
1102 cNode->channel_info = channel;
1107 static struct userData*
1108 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1110 struct userData *ud;
1112 if(access > UL_OWNER)
1115 ud = calloc(1, sizeof(*ud));
1116 ud->channel = channel;
1117 ud->handle = handle;
1119 ud->access = access;
1120 ud->info = info ? strdup(info) : NULL;
1123 ud->next = channel->users;
1125 channel->users->prev = ud;
1126 channel->users = ud;
1128 channel->userCount++;
1132 ud->u_next = ud->handle->channels;
1134 ud->u_next->u_prev = ud;
1135 ud->handle->channels = ud;
1140 static void unregister_channel(struct chanData *channel, const char *reason);
1143 del_channel_user(struct userData *user, int do_gc)
1145 struct chanData *channel = user->channel;
1147 channel->userCount--;
1151 user->prev->next = user->next;
1153 channel->users = user->next;
1155 user->next->prev = user->prev;
1158 user->u_prev->u_next = user->u_next;
1160 user->handle->channels = user->u_next;
1162 user->u_next->u_prev = user->u_prev;
1166 if(do_gc && !channel->users && !IsProtected(channel))
1167 unregister_channel(channel, "lost all users.");
1170 static void expire_ban(void *data);
1172 static struct banData*
1173 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1176 unsigned int ii, l1, l2;
1181 bd = malloc(sizeof(struct banData));
1183 bd->channel = channel;
1185 bd->triggered = triggered;
1186 bd->expires = expires;
1188 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1190 extern const char *hidden_host_suffix;
1191 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1195 l2 = strlen(old_name);
1198 if(irccasecmp(mask + l1 - l2, old_name))
1200 new_mask = alloca(MAXLEN);
1201 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1204 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1206 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1207 bd->reason = reason ? strdup(reason) : NULL;
1210 timeq_add(expires, expire_ban, bd);
1213 bd->next = channel->bans;
1215 channel->bans->prev = bd;
1217 channel->banCount++;
1224 del_channel_ban(struct banData *ban)
1226 ban->channel->banCount--;
1230 ban->prev->next = ban->next;
1232 ban->channel->bans = ban->next;
1235 ban->next->prev = ban->prev;
1238 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1247 expire_ban(void *data)
1249 struct banData *bd = data;
1250 if(!IsSuspended(bd->channel))
1252 struct banList bans;
1253 struct mod_chanmode change;
1255 bans = bd->channel->channel->banlist;
1256 mod_chanmode_init(&change);
1257 for(ii=0; ii<bans.used; ii++)
1259 if(!strcmp(bans.list[ii]->ban, bd->mask))
1262 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1263 change.args[0].u.hostmask = bd->mask;
1264 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1270 del_channel_ban(bd);
1273 static void chanserv_expire_suspension(void *data);
1276 unregister_channel(struct chanData *channel, const char *reason)
1278 struct mod_chanmode change;
1279 char msgbuf[MAXLEN];
1281 /* After channel unregistration, the following must be cleaned
1283 - Channel information.
1286 - Channel suspension data.
1287 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1293 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 if(chanserv_conf.use_registered_mode)
1297 mod_chanmode_init(&change);
1298 change.modes_clear |= MODE_REGISTERED;
1299 mod_chanmode_announce(chanserv, channel->channel, &change);
1302 while(channel->users)
1303 del_channel_user(channel->users, 0);
1305 while(channel->bans)
1306 del_channel_ban(channel->bans);
1308 free(channel->topic);
1309 free(channel->registrar);
1310 free(channel->greeting);
1311 free(channel->user_greeting);
1312 free(channel->topic_mask);
1315 channel->prev->next = channel->next;
1317 channelList = channel->next;
1320 channel->next->prev = channel->prev;
1322 if(channel->suspended)
1324 struct chanNode *cNode = channel->channel;
1325 struct suspended *suspended, *next_suspended;
1327 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1329 next_suspended = suspended->previous;
1330 free(suspended->suspender);
1331 free(suspended->reason);
1332 if(suspended->expires)
1333 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1338 cNode->channel_info = NULL;
1340 channel->channel->channel_info = NULL;
1342 dict_delete(channel->notes);
1343 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1344 if(!IsSuspended(channel))
1345 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1346 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1347 UnlockChannel(channel->channel);
1349 registered_channels--;
1353 expire_channels(UNUSED_ARG(void *data))
1355 struct chanData *channel, *next;
1356 struct userData *user;
1357 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1359 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1360 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1362 for(channel = channelList; channel; channel = next)
1364 next = channel->next;
1366 /* See if the channel can be expired. */
1367 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1368 || IsProtected(channel))
1371 /* Make sure there are no high-ranking users still in the channel. */
1372 for(user=channel->users; user; user=user->next)
1373 if(user->present && (user->access >= UL_PRESENT))
1378 /* Unregister the channel */
1379 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1380 unregister_channel(channel, "registration expired.");
1383 if(chanserv_conf.channel_expire_frequency)
1384 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1388 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1390 char protect = channel->chOpts[chProtect];
1391 struct userData *cs_victim, *cs_aggressor;
1393 /* Don't protect if no one is to be protected, someone is attacking
1394 himself, or if the aggressor is an IRC Operator. */
1395 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1398 /* Don't protect if the victim isn't authenticated (because they
1399 can't be a channel user), unless we are to protect non-users
1401 cs_victim = GetChannelAccess(channel, victim->handle_info);
1402 if(protect != 'a' && !cs_victim)
1405 /* Protect if the aggressor isn't a user because at this point,
1406 the aggressor can only be less than or equal to the victim. */
1407 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1411 /* If the aggressor was a user, then the victim can't be helped. */
1418 if(cs_victim->access > cs_aggressor->access)
1423 if(cs_victim->access >= cs_aggressor->access)
1432 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1434 struct chanData *cData = channel->channel_info;
1435 struct userData *cs_victim;
1437 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1438 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1439 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1441 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1449 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1451 if(IsService(victim))
1453 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1457 if(protect_user(victim, user, channel->channel_info))
1459 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1466 static struct do_not_register *
1467 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1469 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1470 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1471 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1472 strcpy(dnr->reason, reason);
1474 if(dnr->chan_name[0] == '*')
1475 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1476 else if(strpbrk(dnr->chan_name, "*?"))
1477 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1479 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1483 static struct dnrList
1484 chanserv_find_dnrs(const char *chan_name, const char *handle)
1486 struct dnrList list;
1488 struct do_not_register *dnr;
1490 dnrList_init(&list);
1491 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1492 dnrList_append(&list, dnr);
1493 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1494 dnrList_append(&list, dnr);
1496 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1497 if(match_ircglob(chan_name, iter_key(it)))
1498 dnrList_append(&list, iter_data(it));
1503 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1505 struct dnrList list;
1506 struct do_not_register *dnr;
1508 char buf[INTERVALLEN];
1510 list = chanserv_find_dnrs(chan_name, handle);
1511 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1513 dnr = list.list[ii];
1516 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1517 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1520 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1523 reply("CSMSG_MORE_DNRS", list.used - ii);
1528 struct do_not_register *
1529 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1531 struct do_not_register *dnr;
1534 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1538 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1540 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1541 if(match_ircglob(chan_name, iter_key(it)))
1542 return iter_data(it);
1547 static CHANSERV_FUNC(cmd_noregister)
1550 struct do_not_register *dnr;
1551 char buf[INTERVALLEN];
1552 unsigned int matches;
1558 reply("CSMSG_DNR_SEARCH_RESULTS");
1560 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1562 dnr = iter_data(it);
1564 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1566 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1569 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1571 dnr = iter_data(it);
1573 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1575 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1578 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1580 dnr = iter_data(it);
1582 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1584 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1589 reply("MSG_MATCH_COUNT", matches);
1591 reply("MSG_NO_MATCHES");
1597 if(!IsChannelName(target) && (*target != '*'))
1599 reply("CSMSG_NOT_DNR", target);
1605 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1606 if((*target == '*') && !get_handle_info(target + 1))
1608 reply("MSG_HANDLE_UNKNOWN", target + 1);
1611 chanserv_add_dnr(target, user->handle_info->handle, reason);
1612 reply("CSMSG_NOREGISTER_CHANNEL", target);
1616 reply("CSMSG_DNR_SEARCH_RESULTS");
1618 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1620 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1622 reply("MSG_NO_MATCHES");
1626 static CHANSERV_FUNC(cmd_allowregister)
1628 const char *chan_name = argv[1];
1630 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1632 dict_remove(handle_dnrs, chan_name+1);
1633 reply("CSMSG_DNR_REMOVED", chan_name);
1635 else if(dict_find(plain_dnrs, chan_name, NULL))
1637 dict_remove(plain_dnrs, chan_name);
1638 reply("CSMSG_DNR_REMOVED", chan_name);
1640 else if(dict_find(mask_dnrs, chan_name, NULL))
1642 dict_remove(mask_dnrs, chan_name);
1643 reply("CSMSG_DNR_REMOVED", chan_name);
1647 reply("CSMSG_NO_SUCH_DNR", chan_name);
1654 chanserv_get_owned_count(struct handle_info *hi)
1656 struct userData *cList;
1659 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1660 if(cList->access == UL_OWNER)
1665 static CHANSERV_FUNC(cmd_register)
1667 struct mod_chanmode *change;
1668 struct handle_info *handle;
1669 struct chanData *cData;
1670 struct modeNode *mn;
1671 char reason[MAXLEN];
1673 unsigned int new_channel, force=0;
1674 struct do_not_register *dnr;
1678 if(channel->channel_info)
1680 reply("CSMSG_ALREADY_REGGED", channel->name);
1684 if(channel->bad_channel)
1686 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1690 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1692 reply("CSMSG_MUST_BE_OPPED", channel->name);
1697 chan_name = channel->name;
1701 if((argc < 2) || !IsChannelName(argv[1]))
1703 reply("MSG_NOT_CHANNEL_NAME");
1707 if(opserv_bad_channel(argv[1]))
1709 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1714 chan_name = argv[1];
1717 if(argc >= (new_channel+2))
1719 if(!IsHelping(user))
1721 reply("CSMSG_PROXY_FORBIDDEN");
1725 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1727 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1728 dnr = chanserv_is_dnr(chan_name, handle);
1732 handle = user->handle_info;
1733 dnr = chanserv_is_dnr(chan_name, handle);
1737 if(!IsHelping(user))
1738 reply("CSMSG_DNR_CHANNEL", chan_name);
1740 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1744 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1746 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1751 channel = AddChannel(argv[1], now, NULL, NULL);
1753 cData = register_channel(channel, user->handle_info->handle);
1754 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1755 cData->modes = chanserv_conf.default_modes;
1756 if(chanserv_conf.use_registered_mode)
1757 cData->modes.modes_set |= MODE_REGISTERED;
1758 change = mod_chanmode_dup(&cData->modes, 1);
1759 change->args[change->argc].mode = MODE_CHANOP;
1760 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1762 mod_chanmode_announce(chanserv, channel, change);
1763 mod_chanmode_free(change);
1765 /* Initialize the channel's max user record. */
1766 cData->max = channel->members.used;
1768 if(handle != user->handle_info)
1769 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1771 reply("CSMSG_REG_SUCCESS", channel->name);
1773 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1774 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1779 make_confirmation_string(struct userData *uData)
1781 static char strbuf[16];
1786 for(src = uData->handle->handle; *src; )
1787 accum = accum * 31 + toupper(*src++);
1789 for(src = uData->channel->channel->name; *src; )
1790 accum = accum * 31 + toupper(*src++);
1791 sprintf(strbuf, "%08x", accum);
1795 static CHANSERV_FUNC(cmd_unregister)
1798 char reason[MAXLEN];
1799 struct chanData *cData;
1800 struct userData *uData;
1802 cData = channel->channel_info;
1805 reply("CSMSG_NOT_REGISTERED", channel->name);
1809 uData = GetChannelUser(cData, user->handle_info);
1810 if(!uData || (uData->access < UL_OWNER))
1812 reply("CSMSG_NO_ACCESS");
1816 if(IsProtected(cData))
1818 reply("CSMSG_UNREG_NODELETE", channel->name);
1822 if(!IsHelping(user))
1824 const char *confirm_string;
1825 if(IsSuspended(cData))
1827 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1830 confirm_string = make_confirmation_string(uData);
1831 if((argc < 2) || strcmp(argv[1], confirm_string))
1833 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1838 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1839 name = strdup(channel->name);
1840 unregister_channel(cData, reason);
1841 reply("CSMSG_UNREG_SUCCESS", name);
1846 static CHANSERV_FUNC(cmd_move)
1848 struct mod_chanmode change;
1849 struct chanNode *target;
1850 struct modeNode *mn;
1851 struct userData *uData;
1852 char reason[MAXLEN];
1853 struct do_not_register *dnr;
1857 if(IsProtected(channel->channel_info))
1859 reply("CSMSG_MOVE_NODELETE", channel->name);
1863 if(!IsChannelName(argv[1]))
1865 reply("MSG_NOT_CHANNEL_NAME");
1869 if(opserv_bad_channel(argv[1]))
1871 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1875 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1877 for(uData = channel->channel_info->users; uData; uData = uData->next)
1879 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1881 if(!IsHelping(user))
1882 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1884 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1890 mod_chanmode_init(&change);
1891 if(!(target = GetChannel(argv[1])))
1893 target = AddChannel(argv[1], now, NULL, NULL);
1894 if(!IsSuspended(channel->channel_info))
1895 AddChannelUser(chanserv, target);
1897 else if(target->channel_info)
1899 reply("CSMSG_ALREADY_REGGED", target->name);
1902 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1903 && !IsHelping(user))
1905 reply("CSMSG_MUST_BE_OPPED", target->name);
1908 else if(!IsSuspended(channel->channel_info))
1911 change.args[0].mode = MODE_CHANOP;
1912 change.args[0].u.member = AddChannelUser(chanserv, target);
1913 mod_chanmode_announce(chanserv, target, &change);
1916 if(chanserv_conf.use_registered_mode)
1918 /* Clear MODE_REGISTERED from old channel, add it to new. */
1920 change.modes_clear = MODE_REGISTERED;
1921 mod_chanmode_announce(chanserv, channel, &change);
1922 change.modes_clear = 0;
1923 change.modes_set = MODE_REGISTERED;
1924 mod_chanmode_announce(chanserv, target, &change);
1927 /* Move the channel_info to the target channel; it
1928 shouldn't be necessary to clear timeq callbacks
1929 for the old channel. */
1930 target->channel_info = channel->channel_info;
1931 target->channel_info->channel = target;
1932 channel->channel_info = NULL;
1934 reply("CSMSG_MOVE_SUCCESS", target->name);
1936 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1937 if(!IsSuspended(target->channel_info))
1939 char reason2[MAXLEN];
1940 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1941 DelChannelUser(chanserv, channel, reason2, 0);
1943 UnlockChannel(channel);
1944 LockChannel(target);
1945 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1950 merge_users(struct chanData *source, struct chanData *target)
1952 struct userData *suData, *tuData, *next;
1958 /* Insert the source's users into the scratch area. */
1959 for(suData = source->users; suData; suData = suData->next)
1960 dict_insert(merge, suData->handle->handle, suData);
1962 /* Iterate through the target's users, looking for
1963 users common to both channels. The lower access is
1964 removed from either the scratch area or target user
1966 for(tuData = target->users; tuData; tuData = next)
1968 struct userData *choice;
1970 next = tuData->next;
1972 /* If a source user exists with the same handle as a target
1973 channel's user, resolve the conflict by removing one. */
1974 suData = dict_find(merge, tuData->handle->handle, NULL);
1978 /* Pick the data we want to keep. */
1979 /* If the access is the same, use the later seen time. */
1980 if(suData->access == tuData->access)
1981 choice = (suData->seen > tuData->seen) ? suData : tuData;
1982 else /* Otherwise, keep the higher access level. */
1983 choice = (suData->access > tuData->access) ? suData : tuData;
1985 /* Remove the user that wasn't picked. */
1986 if(choice == tuData)
1988 dict_remove(merge, suData->handle->handle);
1989 del_channel_user(suData, 0);
1992 del_channel_user(tuData, 0);
1995 /* Move the remaining users to the target channel. */
1996 for(it = dict_first(merge); it; it = iter_next(it))
1998 suData = iter_data(it);
2000 /* Insert the user into the target channel's linked list. */
2001 suData->prev = NULL;
2002 suData->next = target->users;
2003 suData->channel = target;
2006 target->users->prev = suData;
2007 target->users = suData;
2009 /* Update the user counts for the target channel; the
2010 source counts are left alone. */
2011 target->userCount++;
2014 /* Possible to assert (source->users == NULL) here. */
2015 source->users = NULL;
2020 merge_bans(struct chanData *source, struct chanData *target)
2022 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2024 /* Hold on to the original head of the target ban list
2025 to avoid comparing source bans with source bans. */
2026 tFront = target->bans;
2028 /* Perform a totally expensive O(n*m) merge, ick. */
2029 for(sbData = source->bans; sbData; sbData = sNext)
2031 /* Flag to track whether the ban's been moved
2032 to the destination yet. */
2035 /* Possible to assert (sbData->prev == NULL) here. */
2036 sNext = sbData->next;
2038 for(tbData = tFront; tbData; tbData = tNext)
2040 tNext = tbData->next;
2042 /* Perform two comparisons between each source
2043 and target ban, conflicts are resolved by
2044 keeping the broader ban and copying the later
2045 expiration and triggered time. */
2046 if(match_ircglobs(tbData->mask, sbData->mask))
2048 /* There is a broader ban in the target channel that
2049 overrides one in the source channel; remove the
2050 source ban and break. */
2051 if(sbData->expires > tbData->expires)
2052 tbData->expires = sbData->expires;
2053 if(sbData->triggered > tbData->triggered)
2054 tbData->triggered = sbData->triggered;
2055 del_channel_ban(sbData);
2058 else if(match_ircglobs(sbData->mask, tbData->mask))
2060 /* There is a broader ban in the source channel that
2061 overrides one in the target channel; remove the
2062 target ban, fall through and move the source over. */
2063 if(tbData->expires > sbData->expires)
2064 sbData->expires = tbData->expires;
2065 if(tbData->triggered > sbData->triggered)
2066 sbData->triggered = tbData->triggered;
2067 if(tbData == tFront)
2069 del_channel_ban(tbData);
2072 /* Source bans can override multiple target bans, so
2073 we allow a source to run through this loop multiple
2074 times, but we can only move it once. */
2079 /* Remove the source ban from the source ban list. */
2081 sbData->next->prev = sbData->prev;
2083 /* Modify the source ban's associated channel. */
2084 sbData->channel = target;
2086 /* Insert the ban into the target channel's linked list. */
2087 sbData->prev = NULL;
2088 sbData->next = target->bans;
2091 target->bans->prev = sbData;
2092 target->bans = sbData;
2094 /* Update the user counts for the target channel. */
2099 /* Possible to assert (source->bans == NULL) here. */
2100 source->bans = NULL;
2104 merge_data(struct chanData *source, struct chanData *target)
2106 if(source->visited > target->visited)
2107 target->visited = source->visited;
2111 merge_channel(struct chanData *source, struct chanData *target)
2113 merge_users(source, target);
2114 merge_bans(source, target);
2115 merge_data(source, target);
2118 static CHANSERV_FUNC(cmd_merge)
2120 struct userData *target_user;
2121 struct chanNode *target;
2122 char reason[MAXLEN];
2126 /* Make sure the target channel exists and is registered to the user
2127 performing the command. */
2128 if(!(target = GetChannel(argv[1])))
2130 reply("MSG_INVALID_CHANNEL");
2134 if(!target->channel_info)
2136 reply("CSMSG_NOT_REGISTERED", target->name);
2140 if(IsProtected(channel->channel_info))
2142 reply("CSMSG_MERGE_NODELETE");
2146 if(IsSuspended(target->channel_info))
2148 reply("CSMSG_MERGE_SUSPENDED");
2152 if(channel == target)
2154 reply("CSMSG_MERGE_SELF");
2158 target_user = GetChannelUser(target->channel_info, user->handle_info);
2159 if(!target_user || (target_user->access < UL_OWNER))
2161 reply("CSMSG_MERGE_NOT_OWNER");
2165 /* Merge the channel structures and associated data. */
2166 merge_channel(channel->channel_info, target->channel_info);
2167 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2168 unregister_channel(channel->channel_info, reason);
2169 reply("CSMSG_MERGE_SUCCESS", target->name);
2173 static CHANSERV_FUNC(cmd_opchan)
2175 struct mod_chanmode change;
2176 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2178 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2181 channel->channel_info->may_opchan = 0;
2182 mod_chanmode_init(&change);
2184 change.args[0].mode = MODE_CHANOP;
2185 change.args[0].u.member = GetUserMode(channel, chanserv);
2186 mod_chanmode_announce(chanserv, channel, &change);
2187 reply("CSMSG_OPCHAN_DONE", channel->name);
2191 static CHANSERV_FUNC(cmd_adduser)
2193 struct userData *actee;
2194 struct userData *actor;
2195 struct handle_info *handle;
2196 unsigned short access;
2200 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2202 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2206 access = user_level_from_name(argv[2], UL_OWNER);
2209 reply("CSMSG_INVALID_ACCESS", argv[2]);
2213 actor = GetChannelUser(channel->channel_info, user->handle_info);
2214 if(actor->access <= access)
2216 reply("CSMSG_NO_BUMP_ACCESS");
2220 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2223 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2225 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2229 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2230 scan_user_presence(actee, NULL);
2231 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2235 static CHANSERV_FUNC(cmd_clvl)
2237 struct handle_info *handle;
2238 struct userData *victim;
2239 struct userData *actor;
2240 unsigned short new_access;
2241 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2245 actor = GetChannelUser(channel->channel_info, user->handle_info);
2247 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2250 if(handle == user->handle_info && !privileged)
2252 reply("CSMSG_NO_SELF_CLVL");
2256 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2258 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2262 if(actor->access <= victim->access && !privileged)
2264 reply("MSG_USER_OUTRANKED", handle->handle);
2268 new_access = user_level_from_name(argv[2], UL_OWNER);
2272 reply("CSMSG_INVALID_ACCESS", argv[2]);
2276 if(new_access >= actor->access && !privileged)
2278 reply("CSMSG_NO_BUMP_ACCESS");
2282 victim->access = new_access;
2283 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2287 static CHANSERV_FUNC(cmd_deluser)
2289 struct handle_info *handle;
2290 struct userData *victim;
2291 struct userData *actor;
2292 unsigned short access;
2297 actor = GetChannelUser(channel->channel_info, user->handle_info);
2299 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2302 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2304 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2310 access = user_level_from_name(argv[1], UL_OWNER);
2313 reply("CSMSG_INVALID_ACCESS", argv[1]);
2316 if(access != victim->access)
2318 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2324 access = victim->access;
2327 if((actor->access <= victim->access) && !IsHelping(user))
2329 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2333 chan_name = strdup(channel->name);
2334 del_channel_user(victim, 1);
2335 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2341 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2343 struct userData *actor, *uData, *next;
2345 actor = GetChannelUser(channel->channel_info, user->handle_info);
2347 if(min_access > max_access)
2349 reply("CSMSG_BAD_RANGE", min_access, max_access);
2353 if((actor->access <= max_access) && !IsHelping(user))
2355 reply("CSMSG_NO_ACCESS");
2359 for(uData = channel->channel_info->users; uData; uData = next)
2363 if((uData->access >= min_access)
2364 && (uData->access <= max_access)
2365 && match_ircglob(uData->handle->handle, mask))
2366 del_channel_user(uData, 1);
2369 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2373 static CHANSERV_FUNC(cmd_mdelowner)
2375 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2378 static CHANSERV_FUNC(cmd_mdelcoowner)
2380 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2383 static CHANSERV_FUNC(cmd_mdelmaster)
2385 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2388 static CHANSERV_FUNC(cmd_mdelop)
2390 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2393 static CHANSERV_FUNC(cmd_mdelpeon)
2395 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2399 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2401 struct banData *bData, *next;
2402 char interval[INTERVALLEN];
2407 limit = now - duration;
2408 for(bData = channel->channel_info->bans; bData; bData = next)
2412 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2415 del_channel_ban(bData);
2419 intervalString(interval, duration, user->handle_info);
2420 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2425 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2427 struct userData *actor, *uData, *next;
2428 char interval[INTERVALLEN];
2432 actor = GetChannelUser(channel->channel_info, user->handle_info);
2433 if(min_access > max_access)
2435 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2439 if((actor->access <= max_access) && !IsHelping(user))
2441 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2446 limit = now - duration;
2447 for(uData = channel->channel_info->users; uData; uData = next)
2451 if((uData->seen > limit) || uData->present)
2454 if(((uData->access >= min_access) && (uData->access <= max_access))
2455 || (!max_access && (uData->access < actor->access)))
2457 del_channel_user(uData, 1);
2465 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2467 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2471 static CHANSERV_FUNC(cmd_trim)
2473 unsigned long duration;
2474 unsigned short min_level, max_level;
2478 duration = ParseInterval(argv[2]);
2481 reply("CSMSG_CANNOT_TRIM");
2485 if(!irccasecmp(argv[1], "bans"))
2487 cmd_trim_bans(user, channel, duration);
2490 else if(!irccasecmp(argv[1], "users"))
2492 cmd_trim_users(user, channel, 0, 0, duration);
2495 else if(parse_level_range(&min_level, &max_level, argv[1]))
2497 cmd_trim_users(user, channel, min_level, max_level, duration);
2500 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2502 cmd_trim_users(user, channel, min_level, min_level, duration);
2507 reply("CSMSG_INVALID_TRIM", argv[1]);
2512 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2513 to the user. cmd_all takes advantage of this. */
2514 static CHANSERV_FUNC(cmd_up)
2516 struct mod_chanmode change;
2517 struct userData *uData;
2520 mod_chanmode_init(&change);
2522 change.args[0].u.member = GetUserMode(channel, user);
2523 if(!change.args[0].u.member)
2526 reply("MSG_CHANNEL_ABSENT", channel->name);
2530 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2534 reply("CSMSG_GODMODE_UP", argv[0]);
2537 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2539 change.args[0].mode = MODE_CHANOP;
2540 errmsg = "CSMSG_ALREADY_OPPED";
2542 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2544 change.args[0].mode = MODE_VOICE;
2545 errmsg = "CSMSG_ALREADY_VOICED";
2550 reply("CSMSG_NO_ACCESS");
2553 change.args[0].mode &= ~change.args[0].u.member->modes;
2554 if(!change.args[0].mode)
2557 reply(errmsg, channel->name);
2560 modcmd_chanmode_announce(&change);
2564 static CHANSERV_FUNC(cmd_down)
2566 struct mod_chanmode change;
2568 mod_chanmode_init(&change);
2570 change.args[0].u.member = GetUserMode(channel, user);
2571 if(!change.args[0].u.member)
2574 reply("MSG_CHANNEL_ABSENT", channel->name);
2578 if(!change.args[0].u.member->modes)
2581 reply("CSMSG_ALREADY_DOWN", channel->name);
2585 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2586 modcmd_chanmode_announce(&change);
2590 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)
2592 struct userData *cList;
2594 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2596 if(IsSuspended(cList->channel)
2597 || IsUserSuspended(cList)
2598 || !GetUserMode(cList->channel->channel, user))
2601 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2607 static CHANSERV_FUNC(cmd_upall)
2609 return cmd_all(CSFUNC_ARGS, cmd_up);
2612 static CHANSERV_FUNC(cmd_downall)
2614 return cmd_all(CSFUNC_ARGS, cmd_down);
2617 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2618 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2621 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)
2623 unsigned int ii, valid;
2624 struct userNode *victim;
2625 struct mod_chanmode *change;
2627 change = mod_chanmode_alloc(argc - 1);
2629 for(ii=valid=0; ++ii < argc; )
2631 if(!(victim = GetUserH(argv[ii])))
2633 change->args[valid].mode = mode;
2634 change->args[valid].u.member = GetUserMode(channel, victim);
2635 if(!change->args[valid].u.member)
2637 if(validate && !validate(user, channel, victim))
2642 change->argc = valid;
2643 if(valid < (argc-1))
2644 reply("CSMSG_PROCESS_FAILED");
2647 modcmd_chanmode_announce(change);
2648 reply(action, channel->name);
2650 mod_chanmode_free(change);
2654 static CHANSERV_FUNC(cmd_op)
2656 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2659 static CHANSERV_FUNC(cmd_deop)
2661 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2664 static CHANSERV_FUNC(cmd_voice)
2666 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2669 static CHANSERV_FUNC(cmd_devoice)
2671 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2675 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2681 for(ii=0; ii<channel->members.used; ii++)
2683 struct modeNode *mn = channel->members.list[ii];
2685 if(IsService(mn->user))
2688 if(!user_matches_glob(mn->user, ban, 1))
2691 if(protect_user(mn->user, user, channel->channel_info))
2695 victims[(*victimCount)++] = mn;
2701 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2703 struct userNode *victim;
2704 struct modeNode **victims;
2705 unsigned int offset, n, victimCount, duration = 0;
2706 char *reason = "Bye.", *ban, *name;
2707 char interval[INTERVALLEN];
2709 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2710 REQUIRE_PARAMS(offset);
2713 reason = unsplit_string(argv + offset, argc - offset, NULL);
2714 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2716 /* Truncate the reason to a length of TOPICLEN, as
2717 the ircd does; however, leave room for an ellipsis
2718 and the kicker's nick. */
2719 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2723 if((victim = GetUserH(argv[1])))
2725 victims = alloca(sizeof(victims[0]));
2726 victims[0] = GetUserMode(channel, victim);
2727 /* XXX: The comparison with ACTION_KICK is just because all
2728 * other actions can work on users outside the channel, and we
2729 * want to allow those (e.g. unbans) in that case. If we add
2730 * some other ejection action for in-channel users, change
2732 victimCount = victims[0] ? 1 : 0;
2734 if(IsService(victim))
2736 reply("MSG_SERVICE_IMMUNE", victim->nick);
2740 if((action == ACTION_KICK) && !victimCount)
2742 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2746 if(protect_user(victim, user, channel->channel_info))
2748 reply("CSMSG_USER_PROTECTED", victim->nick);
2752 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2753 name = victim->nick;
2757 if(!is_ircmask(argv[1]))
2759 reply("MSG_NICK_UNKNOWN", argv[1]);
2763 victims = alloca(sizeof(victims[0]) * channel->members.used);
2765 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2767 reply("CSMSG_MASK_PROTECTED", argv[1]);
2771 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2773 reply("CSMSG_LAME_MASK", argv[1]);
2777 if((action == ACTION_KICK) && (victimCount == 0))
2779 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2783 name = ban = strdup(argv[1]);
2786 /* Truncate the ban in place if necessary; we must ensure
2787 that 'ban' is a valid ban mask before sanitizing it. */
2788 sanitize_ircmask(ban);
2790 if(action & ACTION_ADD_BAN)
2792 struct banData *bData, *next;
2794 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2796 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2801 if(action & ACTION_ADD_TIMED_BAN)
2803 duration = ParseInterval(argv[2]);
2807 reply("CSMSG_DURATION_TOO_LOW");
2811 else if(duration > (86400 * 365 * 2))
2813 reply("CSMSG_DURATION_TOO_HIGH");
2819 for(bData = channel->channel_info->bans; bData; bData = next)
2821 if(match_ircglobs(bData->mask, ban))
2823 int exact = !irccasecmp(bData->mask, ban);
2825 /* The ban is redundant; there is already a ban
2826 with the same effect in place. */
2830 free(bData->reason);
2831 bData->reason = strdup(reason);
2832 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2834 reply("CSMSG_REASON_CHANGE", ban);
2838 if(exact && bData->expires)
2842 /* If the ban matches an existing one exactly,
2843 extend the expiration time if the provided
2844 duration is longer. */
2845 if(duration && ((time_t)(now + duration) > bData->expires))
2847 bData->expires = now + duration;
2858 /* Delete the expiration timeq entry and
2859 requeue if necessary. */
2860 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2863 timeq_add(bData->expires, expire_ban, bData);
2867 /* automated kickban */
2870 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2872 reply("CSMSG_BAN_ADDED", name, channel->name);
2878 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2885 if(match_ircglobs(ban, bData->mask))
2887 /* The ban we are adding makes previously existing
2888 bans redundant; silently remove them. */
2889 del_channel_ban(bData);
2893 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);
2895 name = ban = strdup(bData->mask);
2899 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2901 extern const char *hidden_host_suffix;
2902 const char *old_name = chanserv_conf.old_ban_names->list[n];
2904 unsigned int l1, l2;
2907 l2 = strlen(old_name);
2910 if(irccasecmp(ban + l1 - l2, old_name))
2912 new_mask = malloc(MAXLEN);
2913 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2915 name = ban = new_mask;
2920 if(action & ACTION_BAN)
2922 unsigned int exists;
2923 struct mod_chanmode *change;
2925 if(channel->banlist.used >= MAXBANS)
2928 reply("CSMSG_BANLIST_FULL", channel->name);
2933 exists = ChannelBanExists(channel, ban);
2934 change = mod_chanmode_alloc(victimCount + 1);
2935 for(n = 0; n < victimCount; ++n)
2937 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2938 change->args[n].u.member = victims[n];
2942 change->args[n].mode = MODE_BAN;
2943 change->args[n++].u.hostmask = ban;
2947 modcmd_chanmode_announce(change);
2949 mod_chanmode_announce(chanserv, channel, change);
2950 mod_chanmode_free(change);
2952 if(exists && (action == ACTION_BAN))
2955 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2961 if(action & ACTION_KICK)
2963 char kick_reason[MAXLEN];
2964 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2966 for(n = 0; n < victimCount; n++)
2967 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2972 /* No response, since it was automated. */
2974 else if(action & ACTION_ADD_BAN)
2977 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2979 reply("CSMSG_BAN_ADDED", name, channel->name);
2981 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2982 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2983 else if(action & ACTION_BAN)
2984 reply("CSMSG_BAN_DONE", name, channel->name);
2985 else if(action & ACTION_KICK && victimCount)
2986 reply("CSMSG_KICK_DONE", name, channel->name);
2992 static CHANSERV_FUNC(cmd_kickban)
2994 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2997 static CHANSERV_FUNC(cmd_kick)
2999 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3002 static CHANSERV_FUNC(cmd_ban)
3004 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3007 static CHANSERV_FUNC(cmd_addban)
3009 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3012 static CHANSERV_FUNC(cmd_addtimedban)
3014 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3017 static struct mod_chanmode *
3018 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3020 struct mod_chanmode *change;
3021 unsigned char *match;
3022 unsigned int ii, count;
3024 match = alloca(bans->used);
3027 for(ii = count = 0; ii < bans->used; ++ii)
3029 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3036 for(ii = count = 0; ii < bans->used; ++ii)
3038 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3045 change = mod_chanmode_alloc(count);
3046 for(ii = count = 0; ii < bans->used; ++ii)
3050 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3051 change->args[count++].u.hostmask = bans->list[ii]->ban;
3057 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3059 struct userNode *actee;
3065 /* may want to allow a comma delimited list of users... */
3066 if(!(actee = GetUserH(argv[1])))
3068 if(!is_ircmask(argv[1]))
3070 reply("MSG_NICK_UNKNOWN", argv[1]);
3074 mask = strdup(argv[1]);
3077 /* We don't sanitize the mask here because ircu
3079 if(action & ACTION_UNBAN)
3081 struct mod_chanmode *change;
3082 change = find_matching_bans(&channel->banlist, actee, mask);
3085 modcmd_chanmode_announce(change);
3086 mod_chanmode_free(change);
3091 if(action & ACTION_DEL_BAN)
3093 struct banData *ban, *next;
3095 ban = channel->channel_info->bans;
3099 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3102 for( ; ban && !match_ircglobs(mask, ban->mask);
3107 del_channel_ban(ban);
3114 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3116 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3122 static CHANSERV_FUNC(cmd_unban)
3124 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3127 static CHANSERV_FUNC(cmd_delban)
3129 /* it doesn't necessarily have to remove the channel ban - may want
3130 to make that an option. */
3131 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3134 static CHANSERV_FUNC(cmd_unbanme)
3136 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3137 long flags = ACTION_UNBAN;
3139 /* remove permanent bans if the user has the proper access. */
3140 if(uData->access >= UL_MASTER)
3141 flags |= ACTION_DEL_BAN;
3143 argv[1] = user->nick;
3144 return unban_user(user, channel, 2, argv, cmd, flags);
3147 static CHANSERV_FUNC(cmd_unbanall)
3149 struct mod_chanmode *change;
3152 if(!channel->banlist.used)
3154 reply("CSMSG_NO_BANS", channel->name);
3158 change = mod_chanmode_alloc(channel->banlist.used);
3159 for(ii=0; ii<channel->banlist.used; ii++)
3161 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3162 change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
3164 modcmd_chanmode_announce(change);
3165 mod_chanmode_free(change);
3166 reply("CSMSG_BANS_REMOVED", channel->name);
3170 static CHANSERV_FUNC(cmd_open)
3172 struct mod_chanmode *change;
3174 change = find_matching_bans(&channel->banlist, user, NULL);
3176 change = mod_chanmode_alloc(0);
3177 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3178 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3179 && channel->channel_info->modes.modes_set)
3180 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3181 modcmd_chanmode_announce(change);
3182 reply("CSMSG_CHANNEL_OPENED", channel->name);
3183 mod_chanmode_free(change);
3187 static CHANSERV_FUNC(cmd_myaccess)
3189 static struct string_buffer sbuf;
3190 struct handle_info *target_handle;
3191 struct userData *uData;
3194 target_handle = user->handle_info;
3195 else if(!IsHelping(user))
3197 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3200 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3203 if(!target_handle->channels)
3205 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3209 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3210 for(uData = target_handle->channels; uData; uData = uData->u_next)
3212 struct chanData *cData = uData->channel;
3214 if(uData->access > UL_OWNER)
3216 if(IsProtected(cData)
3217 && (target_handle != user->handle_info)
3218 && !GetTrueChannelAccess(cData, user->handle_info))
3221 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3222 if(uData->flags != USER_AUTO_OP)
3223 string_buffer_append(&sbuf, ',');
3224 if(IsUserSuspended(uData))
3225 string_buffer_append(&sbuf, 's');
3226 if(IsUserAutoOp(uData))
3228 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3229 string_buffer_append(&sbuf, 'o');
3230 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3231 string_buffer_append(&sbuf, 'v');
3233 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3234 string_buffer_append(&sbuf, 'i');
3236 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3238 string_buffer_append_string(&sbuf, ")]");
3239 string_buffer_append(&sbuf, '\0');
3240 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3246 static CHANSERV_FUNC(cmd_access)
3248 struct userNode *target;
3249 struct handle_info *target_handle;
3250 struct userData *uData;
3252 char prefix[MAXLEN];
3257 target_handle = target->handle_info;
3259 else if((target = GetUserH(argv[1])))
3261 target_handle = target->handle_info;
3263 else if(argv[1][0] == '*')
3265 if(!(target_handle = get_handle_info(argv[1]+1)))
3267 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3273 reply("MSG_NICK_UNKNOWN", argv[1]);
3277 assert(target || target_handle);
3279 if(target == chanserv)
3281 reply("CSMSG_IS_CHANSERV");
3289 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3294 reply("MSG_USER_AUTHENTICATE", target->nick);
3297 reply("MSG_AUTHENTICATE");
3303 const char *epithet = NULL, *type = NULL;
3306 epithet = chanserv_conf.irc_operator_epithet;
3309 else if(IsNetworkHelper(target))
3311 epithet = chanserv_conf.network_helper_epithet;
3312 type = "network helper";
3314 else if(IsSupportHelper(target))
3316 epithet = chanserv_conf.support_helper_epithet;
3317 type = "support helper";
3321 if(target_handle->epithet)
3322 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3324 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3326 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3330 sprintf(prefix, "%s", target_handle->handle);
3333 if(!channel->channel_info)
3335 reply("CSMSG_NOT_REGISTERED", channel->name);
3339 helping = HANDLE_FLAGGED(target_handle, HELPING)
3340 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3341 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3343 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3344 /* To prevent possible information leaks, only show infolines
3345 * if the requestor is in the channel or it's their own
3347 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3349 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3351 /* Likewise, only say it's suspended if the user has active
3352 * access in that channel or it's their own entry. */
3353 if(IsUserSuspended(uData)
3354 && (GetChannelUser(channel->channel_info, user->handle_info)
3355 || (user->handle_info == uData->handle)))
3357 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3362 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3369 zoot_list(struct listData *list)
3371 struct userData *uData;
3372 unsigned int start, curr, highest, lowest;
3373 struct helpfile_table tmp_table;
3374 const char **temp, *msg;
3376 if(list->table.length == 1)
3379 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3381 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3382 msg = user_find_message(list->user, "MSG_NONE");
3383 send_message_type(4, list->user, list->bot, " %s", msg);
3385 tmp_table.width = list->table.width;
3386 tmp_table.flags = list->table.flags;
3387 list->table.contents[0][0] = " ";
3388 highest = list->highest;
3389 if(list->lowest != 0)
3390 lowest = list->lowest;
3391 else if(highest < 100)
3394 lowest = highest - 100;
3395 for(start = curr = 1; curr < list->table.length; )
3397 uData = list->users[curr-1];
3398 list->table.contents[curr++][0] = " ";
3399 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3402 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3404 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3405 temp = list->table.contents[--start];
3406 list->table.contents[start] = list->table.contents[0];
3407 tmp_table.contents = list->table.contents + start;
3408 tmp_table.length = curr - start;
3409 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3410 list->table.contents[start] = temp;
3412 highest = lowest - 1;
3413 lowest = (highest < 100) ? 0 : (highest - 99);
3419 def_list(struct listData *list)
3423 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3425 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3426 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3427 if(list->table.length == 1)
3429 msg = user_find_message(list->user, "MSG_NONE");
3430 send_message_type(4, list->user, list->bot, " %s", msg);
3435 userData_access_comp(const void *arg_a, const void *arg_b)
3437 const struct userData *a = *(struct userData**)arg_a;
3438 const struct userData *b = *(struct userData**)arg_b;
3440 if(a->access != b->access)
3441 res = b->access - a->access;
3443 res = irccasecmp(a->handle->handle, b->handle->handle);
3448 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3450 void (*send_list)(struct listData *);
3451 struct userData *uData;
3452 struct listData lData;
3453 unsigned int matches;
3457 lData.bot = cmd->parent->bot;
3458 lData.channel = channel;
3459 lData.lowest = lowest;
3460 lData.highest = highest;
3461 lData.search = (argc > 1) ? argv[1] : NULL;
3462 send_list = def_list;
3463 (void)zoot_list; /* since it doesn't show user levels */
3465 if(user->handle_info)
3467 switch(user->handle_info->userlist_style)
3469 case HI_STYLE_DEF: send_list = def_list; break;
3470 case HI_STYLE_ZOOT: send_list = def_list; break;
3474 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3476 for(uData = channel->channel_info->users; uData; uData = uData->next)
3478 if((uData->access < lowest)
3479 || (uData->access > highest)
3480 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3482 lData.users[matches++] = uData;
3484 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3486 lData.table.length = matches+1;
3487 lData.table.width = 4;
3488 lData.table.flags = TABLE_NO_FREE;
3489 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3490 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3491 lData.table.contents[0] = ary;
3494 ary[2] = "Last Seen";
3496 for(matches = 1; matches < lData.table.length; ++matches)
3498 struct userData *uData = lData.users[matches-1];
3499 char seen[INTERVALLEN];
3501 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3502 lData.table.contents[matches] = ary;
3503 ary[0] = strtab(uData->access);
3504 ary[1] = uData->handle->handle;
3507 else if(!uData->seen)
3510 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3511 ary[2] = strdup(ary[2]);
3512 if(IsUserSuspended(uData))
3513 ary[3] = "Suspended";
3514 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3515 ary[3] = "Vacation";
3520 for(matches = 1; matches < lData.table.length; ++matches)
3522 free((char*)lData.table.contents[matches][2]);
3523 free(lData.table.contents[matches]);
3525 free(lData.table.contents[0]);
3526 free(lData.table.contents);
3530 static CHANSERV_FUNC(cmd_users)
3532 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3535 static CHANSERV_FUNC(cmd_wlist)
3537 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3540 static CHANSERV_FUNC(cmd_clist)
3542 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3545 static CHANSERV_FUNC(cmd_mlist)
3547 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3550 static CHANSERV_FUNC(cmd_olist)
3552 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3555 static CHANSERV_FUNC(cmd_plist)
3557 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3560 static CHANSERV_FUNC(cmd_bans)
3562 struct helpfile_table tbl;
3563 unsigned int matches = 0, timed = 0, ii;
3564 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3565 const char *msg_never, *triggered, *expires;
3566 struct banData *ban, **bans;
3573 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3575 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3577 if(search && !match_ircglobs(search, ban->mask))
3579 bans[matches++] = ban;
3584 tbl.length = matches + 1;
3585 tbl.width = 4 + timed;
3587 tbl.flags = TABLE_NO_FREE;
3588 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3589 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3590 tbl.contents[0][0] = "Mask";
3591 tbl.contents[0][1] = "Set By";
3592 tbl.contents[0][2] = "Triggered";
3595 tbl.contents[0][3] = "Expires";
3596 tbl.contents[0][4] = "Reason";
3599 tbl.contents[0][3] = "Reason";
3602 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3604 free(tbl.contents[0]);
3609 msg_never = user_find_message(user, "MSG_NEVER");
3610 for(ii = 0; ii < matches; )
3616 else if(ban->expires)
3617 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3619 expires = msg_never;
3622 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3624 triggered = msg_never;
3626 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3627 tbl.contents[ii][0] = ban->mask;
3628 tbl.contents[ii][1] = ban->owner;
3629 tbl.contents[ii][2] = strdup(triggered);
3632 tbl.contents[ii][3] = strdup(expires);
3633 tbl.contents[ii][4] = ban->reason;
3636 tbl.contents[ii][3] = ban->reason;
3638 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3639 reply("MSG_MATCH_COUNT", matches);
3640 for(ii = 1; ii < tbl.length; ++ii)
3642 free((char*)tbl.contents[ii][2]);
3644 free((char*)tbl.contents[ii][3]);
3645 free(tbl.contents[ii]);
3647 free(tbl.contents[0]);
3653 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3655 struct chanData *cData = channel->channel_info;
3656 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3658 if(cData->topic_mask)
3659 return !match_ircglob(new_topic, cData->topic_mask);
3660 else if(cData->topic)
3661 return irccasecmp(new_topic, cData->topic);
3666 static CHANSERV_FUNC(cmd_topic)
3668 struct chanData *cData;
3671 cData = channel->channel_info;
3676 SetChannelTopic(channel, chanserv, cData->topic, 1);
3677 reply("CSMSG_TOPIC_SET", cData->topic);
3681 reply("CSMSG_NO_TOPIC", channel->name);
3685 topic = unsplit_string(argv + 1, argc - 1, NULL);
3686 /* If they say "!topic *", use an empty topic. */
3687 if((topic[0] == '*') && (topic[1] == 0))
3689 if(bad_topic(channel, user, topic))
3691 char *topic_mask = cData->topic_mask;
3694 char new_topic[TOPICLEN+1], tchar;
3695 int pos=0, starpos=-1, dpos=0, len;
3697 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3704 len = strlen(topic);
3705 if((dpos + len) > TOPICLEN)
3706 len = TOPICLEN + 1 - dpos;
3707 memcpy(new_topic+dpos, topic, len);
3711 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3712 default: new_topic[dpos++] = tchar; break;
3715 if((dpos > TOPICLEN) || tchar)
3718 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3719 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3722 new_topic[dpos] = 0;
3723 SetChannelTopic(channel, chanserv, new_topic, 1);
3725 reply("CSMSG_TOPIC_LOCKED", channel->name);
3730 SetChannelTopic(channel, chanserv, topic, 1);
3732 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3734 /* Grab the topic and save it as the default topic. */
3736 cData->topic = strdup(channel->topic);
3742 static CHANSERV_FUNC(cmd_mode)
3744 struct mod_chanmode *change;
3748 change = &channel->channel_info->modes;
3749 if(change->modes_set || change->modes_clear) {
3750 modcmd_chanmode_announce(change);
3751 reply("CSMSG_DEFAULTED_MODES", channel->name);
3753 reply("CSMSG_NO_MODES", channel->name);
3757 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3760 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3764 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3765 && mode_lock_violated(&channel->channel_info->modes, change))
3768 mod_chanmode_format(&channel->channel_info->modes, modes);
3769 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3773 modcmd_chanmode_announce(change);
3774 mod_chanmode_free(change);
3775 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3779 static CHANSERV_FUNC(cmd_invite)
3781 struct userData *uData;
3782 struct userNode *invite;
3784 uData = GetChannelUser(channel->channel_info, user->handle_info);
3788 if(!(invite = GetUserH(argv[1])))
3790 reply("MSG_NICK_UNKNOWN", argv[1]);
3797 if(GetUserMode(channel, invite))
3799 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3807 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3808 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3811 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3813 irc_invite(chanserv, invite, channel);
3815 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3820 static CHANSERV_FUNC(cmd_inviteme)
3822 if(GetUserMode(channel, user))
3824 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3827 if(channel->channel_info
3828 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3830 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3833 irc_invite(cmd->parent->bot, user, channel);
3838 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3841 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3843 /* We display things based on two dimensions:
3844 * - Issue time: present or absent
3845 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3846 * (in order of precedence, so something both expired and revoked
3847 * only counts as revoked)
3849 combo = (suspended->issued ? 4 : 0)
3850 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3852 case 0: /* no issue time, indefinite expiration */
3853 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3855 case 1: /* no issue time, expires in future */
3856 intervalString(buf1, suspended->expires-now, user->handle_info);
3857 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3859 case 2: /* no issue time, expired */
3860 intervalString(buf1, now-suspended->expires, user->handle_info);
3861 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3863 case 3: /* no issue time, revoked */
3864 intervalString(buf1, now-suspended->revoked, user->handle_info);
3865 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3867 case 4: /* issue time set, indefinite expiration */
3868 intervalString(buf1, now-suspended->issued, user->handle_info);
3869 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3871 case 5: /* issue time set, expires in future */
3872 intervalString(buf1, now-suspended->issued, user->handle_info);
3873 intervalString(buf2, suspended->expires-now, user->handle_info);
3874 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3876 case 6: /* issue time set, expired */
3877 intervalString(buf1, now-suspended->issued, user->handle_info);
3878 intervalString(buf2, now-suspended->expires, user->handle_info);
3879 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3881 case 7: /* issue time set, revoked */
3882 intervalString(buf1, now-suspended->issued, user->handle_info);
3883 intervalString(buf2, now-suspended->revoked, user->handle_info);
3884 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3887 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3892 static CHANSERV_FUNC(cmd_info)
3894 char modes[MAXLEN], buffer[INTERVALLEN];
3895 struct userData *uData, *owner;
3896 struct chanData *cData;
3897 struct do_not_register *dnr;
3902 cData = channel->channel_info;
3903 reply("CSMSG_CHANNEL_INFO", channel->name);
3905 uData = GetChannelUser(cData, user->handle_info);
3906 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3908 mod_chanmode_format(&cData->modes, modes);
3909 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3910 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3913 for(it = dict_first(cData->notes); it; it = iter_next(it))
3917 note = iter_data(it);
3918 if(!note_type_visible_to_user(cData, note->type, user))
3921 padding = PADLEN - 1 - strlen(iter_key(it));
3922 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3925 reply("CSMSG_CHANNEL_MAX", cData->max);
3926 for(owner = cData->users; owner; owner = owner->next)
3927 if(owner->access == UL_OWNER)
3928 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3929 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3930 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3931 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3932 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3934 privileged = IsStaff(user);
3935 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3936 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3938 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3939 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3941 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3943 struct suspended *suspended;
3944 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3945 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3946 show_suspension_info(cmd, user, suspended);
3948 else if(IsSuspended(cData))
3950 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3951 show_suspension_info(cmd, user, cData->suspended);
3956 static CHANSERV_FUNC(cmd_netinfo)
3958 extern time_t boot_time;
3959 extern unsigned long burst_length;
3960 char interval[INTERVALLEN];
3962 reply("CSMSG_NETWORK_INFO");
3963 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3964 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3965 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3966 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3967 reply("CSMSG_NETWORK_BANS", banCount);
3968 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3969 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3970 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3975 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3977 struct helpfile_table table;
3979 struct userNode *user;
3984 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3985 table.contents = alloca(list->used*sizeof(*table.contents));
3986 for(nn=0; nn<list->used; nn++)
3988 user = list->list[nn];
3989 if(user->modes & skip_flags)
3993 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3996 nick = alloca(strlen(user->nick)+3);
3997 sprintf(nick, "(%s)", user->nick);
4001 table.contents[table.length][0] = nick;
4004 table_send(chanserv, to->nick, 0, NULL, table);
4007 static CHANSERV_FUNC(cmd_ircops)
4009 reply("CSMSG_STAFF_OPERS");
4010 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4014 static CHANSERV_FUNC(cmd_helpers)
4016 reply("CSMSG_STAFF_HELPERS");
4017 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4021 static CHANSERV_FUNC(cmd_staff)
4023 reply("CSMSG_NETWORK_STAFF");
4024 cmd_ircops(CSFUNC_ARGS);
4025 cmd_helpers(CSFUNC_ARGS);
4029 static CHANSERV_FUNC(cmd_peek)
4031 struct modeNode *mn;
4032 char modes[MODELEN];
4034 struct helpfile_table table;
4036 irc_make_chanmode(channel, modes);
4038 reply("CSMSG_PEEK_INFO", channel->name);
4039 reply("CSMSG_PEEK_TOPIC", channel->topic);
4040 reply("CSMSG_PEEK_MODES", modes);
4041 reply("CSMSG_PEEK_USERS", channel->members.used);
4045 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4046 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4047 for(n = 0; n < channel->members.used; n++)
4049 mn = channel->members.list[n];
4050 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4052 table.contents[table.length] = alloca(sizeof(**table.contents));
4053 table.contents[table.length][0] = mn->user->nick;
4058 reply("CSMSG_PEEK_OPS");
4059 table_send(chanserv, user->nick, 0, NULL, table);
4062 reply("CSMSG_PEEK_NO_OPS");
4066 static MODCMD_FUNC(cmd_wipeinfo)
4068 struct handle_info *victim;
4069 struct userData *ud, *actor;
4072 actor = GetChannelUser(channel->channel_info, user->handle_info);
4073 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4075 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4077 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4080 if((ud->access >= actor->access) && (ud != actor))
4082 reply("MSG_USER_OUTRANKED", victim->handle);
4088 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4092 static CHANSERV_FUNC(cmd_resync)
4094 struct mod_chanmode *changes;
4095 struct chanData *cData = channel->channel_info;
4096 unsigned int ii, used;
4098 changes = mod_chanmode_alloc(channel->members.used * 2);
4099 for(ii = used = 0; ii < channel->members.used; ++ii)
4101 struct modeNode *mn = channel->members.list[ii];
4102 struct userData *uData;
4104 if(IsService(mn->user))
4107 uData = GetChannelAccess(cData, mn->user->handle_info);
4108 if(!cData->lvlOpts[lvlGiveOps]
4109 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4111 if(!(mn->modes & MODE_CHANOP))
4113 changes->args[used].mode = MODE_CHANOP;
4114 changes->args[used++].u.member = mn;
4117 else if(!cData->lvlOpts[lvlGiveVoice]
4118 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4120 if(mn->modes & MODE_CHANOP)
4122 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4123 changes->args[used++].u.member = mn;
4125 if(!(mn->modes & MODE_VOICE))
4127 changes->args[used].mode = MODE_VOICE;
4128 changes->args[used++].u.member = mn;
4135 changes->args[used].mode = MODE_REMOVE | mn->modes;
4136 changes->args[used++].u.member = mn;
4140 changes->argc = used;
4141 modcmd_chanmode_announce(changes);
4142 mod_chanmode_free(changes);
4143 reply("CSMSG_RESYNCED_USERS", channel->name);
4147 static CHANSERV_FUNC(cmd_seen)
4149 struct userData *uData;
4150 struct handle_info *handle;
4151 char seen[INTERVALLEN];
4155 if(!irccasecmp(argv[1], chanserv->nick))
4157 reply("CSMSG_IS_CHANSERV");
4161 if(!(handle = get_handle_info(argv[1])))
4163 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4167 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4169 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4174 reply("CSMSG_USER_PRESENT", handle->handle);
4175 else if(uData->seen)
4176 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4178 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4180 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4181 reply("CSMSG_USER_VACATION", handle->handle);
4186 static MODCMD_FUNC(cmd_names)
4188 struct userNode *targ;
4189 struct userData *targData;
4190 unsigned int ii, pos;
4193 for(ii=pos=0; ii<channel->members.used; ++ii)
4195 targ = channel->members.list[ii]->user;
4196 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4199 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4202 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4206 if(IsUserSuspended(targData))
4208 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4211 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4212 reply("CSMSG_END_NAMES", channel->name);
4217 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4219 switch(ntype->visible_type)
4221 case NOTE_VIS_ALL: return 1;
4222 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4223 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4228 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4230 struct userData *uData;
4232 switch(ntype->set_access_type)
4234 case NOTE_SET_CHANNEL_ACCESS:
4235 if(!user->handle_info)
4237 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4239 return uData->access >= ntype->set_access.min_ulevel;
4240 case NOTE_SET_CHANNEL_SETTER:
4241 return check_user_level(channel, user, lvlSetters, 1, 0);
4242 case NOTE_SET_PRIVILEGED: default:
4243 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4247 static CHANSERV_FUNC(cmd_note)
4249 struct chanData *cData;
4251 struct note_type *ntype;
4253 cData = channel->channel_info;
4256 reply("CSMSG_NOT_REGISTERED", channel->name);
4260 /* If no arguments, show all visible notes for the channel. */
4266 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4268 note = iter_data(it);
4269 if(!note_type_visible_to_user(cData, note->type, user))
4272 reply("CSMSG_NOTELIST_HEADER", channel->name);
4273 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4276 reply("CSMSG_NOTELIST_END", channel->name);
4278 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4280 /* If one argument, show the named note. */
4283 if((note = dict_find(cData->notes, argv[1], NULL))
4284 && note_type_visible_to_user(cData, note->type, user))
4286 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4288 else if((ntype = dict_find(note_types, argv[1], NULL))
4289 && note_type_visible_to_user(NULL, ntype, user))
4291 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4296 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4300 /* Assume they're trying to set a note. */
4304 ntype = dict_find(note_types, argv[1], NULL);
4307 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4310 else if(note_type_settable_by_user(channel, ntype, user))
4312 note_text = unsplit_string(argv+2, argc-2, NULL);
4313 if((note = dict_find(cData->notes, argv[1], NULL)))
4314 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4315 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4316 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4318 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4320 /* The note is viewable to staff only, so return 0
4321 to keep the invocation from getting logged (or
4322 regular users can see it in !events). */
4328 reply("CSMSG_NO_ACCESS");
4335 static CHANSERV_FUNC(cmd_delnote)
4340 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4341 || !note_type_settable_by_user(channel, note->type, user))
4343 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4346 dict_remove(channel->channel_info->notes, note->type->name);
4347 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4351 static CHANSERV_FUNC(cmd_events)
4353 struct logSearch discrim;
4354 struct logReport report;
4355 unsigned int matches, limit;
4357 limit = (argc > 1) ? atoi(argv[1]) : 10;
4358 if(limit < 1 || limit > 200)
4361 memset(&discrim, 0, sizeof(discrim));
4362 discrim.masks.bot = chanserv;
4363 discrim.masks.channel_name = channel->name;
4365 discrim.masks.command = argv[2];
4366 discrim.limit = limit;
4367 discrim.max_time = INT_MAX;
4368 discrim.severities = 1 << LOG_COMMAND;
4369 report.reporter = chanserv;
4371 reply("CSMSG_EVENT_SEARCH_RESULTS");
4372 matches = log_entry_search(&discrim, log_report_entry, &report);
4374 reply("MSG_MATCH_COUNT", matches);
4376 reply("MSG_NO_MATCHES");
4380 static CHANSERV_FUNC(cmd_say)
4386 msg = unsplit_string(argv + 1, argc - 1, NULL);
4387 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4389 else if(GetUserH(argv[1]))
4392 msg = unsplit_string(argv + 2, argc - 2, NULL);
4393 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4397 reply("MSG_NOT_TARGET_NAME");
4403 static CHANSERV_FUNC(cmd_emote)
4409 /* CTCP is so annoying. */
4410 msg = unsplit_string(argv + 1, argc - 1, NULL);
4411 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4413 else if(GetUserH(argv[1]))
4415 msg = unsplit_string(argv + 2, argc - 2, NULL);
4416 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4420 reply("MSG_NOT_TARGET_NAME");
4426 struct channelList *
4427 chanserv_support_channels(void)
4429 return &chanserv_conf.support_channels;
4432 static CHANSERV_FUNC(cmd_expire)
4434 int channel_count = registered_channels;
4435 expire_channels(NULL);
4436 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4441 chanserv_expire_suspension(void *data)
4443 struct suspended *suspended = data;
4444 struct chanNode *channel;
4445 struct mod_chanmode change;
4447 if(!suspended->expires || (now < suspended->expires))
4448 suspended->revoked = now;
4449 channel = suspended->cData->channel;
4450 suspended->cData->channel = channel;
4451 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4452 mod_chanmode_init(&change);
4454 change.args[0].mode = MODE_CHANOP;
4455 change.args[0].u.member = AddChannelUser(chanserv, channel);
4456 mod_chanmode_announce(chanserv, channel, &change);
4459 static CHANSERV_FUNC(cmd_csuspend)
4461 struct suspended *suspended;
4462 char reason[MAXLEN];
4463 time_t expiry, duration;
4464 struct userData *uData;
4468 if(IsProtected(channel->channel_info))
4470 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4474 if(argv[1][0] == '!')
4476 else if(IsSuspended(channel->channel_info))
4478 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4479 show_suspension_info(cmd, user, channel->channel_info->suspended);
4483 if(!strcmp(argv[1], "0"))
4485 else if((duration = ParseInterval(argv[1])))
4486 expiry = now + duration;
4489 reply("MSG_INVALID_DURATION", argv[1]);
4493 unsplit_string(argv + 2, argc - 2, reason);
4495 suspended = calloc(1, sizeof(*suspended));
4496 suspended->revoked = 0;
4497 suspended->issued = now;
4498 suspended->suspender = strdup(user->handle_info->handle);
4499 suspended->expires = expiry;
4500 suspended->reason = strdup(reason);
4501 suspended->cData = channel->channel_info;
4502 suspended->previous = suspended->cData->suspended;
4503 suspended->cData->suspended = suspended;
4505 if(suspended->expires)
4506 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4508 if(IsSuspended(channel->channel_info))
4510 suspended->previous->revoked = now;
4511 if(suspended->previous->expires)
4512 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4513 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4514 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4518 /* Mark all users in channel as absent. */
4519 for(uData = channel->channel_info->users; uData; uData = uData->next)
4528 /* Mark the channel as suspended, then part. */
4529 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4530 DelChannelUser(chanserv, channel, suspended->reason, 0);
4531 reply("CSMSG_SUSPENDED", channel->name);
4532 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4533 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4538 static CHANSERV_FUNC(cmd_cunsuspend)
4540 struct suspended *suspended;
4541 char message[MAXLEN];
4543 if(!IsSuspended(channel->channel_info))
4545 reply("CSMSG_NOT_SUSPENDED", channel->name);
4549 suspended = channel->channel_info->suspended;
4551 /* Expire the suspension and join ChanServ to the channel. */
4552 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4553 chanserv_expire_suspension(suspended);
4554 reply("CSMSG_UNSUSPENDED", channel->name);
4555 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4556 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4560 typedef struct chanservSearch
4568 unsigned long flags;
4572 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4575 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4580 search = malloc(sizeof(struct chanservSearch));
4581 memset(search, 0, sizeof(*search));
4584 for(i = 0; i < argc; i++)
4586 /* Assume all criteria require arguments. */
4589 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4593 if(!irccasecmp(argv[i], "name"))
4594 search->name = argv[++i];
4595 else if(!irccasecmp(argv[i], "registrar"))
4596 search->registrar = argv[++i];
4597 else if(!irccasecmp(argv[i], "unvisited"))
4598 search->unvisited = ParseInterval(argv[++i]);
4599 else if(!irccasecmp(argv[i], "registered"))
4600 search->registered = ParseInterval(argv[++i]);
4601 else if(!irccasecmp(argv[i], "flags"))
4604 if(!irccasecmp(argv[i], "nodelete"))
4605 search->flags |= CHANNEL_NODELETE;
4606 else if(!irccasecmp(argv[i], "suspended"))
4607 search->flags |= CHANNEL_SUSPENDED;
4610 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4614 else if(!irccasecmp(argv[i], "limit"))
4615 search->limit = strtoul(argv[++i], NULL, 10);
4618 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4623 if(search->name && !strcmp(search->name, "*"))
4625 if(search->registrar && !strcmp(search->registrar, "*"))
4626 search->registrar = 0;
4635 chanserv_channel_match(struct chanData *channel, search_t search)
4637 const char *name = channel->channel->name;
4638 if((search->name && !match_ircglob(name, search->name)) ||
4639 (search->registrar && !channel->registrar) ||
4640 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4641 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4642 (search->registered && (now - channel->registered) > search->registered) ||
4643 (search->flags && ((search->flags & channel->flags) != search->flags)))
4650 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4652 struct chanData *channel;
4653 unsigned int matches = 0;
4655 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4657 if(!chanserv_channel_match(channel, search))
4667 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4672 search_print(struct chanData *channel, void *data)
4674 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4677 static CHANSERV_FUNC(cmd_search)
4680 unsigned int matches;
4681 channel_search_func action;
4685 if(!irccasecmp(argv[1], "count"))
4686 action = search_count;
4687 else if(!irccasecmp(argv[1], "print"))
4688 action = search_print;
4691 reply("CSMSG_ACTION_INVALID", argv[1]);
4695 search = chanserv_search_create(user, argc - 2, argv + 2);
4699 if(action == search_count)
4700 search->limit = INT_MAX;
4702 if(action == search_print)
4703 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4705 matches = chanserv_channel_search(search, action, user);
4708 reply("MSG_MATCH_COUNT", matches);
4710 reply("MSG_NO_MATCHES");
4716 static CHANSERV_FUNC(cmd_unvisited)
4718 struct chanData *cData;
4719 time_t interval = chanserv_conf.channel_expire_delay;
4720 char buffer[INTERVALLEN];
4721 unsigned int limit = 25, matches = 0;
4725 interval = ParseInterval(argv[1]);
4727 limit = atoi(argv[2]);
4730 intervalString(buffer, interval, user->handle_info);
4731 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4733 for(cData = channelList; cData && matches < limit; cData = cData->next)
4735 if((now - cData->visited) < interval)
4738 intervalString(buffer, now - cData->visited, user->handle_info);
4739 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4746 static MODCMD_FUNC(chan_opt_defaulttopic)
4752 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4754 reply("CSMSG_TOPIC_LOCKED", channel->name);
4758 topic = unsplit_string(argv+1, argc-1, NULL);
4760 free(channel->channel_info->topic);
4761 if(topic[0] == '*' && topic[1] == 0)
4763 topic = channel->channel_info->topic = NULL;
4767 topic = channel->channel_info->topic = strdup(topic);
4768 if(channel->channel_info->topic_mask
4769 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4770 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4772 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4775 if(channel->channel_info->topic)
4776 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4778 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4782 static MODCMD_FUNC(chan_opt_topicmask)
4786 struct chanData *cData = channel->channel_info;
4789 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4791 reply("CSMSG_TOPIC_LOCKED", channel->name);
4795 mask = unsplit_string(argv+1, argc-1, NULL);
4797 if(cData->topic_mask)
4798 free(cData->topic_mask);
4799 if(mask[0] == '*' && mask[1] == 0)
4801 cData->topic_mask = 0;
4805 cData->topic_mask = strdup(mask);
4807 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4808 else if(!match_ircglob(cData->topic, cData->topic_mask))
4809 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4813 if(channel->channel_info->topic_mask)
4814 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4816 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4820 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4824 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4828 if(greeting[0] == '*' && greeting[1] == 0)
4832 unsigned int length = strlen(greeting);
4833 if(length > chanserv_conf.greeting_length)
4835 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4838 *data = strdup(greeting);
4847 reply(name, user_find_message(user, "MSG_NONE"));
4851 static MODCMD_FUNC(chan_opt_greeting)
4853 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4856 static MODCMD_FUNC(chan_opt_usergreeting)
4858 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4861 static MODCMD_FUNC(chan_opt_modes)
4863 struct mod_chanmode *new_modes;
4864 char modes[MODELEN];
4868 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4870 reply("CSMSG_NO_ACCESS");
4873 if(argv[1][0] == '*' && argv[1][1] == 0)
4875 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4877 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4879 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4882 else if(new_modes->argc > 1)
4884 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4885 mod_chanmode_free(new_modes);
4890 channel->channel_info->modes = *new_modes;
4891 modcmd_chanmode_announce(new_modes);
4892 mod_chanmode_free(new_modes);
4896 mod_chanmode_format(&channel->channel_info->modes, modes);
4898 reply("CSMSG_SET_MODES", modes);
4900 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4904 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4906 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4908 struct chanData *cData = channel->channel_info;
4913 /* Set flag according to value. */
4914 if(enabled_string(argv[1]))
4916 cData->flags |= mask;
4919 else if(disabled_string(argv[1]))
4921 cData->flags &= ~mask;
4926 reply("MSG_INVALID_BINARY", argv[1]);
4932 /* Find current option value. */
4933 value = (cData->flags & mask) ? 1 : 0;
4937 reply(name, user_find_message(user, "MSG_ON"));
4939 reply(name, user_find_message(user, "MSG_OFF"));
4943 static MODCMD_FUNC(chan_opt_nodelete)
4945 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4947 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4951 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4954 static MODCMD_FUNC(chan_opt_dynlimit)
4956 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4959 static MODCMD_FUNC(chan_opt_offchannel)
4961 struct chanData *cData = channel->channel_info;
4966 /* Set flag according to value. */
4967 if(enabled_string(argv[1]))
4969 if(!IsOffChannel(cData))
4970 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4971 cData->flags |= CHANNEL_OFFCHANNEL;
4974 else if(disabled_string(argv[1]))
4976 if(IsOffChannel(cData))
4978 struct mod_chanmode change;
4979 mod_chanmode_init(&change);
4981 change.args[0].mode = MODE_CHANOP;
4982 change.args[0].u.member = AddChannelUser(chanserv, channel);
4983 mod_chanmode_announce(chanserv, channel, &change);
4985 cData->flags &= ~CHANNEL_OFFCHANNEL;
4990 reply("MSG_INVALID_BINARY", argv[1]);
4996 /* Find current option value. */
4997 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5001 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5003 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5007 static MODCMD_FUNC(chan_opt_defaults)
5009 struct userData *uData;
5010 struct chanData *cData;
5011 const char *confirm;
5012 enum levelOption lvlOpt;
5013 enum charOption chOpt;
5015 cData = channel->channel_info;
5016 uData = GetChannelUser(cData, user->handle_info);
5017 if(!uData || (uData->access < UL_OWNER))
5019 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5022 confirm = make_confirmation_string(uData);
5023 if((argc < 2) || strcmp(argv[1], confirm))
5025 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5028 cData->flags = CHANNEL_DEFAULT_FLAGS;
5029 cData->modes = chanserv_conf.default_modes;
5030 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5031 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5032 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5033 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5034 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5039 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5041 struct chanData *cData = channel->channel_info;
5042 struct userData *uData;
5043 unsigned short value;
5047 if(!check_user_level(channel, user, option, 1, 1))
5049 reply("CSMSG_CANNOT_SET");
5052 value = user_level_from_name(argv[1], UL_OWNER+1);
5053 if(!value && strcmp(argv[1], "0"))
5055 reply("CSMSG_INVALID_ACCESS", argv[1]);
5058 uData = GetChannelUser(cData, user->handle_info);
5059 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5061 reply("CSMSG_BAD_SETLEVEL");
5067 if(value > cData->lvlOpts[lvlGiveOps])
5069 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5074 if(value < cData->lvlOpts[lvlGiveVoice])
5076 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5081 /* This test only applies to owners, since non-owners
5082 * trying to set an option to above their level get caught
5083 * by the CSMSG_BAD_SETLEVEL test above.
5085 if(value > uData->access)
5087 reply("CSMSG_BAD_SETTERS");
5094 cData->lvlOpts[option] = value;
5096 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5100 static MODCMD_FUNC(chan_opt_enfops)
5102 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5105 static MODCMD_FUNC(chan_opt_giveops)
5107 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5110 static MODCMD_FUNC(chan_opt_enfmodes)
5112 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5115 static MODCMD_FUNC(chan_opt_enftopic)
5117 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5120 static MODCMD_FUNC(chan_opt_pubcmd)
5122 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5125 static MODCMD_FUNC(chan_opt_setters)
5127 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5130 static MODCMD_FUNC(chan_opt_ctcpusers)
5132 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5135 static MODCMD_FUNC(chan_opt_userinfo)
5137 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5140 static MODCMD_FUNC(chan_opt_givevoice)
5142 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5145 static MODCMD_FUNC(chan_opt_topicsnarf)
5147 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5150 static MODCMD_FUNC(chan_opt_inviteme)
5152 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5156 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5158 struct chanData *cData = channel->channel_info;
5159 int count = charOptions[option].count, index;
5163 index = atoi(argv[1]);
5165 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5167 reply("CSMSG_INVALID_NUMERIC", index);
5168 /* Show possible values. */
5169 for(index = 0; index < count; index++)
5170 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5174 cData->chOpts[option] = charOptions[option].values[index].value;
5178 /* Find current option value. */
5181 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5185 /* Somehow, the option value is corrupt; reset it to the default. */
5186 cData->chOpts[option] = charOptions[option].default_value;
5191 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5195 static MODCMD_FUNC(chan_opt_protect)
5197 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5200 static MODCMD_FUNC(chan_opt_toys)
5202 return channel_multiple_option(chToys, CSFUNC_ARGS);
5205 static MODCMD_FUNC(chan_opt_ctcpreaction)
5207 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5210 static MODCMD_FUNC(chan_opt_topicrefresh)
5212 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5215 static struct svccmd_list set_shows_list;
5218 handle_svccmd_unbind(struct svccmd *target) {
5220 for(ii=0; ii<set_shows_list.used; ++ii)
5221 if(target == set_shows_list.list[ii])
5222 set_shows_list.used = 0;
5225 static CHANSERV_FUNC(cmd_set)
5227 struct svccmd *subcmd;
5231 /* Check if we need to (re-)initialize set_shows_list. */
5232 if(!set_shows_list.used)
5234 if(!set_shows_list.size)
5236 set_shows_list.size = chanserv_conf.set_shows->used;
5237 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5239 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5241 const char *name = chanserv_conf.set_shows->list[ii];
5242 sprintf(buf, "%s %s", argv[0], name);
5243 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5246 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5249 svccmd_list_append(&set_shows_list, subcmd);
5255 reply("CSMSG_CHANNEL_OPTIONS");
5256 for(ii = 0; ii < set_shows_list.used; ii++)
5258 subcmd = set_shows_list.list[ii];
5259 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5264 sprintf(buf, "%s %s", argv[0], argv[1]);
5265 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5268 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5271 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5273 reply("CSMSG_NO_ACCESS");
5277 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5281 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5283 struct userData *uData;
5285 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5288 reply("CSMSG_NOT_USER", channel->name);
5294 /* Just show current option value. */
5296 else if(enabled_string(argv[1]))
5298 uData->flags |= mask;
5300 else if(disabled_string(argv[1]))
5302 uData->flags &= ~mask;
5306 reply("MSG_INVALID_BINARY", argv[1]);
5310 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5314 static MODCMD_FUNC(user_opt_noautoop)
5316 struct userData *uData;
5318 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5321 reply("CSMSG_NOT_USER", channel->name);
5324 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5325 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5327 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5330 static MODCMD_FUNC(user_opt_autoinvite)
5332 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5335 static MODCMD_FUNC(user_opt_info)
5337 struct userData *uData;
5340 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5344 /* If they got past the command restrictions (which require access)
5345 * but fail this test, we have some fool with security override on.
5347 reply("CSMSG_NOT_USER", channel->name);
5354 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5355 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5357 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5360 bp = strcspn(infoline, "\001");
5363 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5368 if(infoline[0] == '*' && infoline[1] == 0)
5371 uData->info = strdup(infoline);
5374 reply("CSMSG_USET_INFO", uData->info);
5376 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5380 struct svccmd_list uset_shows_list;
5382 static CHANSERV_FUNC(cmd_uset)
5384 struct svccmd *subcmd;
5388 /* Check if we need to (re-)initialize uset_shows_list. */
5389 if(!uset_shows_list.used)
5393 "NoAutoOp", "AutoInvite", "Info"
5396 if(!uset_shows_list.size)
5398 uset_shows_list.size = ArrayLength(options);
5399 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5401 for(ii = 0; ii < ArrayLength(options); ii++)
5403 const char *name = options[ii];
5404 sprintf(buf, "%s %s", argv[0], name);
5405 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5408 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5411 svccmd_list_append(&uset_shows_list, subcmd);
5417 /* Do this so options are presented in a consistent order. */
5418 reply("CSMSG_USER_OPTIONS");
5419 for(ii = 0; ii < uset_shows_list.used; ii++)
5420 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5424 sprintf(buf, "%s %s", argv[0], argv[1]);
5425 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5428 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5432 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5435 static CHANSERV_FUNC(cmd_giveownership)
5437 struct handle_info *new_owner_hi;
5438 struct userData *new_owner, *curr_user;
5439 struct chanData *cData = channel->channel_info;
5440 struct do_not_register *dnr;
5442 unsigned short co_access;
5443 char reason[MAXLEN];
5446 curr_user = GetChannelAccess(cData, user->handle_info);
5447 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5448 if(!curr_user || (curr_user->access != UL_OWNER))
5450 struct userData *owner = NULL;
5451 for(curr_user = channel->channel_info->users;
5453 curr_user = curr_user->next)
5455 if(curr_user->access != UL_OWNER)
5459 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5466 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5468 char delay[INTERVALLEN];
5469 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5470 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5473 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5475 if(new_owner_hi == user->handle_info)
5477 reply("CSMSG_NO_TRANSFER_SELF");
5480 new_owner = GetChannelAccess(cData, new_owner_hi);
5483 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5486 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5488 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5491 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5492 if(!IsHelping(user))
5493 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5495 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5498 if(new_owner->access >= UL_COOWNER)
5499 co_access = new_owner->access;
5501 co_access = UL_COOWNER;
5502 new_owner->access = UL_OWNER;
5504 curr_user->access = co_access;
5505 cData->ownerTransfer = now;
5506 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5507 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5508 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5512 static CHANSERV_FUNC(cmd_suspend)
5514 struct handle_info *hi;
5515 struct userData *self, *target;
5518 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5519 self = GetChannelUser(channel->channel_info, user->handle_info);
5520 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5522 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5525 if(target->access >= self->access)
5527 reply("MSG_USER_OUTRANKED", hi->handle);
5530 if(target->flags & USER_SUSPENDED)
5532 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5537 target->present = 0;
5540 target->flags |= USER_SUSPENDED;
5541 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5545 static CHANSERV_FUNC(cmd_unsuspend)
5547 struct handle_info *hi;
5548 struct userData *self, *target;
5551 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5552 self = GetChannelUser(channel->channel_info, user->handle_info);
5553 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5555 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5558 if(target->access >= self->access)
5560 reply("MSG_USER_OUTRANKED", hi->handle);
5563 if(!(target->flags & USER_SUSPENDED))
5565 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5568 target->flags &= ~USER_SUSPENDED;
5569 scan_user_presence(target, NULL);
5570 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5574 static MODCMD_FUNC(cmd_deleteme)
5576 struct handle_info *hi;
5577 struct userData *target;
5578 const char *confirm_string;
5579 unsigned short access;
5582 hi = user->handle_info;
5583 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5585 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5588 if(target->access == UL_OWNER)
5590 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5593 confirm_string = make_confirmation_string(target);
5594 if((argc < 2) || strcmp(argv[1], confirm_string))
5596 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5599 access = target->access;
5600 channel_name = strdup(channel->name);
5601 del_channel_user(target, 1);
5602 reply("CSMSG_DELETED_YOU", access, channel_name);
5608 chanserv_refresh_topics(UNUSED_ARG(void *data))
5610 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5611 struct chanData *cData;
5614 for(cData = channelList; cData; cData = cData->next)
5616 if(IsSuspended(cData))
5618 opt = cData->chOpts[chTopicRefresh];
5621 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5624 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5625 cData->last_refresh = refresh_num;
5627 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5630 static CHANSERV_FUNC(cmd_unf)
5634 char response[MAXLEN];
5635 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5636 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5637 irc_privmsg(cmd->parent->bot, channel->name, response);
5640 reply("CSMSG_UNF_RESPONSE");
5644 static CHANSERV_FUNC(cmd_ping)
5648 char response[MAXLEN];
5649 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5650 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5651 irc_privmsg(cmd->parent->bot, channel->name, response);
5654 reply("CSMSG_PING_RESPONSE");
5658 static CHANSERV_FUNC(cmd_wut)
5662 char response[MAXLEN];
5663 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5664 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5665 irc_privmsg(cmd->parent->bot, channel->name, response);
5668 reply("CSMSG_WUT_RESPONSE");
5672 static CHANSERV_FUNC(cmd_8ball)
5674 unsigned int i, j, accum;
5679 for(i=1; i<argc; i++)
5680 for(j=0; argv[i][j]; j++)
5681 accum = (accum << 5) - accum + toupper(argv[i][j]);
5682 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5685 char response[MAXLEN];
5686 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5687 irc_privmsg(cmd->parent->bot, channel->name, response);
5690 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5694 static CHANSERV_FUNC(cmd_d)
5696 unsigned long sides, count, modifier, ii, total;
5697 char response[MAXLEN], *sep;
5701 if((count = strtoul(argv[1], &sep, 10)) < 1)
5711 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5712 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5716 else if((sep[0] == '-') && isdigit(sep[1]))
5717 modifier = strtoul(sep, NULL, 10);
5718 else if((sep[0] == '+') && isdigit(sep[1]))
5719 modifier = strtoul(sep+1, NULL, 10);
5726 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5731 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5734 for(total = ii = 0; ii < count; ++ii)
5735 total += (rand() % sides) + 1;
5738 if((count > 1) || modifier)
5740 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5741 sprintf(response, fmt, total, count, sides, modifier);
5745 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5746 sprintf(response, fmt, total, sides);
5749 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5751 send_message_type(4, user, cmd->parent->bot, "%s", response);
5755 static CHANSERV_FUNC(cmd_huggle)
5757 /* CTCP must be via PRIVMSG, never notice */
5759 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5761 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5766 chanserv_adjust_limit(void *data)
5768 struct mod_chanmode change;
5769 struct chanData *cData = data;
5770 struct chanNode *channel = cData->channel;
5773 if(IsSuspended(cData))
5776 cData->limitAdjusted = now;
5777 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5778 if(cData->modes.modes_set & MODE_LIMIT)
5780 if(limit > cData->modes.new_limit)
5781 limit = cData->modes.new_limit;
5782 else if(limit == cData->modes.new_limit)
5786 mod_chanmode_init(&change);
5787 change.modes_set = MODE_LIMIT;
5788 change.new_limit = limit;
5789 mod_chanmode_announce(chanserv, channel, &change);
5793 handle_new_channel(struct chanNode *channel)
5795 struct chanData *cData;
5797 if(!(cData = channel->channel_info))
5800 if(cData->modes.modes_set || cData->modes.modes_clear)
5801 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5803 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5804 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5807 /* Welcome to my worst nightmare. Warning: Read (or modify)
5808 the code below at your own risk. */
5810 handle_join(struct modeNode *mNode)
5812 struct mod_chanmode change;
5813 struct userNode *user = mNode->user;
5814 struct chanNode *channel = mNode->channel;
5815 struct chanData *cData;
5816 struct userData *uData = NULL;
5817 struct banData *bData;
5818 struct handle_info *handle;
5819 unsigned int modes = 0, info = 0;
5822 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5825 cData = channel->channel_info;
5826 if(channel->members.used > cData->max)
5827 cData->max = channel->members.used;
5829 /* Check for bans. If they're joining through a ban, one of two
5831 * 1: Join during a netburst, by riding the break. Kick them
5832 * unless they have ops or voice in the channel.
5833 * 2: They're allowed to join through the ban (an invite in
5834 * ircu2.10, or a +e on Hybrid, or something).
5835 * If they're not joining through a ban, and the banlist is not
5836 * full, see if they're on the banlist for the channel. If so,
5839 if(user->uplink->burst && !mNode->modes)
5842 for(ii = 0; ii < channel->banlist.used; ii++)
5844 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5846 /* Riding a netburst. Naughty. */
5847 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5853 mod_chanmode_init(&change);
5855 if(channel->banlist.used < MAXBANS)
5857 /* Not joining through a ban. */
5858 for(bData = cData->bans;
5859 bData && !user_matches_glob(user, bData->mask, 1);
5860 bData = bData->next);
5864 char kick_reason[MAXLEN];
5865 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5867 bData->triggered = now;
5868 if(bData != cData->bans)
5870 /* Shuffle the ban to the head of the list. */
5872 bData->next->prev = bData->prev;
5874 bData->prev->next = bData->next;
5877 bData->next = cData->bans;
5880 cData->bans->prev = bData;
5881 cData->bans = bData;
5884 change.args[0].mode = MODE_BAN;
5885 change.args[0].u.hostmask = bData->mask;
5886 mod_chanmode_announce(chanserv, channel, &change);
5887 KickChannelUser(user, channel, chanserv, kick_reason);
5892 /* ChanServ will not modify the limits in join-flooded channels.
5893 It will also skip DynLimit processing when the user (or srvx)
5894 is bursting in, because there are likely more incoming. */
5895 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5896 && !user->uplink->burst
5897 && !channel->join_flooded
5898 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5900 /* The user count has begun "bumping" into the channel limit,
5901 so set a timer to raise the limit a bit. Any previous
5902 timers are removed so three incoming users within the delay
5903 results in one limit change, not three. */
5905 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5906 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5909 if(channel->join_flooded)
5911 /* don't automatically give ops or voice during a join flood */
5913 else if(cData->lvlOpts[lvlGiveOps] == 0)
5914 modes |= MODE_CHANOP;
5915 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5916 modes |= MODE_VOICE;
5918 greeting = cData->greeting;
5919 if(user->handle_info)
5921 handle = user->handle_info;
5923 if(IsHelper(user) && !IsHelping(user))
5926 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5928 if(channel == chanserv_conf.support_channels.list[ii])
5930 HANDLE_SET_FLAG(user->handle_info, HELPING);
5936 uData = GetTrueChannelAccess(cData, handle);
5937 if(uData && !IsUserSuspended(uData))
5939 /* Ops and above were handled by the above case. */
5940 if(IsUserAutoOp(uData))
5942 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5943 modes |= MODE_CHANOP;
5944 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5945 modes |= MODE_VOICE;
5947 if(uData->access >= UL_PRESENT)
5948 cData->visited = now;
5949 if(cData->user_greeting)
5950 greeting = cData->user_greeting;
5952 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5953 && ((now - uData->seen) >= chanserv_conf.info_delay)
5960 if(!user->uplink->burst)
5964 if(modes & MODE_CHANOP)
5965 modes &= ~MODE_VOICE;
5966 change.args[0].mode = modes;
5967 change.args[0].u.member = mNode;
5968 mod_chanmode_announce(chanserv, channel, &change);
5970 if(greeting && !user->uplink->burst)
5971 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5973 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5979 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5981 struct mod_chanmode change;
5982 struct userData *channel;
5983 unsigned int ii, jj;
5985 if(!user->handle_info)
5988 mod_chanmode_init(&change);
5990 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5992 struct chanNode *cn;
5993 struct modeNode *mn;
5994 if(IsUserSuspended(channel)
5995 || IsSuspended(channel->channel)
5996 || !(cn = channel->channel->channel))
5999 mn = GetUserMode(cn, user);
6002 if(!IsUserSuspended(channel)
6003 && IsUserAutoInvite(channel)
6004 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6006 && !user->uplink->burst)
6007 irc_invite(chanserv, user, cn);
6011 if(channel->access >= UL_PRESENT)
6012 channel->channel->visited = now;
6014 if(IsUserAutoOp(channel))
6016 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6017 change.args[0].mode = MODE_CHANOP;
6018 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6019 change.args[0].mode = MODE_VOICE;
6021 change.args[0].mode = 0;
6022 change.args[0].u.member = mn;
6023 if(change.args[0].mode)
6024 mod_chanmode_announce(chanserv, cn, &change);
6027 channel->seen = now;
6028 channel->present = 1;
6031 for(ii = 0; ii < user->channels.used; ++ii)
6033 struct chanNode *channel = user->channels.list[ii]->channel;
6034 struct banData *ban;
6036 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6037 || !channel->channel_info
6038 || IsSuspended(channel->channel_info))
6040 for(jj = 0; jj < channel->banlist.used; ++jj)
6041 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6043 if(jj < channel->banlist.used)
6045 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6047 char kick_reason[MAXLEN];
6048 if(!user_matches_glob(user, ban->mask, 1))
6050 change.args[0].mode = MODE_BAN;
6051 change.args[0].u.hostmask = ban->mask;
6052 mod_chanmode_announce(chanserv, channel, &change);
6053 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6054 KickChannelUser(user, channel, chanserv, kick_reason);
6055 ban->triggered = now;
6060 if(IsSupportHelper(user))
6062 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6064 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6066 HANDLE_SET_FLAG(user->handle_info, HELPING);
6074 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6076 struct chanData *cData;
6077 struct userData *uData;
6079 cData = mn->channel->channel_info;
6080 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6083 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6085 /* Allow for a bit of padding so that the limit doesn't
6086 track the user count exactly, which could get annoying. */
6087 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6089 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6090 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6094 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6096 scan_user_presence(uData, mn->user);
6100 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6102 unsigned int ii, jj;
6103 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6105 for(jj = 0; jj < mn->user->channels.used; ++jj)
6106 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6108 if(jj < mn->user->channels.used)
6111 if(ii == chanserv_conf.support_channels.used)
6112 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6117 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6119 struct userData *uData;
6121 if(!channel->channel_info || !kicker || IsService(kicker)
6122 || (kicker == victim) || IsSuspended(channel->channel_info)
6123 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6126 if(protect_user(victim, kicker, channel->channel_info))
6128 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6129 KickChannelUser(kicker, channel, chanserv, reason);
6132 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6137 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6139 struct chanData *cData;
6141 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6144 cData = channel->channel_info;
6145 if(bad_topic(channel, user, channel->topic))
6147 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6148 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6149 SetChannelTopic(channel, chanserv, old_topic, 1);
6150 else if(cData->topic)
6151 SetChannelTopic(channel, chanserv, cData->topic, 1);
6154 /* With topicsnarf, grab the topic and save it as the default topic. */
6155 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6158 cData->topic = strdup(channel->topic);
6164 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6166 struct mod_chanmode *bounce = NULL;
6167 unsigned int bnc, ii;
6170 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6173 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6174 && mode_lock_violated(&channel->channel_info->modes, change))
6176 char correct[MAXLEN];
6177 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6178 mod_chanmode_format(&channel->channel_info->modes, correct);
6179 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6181 for(ii = bnc = 0; ii < change->argc; ++ii)
6183 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6185 const struct userNode *victim = change->args[ii].u.member->user;
6186 if(!protect_user(victim, user, channel->channel_info))
6189 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6192 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6193 bounce->args[bnc].u.member = GetUserMode(channel, user);
6194 if(bounce->args[bnc].u.member)
6198 bounce->args[bnc].mode = MODE_CHANOP;
6199 bounce->args[bnc].u.member = change->args[ii].u.member;
6201 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6203 else if(change->args[ii].mode & MODE_CHANOP)
6205 const struct userNode *victim = change->args[ii].u.member->user;
6206 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6209 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6210 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6211 bounce->args[bnc].u.member = change->args[ii].u.member;
6214 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6216 const char *ban = change->args[ii].u.hostmask;
6217 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6220 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6221 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6222 bounce->args[bnc].u.hostmask = ban;
6224 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6229 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6230 mod_chanmode_announce(chanserv, channel, bounce);
6231 mod_chanmode_free(bounce);
6236 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6238 struct chanNode *channel;
6239 struct banData *bData;
6240 struct mod_chanmode change;
6241 unsigned int ii, jj;
6242 char kick_reason[MAXLEN];
6244 mod_chanmode_init(&change);
6246 change.args[0].mode = MODE_BAN;
6247 for(ii = 0; ii < user->channels.used; ++ii)
6249 channel = user->channels.list[ii]->channel;
6250 /* Need not check for bans if they're opped or voiced. */
6251 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6253 /* Need not check for bans unless channel registration is active. */
6254 if(!channel->channel_info || IsSuspended(channel->channel_info))
6256 /* Look for a matching ban already on the channel. */
6257 for(jj = 0; jj < channel->banlist.used; ++jj)
6258 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6260 /* Need not act if we found one. */
6261 if(jj < channel->banlist.used)
6263 /* Look for a matching ban in this channel. */
6264 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6266 if(!user_matches_glob(user, bData->mask, 1))
6268 change.args[0].u.hostmask = bData->mask;
6269 mod_chanmode_announce(chanserv, channel, &change);
6270 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6271 KickChannelUser(user, channel, chanserv, kick_reason);
6272 bData->triggered = now;
6273 break; /* we don't need to check any more bans in the channel */
6278 static void handle_rename(struct handle_info *handle, const char *old_handle)
6280 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6284 dict_remove2(handle_dnrs, old_handle, 1);
6285 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6286 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6291 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6293 struct userNode *h_user;
6295 if(handle->channels)
6297 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6298 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6300 while(handle->channels)
6301 del_channel_user(handle->channels, 1);
6306 handle_server_link(UNUSED_ARG(struct server *server))
6308 struct chanData *cData;
6310 for(cData = channelList; cData; cData = cData->next)
6312 if(!IsSuspended(cData))
6313 cData->may_opchan = 1;
6314 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6315 && !cData->channel->join_flooded
6316 && ((cData->channel->limit - cData->channel->members.used)
6317 < chanserv_conf.adjust_threshold))
6319 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6320 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6326 chanserv_conf_read(void)
6330 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6331 struct mod_chanmode *change;
6332 struct string_list *strlist;
6333 struct chanNode *chan;
6336 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6338 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6341 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6342 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6343 chanserv_conf.support_channels.used = 0;
6344 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6346 for(ii = 0; ii < strlist->used; ++ii)
6348 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6351 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6353 channelList_append(&chanserv_conf.support_channels, chan);
6356 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6359 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6362 chan = AddChannel(str, now, str2, NULL);
6364 channelList_append(&chanserv_conf.support_channels, chan);
6366 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6367 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6368 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6369 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6370 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6371 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6372 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6373 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6374 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6375 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6376 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6377 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6378 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6379 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6380 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6381 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6382 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6383 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6384 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6385 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6386 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6387 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6388 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6390 NickChange(chanserv, str, 0);
6391 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6392 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6393 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6394 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6395 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6396 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6397 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6398 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6399 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6400 chanserv_conf.max_owned = str ? atoi(str) : 5;
6401 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6402 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6403 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6404 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6405 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6406 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6407 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6410 safestrncpy(mode_line, str, sizeof(mode_line));
6411 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6412 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6414 chanserv_conf.default_modes = *change;
6415 mod_chanmode_free(change);
6417 free_string_list(chanserv_conf.set_shows);
6418 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6420 strlist = string_list_copy(strlist);
6423 static const char *list[] = {
6424 /* free form text */
6425 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6426 /* options based on user level */
6427 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6428 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6429 /* multiple choice options */
6430 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6431 /* binary options */
6432 "DynLimit", "NoDelete",
6437 strlist = alloc_string_list(ArrayLength(list)-1);
6438 for(ii=0; list[ii]; ii++)
6439 string_list_append(strlist, strdup(list[ii]));
6441 chanserv_conf.set_shows = strlist;
6442 /* We don't look things up now, in case the list refers to options
6443 * defined by modules initialized after this point. Just mark the
6444 * function list as invalid, so it will be initialized.
6446 set_shows_list.used = 0;
6447 free_string_list(chanserv_conf.eightball);
6448 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6451 strlist = string_list_copy(strlist);
6455 strlist = alloc_string_list(4);
6456 string_list_append(strlist, strdup("Yes."));
6457 string_list_append(strlist, strdup("No."));
6458 string_list_append(strlist, strdup("Maybe so."));
6460 chanserv_conf.eightball = strlist;
6461 free_string_list(chanserv_conf.old_ban_names);
6462 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6464 strlist = string_list_copy(strlist);
6466 strlist = alloc_string_list(2);
6467 chanserv_conf.old_ban_names = strlist;
6468 /* the variable itself is actually declared in proto-common.c; this is equally
6470 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6471 off_channel = (str && enabled_string(str)) ? 1 : 0;
6472 str = database_get_data(conf_node, "use_registered_mode", RECDB_QSTRING);
6473 chanserv_conf.use_registered_mode = (str && enabled_string(str)) ? 1 : 0;
6477 chanserv_note_type_read(const char *key, struct record_data *rd)
6480 struct note_type *ntype;
6483 if(!(obj = GET_RECORD_OBJECT(rd)))
6485 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6488 if(!(ntype = chanserv_create_note_type(key)))
6490 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6494 /* Figure out set access */
6495 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6497 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6498 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6500 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6502 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6503 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6505 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6507 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6511 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6512 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6513 ntype->set_access.min_opserv = 0;
6516 /* Figure out visibility */
6517 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6518 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6519 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6520 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6521 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6522 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6523 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6524 ntype->visible_type = NOTE_VIS_ALL;
6526 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6528 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6529 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6533 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6535 struct handle_info *handle;
6536 struct userData *uData;
6537 char *seen, *inf, *flags;
6539 unsigned short access;
6541 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6543 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6547 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6548 if(access > UL_OWNER)
6550 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6554 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6555 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6556 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6557 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6558 handle = get_handle_info(key);
6561 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6565 uData = add_channel_user(chan, handle, access, last_seen, inf);
6566 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6570 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6572 struct banData *bData;
6573 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6574 time_t set_time, triggered_time, expires_time;
6576 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6578 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6582 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6583 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6584 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6585 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6586 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6587 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6589 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6590 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6592 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6594 expires_time = set_time + atoi(s_duration);
6598 if(expires_time && (expires_time < now))
6601 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6604 static struct suspended *
6605 chanserv_read_suspended(dict_t obj)
6607 struct suspended *suspended = calloc(1, sizeof(*suspended));
6611 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6612 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6613 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6614 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6615 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6616 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6617 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6618 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6619 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6620 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6625 chanserv_channel_read(const char *key, struct record_data *hir)
6627 struct suspended *suspended;
6628 struct mod_chanmode *modes;
6629 struct chanNode *cNode;
6630 struct chanData *cData;
6631 struct dict *channel, *obj;
6632 char *str, *argv[10];
6636 channel = hir->d.object;
6638 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6641 cNode = AddChannel(key, now, NULL, NULL);
6644 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6647 cData = register_channel(cNode, str);
6650 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6654 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6656 enum levelOption lvlOpt;
6657 enum charOption chOpt;
6659 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6660 cData->flags = atoi(str);
6662 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6664 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6666 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6667 else if(levelOptions[lvlOpt].old_flag)
6669 if(cData->flags & levelOptions[lvlOpt].old_flag)
6670 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6672 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6676 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6678 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6680 cData->chOpts[chOpt] = str[0];
6683 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6685 enum levelOption lvlOpt;
6686 enum charOption chOpt;
6689 cData->flags = base64toint(str, 5);
6690 count = strlen(str += 5);
6691 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6694 if(levelOptions[lvlOpt].old_flag)
6696 if(cData->flags & levelOptions[lvlOpt].old_flag)
6697 lvl = levelOptions[lvlOpt].flag_value;
6699 lvl = levelOptions[lvlOpt].default_value;
6701 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6703 case 'c': lvl = UL_COOWNER; break;
6704 case 'm': lvl = UL_MASTER; break;
6705 case 'n': lvl = UL_OWNER+1; break;
6706 case 'o': lvl = UL_OP; break;
6707 case 'p': lvl = UL_PEON; break;
6708 case 'w': lvl = UL_OWNER; break;
6709 default: lvl = 0; break;
6711 cData->lvlOpts[lvlOpt] = lvl;
6713 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6714 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6717 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6719 suspended = chanserv_read_suspended(obj);
6720 cData->suspended = suspended;
6721 suspended->cData = cData;
6722 /* We could use suspended->expires and suspended->revoked to
6723 * set the CHANNEL_SUSPENDED flag, but we don't. */
6725 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6727 suspended = calloc(1, sizeof(*suspended));
6728 suspended->issued = 0;
6729 suspended->revoked = 0;
6730 suspended->suspender = strdup(str);
6731 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6732 suspended->expires = str ? atoi(str) : 0;
6733 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6734 suspended->reason = strdup(str ? str : "No reason");
6735 suspended->previous = NULL;
6736 cData->suspended = suspended;
6737 suspended->cData = cData;
6741 cData->flags &= ~CHANNEL_SUSPENDED;
6742 suspended = NULL; /* to squelch a warning */
6745 if(IsSuspended(cData)) {
6746 if(suspended->expires > now)
6747 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6748 else if(suspended->expires)
6749 cData->flags &= ~CHANNEL_SUSPENDED;
6752 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6753 struct mod_chanmode change;
6754 mod_chanmode_init(&change);
6756 change.args[0].mode = MODE_CHANOP;
6757 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6758 mod_chanmode_announce(chanserv, cNode, &change);
6761 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6762 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6763 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6764 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6765 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6766 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6767 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6768 cData->max = str ? atoi(str) : 0;
6769 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6770 cData->greeting = str ? strdup(str) : NULL;
6771 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6772 cData->user_greeting = str ? strdup(str) : NULL;
6773 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6774 cData->topic_mask = str ? strdup(str) : NULL;
6775 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6776 cData->topic = str ? strdup(str) : NULL;
6778 if(!IsSuspended(cData)
6779 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6780 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6781 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6782 cData->modes = *modes;
6783 if(chanserv_conf.use_registered_mode)
6784 cData->modes.modes_set |= MODE_REGISTERED;
6785 if(cData->modes.argc > 1)
6786 cData->modes.argc = 1;
6787 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6788 mod_chanmode_free(modes);
6791 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6792 for(it = dict_first(obj); it; it = iter_next(it))
6793 user_read_helper(iter_key(it), iter_data(it), cData);
6795 if(!cData->users && !IsProtected(cData))
6797 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6798 unregister_channel(cData, "has empty user list.");
6802 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6803 for(it = dict_first(obj); it; it = iter_next(it))
6804 ban_read_helper(iter_key(it), iter_data(it), cData);
6806 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6807 for(it = dict_first(obj); it; it = iter_next(it))
6809 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6810 struct record_data *rd = iter_data(it);
6811 const char *note, *setter;
6813 if(rd->type != RECDB_OBJECT)
6815 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6819 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6821 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6823 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6827 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6828 if(!setter) setter = "<unknown>";
6829 chanserv_add_channel_note(cData, ntype, setter, note);
6837 chanserv_dnr_read(const char *key, struct record_data *hir)
6839 const char *setter, *reason, *str;
6840 struct do_not_register *dnr;
6842 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6845 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6848 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6851 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6854 dnr = chanserv_add_dnr(key, setter, reason);
6857 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6859 dnr->set = atoi(str);
6865 chanserv_saxdb_read(struct dict *database)
6867 struct dict *section;
6870 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6871 for(it = dict_first(section); it; it = iter_next(it))
6872 chanserv_note_type_read(iter_key(it), iter_data(it));
6874 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6875 for(it = dict_first(section); it; it = iter_next(it))
6876 chanserv_channel_read(iter_key(it), iter_data(it));
6878 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6879 for(it = dict_first(section); it; it = iter_next(it))
6880 chanserv_dnr_read(iter_key(it), iter_data(it));
6886 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6888 int high_present = 0;
6889 saxdb_start_record(ctx, KEY_USERS, 1);
6890 for(; uData; uData = uData->next)
6892 if((uData->access >= UL_PRESENT) && uData->present)
6894 saxdb_start_record(ctx, uData->handle->handle, 0);
6895 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6896 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6898 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6900 saxdb_write_string(ctx, KEY_INFO, uData->info);
6901 saxdb_end_record(ctx);
6903 saxdb_end_record(ctx);
6904 return high_present;
6908 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6912 saxdb_start_record(ctx, KEY_BANS, 1);
6913 for(; bData; bData = bData->next)
6915 saxdb_start_record(ctx, bData->mask, 0);
6916 saxdb_write_int(ctx, KEY_SET, bData->set);
6917 if(bData->triggered)
6918 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6920 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6922 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6924 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6925 saxdb_end_record(ctx);
6927 saxdb_end_record(ctx);
6931 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6933 saxdb_start_record(ctx, name, 0);
6934 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6935 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6937 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6939 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6941 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6943 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6944 saxdb_end_record(ctx);
6948 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6952 enum levelOption lvlOpt;
6953 enum charOption chOpt;
6955 saxdb_start_record(ctx, channel->channel->name, 1);
6957 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6958 saxdb_write_int(ctx, KEY_MAX, channel->max);
6960 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6961 if(channel->registrar)
6962 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6963 if(channel->greeting)
6964 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6965 if(channel->user_greeting)
6966 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6967 if(channel->topic_mask)
6968 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6969 if(channel->suspended)
6970 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6972 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6973 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6974 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6975 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6976 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6978 buf[0] = channel->chOpts[chOpt];
6980 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6982 saxdb_end_record(ctx);
6984 if(channel->modes.modes_set || channel->modes.modes_clear)
6986 mod_chanmode_format(&channel->modes, buf);
6987 saxdb_write_string(ctx, KEY_MODES, buf);
6990 high_present = chanserv_write_users(ctx, channel->users);
6991 chanserv_write_bans(ctx, channel->bans);
6993 if(dict_size(channel->notes))
6997 saxdb_start_record(ctx, KEY_NOTES, 1);
6998 for(it = dict_first(channel->notes); it; it = iter_next(it))
7000 struct note *note = iter_data(it);
7001 saxdb_start_record(ctx, iter_key(it), 0);
7002 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7003 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7004 saxdb_end_record(ctx);
7006 saxdb_end_record(ctx);
7009 if(channel->ownerTransfer)
7010 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7011 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7012 saxdb_end_record(ctx);
7016 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7020 saxdb_start_record(ctx, ntype->name, 0);
7021 switch(ntype->set_access_type)
7023 case NOTE_SET_CHANNEL_ACCESS:
7024 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7026 case NOTE_SET_CHANNEL_SETTER:
7027 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7029 case NOTE_SET_PRIVILEGED: default:
7030 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7033 switch(ntype->visible_type)
7035 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7036 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7037 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7039 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7040 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7041 saxdb_end_record(ctx);
7045 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7047 struct do_not_register *dnr;
7050 for(it = dict_first(dnrs); it; it = iter_next(it))
7052 dnr = iter_data(it);
7053 saxdb_start_record(ctx, dnr->chan_name, 0);
7055 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7056 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7057 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7058 saxdb_end_record(ctx);
7063 chanserv_saxdb_write(struct saxdb_context *ctx)
7066 struct chanData *channel;
7069 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7070 for(it = dict_first(note_types); it; it = iter_next(it))
7071 chanserv_write_note_type(ctx, iter_data(it));
7072 saxdb_end_record(ctx);
7075 saxdb_start_record(ctx, KEY_DNR, 1);
7076 write_dnrs_helper(ctx, handle_dnrs);
7077 write_dnrs_helper(ctx, plain_dnrs);
7078 write_dnrs_helper(ctx, mask_dnrs);
7079 saxdb_end_record(ctx);
7082 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7083 for(channel = channelList; channel; channel = channel->next)
7084 chanserv_write_channel(ctx, channel);
7085 saxdb_end_record(ctx);
7091 chanserv_db_cleanup(void) {
7093 unreg_part_func(handle_part);
7095 unregister_channel(channelList, "terminating.");
7096 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7097 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7098 free(chanserv_conf.support_channels.list);
7099 dict_delete(handle_dnrs);
7100 dict_delete(plain_dnrs);
7101 dict_delete(mask_dnrs);
7102 dict_delete(note_types);
7103 free_string_list(chanserv_conf.eightball);
7104 free_string_list(chanserv_conf.old_ban_names);
7105 free_string_list(chanserv_conf.set_shows);
7106 free(set_shows_list.list);
7107 free(uset_shows_list.list);
7110 struct userData *helper = helperList;
7111 helperList = helperList->next;
7116 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7117 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7118 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7121 init_chanserv(const char *nick)
7123 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7124 conf_register_reload(chanserv_conf_read);
7126 reg_server_link_func(handle_server_link);
7128 reg_new_channel_func(handle_new_channel);
7129 reg_join_func(handle_join);
7130 reg_part_func(handle_part);
7131 reg_kick_func(handle_kick);
7132 reg_topic_func(handle_topic);
7133 reg_mode_change_func(handle_mode);
7134 reg_nick_change_func(handle_nick_change);
7136 reg_auth_func(handle_auth);
7137 reg_handle_rename_func(handle_rename);
7138 reg_unreg_func(handle_unreg);
7140 handle_dnrs = dict_new();
7141 dict_set_free_data(handle_dnrs, free);
7142 plain_dnrs = dict_new();
7143 dict_set_free_data(plain_dnrs, free);
7144 mask_dnrs = dict_new();
7145 dict_set_free_data(mask_dnrs, free);
7147 reg_svccmd_unbind_func(handle_svccmd_unbind);
7148 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7149 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7150 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7151 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7152 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7153 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7154 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7155 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7156 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7158 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7159 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7161 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7162 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7163 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7164 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7165 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7167 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7168 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7169 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7170 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7171 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7173 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7174 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7175 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7176 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7178 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7179 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7180 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7181 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7182 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7183 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7184 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7185 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7187 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7188 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7189 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7190 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7191 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7192 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7193 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7194 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7195 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7196 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7197 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7198 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7199 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7200 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7202 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7203 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7204 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7205 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7206 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7208 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7209 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7211 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7212 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7213 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7214 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7215 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7216 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7217 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7218 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7219 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7220 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7221 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7223 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7224 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7226 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7227 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7228 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7229 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7231 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7232 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7233 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7234 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7235 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7237 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7238 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7239 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7240 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7241 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7242 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7244 /* Channel options */
7245 DEFINE_CHANNEL_OPTION(defaulttopic);
7246 DEFINE_CHANNEL_OPTION(topicmask);
7247 DEFINE_CHANNEL_OPTION(greeting);
7248 DEFINE_CHANNEL_OPTION(usergreeting);
7249 DEFINE_CHANNEL_OPTION(modes);
7250 DEFINE_CHANNEL_OPTION(enfops);
7251 DEFINE_CHANNEL_OPTION(giveops);
7252 DEFINE_CHANNEL_OPTION(protect);
7253 DEFINE_CHANNEL_OPTION(enfmodes);
7254 DEFINE_CHANNEL_OPTION(enftopic);
7255 DEFINE_CHANNEL_OPTION(pubcmd);
7256 DEFINE_CHANNEL_OPTION(givevoice);
7257 DEFINE_CHANNEL_OPTION(userinfo);
7258 DEFINE_CHANNEL_OPTION(dynlimit);
7259 DEFINE_CHANNEL_OPTION(topicsnarf);
7260 DEFINE_CHANNEL_OPTION(nodelete);
7261 DEFINE_CHANNEL_OPTION(toys);
7262 DEFINE_CHANNEL_OPTION(setters);
7263 DEFINE_CHANNEL_OPTION(topicrefresh);
7264 DEFINE_CHANNEL_OPTION(ctcpusers);
7265 DEFINE_CHANNEL_OPTION(ctcpreaction);
7266 DEFINE_CHANNEL_OPTION(inviteme);
7268 DEFINE_CHANNEL_OPTION(offchannel);
7269 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7271 /* Alias set topic to set defaulttopic for compatibility. */
7272 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7275 DEFINE_USER_OPTION(noautoop);
7276 DEFINE_USER_OPTION(autoinvite);
7277 DEFINE_USER_OPTION(info);
7279 /* Alias uset autovoice to uset autoop. */
7280 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7282 note_types = dict_new();
7283 dict_set_free_data(note_types, chanserv_deref_note_type);
7286 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7287 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7288 service_register(chanserv)->trigger = '!';
7289 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7291 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7293 if(chanserv_conf.channel_expire_frequency)
7294 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7296 if(chanserv_conf.refresh_period)
7298 time_t next_refresh;
7299 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7300 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7303 reg_exit_func(chanserv_db_cleanup);
7304 message_register_table(msgtab);