1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 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_DNR_EXPIRE_FREQ "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
103 #define KEY_OWNER_TRANSFER "owner_transfer"
106 #define KEY_LEVEL "level"
107 #define KEY_INFO "info"
108 #define KEY_SEEN "seen"
111 #define KEY_OWNER "owner"
112 #define KEY_REASON "reason"
113 #define KEY_SET "set"
114 #define KEY_DURATION "duration"
115 #define KEY_EXPIRES "expires"
116 #define KEY_TRIGGERED "triggered"
118 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
119 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
120 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
122 /* Administrative messages */
123 static const struct message_entry msgtab[] = {
124 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
126 /* Channel registration */
127 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
128 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
129 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
130 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
131 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
132 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
134 /* Do-not-register channels */
135 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
136 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
137 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
138 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
140 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
141 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
142 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
143 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
144 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
145 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
146 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
147 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
148 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
150 /* Channel unregistration */
151 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
152 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
153 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
154 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
157 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
158 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
160 /* Channel merging */
161 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
162 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
163 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
164 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
165 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
167 /* Handle unregistration */
168 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
171 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
172 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
173 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
174 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
175 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
176 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
177 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
178 { "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." },
179 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
185 /* Removing yourself from a channel. */
186 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
187 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
188 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
190 /* User management */
191 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
192 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
193 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
194 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
195 { "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." },
196 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
197 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
198 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
200 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
201 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
202 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
203 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
204 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
205 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
206 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
209 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
210 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
211 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
212 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
213 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
214 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
215 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
216 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
217 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
218 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
219 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
220 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
221 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
222 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
223 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
224 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
226 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
228 /* Channel management */
229 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
230 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
231 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
233 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
234 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
235 { "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" },
236 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
237 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
238 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
239 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
241 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
242 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
243 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
244 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
245 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
246 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
247 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
248 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
249 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
250 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
251 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
252 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
253 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
254 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
255 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
256 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
257 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
258 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
259 { "CSMSG_SET_MODES", "$bModes $b %s" },
260 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
261 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
262 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
263 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
264 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
265 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
266 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
267 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
268 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
269 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
270 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
271 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
272 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
273 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
274 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
275 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
276 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
277 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
278 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
279 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
280 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
281 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
282 { "CSMSG_USET_INFO", "$bInfo $b %s" },
284 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
285 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
286 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
287 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
288 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
289 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
290 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
291 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
292 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
294 { "CSMSG_PROTECT_NONE", "No users will be protected." },
295 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
296 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
297 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
298 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
299 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
300 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
301 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
302 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
303 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
308 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
309 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
310 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
311 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
312 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
313 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
314 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
315 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
317 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
318 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
319 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
321 /* Channel userlist */
322 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
327 /* Channel note list */
328 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
329 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
330 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
331 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
332 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
333 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
334 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
335 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
336 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
337 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
338 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
339 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
340 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
341 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
342 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
344 /* Channel [un]suspension */
345 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
346 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
347 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
348 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
349 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
350 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
351 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
353 /* Access information */
354 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
355 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
356 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
357 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
358 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
359 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
360 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
361 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
362 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
363 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
364 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
365 { "CSMSG_UC_H_TITLE", "network helper" },
366 { "CSMSG_LC_H_TITLE", "support helper" },
367 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
369 /* Seen information */
370 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
371 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
372 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
373 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
375 /* Names information */
376 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
377 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
379 /* Channel information */
380 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
381 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
382 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
383 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
384 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
385 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
386 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
387 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
388 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
389 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
390 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
391 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
399 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
400 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
402 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
403 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
404 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
405 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
406 { "CSMSG_PEEK_OPS", "$bOps:$b" },
407 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
409 /* Network information */
410 { "CSMSG_NETWORK_INFO", "Network Information:" },
411 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
412 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
413 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
414 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
416 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
417 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
418 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
421 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
422 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
423 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
425 /* Channel searches */
426 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
427 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
428 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
429 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
431 /* Channel configuration */
432 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
433 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
434 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
435 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
438 { "CSMSG_USER_OPTIONS", "User Options:" },
439 { "CSMSG_USER_PROTECTED", "That user is protected." },
442 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
443 { "CSMSG_PING_RESPONSE", "Pong!" },
444 { "CSMSG_WUT_RESPONSE", "wut" },
445 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
446 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
447 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
448 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
449 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
450 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
451 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
454 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
458 /* eject_user and unban_user flags */
459 #define ACTION_KICK 0x0001
460 #define ACTION_BAN 0x0002
461 #define ACTION_ADD_BAN 0x0004
462 #define ACTION_ADD_TIMED_BAN 0x0008
463 #define ACTION_UNBAN 0x0010
464 #define ACTION_DEL_BAN 0x0020
466 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
467 #define MODELEN 40 + KEYLEN
471 #define CSFUNC_ARGS user, channel, argc, argv, cmd
473 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
474 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
475 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
476 reply("MSG_MISSING_PARAMS", argv[0]); \
480 DECLARE_LIST(dnrList, struct do_not_register *);
481 DEFINE_LIST(dnrList, struct do_not_register *);
483 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
485 struct userNode *chanserv;
488 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
489 static struct log_type *CS_LOG;
493 struct channelList support_channels;
494 struct mod_chanmode default_modes;
496 unsigned long db_backup_frequency;
497 unsigned long channel_expire_frequency;
498 unsigned long dnr_expire_frequency;
501 unsigned int adjust_delay;
502 long channel_expire_delay;
503 unsigned int nodelete_level;
505 unsigned int adjust_threshold;
506 int join_flood_threshold;
508 unsigned int greeting_length;
509 unsigned int refresh_period;
510 unsigned int giveownership_period;
512 unsigned int max_owned;
513 unsigned int max_chan_users;
514 unsigned int max_chan_bans;
515 unsigned int max_userinfo_length;
517 struct string_list *set_shows;
518 struct string_list *eightball;
519 struct string_list *old_ban_names;
521 const char *ctcp_short_ban_duration;
522 const char *ctcp_long_ban_duration;
524 const char *irc_operator_epithet;
525 const char *network_helper_epithet;
526 const char *support_helper_epithet;
531 struct userNode *user;
532 struct userNode *bot;
533 struct chanNode *channel;
535 unsigned short lowest;
536 unsigned short highest;
537 struct userData **users;
538 struct helpfile_table table;
541 enum note_access_type
543 NOTE_SET_CHANNEL_ACCESS,
544 NOTE_SET_CHANNEL_SETTER,
548 enum note_visible_type
551 NOTE_VIS_CHANNEL_USERS,
557 enum note_access_type set_access_type;
559 unsigned int min_opserv;
560 unsigned short min_ulevel;
562 enum note_visible_type visible_type;
563 unsigned int max_length;
570 struct note_type *type;
571 char setter[NICKSERV_HANDLE_LEN+1];
575 static unsigned int registered_channels;
576 static unsigned int banCount;
578 static const struct {
581 unsigned short level;
584 { "peon", "Peon", UL_PEON, '+' },
585 { "op", "Op", UL_OP, '@' },
586 { "master", "Master", UL_MASTER, '%' },
587 { "coowner", "Coowner", UL_COOWNER, '*' },
588 { "owner", "Owner", UL_OWNER, '!' },
589 { "helper", "BUG:", UL_HELPER, 'X' }
592 static const struct {
595 unsigned short default_value;
596 unsigned int old_idx;
597 unsigned int old_flag;
598 unsigned short flag_value;
600 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
601 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
602 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
603 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
604 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
605 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
606 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
607 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
608 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
609 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
610 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
613 struct charOptionValues {
616 } protectValues[] = {
617 { 'a', "CSMSG_PROTECT_ALL" },
618 { 'e', "CSMSG_PROTECT_EQUAL" },
619 { 'l', "CSMSG_PROTECT_LOWER" },
620 { 'n', "CSMSG_PROTECT_NONE" }
622 { 'd', "CSMSG_TOYS_DISABLED" },
623 { 'n', "CSMSG_TOYS_PRIVATE" },
624 { 'p', "CSMSG_TOYS_PUBLIC" }
625 }, topicRefreshValues[] = {
626 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
627 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
628 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
629 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
630 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
631 }, ctcpReactionValues[] = {
632 { 'k', "CSMSG_CTCPREACTION_KICK" },
633 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
634 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
635 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
638 static const struct {
642 unsigned int old_idx;
644 struct charOptionValues *values;
646 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
647 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
648 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
649 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
652 struct userData *helperList;
653 struct chanData *channelList;
654 static struct module *chanserv_module;
655 static unsigned int userCount;
657 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
658 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
659 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
662 user_level_from_name(const char *name, unsigned short clamp_level)
664 unsigned int level = 0, ii;
666 level = strtoul(name, NULL, 10);
667 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
668 if(!irccasecmp(name, accessLevels[ii].name))
669 level = accessLevels[ii].level;
670 if(level > clamp_level)
676 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
679 *minl = strtoul(arg, &sep, 10);
687 *maxl = strtoul(sep+1, &sep, 10);
695 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
697 struct userData *uData, **head;
699 if(!channel || !handle)
702 if(override && HANDLE_FLAGGED(handle, HELPING)
703 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
705 for(uData = helperList;
706 uData && uData->handle != handle;
707 uData = uData->next);
711 uData = calloc(1, sizeof(struct userData));
712 uData->handle = handle;
714 uData->access = UL_HELPER;
720 uData->next = helperList;
722 helperList->prev = uData;
730 for(uData = channel->users; uData; uData = uData->next)
731 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
734 head = &(channel->users);
737 if(uData && (uData != *head))
739 /* Shuffle the user to the head of whatever list he was in. */
741 uData->next->prev = uData->prev;
743 uData->prev->next = uData->next;
749 (**head).prev = uData;
756 /* Returns non-zero if user has at least the minimum access.
757 * exempt_owner is set when handling !set, so the owner can set things
760 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
762 struct userData *uData;
763 struct chanData *cData = channel->channel_info;
764 unsigned short minimum = cData->lvlOpts[opt];
767 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
770 if(minimum <= uData->access)
772 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
777 /* Scan for other users authenticated to the same handle
778 still in the channel. If so, keep them listed as present.
780 user is optional, if not null, it skips checking that userNode
781 (for the handle_part function) */
783 scan_user_presence(struct userData *uData, struct userNode *user)
787 if(IsSuspended(uData->channel)
788 || IsUserSuspended(uData)
789 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
801 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
803 unsigned int eflags, argc;
805 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
807 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
808 if(!channel->channel_info
809 || IsSuspended(channel->channel_info)
811 || !ircncasecmp(text, "ACTION ", 7))
813 /* Figure out the minimum level needed to CTCP the channel */
814 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
816 /* We need to enforce against them; do so. */
818 argv[0] = (char*)text;
819 argv[1] = user->nick;
821 if(GetUserMode(channel, user))
822 eflags |= ACTION_KICK;
823 switch(channel->channel_info->chOpts[chCTCPReaction]) {
824 default: case 'k': /* just do the kick */ break;
826 eflags |= ACTION_BAN;
829 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
830 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
833 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
834 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
837 argv[argc++] = bad_ctcp_reason;
838 eject_user(chanserv, channel, argc, argv, NULL, eflags);
842 chanserv_create_note_type(const char *name)
844 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
845 strcpy(ntype->name, name);
847 dict_insert(note_types, ntype->name, ntype);
852 chanserv_deref_note_type(void *data)
854 struct note_type *ntype = data;
856 if(--ntype->refs > 0)
862 chanserv_flush_note_type(struct note_type *ntype)
864 struct chanData *cData;
865 for(cData = channelList; cData; cData = cData->next)
866 dict_remove(cData->notes, ntype->name);
870 chanserv_truncate_notes(struct note_type *ntype)
872 struct chanData *cData;
874 unsigned int size = sizeof(*note) + ntype->max_length;
876 for(cData = channelList; cData; cData = cData->next) {
877 note = dict_find(cData->notes, ntype->name, NULL);
880 if(strlen(note->note) <= ntype->max_length)
882 dict_remove2(cData->notes, ntype->name, 1);
883 note = realloc(note, size);
884 note->note[ntype->max_length] = 0;
885 dict_insert(cData->notes, ntype->name, note);
889 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
892 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
895 unsigned int len = strlen(text);
897 if(len > type->max_length) len = type->max_length;
898 note = calloc(1, sizeof(*note) + len);
900 strncpy(note->setter, setter, sizeof(note->setter)-1);
901 memcpy(note->note, text, len);
903 dict_insert(channel->notes, type->name, note);
909 chanserv_free_note(void *data)
911 struct note *note = data;
913 chanserv_deref_note_type(note->type);
914 assert(note->type->refs > 0); /* must use delnote to remove the type */
918 static MODCMD_FUNC(cmd_createnote) {
919 struct note_type *ntype;
920 unsigned int arg = 1, existed = 0, max_length;
922 if((ntype = dict_find(note_types, argv[1], NULL)))
925 ntype = chanserv_create_note_type(argv[arg]);
926 if(!irccasecmp(argv[++arg], "privileged"))
929 ntype->set_access_type = NOTE_SET_PRIVILEGED;
930 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
932 else if(!irccasecmp(argv[arg], "channel"))
934 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
937 reply("CSMSG_INVALID_ACCESS", argv[arg]);
940 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
941 ntype->set_access.min_ulevel = ulvl;
943 else if(!irccasecmp(argv[arg], "setter"))
945 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
949 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
953 if(!irccasecmp(argv[++arg], "privileged"))
954 ntype->visible_type = NOTE_VIS_PRIVILEGED;
955 else if(!irccasecmp(argv[arg], "channel_users"))
956 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
957 else if(!irccasecmp(argv[arg], "all"))
958 ntype->visible_type = NOTE_VIS_ALL;
960 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
964 if((arg+1) >= argc) {
965 reply("MSG_MISSING_PARAMS", argv[0]);
968 max_length = strtoul(argv[++arg], NULL, 0);
969 if(max_length < 20 || max_length > 450)
971 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
974 if(existed && (max_length < ntype->max_length))
976 ntype->max_length = max_length;
977 chanserv_truncate_notes(ntype);
979 ntype->max_length = max_length;
982 reply("CSMSG_NOTE_MODIFIED", ntype->name);
984 reply("CSMSG_NOTE_CREATED", ntype->name);
989 dict_remove(note_types, ntype->name);
993 static MODCMD_FUNC(cmd_removenote) {
994 struct note_type *ntype;
997 ntype = dict_find(note_types, argv[1], NULL);
998 force = (argc > 2) && !irccasecmp(argv[2], "force");
1001 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1008 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1011 chanserv_flush_note_type(ntype);
1013 dict_remove(note_types, argv[1]);
1014 reply("CSMSG_NOTE_DELETED", argv[1]);
1019 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1023 if(orig->modes_set & change->modes_clear)
1025 if(orig->modes_clear & change->modes_set)
1027 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1028 && strcmp(orig->new_key, change->new_key))
1030 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1031 && (orig->new_limit != change->new_limit))
1036 static char max_length_text[MAXLEN+1][16];
1038 static struct helpfile_expansion
1039 chanserv_expand_variable(const char *variable)
1041 struct helpfile_expansion exp;
1043 if(!irccasecmp(variable, "notes"))
1046 exp.type = HF_TABLE;
1047 exp.value.table.length = 1;
1048 exp.value.table.width = 3;
1049 exp.value.table.flags = 0;
1050 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1051 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1052 exp.value.table.contents[0][0] = "Note Type";
1053 exp.value.table.contents[0][1] = "Visibility";
1054 exp.value.table.contents[0][2] = "Max Length";
1055 for(it=dict_first(note_types); it; it=iter_next(it))
1057 struct note_type *ntype = iter_data(it);
1060 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1061 row = exp.value.table.length++;
1062 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1063 exp.value.table.contents[row][0] = ntype->name;
1064 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1065 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1067 if(!max_length_text[ntype->max_length][0])
1068 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1069 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1074 exp.type = HF_STRING;
1075 exp.value.str = NULL;
1079 static struct chanData*
1080 register_channel(struct chanNode *cNode, char *registrar)
1082 struct chanData *channel;
1083 enum levelOption lvlOpt;
1084 enum charOption chOpt;
1086 channel = calloc(1, sizeof(struct chanData));
1088 channel->notes = dict_new();
1089 dict_set_free_data(channel->notes, chanserv_free_note);
1091 channel->registrar = strdup(registrar);
1092 channel->registered = now;
1093 channel->visited = now;
1094 channel->limitAdjusted = now;
1095 channel->ownerTransfer = now;
1096 channel->flags = CHANNEL_DEFAULT_FLAGS;
1097 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1098 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1099 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1100 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1102 channel->prev = NULL;
1103 channel->next = channelList;
1106 channelList->prev = channel;
1107 channelList = channel;
1108 registered_channels++;
1110 channel->channel = cNode;
1112 cNode->channel_info = channel;
1117 static struct userData*
1118 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1120 struct userData *ud;
1122 if(access > UL_OWNER)
1125 ud = calloc(1, sizeof(*ud));
1126 ud->channel = channel;
1127 ud->handle = handle;
1129 ud->access = access;
1130 ud->info = info ? strdup(info) : NULL;
1133 ud->next = channel->users;
1135 channel->users->prev = ud;
1136 channel->users = ud;
1138 channel->userCount++;
1142 ud->u_next = ud->handle->channels;
1144 ud->u_next->u_prev = ud;
1145 ud->handle->channels = ud;
1150 static void unregister_channel(struct chanData *channel, const char *reason);
1153 del_channel_user(struct userData *user, int do_gc)
1155 struct chanData *channel = user->channel;
1157 channel->userCount--;
1161 user->prev->next = user->next;
1163 channel->users = user->next;
1165 user->next->prev = user->prev;
1168 user->u_prev->u_next = user->u_next;
1170 user->handle->channels = user->u_next;
1172 user->u_next->u_prev = user->u_prev;
1176 if(do_gc && !channel->users && !IsProtected(channel))
1177 unregister_channel(channel, "lost all users.");
1180 static void expire_ban(void *data);
1182 static struct banData*
1183 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1186 unsigned int ii, l1, l2;
1191 bd = malloc(sizeof(struct banData));
1193 bd->channel = channel;
1195 bd->triggered = triggered;
1196 bd->expires = expires;
1198 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1200 extern const char *hidden_host_suffix;
1201 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1205 l2 = strlen(old_name);
1208 if(irccasecmp(mask + l1 - l2, old_name))
1210 new_mask = alloca(MAXLEN);
1211 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1214 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1216 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1217 bd->reason = strdup(reason);
1220 timeq_add(expires, expire_ban, bd);
1223 bd->next = channel->bans;
1225 channel->bans->prev = bd;
1227 channel->banCount++;
1234 del_channel_ban(struct banData *ban)
1236 ban->channel->banCount--;
1240 ban->prev->next = ban->next;
1242 ban->channel->bans = ban->next;
1245 ban->next->prev = ban->prev;
1248 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1257 expire_ban(void *data)
1259 struct banData *bd = data;
1260 if(!IsSuspended(bd->channel))
1262 struct banList bans;
1263 struct mod_chanmode change;
1265 bans = bd->channel->channel->banlist;
1266 mod_chanmode_init(&change);
1267 for(ii=0; ii<bans.used; ii++)
1269 if(!strcmp(bans.list[ii]->ban, bd->mask))
1272 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1273 change.args[0].u.hostmask = bd->mask;
1274 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1280 del_channel_ban(bd);
1283 static void chanserv_expire_suspension(void *data);
1286 unregister_channel(struct chanData *channel, const char *reason)
1288 struct mod_chanmode change;
1289 char msgbuf[MAXLEN];
1291 /* After channel unregistration, the following must be cleaned
1293 - Channel information.
1296 - Channel suspension data.
1297 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1303 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1307 mod_chanmode_init(&change);
1308 change.modes_clear |= MODE_REGISTERED;
1309 mod_chanmode_announce(chanserv, channel->channel, &change);
1312 while(channel->users)
1313 del_channel_user(channel->users, 0);
1315 while(channel->bans)
1316 del_channel_ban(channel->bans);
1318 free(channel->topic);
1319 free(channel->registrar);
1320 free(channel->greeting);
1321 free(channel->user_greeting);
1322 free(channel->topic_mask);
1325 channel->prev->next = channel->next;
1327 channelList = channel->next;
1330 channel->next->prev = channel->prev;
1332 if(channel->suspended)
1334 struct chanNode *cNode = channel->channel;
1335 struct suspended *suspended, *next_suspended;
1337 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1339 next_suspended = suspended->previous;
1340 free(suspended->suspender);
1341 free(suspended->reason);
1342 if(suspended->expires)
1343 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1348 cNode->channel_info = NULL;
1350 channel->channel->channel_info = NULL;
1352 dict_delete(channel->notes);
1353 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1354 if(!IsSuspended(channel))
1355 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1356 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1357 UnlockChannel(channel->channel);
1359 registered_channels--;
1363 expire_channels(UNUSED_ARG(void *data))
1365 struct chanData *channel, *next;
1366 struct userData *user;
1367 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1369 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1370 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1372 for(channel = channelList; channel; channel = next)
1374 next = channel->next;
1376 /* See if the channel can be expired. */
1377 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1378 || IsProtected(channel))
1381 /* Make sure there are no high-ranking users still in the channel. */
1382 for(user=channel->users; user; user=user->next)
1383 if(user->present && (user->access >= UL_PRESENT))
1388 /* Unregister the channel */
1389 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1390 unregister_channel(channel, "registration expired.");
1393 if(chanserv_conf.channel_expire_frequency)
1394 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1398 expire_dnrs(UNUSED_ARG(void *data))
1400 dict_iterator_t it, next;
1401 struct do_not_register *dnr;
1403 for(it = dict_first(handle_dnrs); it; it = next)
1405 dnr = iter_data(it);
1406 next = iter_next(it);
1407 if(dnr->expires && dnr->expires <= now)
1408 dict_remove(handle_dnrs, dnr->chan_name + 1);
1410 for(it = dict_first(plain_dnrs); it; it = next)
1412 dnr = iter_data(it);
1413 next = iter_next(it);
1414 if(dnr->expires && dnr->expires <= now)
1415 dict_remove(plain_dnrs, dnr->chan_name + 1);
1417 for(it = dict_first(mask_dnrs); it; it = next)
1419 dnr = iter_data(it);
1420 next = iter_next(it);
1421 if(dnr->expires && dnr->expires <= now)
1422 dict_remove(mask_dnrs, dnr->chan_name + 1);
1425 if(chanserv_conf.dnr_expire_frequency)
1426 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1430 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1432 char protect = channel->chOpts[chProtect];
1433 struct userData *cs_victim, *cs_aggressor;
1435 /* Don't protect if no one is to be protected, someone is attacking
1436 himself, or if the aggressor is an IRC Operator. */
1437 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1440 /* Don't protect if the victim isn't authenticated (because they
1441 can't be a channel user), unless we are to protect non-users
1443 cs_victim = GetChannelAccess(channel, victim->handle_info);
1444 if(protect != 'a' && !cs_victim)
1447 /* Protect if the aggressor isn't a user because at this point,
1448 the aggressor can only be less than or equal to the victim. */
1449 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1453 /* If the aggressor was a user, then the victim can't be helped. */
1460 if(cs_victim->access > cs_aggressor->access)
1465 if(cs_victim->access >= cs_aggressor->access)
1474 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1476 struct chanData *cData = channel->channel_info;
1477 struct userData *cs_victim;
1479 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1480 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1481 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1483 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1491 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1493 if(IsService(victim))
1495 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1499 if(protect_user(victim, user, channel->channel_info))
1501 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1508 static struct do_not_register *
1509 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1511 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1512 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1513 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1514 strcpy(dnr->reason, reason);
1516 dnr->expires = expires;
1517 if(dnr->chan_name[0] == '*')
1518 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1519 else if(strpbrk(dnr->chan_name, "*?"))
1520 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1522 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1526 static struct dnrList
1527 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1529 struct dnrList list;
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 dnrList_init(&list);
1535 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, handle);
1539 else if(list.used < max)
1540 dnrList_append(&list, dnr);
1543 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1545 if(dnr->expires && dnr->expires <= now)
1546 dict_remove(plain_dnrs, chan_name);
1547 else if(list.used < max)
1548 dnrList_append(&list, dnr);
1553 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1555 next = iter_next(it);
1556 if(!match_ircglob(chan_name, iter_key(it)))
1558 dnr = iter_data(it);
1559 if(dnr->expires && dnr->expires <= now)
1560 dict_remove(mask_dnrs, iter_key(it));
1562 dnrList_append(&list, dnr);
1569 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1571 struct userNode *user;
1572 char buf1[INTERVALLEN];
1573 char buf2[INTERVALLEN];
1577 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
1580 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
1581 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1585 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1588 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1593 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1595 struct dnrList list;
1598 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1599 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1600 dnr_print_func(list.list[ii], user);
1602 reply("CSMSG_MORE_DNRS", list.used - ii);
1607 struct do_not_register *
1608 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1610 struct dnrList list;
1611 struct do_not_register *dnr;
1613 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1614 dnr = list.used ? list.list[0] : NULL;
1619 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1621 struct do_not_register *dnr;
1622 dict_iterator_t it, next;
1623 unsigned int matches = 0;
1625 for(it = dict_first(dict); it; it = next)
1627 dnr = iter_data(it);
1628 next = iter_next(it);
1629 if(dnr->expires && dnr->expires <= now)
1631 dict_remove(dict, iter_key(it));
1634 dnr_print_func(dnr, user);
1641 static CHANSERV_FUNC(cmd_noregister)
1644 time_t expiry, duration;
1645 unsigned int matches;
1649 reply("CSMSG_DNR_SEARCH_RESULTS");
1650 matches = send_dnrs(user, handle_dnrs);
1651 matches += send_dnrs(user, plain_dnrs);
1652 matches += send_dnrs(user, mask_dnrs);
1654 reply("MSG_MATCH_COUNT", matches);
1656 reply("MSG_NO_MATCHES");
1662 if(!IsChannelName(target) && (*target != '*'))
1664 reply("CSMSG_NOT_DNR", target);
1672 reply("MSG_INVALID_DURATION", argv[2]);
1676 if(!strcmp(argv[2], "0"))
1678 else if((duration = ParseInterval(argv[2])))
1679 expiry = now + duration;
1682 reply("MSG_INVALID_DURATION", argv[2]);
1686 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1687 if((*target == '*') && !get_handle_info(target + 1))
1689 reply("MSG_HANDLE_UNKNOWN", target + 1);
1692 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1693 reply("CSMSG_NOREGISTER_CHANNEL", target);
1697 reply("CSMSG_DNR_SEARCH_RESULTS");
1699 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1701 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1703 reply("MSG_NO_MATCHES");
1707 static CHANSERV_FUNC(cmd_allowregister)
1709 const char *chan_name = argv[1];
1711 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1712 || dict_remove(plain_dnrs, chan_name)
1713 || dict_remove(mask_dnrs, chan_name))
1715 reply("CSMSG_DNR_REMOVED", chan_name);
1718 reply("CSMSG_NO_SUCH_DNR", chan_name);
1723 struct userNode *source;
1727 time_t min_set, max_set;
1728 time_t min_expires, max_expires;
1733 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1735 return !((dnr->set < search->min_set)
1736 || (dnr->set > search->max_set)
1737 || (dnr->expires < search->min_expires)
1738 || (search->max_expires
1739 && ((dnr->expires == 0)
1740 || (dnr->expires > search->max_expires)))
1741 || (search->chan_mask
1742 && !match_ircglob(dnr->chan_name, search->chan_mask))
1743 || (search->setter_mask
1744 && !match_ircglob(dnr->setter, search->setter_mask))
1745 || (search->reason_mask
1746 && !match_ircglob(dnr->reason, search->reason_mask)));
1749 static struct dnr_search *
1750 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1752 struct dnr_search *discrim;
1755 discrim = calloc(1, sizeof(*discrim));
1756 discrim->source = user;
1757 discrim->chan_mask = NULL;
1758 discrim->setter_mask = NULL;
1759 discrim->reason_mask = NULL;
1760 discrim->max_set = INT_MAX;
1761 discrim->limit = 50;
1763 for(ii=0; ii<argc; ++ii)
1767 reply("MSG_MISSING_PARAMS", argv[ii]);
1770 else if(0 == irccasecmp(argv[ii], "channel"))
1772 discrim->chan_mask = argv[++ii];
1774 else if(0 == irccasecmp(argv[ii], "setter"))
1776 discrim->setter_mask = argv[++ii];
1778 else if(0 == irccasecmp(argv[ii], "reason"))
1780 discrim->reason_mask = argv[++ii];
1782 else if(0 == irccasecmp(argv[ii], "limit"))
1784 discrim->limit = strtoul(argv[++ii], NULL, 0);
1786 else if(0 == irccasecmp(argv[ii], "set"))
1788 const char *cmp = argv[++ii];
1791 discrim->min_set = now - ParseInterval(cmp + 2);
1793 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1794 } else if(cmp[0] == '=') {
1795 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1796 } else if(cmp[0] == '>') {
1798 discrim->max_set = now - ParseInterval(cmp + 2);
1800 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1802 discrim->max_set = now - (ParseInterval(cmp) - 1);
1805 else if(0 == irccasecmp(argv[ii], "expires"))
1807 const char *cmp = argv[++ii];
1810 discrim->max_expires = now + ParseInterval(cmp + 2);
1812 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1813 } else if(cmp[0] == '=') {
1814 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1815 } else if(cmp[0] == '>') {
1817 discrim->min_expires = now + ParseInterval(cmp + 2);
1819 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1821 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1826 reply("MSG_INVALID_CRITERIA", argv[ii]);
1837 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1840 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1842 struct do_not_register *dnr;
1843 dict_iterator_t next;
1848 /* Initialize local variables. */
1851 if(discrim->chan_mask)
1853 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1854 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1858 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1860 /* Check against account-based DNRs. */
1861 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1862 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1865 else if(target_fixed)
1867 /* Check against channel-based DNRs. */
1868 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1869 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1874 /* Exhaustively search account DNRs. */
1875 for(it = dict_first(handle_dnrs); it; it = next)
1877 next = iter_next(it);
1878 dnr = iter_data(it);
1879 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1883 /* Do the same for channel DNRs. */
1884 for(it = dict_first(plain_dnrs); it; it = next)
1886 next = iter_next(it);
1887 dnr = iter_data(it);
1888 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1892 /* Do the same for wildcarded channel DNRs. */
1893 for(it = dict_first(mask_dnrs); it; it = next)
1895 next = iter_next(it);
1896 dnr = iter_data(it);
1897 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1905 dnr_remove_func(struct do_not_register *match, void *extra)
1907 struct userNode *user;
1910 chan_name = alloca(strlen(match->chan_name) + 1);
1911 strcpy(chan_name, match->chan_name);
1913 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1914 || dict_remove(plain_dnrs, chan_name)
1915 || dict_remove(mask_dnrs, chan_name))
1917 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1923 dnr_count_func(struct do_not_register *match, void *extra)
1925 return 0; (void)match; (void)extra;
1928 static MODCMD_FUNC(cmd_dnrsearch)
1930 struct dnr_search *discrim;
1931 dnr_search_func action;
1932 struct svccmd *subcmd;
1933 unsigned int matches;
1936 sprintf(buf, "dnrsearch %s", argv[1]);
1937 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1940 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1943 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1945 if(!irccasecmp(argv[1], "print"))
1946 action = dnr_print_func;
1947 else if(!irccasecmp(argv[1], "remove"))
1948 action = dnr_remove_func;
1949 else if(!irccasecmp(argv[1], "count"))
1950 action = dnr_count_func;
1953 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1957 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1961 if(action == dnr_print_func)
1962 reply("CSMSG_DNR_SEARCH_RESULTS");
1963 matches = dnr_search(discrim, action, user);
1965 reply("MSG_MATCH_COUNT", matches);
1967 reply("MSG_NO_MATCHES");
1973 chanserv_get_owned_count(struct handle_info *hi)
1975 struct userData *cList;
1978 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1979 if(cList->access == UL_OWNER)
1984 static CHANSERV_FUNC(cmd_register)
1986 struct handle_info *handle;
1987 struct chanData *cData;
1988 struct modeNode *mn;
1989 char reason[MAXLEN];
1991 unsigned int new_channel, force=0;
1992 struct do_not_register *dnr;
1996 if(channel->channel_info)
1998 reply("CSMSG_ALREADY_REGGED", channel->name);
2002 if(channel->bad_channel)
2004 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2009 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2011 reply("CSMSG_MUST_BE_OPPED", channel->name);
2016 chan_name = channel->name;
2020 if((argc < 2) || !IsChannelName(argv[1]))
2022 reply("MSG_NOT_CHANNEL_NAME");
2026 if(opserv_bad_channel(argv[1]))
2028 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2033 chan_name = argv[1];
2036 if(argc >= (new_channel+2))
2038 if(!IsHelping(user))
2040 reply("CSMSG_PROXY_FORBIDDEN");
2044 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2046 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2047 dnr = chanserv_is_dnr(chan_name, handle);
2051 handle = user->handle_info;
2052 dnr = chanserv_is_dnr(chan_name, handle);
2056 if(!IsHelping(user))
2057 reply("CSMSG_DNR_CHANNEL", chan_name);
2059 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2063 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2065 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2070 channel = AddChannel(argv[1], now, NULL, NULL);
2072 cData = register_channel(channel, user->handle_info->handle);
2073 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2074 cData->modes = chanserv_conf.default_modes;
2076 cData->modes.modes_set |= MODE_REGISTERED;
2077 if (IsOffChannel(cData))
2079 mod_chanmode_announce(chanserv, channel, &cData->modes);
2083 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2084 change->args[change->argc].mode = MODE_CHANOP;
2085 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2087 mod_chanmode_announce(chanserv, channel, change);
2088 mod_chanmode_free(change);
2091 /* Initialize the channel's max user record. */
2092 cData->max = channel->members.used;
2094 if(handle != user->handle_info)
2095 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2097 reply("CSMSG_REG_SUCCESS", channel->name);
2099 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2100 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2105 make_confirmation_string(struct userData *uData)
2107 static char strbuf[16];
2112 for(src = uData->handle->handle; *src; )
2113 accum = accum * 31 + toupper(*src++);
2115 for(src = uData->channel->channel->name; *src; )
2116 accum = accum * 31 + toupper(*src++);
2117 sprintf(strbuf, "%08x", accum);
2121 static CHANSERV_FUNC(cmd_unregister)
2124 char reason[MAXLEN];
2125 struct chanData *cData;
2126 struct userData *uData;
2128 cData = channel->channel_info;
2131 reply("CSMSG_NOT_REGISTERED", channel->name);
2135 uData = GetChannelUser(cData, user->handle_info);
2136 if(!uData || (uData->access < UL_OWNER))
2138 reply("CSMSG_NO_ACCESS");
2142 if(IsProtected(cData))
2144 reply("CSMSG_UNREG_NODELETE", channel->name);
2148 if(!IsHelping(user))
2150 const char *confirm_string;
2151 if(IsSuspended(cData))
2153 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2156 confirm_string = make_confirmation_string(uData);
2157 if((argc < 2) || strcmp(argv[1], confirm_string))
2159 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2164 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2165 name = strdup(channel->name);
2166 unregister_channel(cData, reason);
2167 reply("CSMSG_UNREG_SUCCESS", name);
2172 static CHANSERV_FUNC(cmd_move)
2174 struct mod_chanmode change;
2175 struct chanNode *target;
2176 struct modeNode *mn;
2177 struct userData *uData;
2178 char reason[MAXLEN];
2179 struct do_not_register *dnr;
2183 if(IsProtected(channel->channel_info))
2185 reply("CSMSG_MOVE_NODELETE", channel->name);
2189 if(!IsChannelName(argv[1]))
2191 reply("MSG_NOT_CHANNEL_NAME");
2195 if(opserv_bad_channel(argv[1]))
2197 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2201 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2203 for(uData = channel->channel_info->users; uData; uData = uData->next)
2205 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2207 if(!IsHelping(user))
2208 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2210 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2216 mod_chanmode_init(&change);
2217 if(!(target = GetChannel(argv[1])))
2219 target = AddChannel(argv[1], now, NULL, NULL);
2220 if(!IsSuspended(channel->channel_info))
2221 AddChannelUser(chanserv, target);
2223 else if(target->channel_info)
2225 reply("CSMSG_ALREADY_REGGED", target->name);
2228 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2229 && !IsHelping(user))
2231 reply("CSMSG_MUST_BE_OPPED", target->name);
2234 else if(!IsSuspended(channel->channel_info))
2237 change.args[0].mode = MODE_CHANOP;
2238 change.args[0].u.member = AddChannelUser(chanserv, target);
2239 mod_chanmode_announce(chanserv, target, &change);
2244 /* Clear MODE_REGISTERED from old channel, add it to new. */
2246 change.modes_clear = MODE_REGISTERED;
2247 mod_chanmode_announce(chanserv, channel, &change);
2248 change.modes_clear = 0;
2249 change.modes_set = MODE_REGISTERED;
2250 mod_chanmode_announce(chanserv, target, &change);
2253 /* Move the channel_info to the target channel; it
2254 shouldn't be necessary to clear timeq callbacks
2255 for the old channel. */
2256 target->channel_info = channel->channel_info;
2257 target->channel_info->channel = target;
2258 channel->channel_info = NULL;
2260 reply("CSMSG_MOVE_SUCCESS", target->name);
2262 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2263 if(!IsSuspended(target->channel_info))
2265 char reason2[MAXLEN];
2266 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2267 DelChannelUser(chanserv, channel, reason2, 0);
2269 UnlockChannel(channel);
2270 LockChannel(target);
2271 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2276 merge_users(struct chanData *source, struct chanData *target)
2278 struct userData *suData, *tuData, *next;
2284 /* Insert the source's users into the scratch area. */
2285 for(suData = source->users; suData; suData = suData->next)
2286 dict_insert(merge, suData->handle->handle, suData);
2288 /* Iterate through the target's users, looking for
2289 users common to both channels. The lower access is
2290 removed from either the scratch area or target user
2292 for(tuData = target->users; tuData; tuData = next)
2294 struct userData *choice;
2296 next = tuData->next;
2298 /* If a source user exists with the same handle as a target
2299 channel's user, resolve the conflict by removing one. */
2300 suData = dict_find(merge, tuData->handle->handle, NULL);
2304 /* Pick the data we want to keep. */
2305 /* If the access is the same, use the later seen time. */
2306 if(suData->access == tuData->access)
2307 choice = (suData->seen > tuData->seen) ? suData : tuData;
2308 else /* Otherwise, keep the higher access level. */
2309 choice = (suData->access > tuData->access) ? suData : tuData;
2311 /* Remove the user that wasn't picked. */
2312 if(choice == tuData)
2314 dict_remove(merge, suData->handle->handle);
2315 del_channel_user(suData, 0);
2318 del_channel_user(tuData, 0);
2321 /* Move the remaining users to the target channel. */
2322 for(it = dict_first(merge); it; it = iter_next(it))
2324 suData = iter_data(it);
2326 /* Insert the user into the target channel's linked list. */
2327 suData->prev = NULL;
2328 suData->next = target->users;
2329 suData->channel = target;
2332 target->users->prev = suData;
2333 target->users = suData;
2335 /* Update the user counts for the target channel; the
2336 source counts are left alone. */
2337 target->userCount++;
2340 /* Possible to assert (source->users == NULL) here. */
2341 source->users = NULL;
2346 merge_bans(struct chanData *source, struct chanData *target)
2348 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2350 /* Hold on to the original head of the target ban list
2351 to avoid comparing source bans with source bans. */
2352 tFront = target->bans;
2354 /* Perform a totally expensive O(n*m) merge, ick. */
2355 for(sbData = source->bans; sbData; sbData = sNext)
2357 /* Flag to track whether the ban's been moved
2358 to the destination yet. */
2361 /* Possible to assert (sbData->prev == NULL) here. */
2362 sNext = sbData->next;
2364 for(tbData = tFront; tbData; tbData = tNext)
2366 tNext = tbData->next;
2368 /* Perform two comparisons between each source
2369 and target ban, conflicts are resolved by
2370 keeping the broader ban and copying the later
2371 expiration and triggered time. */
2372 if(match_ircglobs(tbData->mask, sbData->mask))
2374 /* There is a broader ban in the target channel that
2375 overrides one in the source channel; remove the
2376 source ban and break. */
2377 if(sbData->expires > tbData->expires)
2378 tbData->expires = sbData->expires;
2379 if(sbData->triggered > tbData->triggered)
2380 tbData->triggered = sbData->triggered;
2381 del_channel_ban(sbData);
2384 else if(match_ircglobs(sbData->mask, tbData->mask))
2386 /* There is a broader ban in the source channel that
2387 overrides one in the target channel; remove the
2388 target ban, fall through and move the source over. */
2389 if(tbData->expires > sbData->expires)
2390 sbData->expires = tbData->expires;
2391 if(tbData->triggered > sbData->triggered)
2392 sbData->triggered = tbData->triggered;
2393 if(tbData == tFront)
2395 del_channel_ban(tbData);
2398 /* Source bans can override multiple target bans, so
2399 we allow a source to run through this loop multiple
2400 times, but we can only move it once. */
2405 /* Remove the source ban from the source ban list. */
2407 sbData->next->prev = sbData->prev;
2409 /* Modify the source ban's associated channel. */
2410 sbData->channel = target;
2412 /* Insert the ban into the target channel's linked list. */
2413 sbData->prev = NULL;
2414 sbData->next = target->bans;
2417 target->bans->prev = sbData;
2418 target->bans = sbData;
2420 /* Update the user counts for the target channel. */
2425 /* Possible to assert (source->bans == NULL) here. */
2426 source->bans = NULL;
2430 merge_data(struct chanData *source, struct chanData *target)
2432 /* Use more recent visited and owner-transfer time; use older
2433 * registered time. Bitwise or may_opchan. Use higher max.
2434 * Do not touch last_refresh, ban count or user counts.
2436 if(source->visited > target->visited)
2437 target->visited = source->visited;
2438 if(source->registered < target->registered)
2439 target->registered = source->registered;
2440 if(source->ownerTransfer > target->ownerTransfer)
2441 target->ownerTransfer = source->ownerTransfer;
2442 if(source->may_opchan)
2443 target->may_opchan = 1;
2444 if(source->max > target->max)
2445 target->max = source->max;
2449 merge_channel(struct chanData *source, struct chanData *target)
2451 merge_users(source, target);
2452 merge_bans(source, target);
2453 merge_data(source, target);
2456 static CHANSERV_FUNC(cmd_merge)
2458 struct userData *target_user;
2459 struct chanNode *target;
2460 char reason[MAXLEN];
2464 /* Make sure the target channel exists and is registered to the user
2465 performing the command. */
2466 if(!(target = GetChannel(argv[1])))
2468 reply("MSG_INVALID_CHANNEL");
2472 if(!target->channel_info)
2474 reply("CSMSG_NOT_REGISTERED", target->name);
2478 if(IsProtected(channel->channel_info))
2480 reply("CSMSG_MERGE_NODELETE");
2484 if(IsSuspended(target->channel_info))
2486 reply("CSMSG_MERGE_SUSPENDED");
2490 if(channel == target)
2492 reply("CSMSG_MERGE_SELF");
2496 target_user = GetChannelUser(target->channel_info, user->handle_info);
2497 if(!target_user || (target_user->access < UL_OWNER))
2499 reply("CSMSG_MERGE_NOT_OWNER");
2503 /* Merge the channel structures and associated data. */
2504 merge_channel(channel->channel_info, target->channel_info);
2505 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2506 unregister_channel(channel->channel_info, reason);
2507 reply("CSMSG_MERGE_SUCCESS", target->name);
2511 static CHANSERV_FUNC(cmd_opchan)
2513 struct mod_chanmode change;
2514 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2516 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2519 channel->channel_info->may_opchan = 0;
2520 mod_chanmode_init(&change);
2522 change.args[0].mode = MODE_CHANOP;
2523 change.args[0].u.member = GetUserMode(channel, chanserv);
2524 mod_chanmode_announce(chanserv, channel, &change);
2525 reply("CSMSG_OPCHAN_DONE", channel->name);
2529 static CHANSERV_FUNC(cmd_adduser)
2531 struct userData *actee;
2532 struct userData *actor, *real_actor;
2533 struct handle_info *handle;
2534 unsigned short access, override = 0;
2538 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2540 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2544 access = user_level_from_name(argv[2], UL_OWNER);
2547 reply("CSMSG_INVALID_ACCESS", argv[2]);
2551 actor = GetChannelUser(channel->channel_info, user->handle_info);
2552 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2554 if(actor->access <= access)
2556 reply("CSMSG_NO_BUMP_ACCESS");
2560 /* Trying to add someone with equal/more access? */
2561 if (!real_actor || real_actor->access <= access)
2562 override = CMD_LOG_OVERRIDE;
2564 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2567 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2569 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2573 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2574 scan_user_presence(actee, NULL);
2575 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2576 return 1 | override;
2579 static CHANSERV_FUNC(cmd_clvl)
2581 struct handle_info *handle;
2582 struct userData *victim;
2583 struct userData *actor, *real_actor;
2584 unsigned short new_access, override = 0;
2585 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2589 actor = GetChannelUser(channel->channel_info, user->handle_info);
2590 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2592 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2595 if(handle == user->handle_info && !privileged)
2597 reply("CSMSG_NO_SELF_CLVL");
2601 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2603 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2607 if(actor->access <= victim->access && !privileged)
2609 reply("MSG_USER_OUTRANKED", handle->handle);
2613 new_access = user_level_from_name(argv[2], UL_OWNER);
2617 reply("CSMSG_INVALID_ACCESS", argv[2]);
2621 if(new_access >= actor->access && !privileged)
2623 reply("CSMSG_NO_BUMP_ACCESS");
2627 /* Trying to clvl a equal/higher user? */
2628 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2629 override = CMD_LOG_OVERRIDE;
2630 /* Trying to clvl someone to equal/higher access? */
2631 if(!real_actor || new_access >= real_actor->access)
2632 override = CMD_LOG_OVERRIDE;
2633 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2634 * If they lower their own access it's not a big problem.
2637 victim->access = new_access;
2638 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2639 return 1 | override;
2642 static CHANSERV_FUNC(cmd_deluser)
2644 struct handle_info *handle;
2645 struct userData *victim;
2646 struct userData *actor, *real_actor;
2647 unsigned short access, override = 0;
2652 actor = GetChannelUser(channel->channel_info, user->handle_info);
2653 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2655 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2658 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2660 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2666 access = user_level_from_name(argv[1], UL_OWNER);
2669 reply("CSMSG_INVALID_ACCESS", argv[1]);
2672 if(access != victim->access)
2674 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2680 access = victim->access;
2683 if((actor->access <= victim->access) && !IsHelping(user))
2685 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2689 /* If people delete themselves it is an override, but they
2690 * could've used deleteme so we don't log it as an override
2692 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2693 override = CMD_LOG_OVERRIDE;
2695 chan_name = strdup(channel->name);
2696 del_channel_user(victim, 1);
2697 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2699 return 1 | override;
2703 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2705 struct userData *actor, *real_actor, *uData, *next;
2706 unsigned int override = 0;
2708 actor = GetChannelUser(channel->channel_info, user->handle_info);
2709 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2711 if(min_access > max_access)
2713 reply("CSMSG_BAD_RANGE", min_access, max_access);
2717 if((actor->access <= max_access) && !IsHelping(user))
2719 reply("CSMSG_NO_ACCESS");
2723 if(!real_actor || real_actor->access <= max_access)
2724 override = CMD_LOG_OVERRIDE;
2726 for(uData = channel->channel_info->users; uData; uData = next)
2730 if((uData->access >= min_access)
2731 && (uData->access <= max_access)
2732 && match_ircglob(uData->handle->handle, mask))
2733 del_channel_user(uData, 1);
2736 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2737 return 1 | override;
2740 static CHANSERV_FUNC(cmd_mdelowner)
2742 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2745 static CHANSERV_FUNC(cmd_mdelcoowner)
2747 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2750 static CHANSERV_FUNC(cmd_mdelmaster)
2752 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2755 static CHANSERV_FUNC(cmd_mdelop)
2757 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2760 static CHANSERV_FUNC(cmd_mdelpeon)
2762 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2766 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2768 struct banData *bData, *next;
2769 char interval[INTERVALLEN];
2774 limit = now - duration;
2775 for(bData = channel->channel_info->bans; bData; bData = next)
2779 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2782 del_channel_ban(bData);
2786 intervalString(interval, duration, user->handle_info);
2787 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2792 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2794 struct userData *actor, *uData, *next;
2795 char interval[INTERVALLEN];
2799 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2800 if(min_access > max_access)
2802 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2806 if(!actor || actor->access <= max_access)
2808 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2813 limit = now - duration;
2814 for(uData = channel->channel_info->users; uData; uData = next)
2818 if((uData->seen > limit)
2820 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2823 if(((uData->access >= min_access) && (uData->access <= max_access))
2824 || (!max_access && (uData->access < actor->access)))
2826 del_channel_user(uData, 1);
2834 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2836 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2840 static CHANSERV_FUNC(cmd_trim)
2842 unsigned long duration;
2843 unsigned short min_level, max_level;
2848 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2849 duration = ParseInterval(argv[2]);
2852 reply("CSMSG_CANNOT_TRIM");
2856 if(!irccasecmp(argv[1], "bans"))
2858 cmd_trim_bans(user, channel, duration);
2861 else if(!irccasecmp(argv[1], "users"))
2863 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2866 else if(parse_level_range(&min_level, &max_level, argv[1]))
2868 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2871 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2873 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2878 reply("CSMSG_INVALID_TRIM", argv[1]);
2883 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2884 to the user. cmd_all takes advantage of this. */
2885 static CHANSERV_FUNC(cmd_up)
2887 struct mod_chanmode change;
2888 struct userData *uData;
2891 mod_chanmode_init(&change);
2893 change.args[0].u.member = GetUserMode(channel, user);
2894 if(!change.args[0].u.member)
2897 reply("MSG_CHANNEL_ABSENT", channel->name);
2901 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2905 reply("CSMSG_GODMODE_UP", argv[0]);
2908 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2910 change.args[0].mode = MODE_CHANOP;
2911 errmsg = "CSMSG_ALREADY_OPPED";
2913 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2915 change.args[0].mode = MODE_VOICE;
2916 errmsg = "CSMSG_ALREADY_VOICED";
2921 reply("CSMSG_NO_ACCESS");
2924 change.args[0].mode &= ~change.args[0].u.member->modes;
2925 if(!change.args[0].mode)
2928 reply(errmsg, channel->name);
2931 modcmd_chanmode_announce(&change);
2935 static CHANSERV_FUNC(cmd_down)
2937 struct mod_chanmode change;
2939 mod_chanmode_init(&change);
2941 change.args[0].u.member = GetUserMode(channel, user);
2942 if(!change.args[0].u.member)
2945 reply("MSG_CHANNEL_ABSENT", channel->name);
2949 if(!change.args[0].u.member->modes)
2952 reply("CSMSG_ALREADY_DOWN", channel->name);
2956 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2957 modcmd_chanmode_announce(&change);
2961 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)
2963 struct userData *cList;
2965 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2967 if(IsSuspended(cList->channel)
2968 || IsUserSuspended(cList)
2969 || !GetUserMode(cList->channel->channel, user))
2972 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2978 static CHANSERV_FUNC(cmd_upall)
2980 return cmd_all(CSFUNC_ARGS, cmd_up);
2983 static CHANSERV_FUNC(cmd_downall)
2985 return cmd_all(CSFUNC_ARGS, cmd_down);
2988 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2989 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2992 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)
2994 unsigned int ii, valid;
2995 struct userNode *victim;
2996 struct mod_chanmode *change;
2998 change = mod_chanmode_alloc(argc - 1);
3000 for(ii=valid=0; ++ii < argc; )
3002 if(!(victim = GetUserH(argv[ii])))
3004 change->args[valid].mode = mode;
3005 change->args[valid].u.member = GetUserMode(channel, victim);
3006 if(!change->args[valid].u.member)
3008 if(validate && !validate(user, channel, victim))
3013 change->argc = valid;
3014 if(valid < (argc-1))
3015 reply("CSMSG_PROCESS_FAILED");
3018 modcmd_chanmode_announce(change);
3019 reply(action, channel->name);
3021 mod_chanmode_free(change);
3025 static CHANSERV_FUNC(cmd_op)
3027 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3030 static CHANSERV_FUNC(cmd_deop)
3032 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3035 static CHANSERV_FUNC(cmd_voice)
3037 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3040 static CHANSERV_FUNC(cmd_devoice)
3042 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3046 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3052 for(ii=0; ii<channel->members.used; ii++)
3054 struct modeNode *mn = channel->members.list[ii];
3056 if(IsService(mn->user))
3059 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3062 if(protect_user(mn->user, user, channel->channel_info))
3066 victims[(*victimCount)++] = mn;
3072 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3074 struct userNode *victim;
3075 struct modeNode **victims;
3076 unsigned int offset, n, victimCount, duration = 0;
3077 char *reason = "Bye.", *ban, *name;
3078 char interval[INTERVALLEN];
3080 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3081 REQUIRE_PARAMS(offset);
3084 reason = unsplit_string(argv + offset, argc - offset, NULL);
3085 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3087 /* Truncate the reason to a length of TOPICLEN, as
3088 the ircd does; however, leave room for an ellipsis
3089 and the kicker's nick. */
3090 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3094 if((victim = GetUserH(argv[1])))
3096 victims = alloca(sizeof(victims[0]));
3097 victims[0] = GetUserMode(channel, victim);
3098 /* XXX: The comparison with ACTION_KICK is just because all
3099 * other actions can work on users outside the channel, and we
3100 * want to allow those (e.g. unbans) in that case. If we add
3101 * some other ejection action for in-channel users, change
3103 victimCount = victims[0] ? 1 : 0;
3105 if(IsService(victim))
3107 reply("MSG_SERVICE_IMMUNE", victim->nick);
3111 if((action == ACTION_KICK) && !victimCount)
3113 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3117 if(protect_user(victim, user, channel->channel_info))
3119 reply("CSMSG_USER_PROTECTED", victim->nick);
3123 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3124 name = victim->nick;
3126 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3128 struct handle_info *hi;
3129 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3130 const char *accountname = argv[1] + 1;
3132 if(!(hi = get_handle_info(accountname)))
3134 reply("MSG_HANDLE_UNKNOWN", accountname);
3138 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3139 victims = alloca(sizeof(victims[0]) * channel->members.used);
3141 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3143 reply("CSMSG_MASK_PROTECTED", banmask);
3147 if((action == ACTION_KICK) && (victimCount == 0))
3149 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3153 name = ban = strdup(banmask);
3157 if(!is_ircmask(argv[1]))
3159 reply("MSG_NICK_UNKNOWN", argv[1]);
3163 victims = alloca(sizeof(victims[0]) * channel->members.used);
3165 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3167 reply("CSMSG_MASK_PROTECTED", argv[1]);
3171 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3173 reply("CSMSG_LAME_MASK", argv[1]);
3177 if((action == ACTION_KICK) && (victimCount == 0))
3179 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3183 name = ban = strdup(argv[1]);
3186 /* Truncate the ban in place if necessary; we must ensure
3187 that 'ban' is a valid ban mask before sanitizing it. */
3188 sanitize_ircmask(ban);
3190 if(action & ACTION_ADD_BAN)
3192 struct banData *bData, *next;
3194 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3196 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3201 if(action & ACTION_ADD_TIMED_BAN)
3203 duration = ParseInterval(argv[2]);
3207 reply("CSMSG_DURATION_TOO_LOW");
3211 else if(duration > (86400 * 365 * 2))
3213 reply("CSMSG_DURATION_TOO_HIGH");
3219 for(bData = channel->channel_info->bans; bData; bData = next)
3221 if(match_ircglobs(bData->mask, ban))
3223 int exact = !irccasecmp(bData->mask, ban);
3225 /* The ban is redundant; there is already a ban
3226 with the same effect in place. */
3230 free(bData->reason);
3231 bData->reason = strdup(reason);
3232 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3234 reply("CSMSG_REASON_CHANGE", ban);
3238 if(exact && bData->expires)
3242 /* If the ban matches an existing one exactly,
3243 extend the expiration time if the provided
3244 duration is longer. */
3245 if(duration && ((time_t)(now + duration) > bData->expires))
3247 bData->expires = now + duration;
3258 /* Delete the expiration timeq entry and
3259 requeue if necessary. */
3260 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3263 timeq_add(bData->expires, expire_ban, bData);
3267 /* automated kickban */
3270 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3272 reply("CSMSG_BAN_ADDED", name, channel->name);
3278 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3285 if(match_ircglobs(ban, bData->mask))
3287 /* The ban we are adding makes previously existing
3288 bans redundant; silently remove them. */
3289 del_channel_ban(bData);
3293 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);
3295 name = ban = strdup(bData->mask);
3299 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3301 extern const char *hidden_host_suffix;
3302 const char *old_name = chanserv_conf.old_ban_names->list[n];
3304 unsigned int l1, l2;
3307 l2 = strlen(old_name);
3310 if(irccasecmp(ban + l1 - l2, old_name))
3312 new_mask = malloc(MAXLEN);
3313 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3315 name = ban = new_mask;
3320 if(action & ACTION_BAN)
3322 unsigned int exists;
3323 struct mod_chanmode *change;
3325 if(channel->banlist.used >= MAXBANS)
3328 reply("CSMSG_BANLIST_FULL", channel->name);
3333 exists = ChannelBanExists(channel, ban);
3334 change = mod_chanmode_alloc(victimCount + 1);
3335 for(n = 0; n < victimCount; ++n)
3337 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3338 change->args[n].u.member = victims[n];
3342 change->args[n].mode = MODE_BAN;
3343 change->args[n++].u.hostmask = ban;
3347 modcmd_chanmode_announce(change);
3349 mod_chanmode_announce(chanserv, channel, change);
3350 mod_chanmode_free(change);
3352 if(exists && (action == ACTION_BAN))
3355 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3361 if(action & ACTION_KICK)
3363 char kick_reason[MAXLEN];
3364 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3366 for(n = 0; n < victimCount; n++)
3367 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3372 /* No response, since it was automated. */
3374 else if(action & ACTION_ADD_BAN)
3377 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3379 reply("CSMSG_BAN_ADDED", name, channel->name);
3381 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3382 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3383 else if(action & ACTION_BAN)
3384 reply("CSMSG_BAN_DONE", name, channel->name);
3385 else if(action & ACTION_KICK && victimCount)
3386 reply("CSMSG_KICK_DONE", name, channel->name);
3392 static CHANSERV_FUNC(cmd_kickban)
3394 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3397 static CHANSERV_FUNC(cmd_kick)
3399 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3402 static CHANSERV_FUNC(cmd_ban)
3404 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3407 static CHANSERV_FUNC(cmd_addban)
3409 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3412 static CHANSERV_FUNC(cmd_addtimedban)
3414 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3417 static struct mod_chanmode *
3418 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3420 struct mod_chanmode *change;
3421 unsigned char *match;
3422 unsigned int ii, count;
3424 match = alloca(bans->used);
3427 for(ii = count = 0; ii < bans->used; ++ii)
3429 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3430 MATCH_USENICK | MATCH_VISIBLE);
3437 for(ii = count = 0; ii < bans->used; ++ii)
3439 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3446 change = mod_chanmode_alloc(count);
3447 for(ii = count = 0; ii < bans->used; ++ii)
3451 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3452 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3454 assert(count == change->argc);
3459 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3461 struct userNode *actee;
3467 /* may want to allow a comma delimited list of users... */
3468 if(!(actee = GetUserH(argv[1])))
3470 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3472 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3473 const char *accountname = argv[1] + 1;
3475 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3476 mask = strdup(banmask);
3478 else if(!is_ircmask(argv[1]))
3480 reply("MSG_NICK_UNKNOWN", argv[1]);
3485 mask = strdup(argv[1]);
3489 /* We don't sanitize the mask here because ircu
3491 if(action & ACTION_UNBAN)
3493 struct mod_chanmode *change;
3494 change = find_matching_bans(&channel->banlist, actee, mask);
3499 modcmd_chanmode_announce(change);
3500 for(ii = 0; ii < change->argc; ++ii)
3501 free((char*)change->args[ii].u.hostmask);
3502 mod_chanmode_free(change);
3507 if(action & ACTION_DEL_BAN)
3509 struct banData *ban, *next;
3511 ban = channel->channel_info->bans;
3515 for( ; ban && !user_matches_glob(actee, ban->mask,
3516 MATCH_USENICK | MATCH_VISIBLE);
3519 for( ; ban && !match_ircglobs(mask, ban->mask);
3524 del_channel_ban(ban);
3531 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3533 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3539 static CHANSERV_FUNC(cmd_unban)
3541 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3544 static CHANSERV_FUNC(cmd_delban)
3546 /* it doesn't necessarily have to remove the channel ban - may want
3547 to make that an option. */
3548 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3551 static CHANSERV_FUNC(cmd_unbanme)
3553 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3554 long flags = ACTION_UNBAN;
3556 /* remove permanent bans if the user has the proper access. */
3557 if(uData->access >= UL_MASTER)
3558 flags |= ACTION_DEL_BAN;
3560 argv[1] = user->nick;
3561 return unban_user(user, channel, 2, argv, cmd, flags);
3564 static CHANSERV_FUNC(cmd_unbanall)
3566 struct mod_chanmode *change;
3569 if(!channel->banlist.used)
3571 reply("CSMSG_NO_BANS", channel->name);
3575 change = mod_chanmode_alloc(channel->banlist.used);
3576 for(ii=0; ii<channel->banlist.used; ii++)
3578 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3579 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3581 modcmd_chanmode_announce(change);
3582 for(ii = 0; ii < change->argc; ++ii)
3583 free((char*)change->args[ii].u.hostmask);
3584 mod_chanmode_free(change);
3585 reply("CSMSG_BANS_REMOVED", channel->name);
3589 static CHANSERV_FUNC(cmd_open)
3591 struct mod_chanmode *change;
3594 change = find_matching_bans(&channel->banlist, user, NULL);
3596 change = mod_chanmode_alloc(0);
3597 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3598 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3599 && channel->channel_info->modes.modes_set)
3600 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3601 modcmd_chanmode_announce(change);
3602 reply("CSMSG_CHANNEL_OPENED", channel->name);
3603 for(ii = 0; ii < change->argc; ++ii)
3604 free((char*)change->args[ii].u.hostmask);
3605 mod_chanmode_free(change);
3609 static CHANSERV_FUNC(cmd_myaccess)
3611 static struct string_buffer sbuf;
3612 struct handle_info *target_handle;
3613 struct userData *uData;
3616 target_handle = user->handle_info;
3617 else if(!IsHelping(user))
3619 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3622 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3625 if(!target_handle->channels)
3627 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3631 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3632 for(uData = target_handle->channels; uData; uData = uData->u_next)
3634 struct chanData *cData = uData->channel;
3636 if(uData->access > UL_OWNER)
3638 if(IsProtected(cData)
3639 && (target_handle != user->handle_info)
3640 && !GetTrueChannelAccess(cData, user->handle_info))
3643 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3644 if(uData->flags != USER_AUTO_OP)
3645 string_buffer_append(&sbuf, ',');
3646 if(IsUserSuspended(uData))
3647 string_buffer_append(&sbuf, 's');
3648 if(IsUserAutoOp(uData))
3650 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3651 string_buffer_append(&sbuf, 'o');
3652 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3653 string_buffer_append(&sbuf, 'v');
3655 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3656 string_buffer_append(&sbuf, 'i');
3658 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3660 string_buffer_append_string(&sbuf, ")]");
3661 string_buffer_append(&sbuf, '\0');
3662 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3668 static CHANSERV_FUNC(cmd_access)
3670 struct userNode *target;
3671 struct handle_info *target_handle;
3672 struct userData *uData;
3674 char prefix[MAXLEN];
3679 target_handle = target->handle_info;
3681 else if((target = GetUserH(argv[1])))
3683 target_handle = target->handle_info;
3685 else if(argv[1][0] == '*')
3687 if(!(target_handle = get_handle_info(argv[1]+1)))
3689 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3695 reply("MSG_NICK_UNKNOWN", argv[1]);
3699 assert(target || target_handle);
3701 if(target == chanserv)
3703 reply("CSMSG_IS_CHANSERV");
3711 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3716 reply("MSG_USER_AUTHENTICATE", target->nick);
3719 reply("MSG_AUTHENTICATE");
3725 const char *epithet = NULL, *type = NULL;
3728 epithet = chanserv_conf.irc_operator_epithet;
3729 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3731 else if(IsNetworkHelper(target))
3733 epithet = chanserv_conf.network_helper_epithet;
3734 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3736 else if(IsSupportHelper(target))
3738 epithet = chanserv_conf.support_helper_epithet;
3739 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3743 if(target_handle->epithet)
3744 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3746 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3748 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3752 sprintf(prefix, "%s", target_handle->handle);
3755 if(!channel->channel_info)
3757 reply("CSMSG_NOT_REGISTERED", channel->name);
3761 helping = HANDLE_FLAGGED(target_handle, HELPING)
3762 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3763 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3765 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3766 /* To prevent possible information leaks, only show infolines
3767 * if the requestor is in the channel or it's their own
3769 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3771 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3773 /* Likewise, only say it's suspended if the user has active
3774 * access in that channel or it's their own entry. */
3775 if(IsUserSuspended(uData)
3776 && (GetChannelUser(channel->channel_info, user->handle_info)
3777 || (user->handle_info == uData->handle)))
3779 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3784 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3791 zoot_list(struct listData *list)
3793 struct userData *uData;
3794 unsigned int start, curr, highest, lowest;
3795 struct helpfile_table tmp_table;
3796 const char **temp, *msg;
3798 if(list->table.length == 1)
3801 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3803 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3804 msg = user_find_message(list->user, "MSG_NONE");
3805 send_message_type(4, list->user, list->bot, " %s", msg);
3807 tmp_table.width = list->table.width;
3808 tmp_table.flags = list->table.flags;
3809 list->table.contents[0][0] = " ";
3810 highest = list->highest;
3811 if(list->lowest != 0)
3812 lowest = list->lowest;
3813 else if(highest < 100)
3816 lowest = highest - 100;
3817 for(start = curr = 1; curr < list->table.length; )
3819 uData = list->users[curr-1];
3820 list->table.contents[curr++][0] = " ";
3821 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3824 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3826 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3827 temp = list->table.contents[--start];
3828 list->table.contents[start] = list->table.contents[0];
3829 tmp_table.contents = list->table.contents + start;
3830 tmp_table.length = curr - start;
3831 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3832 list->table.contents[start] = temp;
3834 highest = lowest - 1;
3835 lowest = (highest < 100) ? 0 : (highest - 99);
3841 def_list(struct listData *list)
3845 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3847 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3848 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3849 if(list->table.length == 1)
3851 msg = user_find_message(list->user, "MSG_NONE");
3852 send_message_type(4, list->user, list->bot, " %s", msg);
3857 userData_access_comp(const void *arg_a, const void *arg_b)
3859 const struct userData *a = *(struct userData**)arg_a;
3860 const struct userData *b = *(struct userData**)arg_b;
3862 if(a->access != b->access)
3863 res = b->access - a->access;
3865 res = irccasecmp(a->handle->handle, b->handle->handle);
3870 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3872 void (*send_list)(struct listData *);
3873 struct userData *uData;
3874 struct listData lData;
3875 unsigned int matches;
3879 lData.bot = cmd->parent->bot;
3880 lData.channel = channel;
3881 lData.lowest = lowest;
3882 lData.highest = highest;
3883 lData.search = (argc > 1) ? argv[1] : NULL;
3884 send_list = def_list;
3885 (void)zoot_list; /* since it doesn't show user levels */
3887 if(user->handle_info)
3889 switch(user->handle_info->userlist_style)
3891 case HI_STYLE_DEF: send_list = def_list; break;
3892 case HI_STYLE_ZOOT: send_list = def_list; break;
3896 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3898 for(uData = channel->channel_info->users; uData; uData = uData->next)
3900 if((uData->access < lowest)
3901 || (uData->access > highest)
3902 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3904 lData.users[matches++] = uData;
3906 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3908 lData.table.length = matches+1;
3909 lData.table.width = 4;
3910 lData.table.flags = TABLE_NO_FREE;
3911 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3912 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3913 lData.table.contents[0] = ary;
3916 ary[2] = "Last Seen";
3918 for(matches = 1; matches < lData.table.length; ++matches)
3920 struct userData *uData = lData.users[matches-1];
3921 char seen[INTERVALLEN];
3923 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3924 lData.table.contents[matches] = ary;
3925 ary[0] = strtab(uData->access);
3926 ary[1] = uData->handle->handle;
3929 else if(!uData->seen)
3932 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3933 ary[2] = strdup(ary[2]);
3934 if(IsUserSuspended(uData))
3935 ary[3] = "Suspended";
3936 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3937 ary[3] = "Vacation";
3942 for(matches = 1; matches < lData.table.length; ++matches)
3944 free((char*)lData.table.contents[matches][2]);
3945 free(lData.table.contents[matches]);
3947 free(lData.table.contents[0]);
3948 free(lData.table.contents);
3952 static CHANSERV_FUNC(cmd_users)
3954 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3957 static CHANSERV_FUNC(cmd_wlist)
3959 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3962 static CHANSERV_FUNC(cmd_clist)
3964 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3967 static CHANSERV_FUNC(cmd_mlist)
3969 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3972 static CHANSERV_FUNC(cmd_olist)
3974 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3977 static CHANSERV_FUNC(cmd_plist)
3979 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3982 static CHANSERV_FUNC(cmd_bans)
3984 struct userNode *search_u = NULL;
3985 struct helpfile_table tbl;
3986 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3987 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3988 const char *msg_never, *triggered, *expires;
3989 struct banData *ban, **bans;
3993 else if(strchr(search = argv[1], '!'))
3996 search_wilds = search[strcspn(search, "?*")];
3998 else if(!(search_u = GetUserH(search)))
3999 reply("MSG_NICK_UNKNOWN", search);
4001 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4003 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4007 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4012 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4015 bans[matches++] = ban;
4020 tbl.length = matches + 1;
4021 tbl.width = 4 + timed;
4023 tbl.flags = TABLE_NO_FREE;
4024 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4025 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4026 tbl.contents[0][0] = "Mask";
4027 tbl.contents[0][1] = "Set By";
4028 tbl.contents[0][2] = "Triggered";
4031 tbl.contents[0][3] = "Expires";
4032 tbl.contents[0][4] = "Reason";
4035 tbl.contents[0][3] = "Reason";
4038 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4040 free(tbl.contents[0]);
4045 msg_never = user_find_message(user, "MSG_NEVER");
4046 for(ii = 0; ii < matches; )
4052 else if(ban->expires)
4053 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4055 expires = msg_never;
4058 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4060 triggered = msg_never;
4062 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4063 tbl.contents[ii][0] = ban->mask;
4064 tbl.contents[ii][1] = ban->owner;
4065 tbl.contents[ii][2] = strdup(triggered);
4068 tbl.contents[ii][3] = strdup(expires);
4069 tbl.contents[ii][4] = ban->reason;
4072 tbl.contents[ii][3] = ban->reason;
4074 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4075 reply("MSG_MATCH_COUNT", matches);
4076 for(ii = 1; ii < tbl.length; ++ii)
4078 free((char*)tbl.contents[ii][2]);
4080 free((char*)tbl.contents[ii][3]);
4081 free(tbl.contents[ii]);
4083 free(tbl.contents[0]);
4089 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4091 struct chanData *cData = channel->channel_info;
4092 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4094 if(cData->topic_mask)
4095 return !match_ircglob(new_topic, cData->topic_mask);
4096 else if(cData->topic)
4097 return irccasecmp(new_topic, cData->topic);
4102 static CHANSERV_FUNC(cmd_topic)
4104 struct chanData *cData;
4107 cData = channel->channel_info;
4112 SetChannelTopic(channel, chanserv, cData->topic, 1);
4113 reply("CSMSG_TOPIC_SET", cData->topic);
4117 reply("CSMSG_NO_TOPIC", channel->name);
4121 topic = unsplit_string(argv + 1, argc - 1, NULL);
4122 /* If they say "!topic *", use an empty topic. */
4123 if((topic[0] == '*') && (topic[1] == 0))
4125 if(bad_topic(channel, user, topic))
4127 char *topic_mask = cData->topic_mask;
4130 char new_topic[TOPICLEN+1], tchar;
4131 int pos=0, starpos=-1, dpos=0, len;
4133 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4140 len = strlen(topic);
4141 if((dpos + len) > TOPICLEN)
4142 len = TOPICLEN + 1 - dpos;
4143 memcpy(new_topic+dpos, topic, len);
4147 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4148 default: new_topic[dpos++] = tchar; break;
4151 if((dpos > TOPICLEN) || tchar)
4154 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4155 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4158 new_topic[dpos] = 0;
4159 SetChannelTopic(channel, chanserv, new_topic, 1);
4161 reply("CSMSG_TOPIC_LOCKED", channel->name);
4166 SetChannelTopic(channel, chanserv, topic, 1);
4168 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4170 /* Grab the topic and save it as the default topic. */
4172 cData->topic = strdup(channel->topic);
4178 static CHANSERV_FUNC(cmd_mode)
4180 struct userData *uData;
4181 struct mod_chanmode *change;
4186 change = &channel->channel_info->modes;
4187 if(change->modes_set || change->modes_clear) {
4188 modcmd_chanmode_announce(change);
4189 reply("CSMSG_DEFAULTED_MODES", channel->name);
4191 reply("CSMSG_NO_MODES", channel->name);
4195 uData = GetChannelUser(channel->channel_info, user->handle_info);
4197 base_oplevel = MAXOPLEVEL;
4198 else if (uData->access >= UL_OWNER)
4201 base_oplevel = 1 + UL_OWNER - uData->access;
4202 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4205 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4209 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4210 && mode_lock_violated(&channel->channel_info->modes, change))
4213 mod_chanmode_format(&channel->channel_info->modes, modes);
4214 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4218 modcmd_chanmode_announce(change);
4219 mod_chanmode_free(change);
4220 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4224 static CHANSERV_FUNC(cmd_invite)
4226 struct userData *uData;
4227 struct userNode *invite;
4229 uData = GetChannelUser(channel->channel_info, user->handle_info);
4233 if(!(invite = GetUserH(argv[1])))
4235 reply("MSG_NICK_UNKNOWN", argv[1]);
4242 if(GetUserMode(channel, invite))
4244 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4252 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4253 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4256 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4258 irc_invite(chanserv, invite, channel);
4260 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4265 static CHANSERV_FUNC(cmd_inviteme)
4267 if(GetUserMode(channel, user))
4269 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4272 if(channel->channel_info
4273 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4275 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4278 irc_invite(cmd->parent->bot, user, channel);
4283 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4286 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4288 /* We display things based on two dimensions:
4289 * - Issue time: present or absent
4290 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4291 * (in order of precedence, so something both expired and revoked
4292 * only counts as revoked)
4294 combo = (suspended->issued ? 4 : 0)
4295 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4297 case 0: /* no issue time, indefinite expiration */
4298 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4300 case 1: /* no issue time, expires in future */
4301 intervalString(buf1, suspended->expires-now, user->handle_info);
4302 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4304 case 2: /* no issue time, expired */
4305 intervalString(buf1, now-suspended->expires, user->handle_info);
4306 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4308 case 3: /* no issue time, revoked */
4309 intervalString(buf1, now-suspended->revoked, user->handle_info);
4310 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4312 case 4: /* issue time set, indefinite expiration */
4313 intervalString(buf1, now-suspended->issued, user->handle_info);
4314 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4316 case 5: /* issue time set, expires in future */
4317 intervalString(buf1, now-suspended->issued, user->handle_info);
4318 intervalString(buf2, suspended->expires-now, user->handle_info);
4319 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4321 case 6: /* issue time set, expired */
4322 intervalString(buf1, now-suspended->issued, user->handle_info);
4323 intervalString(buf2, now-suspended->expires, user->handle_info);
4324 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4326 case 7: /* issue time set, revoked */
4327 intervalString(buf1, now-suspended->issued, user->handle_info);
4328 intervalString(buf2, now-suspended->revoked, user->handle_info);
4329 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4332 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4337 static CHANSERV_FUNC(cmd_info)
4339 char modes[MAXLEN], buffer[INTERVALLEN];
4340 struct userData *uData, *owner;
4341 struct chanData *cData;
4342 struct do_not_register *dnr;
4347 cData = channel->channel_info;
4348 reply("CSMSG_CHANNEL_INFO", channel->name);
4350 uData = GetChannelUser(cData, user->handle_info);
4351 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4353 mod_chanmode_format(&cData->modes, modes);
4354 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4355 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4358 for(it = dict_first(cData->notes); it; it = iter_next(it))
4362 note = iter_data(it);
4363 if(!note_type_visible_to_user(cData, note->type, user))
4366 padding = PADLEN - 1 - strlen(iter_key(it));
4367 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4370 reply("CSMSG_CHANNEL_MAX", cData->max);
4371 for(owner = cData->users; owner; owner = owner->next)
4372 if(owner->access == UL_OWNER)
4373 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4374 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4375 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4376 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4378 privileged = IsStaff(user);
4380 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4381 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4382 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4384 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4385 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4387 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4389 struct suspended *suspended;
4390 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4391 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4392 show_suspension_info(cmd, user, suspended);
4394 else if(IsSuspended(cData))
4396 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4397 show_suspension_info(cmd, user, cData->suspended);
4402 static CHANSERV_FUNC(cmd_netinfo)
4404 extern time_t boot_time;
4405 extern unsigned long burst_length;
4406 char interval[INTERVALLEN];
4408 reply("CSMSG_NETWORK_INFO");
4409 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4410 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4411 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4412 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4413 reply("CSMSG_NETWORK_BANS", banCount);
4414 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4415 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4416 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4421 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4423 struct helpfile_table table;
4425 struct userNode *user;
4430 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4431 table.contents = alloca(list->used*sizeof(*table.contents));
4432 for(nn=0; nn<list->used; nn++)
4434 user = list->list[nn];
4435 if(user->modes & skip_flags)
4439 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4442 nick = alloca(strlen(user->nick)+3);
4443 sprintf(nick, "(%s)", user->nick);
4447 table.contents[table.length][0] = nick;
4450 table_send(chanserv, to->nick, 0, NULL, table);
4453 static CHANSERV_FUNC(cmd_ircops)
4455 reply("CSMSG_STAFF_OPERS");
4456 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4460 static CHANSERV_FUNC(cmd_helpers)
4462 reply("CSMSG_STAFF_HELPERS");
4463 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4467 static CHANSERV_FUNC(cmd_staff)
4469 reply("CSMSG_NETWORK_STAFF");
4470 cmd_ircops(CSFUNC_ARGS);
4471 cmd_helpers(CSFUNC_ARGS);
4475 static CHANSERV_FUNC(cmd_peek)
4477 struct modeNode *mn;
4478 char modes[MODELEN];
4480 struct helpfile_table table;
4482 irc_make_chanmode(channel, modes);
4484 reply("CSMSG_PEEK_INFO", channel->name);
4485 reply("CSMSG_PEEK_TOPIC", channel->topic);
4486 reply("CSMSG_PEEK_MODES", modes);
4487 reply("CSMSG_PEEK_USERS", channel->members.used);
4491 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4492 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4493 for(n = 0; n < channel->members.used; n++)
4495 mn = channel->members.list[n];
4496 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4498 table.contents[table.length] = alloca(sizeof(**table.contents));
4499 table.contents[table.length][0] = mn->user->nick;
4504 reply("CSMSG_PEEK_OPS");
4505 table_send(chanserv, user->nick, 0, NULL, table);
4508 reply("CSMSG_PEEK_NO_OPS");
4512 static MODCMD_FUNC(cmd_wipeinfo)
4514 struct handle_info *victim;
4515 struct userData *ud, *actor, *real_actor;
4516 unsigned int override = 0;
4519 actor = GetChannelUser(channel->channel_info, user->handle_info);
4520 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4521 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4523 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4525 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4528 if((ud->access >= actor->access) && (ud != actor))
4530 reply("MSG_USER_OUTRANKED", victim->handle);
4533 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4534 override = CMD_LOG_OVERRIDE;
4538 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4539 return 1 | override;
4542 static CHANSERV_FUNC(cmd_resync)
4544 struct mod_chanmode *changes;
4545 struct chanData *cData = channel->channel_info;
4546 unsigned int ii, used;
4548 changes = mod_chanmode_alloc(channel->members.used * 2);
4549 for(ii = used = 0; ii < channel->members.used; ++ii)
4551 struct modeNode *mn = channel->members.list[ii];
4552 struct userData *uData;
4554 if(IsService(mn->user))
4557 uData = GetChannelAccess(cData, mn->user->handle_info);
4558 if(!cData->lvlOpts[lvlGiveOps]
4559 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4561 if(!(mn->modes & MODE_CHANOP))
4563 changes->args[used].mode = MODE_CHANOP;
4564 changes->args[used++].u.member = mn;
4567 else if(!cData->lvlOpts[lvlGiveVoice]
4568 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4570 if(mn->modes & MODE_CHANOP)
4572 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4573 changes->args[used++].u.member = mn;
4575 if(!(mn->modes & MODE_VOICE))
4577 changes->args[used].mode = MODE_VOICE;
4578 changes->args[used++].u.member = mn;
4585 changes->args[used].mode = MODE_REMOVE | mn->modes;
4586 changes->args[used++].u.member = mn;
4590 changes->argc = used;
4591 modcmd_chanmode_announce(changes);
4592 mod_chanmode_free(changes);
4593 reply("CSMSG_RESYNCED_USERS", channel->name);
4597 static CHANSERV_FUNC(cmd_seen)
4599 struct userData *uData;
4600 struct handle_info *handle;
4601 char seen[INTERVALLEN];
4605 if(!irccasecmp(argv[1], chanserv->nick))
4607 reply("CSMSG_IS_CHANSERV");
4611 if(!(handle = get_handle_info(argv[1])))
4613 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4617 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4619 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4624 reply("CSMSG_USER_PRESENT", handle->handle);
4625 else if(uData->seen)
4626 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4628 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4630 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4631 reply("CSMSG_USER_VACATION", handle->handle);
4636 static MODCMD_FUNC(cmd_names)
4638 struct userNode *targ;
4639 struct userData *targData;
4640 unsigned int ii, pos;
4643 for(ii=pos=0; ii<channel->members.used; ++ii)
4645 targ = channel->members.list[ii]->user;
4646 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4649 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4652 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4656 if(IsUserSuspended(targData))
4658 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4661 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4662 reply("CSMSG_END_NAMES", channel->name);
4667 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4669 switch(ntype->visible_type)
4671 case NOTE_VIS_ALL: return 1;
4672 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4673 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4678 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4680 struct userData *uData;
4682 switch(ntype->set_access_type)
4684 case NOTE_SET_CHANNEL_ACCESS:
4685 if(!user->handle_info)
4687 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4689 return uData->access >= ntype->set_access.min_ulevel;
4690 case NOTE_SET_CHANNEL_SETTER:
4691 return check_user_level(channel, user, lvlSetters, 1, 0);
4692 case NOTE_SET_PRIVILEGED: default:
4693 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4697 static CHANSERV_FUNC(cmd_note)
4699 struct chanData *cData;
4701 struct note_type *ntype;
4703 cData = channel->channel_info;
4706 reply("CSMSG_NOT_REGISTERED", channel->name);
4710 /* If no arguments, show all visible notes for the channel. */
4716 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4718 note = iter_data(it);
4719 if(!note_type_visible_to_user(cData, note->type, user))
4722 reply("CSMSG_NOTELIST_HEADER", channel->name);
4723 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4726 reply("CSMSG_NOTELIST_END", channel->name);
4728 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4730 /* If one argument, show the named note. */
4733 if((note = dict_find(cData->notes, argv[1], NULL))
4734 && note_type_visible_to_user(cData, note->type, user))
4736 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4738 else if((ntype = dict_find(note_types, argv[1], NULL))
4739 && note_type_visible_to_user(NULL, ntype, user))
4741 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4746 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4750 /* Assume they're trying to set a note. */
4754 ntype = dict_find(note_types, argv[1], NULL);
4757 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4760 else if(note_type_settable_by_user(channel, ntype, user))
4762 note_text = unsplit_string(argv+2, argc-2, NULL);
4763 if((note = dict_find(cData->notes, argv[1], NULL)))
4764 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4765 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4766 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4768 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4770 /* The note is viewable to staff only, so return 0
4771 to keep the invocation from getting logged (or
4772 regular users can see it in !events). */
4778 reply("CSMSG_NO_ACCESS");
4785 static CHANSERV_FUNC(cmd_delnote)
4790 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4791 || !note_type_settable_by_user(channel, note->type, user))
4793 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4796 dict_remove(channel->channel_info->notes, note->type->name);
4797 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4801 static CHANSERV_FUNC(cmd_events)
4803 struct logSearch discrim;
4804 struct logReport report;
4805 unsigned int matches, limit;
4807 limit = (argc > 1) ? atoi(argv[1]) : 10;
4808 if(limit < 1 || limit > 200)
4811 memset(&discrim, 0, sizeof(discrim));
4812 discrim.masks.bot = chanserv;
4813 discrim.masks.channel_name = channel->name;
4815 discrim.masks.command = argv[2];
4816 discrim.limit = limit;
4817 discrim.max_time = INT_MAX;
4818 discrim.severities = 1 << LOG_COMMAND;
4819 report.reporter = chanserv;
4821 reply("CSMSG_EVENT_SEARCH_RESULTS");
4822 matches = log_entry_search(&discrim, log_report_entry, &report);
4824 reply("MSG_MATCH_COUNT", matches);
4826 reply("MSG_NO_MATCHES");
4830 static CHANSERV_FUNC(cmd_say)
4836 msg = unsplit_string(argv + 1, argc - 1, NULL);
4837 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4839 else if(GetUserH(argv[1]))
4842 msg = unsplit_string(argv + 2, argc - 2, NULL);
4843 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4847 reply("MSG_NOT_TARGET_NAME");
4853 static CHANSERV_FUNC(cmd_emote)
4859 /* CTCP is so annoying. */
4860 msg = unsplit_string(argv + 1, argc - 1, NULL);
4861 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4863 else if(GetUserH(argv[1]))
4865 msg = unsplit_string(argv + 2, argc - 2, NULL);
4866 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4870 reply("MSG_NOT_TARGET_NAME");
4876 struct channelList *
4877 chanserv_support_channels(void)
4879 return &chanserv_conf.support_channels;
4882 static CHANSERV_FUNC(cmd_expire)
4884 int channel_count = registered_channels;
4885 expire_channels(NULL);
4886 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4891 chanserv_expire_suspension(void *data)
4893 struct suspended *suspended = data;
4894 struct chanNode *channel;
4896 if(!suspended->expires || (now < suspended->expires))
4897 suspended->revoked = now;
4898 channel = suspended->cData->channel;
4899 suspended->cData->channel = channel;
4900 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4901 if(!IsOffChannel(suspended->cData))
4903 struct mod_chanmode change;
4904 mod_chanmode_init(&change);
4906 change.args[0].mode = MODE_CHANOP;
4907 change.args[0].u.member = AddChannelUser(chanserv, channel);
4908 mod_chanmode_announce(chanserv, channel, &change);
4912 static CHANSERV_FUNC(cmd_csuspend)
4914 struct suspended *suspended;
4915 char reason[MAXLEN];
4916 time_t expiry, duration;
4917 struct userData *uData;
4921 if(IsProtected(channel->channel_info))
4923 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4927 if(argv[1][0] == '!')
4929 else if(IsSuspended(channel->channel_info))
4931 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4932 show_suspension_info(cmd, user, channel->channel_info->suspended);
4936 if(!strcmp(argv[1], "0"))
4938 else if((duration = ParseInterval(argv[1])))
4939 expiry = now + duration;
4942 reply("MSG_INVALID_DURATION", argv[1]);
4946 unsplit_string(argv + 2, argc - 2, reason);
4948 suspended = calloc(1, sizeof(*suspended));
4949 suspended->revoked = 0;
4950 suspended->issued = now;
4951 suspended->suspender = strdup(user->handle_info->handle);
4952 suspended->expires = expiry;
4953 suspended->reason = strdup(reason);
4954 suspended->cData = channel->channel_info;
4955 suspended->previous = suspended->cData->suspended;
4956 suspended->cData->suspended = suspended;
4958 if(suspended->expires)
4959 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4961 if(IsSuspended(channel->channel_info))
4963 suspended->previous->revoked = now;
4964 if(suspended->previous->expires)
4965 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4966 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4967 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4971 /* Mark all users in channel as absent. */
4972 for(uData = channel->channel_info->users; uData; uData = uData->next)
4981 /* Mark the channel as suspended, then part. */
4982 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4983 DelChannelUser(chanserv, channel, suspended->reason, 0);
4984 reply("CSMSG_SUSPENDED", channel->name);
4985 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4986 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4991 static CHANSERV_FUNC(cmd_cunsuspend)
4993 struct suspended *suspended;
4994 char message[MAXLEN];
4996 if(!IsSuspended(channel->channel_info))
4998 reply("CSMSG_NOT_SUSPENDED", channel->name);
5002 suspended = channel->channel_info->suspended;
5004 /* Expire the suspension and join ChanServ to the channel. */
5005 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5006 chanserv_expire_suspension(suspended);
5007 reply("CSMSG_UNSUSPENDED", channel->name);
5008 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5009 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5013 typedef struct chanservSearch
5021 unsigned long flags;
5025 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5028 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5033 search = malloc(sizeof(struct chanservSearch));
5034 memset(search, 0, sizeof(*search));
5037 for(i = 0; i < argc; i++)
5039 /* Assume all criteria require arguments. */
5042 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5046 if(!irccasecmp(argv[i], "name"))
5047 search->name = argv[++i];
5048 else if(!irccasecmp(argv[i], "registrar"))
5049 search->registrar = argv[++i];
5050 else if(!irccasecmp(argv[i], "unvisited"))
5051 search->unvisited = ParseInterval(argv[++i]);
5052 else if(!irccasecmp(argv[i], "registered"))
5053 search->registered = ParseInterval(argv[++i]);
5054 else if(!irccasecmp(argv[i], "flags"))
5057 if(!irccasecmp(argv[i], "nodelete"))
5058 search->flags |= CHANNEL_NODELETE;
5059 else if(!irccasecmp(argv[i], "suspended"))
5060 search->flags |= CHANNEL_SUSPENDED;
5061 else if(!irccasecmp(argv[i], "unreviewed"))
5062 search->flags |= CHANNEL_UNREVIEWED;
5065 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5069 else if(!irccasecmp(argv[i], "limit"))
5070 search->limit = strtoul(argv[++i], NULL, 10);
5073 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5078 if(search->name && !strcmp(search->name, "*"))
5080 if(search->registrar && !strcmp(search->registrar, "*"))
5081 search->registrar = 0;
5090 chanserv_channel_match(struct chanData *channel, search_t search)
5092 const char *name = channel->channel->name;
5093 if((search->name && !match_ircglob(name, search->name)) ||
5094 (search->registrar && !channel->registrar) ||
5095 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5096 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5097 (search->registered && (now - channel->registered) > search->registered) ||
5098 (search->flags && ((search->flags & channel->flags) != search->flags)))
5105 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5107 struct chanData *channel;
5108 unsigned int matches = 0;
5110 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5112 if(!chanserv_channel_match(channel, search))
5122 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5127 search_print(struct chanData *channel, void *data)
5129 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5132 static CHANSERV_FUNC(cmd_search)
5135 unsigned int matches;
5136 channel_search_func action;
5140 if(!irccasecmp(argv[1], "count"))
5141 action = search_count;
5142 else if(!irccasecmp(argv[1], "print"))
5143 action = search_print;
5146 reply("CSMSG_ACTION_INVALID", argv[1]);
5150 search = chanserv_search_create(user, argc - 2, argv + 2);
5154 if(action == search_count)
5155 search->limit = INT_MAX;
5157 if(action == search_print)
5158 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5160 matches = chanserv_channel_search(search, action, user);
5163 reply("MSG_MATCH_COUNT", matches);
5165 reply("MSG_NO_MATCHES");
5171 static CHANSERV_FUNC(cmd_unvisited)
5173 struct chanData *cData;
5174 time_t interval = chanserv_conf.channel_expire_delay;
5175 char buffer[INTERVALLEN];
5176 unsigned int limit = 25, matches = 0;
5180 interval = ParseInterval(argv[1]);
5182 limit = atoi(argv[2]);
5185 intervalString(buffer, interval, user->handle_info);
5186 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5188 for(cData = channelList; cData && matches < limit; cData = cData->next)
5190 if((now - cData->visited) < interval)
5193 intervalString(buffer, now - cData->visited, user->handle_info);
5194 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5201 static MODCMD_FUNC(chan_opt_defaulttopic)
5207 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5209 reply("CSMSG_TOPIC_LOCKED", channel->name);
5213 topic = unsplit_string(argv+1, argc-1, NULL);
5215 free(channel->channel_info->topic);
5216 if(topic[0] == '*' && topic[1] == 0)
5218 topic = channel->channel_info->topic = NULL;
5222 topic = channel->channel_info->topic = strdup(topic);
5223 if(channel->channel_info->topic_mask
5224 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5225 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5227 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5230 if(channel->channel_info->topic)
5231 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5233 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5237 static MODCMD_FUNC(chan_opt_topicmask)
5241 struct chanData *cData = channel->channel_info;
5244 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5246 reply("CSMSG_TOPIC_LOCKED", channel->name);
5250 mask = unsplit_string(argv+1, argc-1, NULL);
5252 if(cData->topic_mask)
5253 free(cData->topic_mask);
5254 if(mask[0] == '*' && mask[1] == 0)
5256 cData->topic_mask = 0;
5260 cData->topic_mask = strdup(mask);
5262 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5263 else if(!match_ircglob(cData->topic, cData->topic_mask))
5264 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5268 if(channel->channel_info->topic_mask)
5269 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5271 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5275 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5279 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5283 if(greeting[0] == '*' && greeting[1] == 0)
5287 unsigned int length = strlen(greeting);
5288 if(length > chanserv_conf.greeting_length)
5290 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5293 *data = strdup(greeting);
5302 reply(name, user_find_message(user, "MSG_NONE"));
5306 static MODCMD_FUNC(chan_opt_greeting)
5308 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5311 static MODCMD_FUNC(chan_opt_usergreeting)
5313 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5316 static MODCMD_FUNC(chan_opt_modes)
5318 struct mod_chanmode *new_modes;
5319 char modes[MODELEN];
5323 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5325 reply("CSMSG_NO_ACCESS");
5328 if(argv[1][0] == '*' && argv[1][1] == 0)
5330 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5332 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5334 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5337 else if(new_modes->argc > 1)
5339 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5340 mod_chanmode_free(new_modes);
5345 channel->channel_info->modes = *new_modes;
5346 modcmd_chanmode_announce(new_modes);
5347 mod_chanmode_free(new_modes);
5351 mod_chanmode_format(&channel->channel_info->modes, modes);
5353 reply("CSMSG_SET_MODES", modes);
5355 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5359 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5361 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5363 struct chanData *cData = channel->channel_info;
5368 /* Set flag according to value. */
5369 if(enabled_string(argv[1]))
5371 cData->flags |= mask;
5374 else if(disabled_string(argv[1]))
5376 cData->flags &= ~mask;
5381 reply("MSG_INVALID_BINARY", argv[1]);
5387 /* Find current option value. */
5388 value = (cData->flags & mask) ? 1 : 0;
5392 reply(name, user_find_message(user, "MSG_ON"));
5394 reply(name, user_find_message(user, "MSG_OFF"));
5398 static MODCMD_FUNC(chan_opt_nodelete)
5400 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5402 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5406 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5409 static MODCMD_FUNC(chan_opt_dynlimit)
5411 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5414 static MODCMD_FUNC(chan_opt_offchannel)
5416 struct chanData *cData = channel->channel_info;
5421 /* Set flag according to value. */
5422 if(enabled_string(argv[1]))
5424 if(!IsOffChannel(cData))
5425 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5426 cData->flags |= CHANNEL_OFFCHANNEL;
5429 else if(disabled_string(argv[1]))
5431 if(IsOffChannel(cData))
5433 struct mod_chanmode change;
5434 mod_chanmode_init(&change);
5436 change.args[0].mode = MODE_CHANOP;
5437 change.args[0].u.member = AddChannelUser(chanserv, channel);
5438 mod_chanmode_announce(chanserv, channel, &change);
5440 cData->flags &= ~CHANNEL_OFFCHANNEL;
5445 reply("MSG_INVALID_BINARY", argv[1]);
5451 /* Find current option value. */
5452 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5456 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5458 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5462 static MODCMD_FUNC(chan_opt_unreviewed)
5464 struct chanData *cData = channel->channel_info;
5465 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5471 /* The two directions can have different ACLs. */
5472 if(enabled_string(argv[1]))
5474 else if(disabled_string(argv[1]))
5478 reply("MSG_INVALID_BINARY", argv[1]);
5482 if (new_value != value)
5484 struct svccmd *subcmd;
5485 char subcmd_name[32];
5487 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5488 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5491 reply("MSG_COMMAND_DISABLED", subcmd_name);
5494 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5498 cData->flags |= CHANNEL_UNREVIEWED;
5501 free(cData->registrar);
5502 cData->registrar = strdup(user->handle_info->handle);
5503 cData->flags &= ~CHANNEL_UNREVIEWED;
5510 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5512 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5516 static MODCMD_FUNC(chan_opt_defaults)
5518 struct userData *uData;
5519 struct chanData *cData;
5520 const char *confirm;
5521 enum levelOption lvlOpt;
5522 enum charOption chOpt;
5524 cData = channel->channel_info;
5525 uData = GetChannelUser(cData, user->handle_info);
5526 if(!uData || (uData->access < UL_OWNER))
5528 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5531 confirm = make_confirmation_string(uData);
5532 if((argc < 2) || strcmp(argv[1], confirm))
5534 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5537 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5538 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5539 cData->modes = chanserv_conf.default_modes;
5540 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5541 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5542 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5543 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5544 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5549 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5551 struct chanData *cData = channel->channel_info;
5552 struct userData *uData;
5553 unsigned short value;
5557 if(!check_user_level(channel, user, option, 1, 1))
5559 reply("CSMSG_CANNOT_SET");
5562 value = user_level_from_name(argv[1], UL_OWNER+1);
5563 if(!value && strcmp(argv[1], "0"))
5565 reply("CSMSG_INVALID_ACCESS", argv[1]);
5568 uData = GetChannelUser(cData, user->handle_info);
5569 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5571 reply("CSMSG_BAD_SETLEVEL");
5577 if(value > cData->lvlOpts[lvlGiveOps])
5579 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5584 if(value < cData->lvlOpts[lvlGiveVoice])
5586 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5591 /* This test only applies to owners, since non-owners
5592 * trying to set an option to above their level get caught
5593 * by the CSMSG_BAD_SETLEVEL test above.
5595 if(value > uData->access)
5597 reply("CSMSG_BAD_SETTERS");
5604 cData->lvlOpts[option] = value;
5606 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5610 static MODCMD_FUNC(chan_opt_enfops)
5612 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5615 static MODCMD_FUNC(chan_opt_giveops)
5617 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5620 static MODCMD_FUNC(chan_opt_enfmodes)
5622 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5625 static MODCMD_FUNC(chan_opt_enftopic)
5627 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5630 static MODCMD_FUNC(chan_opt_pubcmd)
5632 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5635 static MODCMD_FUNC(chan_opt_setters)
5637 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5640 static MODCMD_FUNC(chan_opt_ctcpusers)
5642 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5645 static MODCMD_FUNC(chan_opt_userinfo)
5647 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5650 static MODCMD_FUNC(chan_opt_givevoice)
5652 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5655 static MODCMD_FUNC(chan_opt_topicsnarf)
5657 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5660 static MODCMD_FUNC(chan_opt_inviteme)
5662 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5666 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5668 struct chanData *cData = channel->channel_info;
5669 int count = charOptions[option].count, index;
5673 index = atoi(argv[1]);
5675 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5677 reply("CSMSG_INVALID_NUMERIC", index);
5678 /* Show possible values. */
5679 for(index = 0; index < count; index++)
5680 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5684 cData->chOpts[option] = charOptions[option].values[index].value;
5688 /* Find current option value. */
5691 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5695 /* Somehow, the option value is corrupt; reset it to the default. */
5696 cData->chOpts[option] = charOptions[option].default_value;
5701 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5705 static MODCMD_FUNC(chan_opt_protect)
5707 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5710 static MODCMD_FUNC(chan_opt_toys)
5712 return channel_multiple_option(chToys, CSFUNC_ARGS);
5715 static MODCMD_FUNC(chan_opt_ctcpreaction)
5717 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5720 static MODCMD_FUNC(chan_opt_topicrefresh)
5722 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5725 static struct svccmd_list set_shows_list;
5728 handle_svccmd_unbind(struct svccmd *target) {
5730 for(ii=0; ii<set_shows_list.used; ++ii)
5731 if(target == set_shows_list.list[ii])
5732 set_shows_list.used = 0;
5735 static CHANSERV_FUNC(cmd_set)
5737 struct svccmd *subcmd;
5741 /* Check if we need to (re-)initialize set_shows_list. */
5742 if(!set_shows_list.used)
5744 if(!set_shows_list.size)
5746 set_shows_list.size = chanserv_conf.set_shows->used;
5747 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5749 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5751 const char *name = chanserv_conf.set_shows->list[ii];
5752 sprintf(buf, "%s %s", argv[0], name);
5753 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5756 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5759 svccmd_list_append(&set_shows_list, subcmd);
5765 reply("CSMSG_CHANNEL_OPTIONS");
5766 for(ii = 0; ii < set_shows_list.used; ii++)
5768 subcmd = set_shows_list.list[ii];
5769 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5774 sprintf(buf, "%s %s", argv[0], argv[1]);
5775 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5778 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5781 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5783 reply("CSMSG_NO_ACCESS");
5789 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5793 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5795 struct userData *uData;
5797 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5800 reply("CSMSG_NOT_USER", channel->name);
5806 /* Just show current option value. */
5808 else if(enabled_string(argv[1]))
5810 uData->flags |= mask;
5812 else if(disabled_string(argv[1]))
5814 uData->flags &= ~mask;
5818 reply("MSG_INVALID_BINARY", argv[1]);
5822 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5826 static MODCMD_FUNC(user_opt_noautoop)
5828 struct userData *uData;
5830 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5833 reply("CSMSG_NOT_USER", channel->name);
5836 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5837 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5839 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5842 static MODCMD_FUNC(user_opt_autoinvite)
5844 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5847 static MODCMD_FUNC(user_opt_info)
5849 struct userData *uData;
5852 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5856 /* If they got past the command restrictions (which require access)
5857 * but fail this test, we have some fool with security override on.
5859 reply("CSMSG_NOT_USER", channel->name);
5866 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5867 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5869 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5872 bp = strcspn(infoline, "\001");
5875 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5880 if(infoline[0] == '*' && infoline[1] == 0)
5883 uData->info = strdup(infoline);
5886 reply("CSMSG_USET_INFO", uData->info);
5888 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5892 struct svccmd_list uset_shows_list;
5894 static CHANSERV_FUNC(cmd_uset)
5896 struct svccmd *subcmd;
5900 /* Check if we need to (re-)initialize uset_shows_list. */
5901 if(!uset_shows_list.used)
5905 "NoAutoOp", "AutoInvite", "Info"
5908 if(!uset_shows_list.size)
5910 uset_shows_list.size = ArrayLength(options);
5911 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5913 for(ii = 0; ii < ArrayLength(options); ii++)
5915 const char *name = options[ii];
5916 sprintf(buf, "%s %s", argv[0], name);
5917 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5920 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5923 svccmd_list_append(&uset_shows_list, subcmd);
5929 /* Do this so options are presented in a consistent order. */
5930 reply("CSMSG_USER_OPTIONS");
5931 for(ii = 0; ii < uset_shows_list.used; ii++)
5932 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5936 sprintf(buf, "%s %s", argv[0], argv[1]);
5937 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5940 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5944 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5947 static CHANSERV_FUNC(cmd_giveownership)
5949 struct handle_info *new_owner_hi;
5950 struct userData *new_owner;
5951 struct userData *curr_user;
5952 struct userData *invoker;
5953 struct chanData *cData = channel->channel_info;
5954 struct do_not_register *dnr;
5955 const char *confirm;
5957 unsigned short co_access;
5958 char reason[MAXLEN];
5961 curr_user = GetChannelAccess(cData, user->handle_info);
5962 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5963 if(!curr_user || (curr_user->access != UL_OWNER))
5965 struct userData *owner = NULL;
5966 for(curr_user = channel->channel_info->users;
5968 curr_user = curr_user->next)
5970 if(curr_user->access != UL_OWNER)
5974 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5981 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5983 char delay[INTERVALLEN];
5984 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5985 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5988 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5990 if(new_owner_hi == user->handle_info)
5992 reply("CSMSG_NO_TRANSFER_SELF");
5995 new_owner = GetChannelAccess(cData, new_owner_hi);
6000 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6004 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6008 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6010 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6013 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6014 if(!IsHelping(user))
6015 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6017 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6020 invoker = GetChannelUser(cData, user->handle_info);
6021 if(invoker->access <= UL_OWNER)
6023 confirm = make_confirmation_string(curr_user);
6024 if((argc < 3) || strcmp(argv[2], confirm))
6026 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6030 if(new_owner->access >= UL_COOWNER)
6031 co_access = new_owner->access;
6033 co_access = UL_COOWNER;
6034 new_owner->access = UL_OWNER;
6036 curr_user->access = co_access;
6037 cData->ownerTransfer = now;
6038 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6039 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6040 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6044 static CHANSERV_FUNC(cmd_suspend)
6046 struct handle_info *hi;
6047 struct userData *self, *real_self, *target;
6048 unsigned int override = 0;
6051 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6052 self = GetChannelUser(channel->channel_info, user->handle_info);
6053 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6054 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6056 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6059 if(target->access >= self->access)
6061 reply("MSG_USER_OUTRANKED", hi->handle);
6064 if(target->flags & USER_SUSPENDED)
6066 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6071 target->present = 0;
6074 if(!real_self || target->access >= real_self->access)
6075 override = CMD_LOG_OVERRIDE;
6076 target->flags |= USER_SUSPENDED;
6077 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6078 return 1 | override;
6081 static CHANSERV_FUNC(cmd_unsuspend)
6083 struct handle_info *hi;
6084 struct userData *self, *real_self, *target;
6085 unsigned int override = 0;
6088 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6089 self = GetChannelUser(channel->channel_info, user->handle_info);
6090 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6091 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6093 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6096 if(target->access >= self->access)
6098 reply("MSG_USER_OUTRANKED", hi->handle);
6101 if(!(target->flags & USER_SUSPENDED))
6103 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6106 if(!real_self || target->access >= real_self->access)
6107 override = CMD_LOG_OVERRIDE;
6108 target->flags &= ~USER_SUSPENDED;
6109 scan_user_presence(target, NULL);
6110 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6111 return 1 | override;
6114 static MODCMD_FUNC(cmd_deleteme)
6116 struct handle_info *hi;
6117 struct userData *target;
6118 const char *confirm_string;
6119 unsigned short access;
6122 hi = user->handle_info;
6123 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6125 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6128 if(target->access == UL_OWNER)
6130 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6133 confirm_string = make_confirmation_string(target);
6134 if((argc < 2) || strcmp(argv[1], confirm_string))
6136 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6139 access = target->access;
6140 channel_name = strdup(channel->name);
6141 del_channel_user(target, 1);
6142 reply("CSMSG_DELETED_YOU", access, channel_name);
6148 chanserv_refresh_topics(UNUSED_ARG(void *data))
6150 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6151 struct chanData *cData;
6154 for(cData = channelList; cData; cData = cData->next)
6156 if(IsSuspended(cData))
6158 opt = cData->chOpts[chTopicRefresh];
6161 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6164 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6165 cData->last_refresh = refresh_num;
6167 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6170 static CHANSERV_FUNC(cmd_unf)
6174 char response[MAXLEN];
6175 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6176 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6177 irc_privmsg(cmd->parent->bot, channel->name, response);
6180 reply("CSMSG_UNF_RESPONSE");
6184 static CHANSERV_FUNC(cmd_ping)
6188 char response[MAXLEN];
6189 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6190 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6191 irc_privmsg(cmd->parent->bot, channel->name, response);
6194 reply("CSMSG_PING_RESPONSE");
6198 static CHANSERV_FUNC(cmd_wut)
6202 char response[MAXLEN];
6203 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6204 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6205 irc_privmsg(cmd->parent->bot, channel->name, response);
6208 reply("CSMSG_WUT_RESPONSE");
6212 static CHANSERV_FUNC(cmd_8ball)
6214 unsigned int i, j, accum;
6219 for(i=1; i<argc; i++)
6220 for(j=0; argv[i][j]; j++)
6221 accum = (accum << 5) - accum + toupper(argv[i][j]);
6222 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6225 char response[MAXLEN];
6226 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6227 irc_privmsg(cmd->parent->bot, channel->name, response);
6230 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6234 static CHANSERV_FUNC(cmd_d)
6236 unsigned long sides, count, modifier, ii, total;
6237 char response[MAXLEN], *sep;
6241 if((count = strtoul(argv[1], &sep, 10)) < 1)
6251 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6252 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6256 else if((sep[0] == '-') && isdigit(sep[1]))
6257 modifier = strtoul(sep, NULL, 10);
6258 else if((sep[0] == '+') && isdigit(sep[1]))
6259 modifier = strtoul(sep+1, NULL, 10);
6266 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6271 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6274 for(total = ii = 0; ii < count; ++ii)
6275 total += (rand() % sides) + 1;
6278 if((count > 1) || modifier)
6280 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6281 sprintf(response, fmt, total, count, sides, modifier);
6285 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6286 sprintf(response, fmt, total, sides);
6289 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6291 send_message_type(4, user, cmd->parent->bot, "%s", response);
6295 static CHANSERV_FUNC(cmd_huggle)
6297 /* CTCP must be via PRIVMSG, never notice */
6299 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6301 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6306 chanserv_adjust_limit(void *data)
6308 struct mod_chanmode change;
6309 struct chanData *cData = data;
6310 struct chanNode *channel = cData->channel;
6313 if(IsSuspended(cData))
6316 cData->limitAdjusted = now;
6317 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6318 if(cData->modes.modes_set & MODE_LIMIT)
6320 if(limit > cData->modes.new_limit)
6321 limit = cData->modes.new_limit;
6322 else if(limit == cData->modes.new_limit)
6326 mod_chanmode_init(&change);
6327 change.modes_set = MODE_LIMIT;
6328 change.new_limit = limit;
6329 mod_chanmode_announce(chanserv, channel, &change);
6333 handle_new_channel(struct chanNode *channel)
6335 struct chanData *cData;
6337 if(!(cData = channel->channel_info))
6340 if(cData->modes.modes_set || cData->modes.modes_clear)
6341 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6343 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6344 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6347 /* Welcome to my worst nightmare. Warning: Read (or modify)
6348 the code below at your own risk. */
6350 handle_join(struct modeNode *mNode)
6352 struct mod_chanmode change;
6353 struct userNode *user = mNode->user;
6354 struct chanNode *channel = mNode->channel;
6355 struct chanData *cData;
6356 struct userData *uData = NULL;
6357 struct banData *bData;
6358 struct handle_info *handle;
6359 unsigned int modes = 0, info = 0;
6362 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6365 cData = channel->channel_info;
6366 if(channel->members.used > cData->max)
6367 cData->max = channel->members.used;
6369 /* Check for bans. If they're joining through a ban, one of two
6371 * 1: Join during a netburst, by riding the break. Kick them
6372 * unless they have ops or voice in the channel.
6373 * 2: They're allowed to join through the ban (an invite in
6374 * ircu2.10, or a +e on Hybrid, or something).
6375 * If they're not joining through a ban, and the banlist is not
6376 * full, see if they're on the banlist for the channel. If so,
6379 if(user->uplink->burst && !mNode->modes)
6382 for(ii = 0; ii < channel->banlist.used; ii++)
6384 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6386 /* Riding a netburst. Naughty. */
6387 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6393 mod_chanmode_init(&change);
6395 if(channel->banlist.used < MAXBANS)
6397 /* Not joining through a ban. */
6398 for(bData = cData->bans;
6399 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6400 bData = bData->next);
6404 char kick_reason[MAXLEN];
6405 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6407 bData->triggered = now;
6408 if(bData != cData->bans)
6410 /* Shuffle the ban to the head of the list. */
6412 bData->next->prev = bData->prev;
6414 bData->prev->next = bData->next;
6417 bData->next = cData->bans;
6420 cData->bans->prev = bData;
6421 cData->bans = bData;
6424 change.args[0].mode = MODE_BAN;
6425 change.args[0].u.hostmask = bData->mask;
6426 mod_chanmode_announce(chanserv, channel, &change);
6427 KickChannelUser(user, channel, chanserv, kick_reason);
6432 /* ChanServ will not modify the limits in join-flooded channels.
6433 It will also skip DynLimit processing when the user (or srvx)
6434 is bursting in, because there are likely more incoming. */
6435 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6436 && !user->uplink->burst
6437 && !channel->join_flooded
6438 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6440 /* The user count has begun "bumping" into the channel limit,
6441 so set a timer to raise the limit a bit. Any previous
6442 timers are removed so three incoming users within the delay
6443 results in one limit change, not three. */
6445 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6446 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6449 if(channel->join_flooded)
6451 /* don't automatically give ops or voice during a join flood */
6453 else if(cData->lvlOpts[lvlGiveOps] == 0)
6454 modes |= MODE_CHANOP;
6455 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6456 modes |= MODE_VOICE;
6458 greeting = cData->greeting;
6459 if(user->handle_info)
6461 handle = user->handle_info;
6463 if(IsHelper(user) && !IsHelping(user))
6466 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6468 if(channel == chanserv_conf.support_channels.list[ii])
6470 HANDLE_SET_FLAG(user->handle_info, HELPING);
6476 uData = GetTrueChannelAccess(cData, handle);
6477 if(uData && !IsUserSuspended(uData))
6479 /* Ops and above were handled by the above case. */
6480 if(IsUserAutoOp(uData))
6482 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6483 modes |= MODE_CHANOP;
6484 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6485 modes |= MODE_VOICE;
6487 if(uData->access >= UL_PRESENT)
6488 cData->visited = now;
6489 if(cData->user_greeting)
6490 greeting = cData->user_greeting;
6492 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6493 && ((now - uData->seen) >= chanserv_conf.info_delay)
6501 /* If user joining normally (not during burst), apply op or voice,
6502 * and send greeting/userinfo as appropriate.
6504 if(!user->uplink->burst)
6508 if(modes & MODE_CHANOP)
6509 modes &= ~MODE_VOICE;
6510 change.args[0].mode = modes;
6511 change.args[0].u.member = mNode;
6512 mod_chanmode_announce(chanserv, channel, &change);
6515 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6517 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6523 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6525 struct mod_chanmode change;
6526 struct userData *channel;
6527 unsigned int ii, jj;
6529 if(!user->handle_info)
6532 mod_chanmode_init(&change);
6534 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6536 struct chanNode *cn;
6537 struct modeNode *mn;
6538 if(IsUserSuspended(channel)
6539 || IsSuspended(channel->channel)
6540 || !(cn = channel->channel->channel))
6543 mn = GetUserMode(cn, user);
6546 if(!IsUserSuspended(channel)
6547 && IsUserAutoInvite(channel)
6548 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6550 && !user->uplink->burst)
6551 irc_invite(chanserv, user, cn);
6555 if(channel->access >= UL_PRESENT)
6556 channel->channel->visited = now;
6558 if(IsUserAutoOp(channel))
6560 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6561 change.args[0].mode = MODE_CHANOP;
6562 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6563 change.args[0].mode = MODE_VOICE;
6565 change.args[0].mode = 0;
6566 change.args[0].u.member = mn;
6567 if(change.args[0].mode)
6568 mod_chanmode_announce(chanserv, cn, &change);
6571 channel->seen = now;
6572 channel->present = 1;
6575 for(ii = 0; ii < user->channels.used; ++ii)
6577 struct chanNode *channel = user->channels.list[ii]->channel;
6578 struct banData *ban;
6580 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6581 || !channel->channel_info
6582 || IsSuspended(channel->channel_info))
6584 for(jj = 0; jj < channel->banlist.used; ++jj)
6585 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6587 if(jj < channel->banlist.used)
6589 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6591 char kick_reason[MAXLEN];
6592 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6594 change.args[0].mode = MODE_BAN;
6595 change.args[0].u.hostmask = ban->mask;
6596 mod_chanmode_announce(chanserv, channel, &change);
6597 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6598 KickChannelUser(user, channel, chanserv, kick_reason);
6599 ban->triggered = now;
6604 if(IsSupportHelper(user))
6606 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6608 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6610 HANDLE_SET_FLAG(user->handle_info, HELPING);
6618 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6620 struct chanData *cData;
6621 struct userData *uData;
6623 cData = mn->channel->channel_info;
6624 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6627 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6629 /* Allow for a bit of padding so that the limit doesn't
6630 track the user count exactly, which could get annoying. */
6631 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6633 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6634 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6638 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6640 scan_user_presence(uData, mn->user);
6642 if (uData->access >= UL_PRESENT)
6643 cData->visited = now;
6646 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6648 unsigned int ii, jj;
6649 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6651 for(jj = 0; jj < mn->user->channels.used; ++jj)
6652 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6654 if(jj < mn->user->channels.used)
6657 if(ii == chanserv_conf.support_channels.used)
6658 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6663 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6665 struct userData *uData;
6667 if(!channel->channel_info || !kicker || IsService(kicker)
6668 || (kicker == victim) || IsSuspended(channel->channel_info)
6669 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6672 if(protect_user(victim, kicker, channel->channel_info))
6674 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6675 KickChannelUser(kicker, channel, chanserv, reason);
6678 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6683 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6685 struct chanData *cData;
6687 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6690 cData = channel->channel_info;
6691 if(bad_topic(channel, user, channel->topic))
6693 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6694 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6695 SetChannelTopic(channel, chanserv, old_topic, 1);
6696 else if(cData->topic)
6697 SetChannelTopic(channel, chanserv, cData->topic, 1);
6700 /* With topicsnarf, grab the topic and save it as the default topic. */
6701 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6704 cData->topic = strdup(channel->topic);
6710 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6712 struct mod_chanmode *bounce = NULL;
6713 unsigned int bnc, ii;
6716 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6719 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6720 && mode_lock_violated(&channel->channel_info->modes, change))
6722 char correct[MAXLEN];
6723 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6724 mod_chanmode_format(&channel->channel_info->modes, correct);
6725 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6727 for(ii = bnc = 0; ii < change->argc; ++ii)
6729 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6731 const struct userNode *victim = change->args[ii].u.member->user;
6732 if(!protect_user(victim, user, channel->channel_info))
6735 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6738 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6739 bounce->args[bnc].u.member = GetUserMode(channel, user);
6740 if(bounce->args[bnc].u.member)
6744 bounce->args[bnc].mode = MODE_CHANOP;
6745 bounce->args[bnc].u.member = change->args[ii].u.member;
6747 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6749 else if(change->args[ii].mode & MODE_CHANOP)
6751 const struct userNode *victim = change->args[ii].u.member->user;
6752 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6755 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6756 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6757 bounce->args[bnc].u.member = change->args[ii].u.member;
6760 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6762 const char *ban = change->args[ii].u.hostmask;
6763 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6766 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6767 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6768 bounce->args[bnc].u.hostmask = strdup(ban);
6770 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6775 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6776 mod_chanmode_announce(chanserv, channel, bounce);
6777 for(ii = 0; ii < change->argc; ++ii)
6778 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6779 free((char*)bounce->args[ii].u.hostmask);
6780 mod_chanmode_free(bounce);
6785 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6787 struct chanNode *channel;
6788 struct banData *bData;
6789 struct mod_chanmode change;
6790 unsigned int ii, jj;
6791 char kick_reason[MAXLEN];
6793 mod_chanmode_init(&change);
6795 change.args[0].mode = MODE_BAN;
6796 for(ii = 0; ii < user->channels.used; ++ii)
6798 channel = user->channels.list[ii]->channel;
6799 /* Need not check for bans if they're opped or voiced. */
6800 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6802 /* Need not check for bans unless channel registration is active. */
6803 if(!channel->channel_info || IsSuspended(channel->channel_info))
6805 /* Look for a matching ban already on the channel. */
6806 for(jj = 0; jj < channel->banlist.used; ++jj)
6807 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6809 /* Need not act if we found one. */
6810 if(jj < channel->banlist.used)
6812 /* Look for a matching ban in this channel. */
6813 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6815 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6817 change.args[0].u.hostmask = bData->mask;
6818 mod_chanmode_announce(chanserv, channel, &change);
6819 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6820 KickChannelUser(user, channel, chanserv, kick_reason);
6821 bData->triggered = now;
6822 break; /* we don't need to check any more bans in the channel */
6827 static void handle_rename(struct handle_info *handle, const char *old_handle)
6829 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6833 dict_remove2(handle_dnrs, old_handle, 1);
6834 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6835 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6840 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6842 struct userNode *h_user;
6844 if(handle->channels)
6846 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6847 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6849 while(handle->channels)
6850 del_channel_user(handle->channels, 1);
6855 handle_server_link(UNUSED_ARG(struct server *server))
6857 struct chanData *cData;
6859 for(cData = channelList; cData; cData = cData->next)
6861 if(!IsSuspended(cData))
6862 cData->may_opchan = 1;
6863 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6864 && !cData->channel->join_flooded
6865 && ((cData->channel->limit - cData->channel->members.used)
6866 < chanserv_conf.adjust_threshold))
6868 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6869 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6875 chanserv_conf_read(void)
6879 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6880 struct mod_chanmode *change;
6881 struct string_list *strlist;
6882 struct chanNode *chan;
6885 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6887 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6890 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6891 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6892 chanserv_conf.support_channels.used = 0;
6893 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6895 for(ii = 0; ii < strlist->used; ++ii)
6897 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6900 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6902 channelList_append(&chanserv_conf.support_channels, chan);
6905 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6908 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6911 chan = AddChannel(str, now, str2, NULL);
6913 channelList_append(&chanserv_conf.support_channels, chan);
6915 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6916 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6917 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6918 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6919 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6920 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6921 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6922 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6923 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6924 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6925 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6926 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6927 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6928 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6929 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6930 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6931 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6932 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6933 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6934 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6935 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6936 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6937 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6938 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6939 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6941 NickChange(chanserv, str, 0);
6942 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6943 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6944 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6945 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6946 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6947 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6948 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6949 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6950 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6951 chanserv_conf.max_owned = str ? atoi(str) : 5;
6952 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6953 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6954 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6955 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6956 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6957 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6958 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6961 safestrncpy(mode_line, str, sizeof(mode_line));
6962 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6963 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6964 && (change->argc < 2))
6966 chanserv_conf.default_modes = *change;
6967 mod_chanmode_free(change);
6969 free_string_list(chanserv_conf.set_shows);
6970 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6972 strlist = string_list_copy(strlist);
6975 static const char *list[] = {
6976 /* free form text */
6977 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6978 /* options based on user level */
6979 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6980 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6981 /* multiple choice options */
6982 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6983 /* binary options */
6984 "DynLimit", "NoDelete",
6989 strlist = alloc_string_list(ArrayLength(list)-1);
6990 for(ii=0; list[ii]; ii++)
6991 string_list_append(strlist, strdup(list[ii]));
6993 chanserv_conf.set_shows = strlist;
6994 /* We don't look things up now, in case the list refers to options
6995 * defined by modules initialized after this point. Just mark the
6996 * function list as invalid, so it will be initialized.
6998 set_shows_list.used = 0;
6999 free_string_list(chanserv_conf.eightball);
7000 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7003 strlist = string_list_copy(strlist);
7007 strlist = alloc_string_list(4);
7008 string_list_append(strlist, strdup("Yes."));
7009 string_list_append(strlist, strdup("No."));
7010 string_list_append(strlist, strdup("Maybe so."));
7012 chanserv_conf.eightball = strlist;
7013 free_string_list(chanserv_conf.old_ban_names);
7014 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7016 strlist = string_list_copy(strlist);
7018 strlist = alloc_string_list(2);
7019 chanserv_conf.old_ban_names = strlist;
7020 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7021 off_channel = str ? atoi(str) : 0;
7025 chanserv_note_type_read(const char *key, struct record_data *rd)
7028 struct note_type *ntype;
7031 if(!(obj = GET_RECORD_OBJECT(rd)))
7033 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7036 if(!(ntype = chanserv_create_note_type(key)))
7038 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7042 /* Figure out set access */
7043 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7045 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7046 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7048 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7050 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7051 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7053 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7055 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7059 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7060 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7061 ntype->set_access.min_opserv = 0;
7064 /* Figure out visibility */
7065 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7066 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7067 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7068 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7069 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7070 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7071 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7072 ntype->visible_type = NOTE_VIS_ALL;
7074 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7076 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7077 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7081 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7083 struct handle_info *handle;
7084 struct userData *uData;
7085 char *seen, *inf, *flags;
7087 unsigned short access;
7089 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7091 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7095 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7096 if(access > UL_OWNER)
7098 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7102 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7103 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7104 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7105 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7106 handle = get_handle_info(key);
7109 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7113 uData = add_channel_user(chan, handle, access, last_seen, inf);
7114 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7118 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7120 struct banData *bData;
7121 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7122 time_t set_time, triggered_time, expires_time;
7124 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7126 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7130 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7131 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7132 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7133 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7134 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7135 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7136 if (!reason || !owner)
7139 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7140 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7142 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7144 expires_time = set_time + atoi(s_duration);
7148 if(!reason || (expires_time && (expires_time < now)))
7151 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7154 static struct suspended *
7155 chanserv_read_suspended(dict_t obj)
7157 struct suspended *suspended = calloc(1, sizeof(*suspended));
7161 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7162 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7163 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7164 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7165 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7166 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7167 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7168 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7169 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7170 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7175 chanserv_channel_read(const char *key, struct record_data *hir)
7177 struct suspended *suspended;
7178 struct mod_chanmode *modes;
7179 struct chanNode *cNode;
7180 struct chanData *cData;
7181 struct dict *channel, *obj;
7182 char *str, *argv[10];
7186 channel = hir->d.object;
7188 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7191 cNode = AddChannel(key, now, NULL, NULL);
7194 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7197 cData = register_channel(cNode, str);
7200 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7204 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7206 enum levelOption lvlOpt;
7207 enum charOption chOpt;
7209 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7210 cData->flags = atoi(str);
7212 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7214 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7216 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7217 else if(levelOptions[lvlOpt].old_flag)
7219 if(cData->flags & levelOptions[lvlOpt].old_flag)
7220 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7222 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7226 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7228 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7230 cData->chOpts[chOpt] = str[0];
7233 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7235 enum levelOption lvlOpt;
7236 enum charOption chOpt;
7239 cData->flags = base64toint(str, 5);
7240 count = strlen(str += 5);
7241 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7244 if(levelOptions[lvlOpt].old_flag)
7246 if(cData->flags & levelOptions[lvlOpt].old_flag)
7247 lvl = levelOptions[lvlOpt].flag_value;
7249 lvl = levelOptions[lvlOpt].default_value;
7251 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7253 case 'c': lvl = UL_COOWNER; break;
7254 case 'm': lvl = UL_MASTER; break;
7255 case 'n': lvl = UL_OWNER+1; break;
7256 case 'o': lvl = UL_OP; break;
7257 case 'p': lvl = UL_PEON; break;
7258 case 'w': lvl = UL_OWNER; break;
7259 default: lvl = 0; break;
7261 cData->lvlOpts[lvlOpt] = lvl;
7263 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7264 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7267 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7269 suspended = chanserv_read_suspended(obj);
7270 cData->suspended = suspended;
7271 suspended->cData = cData;
7272 /* We could use suspended->expires and suspended->revoked to
7273 * set the CHANNEL_SUSPENDED flag, but we don't. */
7275 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7277 suspended = calloc(1, sizeof(*suspended));
7278 suspended->issued = 0;
7279 suspended->revoked = 0;
7280 suspended->suspender = strdup(str);
7281 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7282 suspended->expires = str ? atoi(str) : 0;
7283 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7284 suspended->reason = strdup(str ? str : "No reason");
7285 suspended->previous = NULL;
7286 cData->suspended = suspended;
7287 suspended->cData = cData;
7291 cData->flags &= ~CHANNEL_SUSPENDED;
7292 suspended = NULL; /* to squelch a warning */
7295 if(IsSuspended(cData)) {
7296 if(suspended->expires > now)
7297 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7298 else if(suspended->expires)
7299 cData->flags &= ~CHANNEL_SUSPENDED;
7302 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7303 struct mod_chanmode change;
7304 mod_chanmode_init(&change);
7306 change.args[0].mode = MODE_CHANOP;
7307 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7308 mod_chanmode_announce(chanserv, cNode, &change);
7311 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7312 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7313 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7314 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7315 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7316 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7317 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7318 cData->max = str ? atoi(str) : 0;
7319 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7320 cData->greeting = str ? strdup(str) : NULL;
7321 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7322 cData->user_greeting = str ? strdup(str) : NULL;
7323 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7324 cData->topic_mask = str ? strdup(str) : NULL;
7325 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7326 cData->topic = str ? strdup(str) : NULL;
7328 if(!IsSuspended(cData)
7329 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7330 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7331 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7332 cData->modes = *modes;
7334 cData->modes.modes_set |= MODE_REGISTERED;
7335 if(cData->modes.argc > 1)
7336 cData->modes.argc = 1;
7337 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7338 mod_chanmode_free(modes);
7341 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7342 for(it = dict_first(obj); it; it = iter_next(it))
7343 user_read_helper(iter_key(it), iter_data(it), cData);
7345 if(!cData->users && !IsProtected(cData))
7347 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7348 unregister_channel(cData, "has empty user list.");
7352 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7353 for(it = dict_first(obj); it; it = iter_next(it))
7354 ban_read_helper(iter_key(it), iter_data(it), cData);
7356 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7357 for(it = dict_first(obj); it; it = iter_next(it))
7359 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7360 struct record_data *rd = iter_data(it);
7361 const char *note, *setter;
7363 if(rd->type != RECDB_OBJECT)
7365 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7369 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7371 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7373 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7377 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7378 if(!setter) setter = "<unknown>";
7379 chanserv_add_channel_note(cData, ntype, setter, note);
7387 chanserv_dnr_read(const char *key, struct record_data *hir)
7389 const char *setter, *reason, *str;
7390 struct do_not_register *dnr;
7393 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7396 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7399 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7402 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7405 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7406 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7407 if(expiry && expiry <= now)
7409 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7412 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7414 dnr->set = atoi(str);
7420 chanserv_saxdb_read(struct dict *database)
7422 struct dict *section;
7425 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7426 for(it = dict_first(section); it; it = iter_next(it))
7427 chanserv_note_type_read(iter_key(it), iter_data(it));
7429 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7430 for(it = dict_first(section); it; it = iter_next(it))
7431 chanserv_channel_read(iter_key(it), iter_data(it));
7433 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7434 for(it = dict_first(section); it; it = iter_next(it))
7435 chanserv_dnr_read(iter_key(it), iter_data(it));
7441 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7443 int high_present = 0;
7444 saxdb_start_record(ctx, KEY_USERS, 1);
7445 for(; uData; uData = uData->next)
7447 if((uData->access >= UL_PRESENT) && uData->present)
7449 saxdb_start_record(ctx, uData->handle->handle, 0);
7450 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7451 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7453 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7455 saxdb_write_string(ctx, KEY_INFO, uData->info);
7456 saxdb_end_record(ctx);
7458 saxdb_end_record(ctx);
7459 return high_present;
7463 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7467 saxdb_start_record(ctx, KEY_BANS, 1);
7468 for(; bData; bData = bData->next)
7470 saxdb_start_record(ctx, bData->mask, 0);
7471 saxdb_write_int(ctx, KEY_SET, bData->set);
7472 if(bData->triggered)
7473 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7475 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7477 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7479 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7480 saxdb_end_record(ctx);
7482 saxdb_end_record(ctx);
7486 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7488 saxdb_start_record(ctx, name, 0);
7489 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7490 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7492 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7494 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7496 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7498 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7499 saxdb_end_record(ctx);
7503 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7507 enum levelOption lvlOpt;
7508 enum charOption chOpt;
7510 saxdb_start_record(ctx, channel->channel->name, 1);
7512 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7513 saxdb_write_int(ctx, KEY_MAX, channel->max);
7515 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7516 if(channel->registrar)
7517 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7518 if(channel->greeting)
7519 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7520 if(channel->user_greeting)
7521 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7522 if(channel->topic_mask)
7523 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7524 if(channel->suspended)
7525 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7527 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7528 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7529 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7530 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7531 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7533 buf[0] = channel->chOpts[chOpt];
7535 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7537 saxdb_end_record(ctx);
7539 if(channel->modes.modes_set || channel->modes.modes_clear)
7541 mod_chanmode_format(&channel->modes, buf);
7542 saxdb_write_string(ctx, KEY_MODES, buf);
7545 high_present = chanserv_write_users(ctx, channel->users);
7546 chanserv_write_bans(ctx, channel->bans);
7548 if(dict_size(channel->notes))
7552 saxdb_start_record(ctx, KEY_NOTES, 1);
7553 for(it = dict_first(channel->notes); it; it = iter_next(it))
7555 struct note *note = iter_data(it);
7556 saxdb_start_record(ctx, iter_key(it), 0);
7557 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7558 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7559 saxdb_end_record(ctx);
7561 saxdb_end_record(ctx);
7564 if(channel->ownerTransfer)
7565 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7566 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7567 saxdb_end_record(ctx);
7571 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7575 saxdb_start_record(ctx, ntype->name, 0);
7576 switch(ntype->set_access_type)
7578 case NOTE_SET_CHANNEL_ACCESS:
7579 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7581 case NOTE_SET_CHANNEL_SETTER:
7582 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7584 case NOTE_SET_PRIVILEGED: default:
7585 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7588 switch(ntype->visible_type)
7590 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7591 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7592 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7594 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7595 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7596 saxdb_end_record(ctx);
7600 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7602 struct do_not_register *dnr;
7603 dict_iterator_t it, next;
7605 for(it = dict_first(dnrs); it; it = next)
7607 next = iter_next(it);
7608 dnr = iter_data(it);
7609 if(dnr->expires && dnr->expires <= now)
7611 dict_remove(dnrs, iter_key(it));
7614 saxdb_start_record(ctx, dnr->chan_name, 0);
7616 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7618 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7619 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7620 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7621 saxdb_end_record(ctx);
7626 chanserv_saxdb_write(struct saxdb_context *ctx)
7629 struct chanData *channel;
7632 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7633 for(it = dict_first(note_types); it; it = iter_next(it))
7634 chanserv_write_note_type(ctx, iter_data(it));
7635 saxdb_end_record(ctx);
7638 saxdb_start_record(ctx, KEY_DNR, 1);
7639 write_dnrs_helper(ctx, handle_dnrs);
7640 write_dnrs_helper(ctx, plain_dnrs);
7641 write_dnrs_helper(ctx, mask_dnrs);
7642 saxdb_end_record(ctx);
7645 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7646 for(channel = channelList; channel; channel = channel->next)
7647 chanserv_write_channel(ctx, channel);
7648 saxdb_end_record(ctx);
7654 chanserv_db_cleanup(void) {
7656 unreg_part_func(handle_part);
7658 unregister_channel(channelList, "terminating.");
7659 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7660 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7661 free(chanserv_conf.support_channels.list);
7662 dict_delete(handle_dnrs);
7663 dict_delete(plain_dnrs);
7664 dict_delete(mask_dnrs);
7665 dict_delete(note_types);
7666 free_string_list(chanserv_conf.eightball);
7667 free_string_list(chanserv_conf.old_ban_names);
7668 free_string_list(chanserv_conf.set_shows);
7669 free(set_shows_list.list);
7670 free(uset_shows_list.list);
7673 struct userData *helper = helperList;
7674 helperList = helperList->next;
7679 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7680 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7681 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7684 init_chanserv(const char *nick)
7686 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7687 conf_register_reload(chanserv_conf_read);
7691 reg_server_link_func(handle_server_link);
7692 reg_new_channel_func(handle_new_channel);
7693 reg_join_func(handle_join);
7694 reg_part_func(handle_part);
7695 reg_kick_func(handle_kick);
7696 reg_topic_func(handle_topic);
7697 reg_mode_change_func(handle_mode);
7698 reg_nick_change_func(handle_nick_change);
7699 reg_auth_func(handle_auth);
7702 reg_handle_rename_func(handle_rename);
7703 reg_unreg_func(handle_unreg);
7705 handle_dnrs = dict_new();
7706 dict_set_free_data(handle_dnrs, free);
7707 plain_dnrs = dict_new();
7708 dict_set_free_data(plain_dnrs, free);
7709 mask_dnrs = dict_new();
7710 dict_set_free_data(mask_dnrs, free);
7712 reg_svccmd_unbind_func(handle_svccmd_unbind);
7713 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7714 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7715 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7716 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7717 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7718 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7719 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7720 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7721 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7722 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7723 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7724 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7725 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7727 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7728 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7730 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7731 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7732 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7733 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7734 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7736 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7737 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7738 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7739 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7740 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7742 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7743 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7744 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7745 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7747 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7748 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7749 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7750 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7751 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7752 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7753 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7754 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7756 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7757 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7758 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7759 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7760 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7761 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7762 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7763 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7764 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7765 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7766 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7767 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7768 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7769 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7771 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7772 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7773 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7774 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7775 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7777 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7778 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7780 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7781 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7782 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7783 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7784 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7785 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7786 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7787 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7788 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7789 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7790 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7792 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7793 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7795 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7796 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7797 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7798 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7800 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7801 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7802 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7803 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7804 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7806 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7807 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7808 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7809 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7810 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7811 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7813 /* Channel options */
7814 DEFINE_CHANNEL_OPTION(defaulttopic);
7815 DEFINE_CHANNEL_OPTION(topicmask);
7816 DEFINE_CHANNEL_OPTION(greeting);
7817 DEFINE_CHANNEL_OPTION(usergreeting);
7818 DEFINE_CHANNEL_OPTION(modes);
7819 DEFINE_CHANNEL_OPTION(enfops);
7820 DEFINE_CHANNEL_OPTION(giveops);
7821 DEFINE_CHANNEL_OPTION(protect);
7822 DEFINE_CHANNEL_OPTION(enfmodes);
7823 DEFINE_CHANNEL_OPTION(enftopic);
7824 DEFINE_CHANNEL_OPTION(pubcmd);
7825 DEFINE_CHANNEL_OPTION(givevoice);
7826 DEFINE_CHANNEL_OPTION(userinfo);
7827 DEFINE_CHANNEL_OPTION(dynlimit);
7828 DEFINE_CHANNEL_OPTION(topicsnarf);
7829 DEFINE_CHANNEL_OPTION(nodelete);
7830 DEFINE_CHANNEL_OPTION(toys);
7831 DEFINE_CHANNEL_OPTION(setters);
7832 DEFINE_CHANNEL_OPTION(topicrefresh);
7833 DEFINE_CHANNEL_OPTION(ctcpusers);
7834 DEFINE_CHANNEL_OPTION(ctcpreaction);
7835 DEFINE_CHANNEL_OPTION(inviteme);
7836 DEFINE_CHANNEL_OPTION(unreviewed);
7837 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7838 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7840 DEFINE_CHANNEL_OPTION(offchannel);
7841 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7843 /* Alias set topic to set defaulttopic for compatibility. */
7844 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7847 DEFINE_USER_OPTION(noautoop);
7848 DEFINE_USER_OPTION(autoinvite);
7849 DEFINE_USER_OPTION(info);
7851 /* Alias uset autovoice to uset autoop. */
7852 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7854 note_types = dict_new();
7855 dict_set_free_data(note_types, chanserv_deref_note_type);
7858 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7859 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7860 service_register(chanserv)->trigger = '!';
7861 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7863 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7865 if(chanserv_conf.channel_expire_frequency)
7866 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7868 if(chanserv_conf.dnr_expire_frequency)
7869 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7871 if(chanserv_conf.refresh_period)
7873 time_t next_refresh;
7874 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7875 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7878 reg_exit_func(chanserv_db_cleanup);
7879 message_register_table(msgtab);