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))
1401 struct do_not_register *dnr;
1403 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1405 dnr = iter_data(it);
1406 if(!dnr->expires || dnr->expires > now)
1408 dict_remove(handle_dnrs, dnr->chan_name + 1);
1410 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1412 dnr = iter_data(it);
1413 if(!dnr->expires || dnr->expires > now)
1415 dict_remove(plain_dnrs, dnr->chan_name);
1417 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1419 dnr = iter_data(it);
1420 if(!dnr->expires || dnr->expires > now)
1422 dict_remove(mask_dnrs, dnr->chan_name);
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 && ((dnr->expires < search->min_expires)
1738 || (dnr->expires > search->max_expires)))
1739 || (search->chan_mask
1740 && !match_ircglob(dnr->chan_name, search->chan_mask))
1741 || (search->setter_mask
1742 && !match_ircglob(dnr->setter, search->setter_mask))
1743 || (search->reason_mask
1744 && !match_ircglob(dnr->reason, search->reason_mask)));
1747 static struct dnr_search *
1748 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1750 struct dnr_search *discrim;
1753 discrim = calloc(1, sizeof(*discrim));
1754 discrim->source = user;
1755 discrim->chan_mask = NULL;
1756 discrim->setter_mask = NULL;
1757 discrim->reason_mask = NULL;
1758 discrim->max_set = INT_MAX;
1759 discrim->max_expires = INT_MAX;
1760 discrim->limit = 50;
1762 for(ii=0; ii<argc; ++ii)
1766 reply("MSG_MISSING_PARAMS", argv[ii]);
1769 else if(0 == irccasecmp(argv[ii], "channel"))
1771 discrim->chan_mask = argv[++ii];
1773 else if(0 == irccasecmp(argv[ii], "setter"))
1775 discrim->setter_mask = argv[++ii];
1777 else if(0 == irccasecmp(argv[ii], "reason"))
1779 discrim->reason_mask = argv[++ii];
1781 else if(0 == irccasecmp(argv[ii], "limit"))
1783 discrim->limit = strtoul(argv[++ii], NULL, 0);
1785 else if(0 == irccasecmp(argv[ii], "set"))
1787 const char *cmp = argv[++ii];
1790 discrim->min_set = now - ParseInterval(cmp + 2);
1792 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1793 } else if(cmp[0] == '=') {
1794 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1795 } else if(cmp[0] == '>') {
1797 discrim->max_set = now - ParseInterval(cmp + 2);
1799 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1801 discrim->max_set = now - (ParseInterval(cmp) - 1);
1804 else if(0 == irccasecmp(argv[ii], "expires"))
1806 const char *cmp = argv[++ii];
1809 discrim->max_expires = now + ParseInterval(cmp + 2);
1811 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1812 } else if(cmp[0] == '=') {
1813 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1814 } else if(cmp[0] == '>') {
1816 discrim->min_expires = now + ParseInterval(cmp + 2);
1818 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1820 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1825 reply("MSG_INVALID_CRITERIA", argv[ii]);
1836 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1839 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1841 struct do_not_register *dnr;
1842 dict_iterator_t next;
1847 /* Initialize local variables. */
1850 if(discrim->chan_mask)
1852 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1853 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1857 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1859 /* Check against account-based DNRs. */
1860 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1861 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1864 else if(target_fixed)
1866 /* Check against channel-based DNRs. */
1867 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1868 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1873 /* Exhaustively search account DNRs. */
1874 for(it = dict_first(handle_dnrs); it; it = next)
1876 next = iter_next(it);
1877 dnr = iter_data(it);
1878 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1882 /* Do the same for channel DNRs. */
1883 for(it = dict_first(plain_dnrs); it; it = next)
1885 next = iter_next(it);
1886 dnr = iter_data(it);
1887 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1891 /* Do the same for wildcarded channel DNRs. */
1892 for(it = dict_first(mask_dnrs); it; it = next)
1894 next = iter_next(it);
1895 dnr = iter_data(it);
1896 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1904 dnr_remove_func(struct do_not_register *match, void *extra)
1906 struct userNode *user;
1909 chan_name = alloca(strlen(match->chan_name) + 1);
1910 strcpy(chan_name, match->chan_name);
1912 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1913 || dict_remove(plain_dnrs, chan_name)
1914 || dict_remove(mask_dnrs, chan_name))
1916 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1922 dnr_count_func(struct do_not_register *match, void *extra)
1924 return 0; (void)match; (void)extra;
1927 static MODCMD_FUNC(cmd_dnrsearch)
1929 struct dnr_search *discrim;
1930 dnr_search_func action;
1931 struct svccmd *subcmd;
1932 unsigned int matches;
1935 sprintf(buf, "dnrsearch %s", argv[1]);
1936 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1939 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1942 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1944 if(!irccasecmp(argv[1], "print"))
1945 action = dnr_print_func;
1946 else if(!irccasecmp(argv[1], "remove"))
1947 action = dnr_remove_func;
1948 else if(!irccasecmp(argv[1], "count"))
1949 action = dnr_count_func;
1952 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1956 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1960 if(action == dnr_print_func)
1961 reply("CSMSG_DNR_SEARCH_RESULTS");
1962 matches = dnr_search(discrim, action, user);
1964 reply("MSG_MATCH_COUNT", matches);
1966 reply("MSG_NO_MATCHES");
1972 chanserv_get_owned_count(struct handle_info *hi)
1974 struct userData *cList;
1977 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1978 if(cList->access == UL_OWNER)
1983 static CHANSERV_FUNC(cmd_register)
1985 struct handle_info *handle;
1986 struct chanData *cData;
1987 struct modeNode *mn;
1988 char reason[MAXLEN];
1990 unsigned int new_channel, force=0;
1991 struct do_not_register *dnr;
1995 if(channel->channel_info)
1997 reply("CSMSG_ALREADY_REGGED", channel->name);
2001 if(channel->bad_channel)
2003 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2008 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2010 reply("CSMSG_MUST_BE_OPPED", channel->name);
2015 chan_name = channel->name;
2019 if((argc < 2) || !IsChannelName(argv[1]))
2021 reply("MSG_NOT_CHANNEL_NAME");
2025 if(opserv_bad_channel(argv[1]))
2027 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2032 chan_name = argv[1];
2035 if(argc >= (new_channel+2))
2037 if(!IsHelping(user))
2039 reply("CSMSG_PROXY_FORBIDDEN");
2043 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2045 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2046 dnr = chanserv_is_dnr(chan_name, handle);
2050 handle = user->handle_info;
2051 dnr = chanserv_is_dnr(chan_name, handle);
2055 if(!IsHelping(user))
2056 reply("CSMSG_DNR_CHANNEL", chan_name);
2058 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2062 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2064 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2069 channel = AddChannel(argv[1], now, NULL, NULL);
2071 cData = register_channel(channel, user->handle_info->handle);
2072 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2073 cData->modes = chanserv_conf.default_modes;
2075 cData->modes.modes_set |= MODE_REGISTERED;
2076 if (IsOffChannel(cData))
2078 mod_chanmode_announce(chanserv, channel, &cData->modes);
2082 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2083 change->args[change->argc].mode = MODE_CHANOP;
2084 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2086 mod_chanmode_announce(chanserv, channel, change);
2087 mod_chanmode_free(change);
2090 /* Initialize the channel's max user record. */
2091 cData->max = channel->members.used;
2093 if(handle != user->handle_info)
2094 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2096 reply("CSMSG_REG_SUCCESS", channel->name);
2098 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2099 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2104 make_confirmation_string(struct userData *uData)
2106 static char strbuf[16];
2111 for(src = uData->handle->handle; *src; )
2112 accum = accum * 31 + toupper(*src++);
2114 for(src = uData->channel->channel->name; *src; )
2115 accum = accum * 31 + toupper(*src++);
2116 sprintf(strbuf, "%08x", accum);
2120 static CHANSERV_FUNC(cmd_unregister)
2123 char reason[MAXLEN];
2124 struct chanData *cData;
2125 struct userData *uData;
2127 cData = channel->channel_info;
2130 reply("CSMSG_NOT_REGISTERED", channel->name);
2134 uData = GetChannelUser(cData, user->handle_info);
2135 if(!uData || (uData->access < UL_OWNER))
2137 reply("CSMSG_NO_ACCESS");
2141 if(IsProtected(cData))
2143 reply("CSMSG_UNREG_NODELETE", channel->name);
2147 if(!IsHelping(user))
2149 const char *confirm_string;
2150 if(IsSuspended(cData))
2152 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2155 confirm_string = make_confirmation_string(uData);
2156 if((argc < 2) || strcmp(argv[1], confirm_string))
2158 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2163 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2164 name = strdup(channel->name);
2165 unregister_channel(cData, reason);
2166 reply("CSMSG_UNREG_SUCCESS", name);
2171 static CHANSERV_FUNC(cmd_move)
2173 struct mod_chanmode change;
2174 struct chanNode *target;
2175 struct modeNode *mn;
2176 struct userData *uData;
2177 char reason[MAXLEN];
2178 struct do_not_register *dnr;
2182 if(IsProtected(channel->channel_info))
2184 reply("CSMSG_MOVE_NODELETE", channel->name);
2188 if(!IsChannelName(argv[1]))
2190 reply("MSG_NOT_CHANNEL_NAME");
2194 if(opserv_bad_channel(argv[1]))
2196 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2200 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2202 for(uData = channel->channel_info->users; uData; uData = uData->next)
2204 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2206 if(!IsHelping(user))
2207 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2209 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2215 mod_chanmode_init(&change);
2216 if(!(target = GetChannel(argv[1])))
2218 target = AddChannel(argv[1], now, NULL, NULL);
2219 if(!IsSuspended(channel->channel_info))
2220 AddChannelUser(chanserv, target);
2222 else if(target->channel_info)
2224 reply("CSMSG_ALREADY_REGGED", target->name);
2227 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2228 && !IsHelping(user))
2230 reply("CSMSG_MUST_BE_OPPED", target->name);
2233 else if(!IsSuspended(channel->channel_info))
2236 change.args[0].mode = MODE_CHANOP;
2237 change.args[0].u.member = AddChannelUser(chanserv, target);
2238 mod_chanmode_announce(chanserv, target, &change);
2243 /* Clear MODE_REGISTERED from old channel, add it to new. */
2245 change.modes_clear = MODE_REGISTERED;
2246 mod_chanmode_announce(chanserv, channel, &change);
2247 change.modes_clear = 0;
2248 change.modes_set = MODE_REGISTERED;
2249 mod_chanmode_announce(chanserv, target, &change);
2252 /* Move the channel_info to the target channel; it
2253 shouldn't be necessary to clear timeq callbacks
2254 for the old channel. */
2255 target->channel_info = channel->channel_info;
2256 target->channel_info->channel = target;
2257 channel->channel_info = NULL;
2259 reply("CSMSG_MOVE_SUCCESS", target->name);
2261 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2262 if(!IsSuspended(target->channel_info))
2264 char reason2[MAXLEN];
2265 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2266 DelChannelUser(chanserv, channel, reason2, 0);
2268 UnlockChannel(channel);
2269 LockChannel(target);
2270 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2275 merge_users(struct chanData *source, struct chanData *target)
2277 struct userData *suData, *tuData, *next;
2283 /* Insert the source's users into the scratch area. */
2284 for(suData = source->users; suData; suData = suData->next)
2285 dict_insert(merge, suData->handle->handle, suData);
2287 /* Iterate through the target's users, looking for
2288 users common to both channels. The lower access is
2289 removed from either the scratch area or target user
2291 for(tuData = target->users; tuData; tuData = next)
2293 struct userData *choice;
2295 next = tuData->next;
2297 /* If a source user exists with the same handle as a target
2298 channel's user, resolve the conflict by removing one. */
2299 suData = dict_find(merge, tuData->handle->handle, NULL);
2303 /* Pick the data we want to keep. */
2304 /* If the access is the same, use the later seen time. */
2305 if(suData->access == tuData->access)
2306 choice = (suData->seen > tuData->seen) ? suData : tuData;
2307 else /* Otherwise, keep the higher access level. */
2308 choice = (suData->access > tuData->access) ? suData : tuData;
2310 /* Remove the user that wasn't picked. */
2311 if(choice == tuData)
2313 dict_remove(merge, suData->handle->handle);
2314 del_channel_user(suData, 0);
2317 del_channel_user(tuData, 0);
2320 /* Move the remaining users to the target channel. */
2321 for(it = dict_first(merge); it; it = iter_next(it))
2323 suData = iter_data(it);
2325 /* Insert the user into the target channel's linked list. */
2326 suData->prev = NULL;
2327 suData->next = target->users;
2328 suData->channel = target;
2331 target->users->prev = suData;
2332 target->users = suData;
2334 /* Update the user counts for the target channel; the
2335 source counts are left alone. */
2336 target->userCount++;
2339 /* Possible to assert (source->users == NULL) here. */
2340 source->users = NULL;
2345 merge_bans(struct chanData *source, struct chanData *target)
2347 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2349 /* Hold on to the original head of the target ban list
2350 to avoid comparing source bans with source bans. */
2351 tFront = target->bans;
2353 /* Perform a totally expensive O(n*m) merge, ick. */
2354 for(sbData = source->bans; sbData; sbData = sNext)
2356 /* Flag to track whether the ban's been moved
2357 to the destination yet. */
2360 /* Possible to assert (sbData->prev == NULL) here. */
2361 sNext = sbData->next;
2363 for(tbData = tFront; tbData; tbData = tNext)
2365 tNext = tbData->next;
2367 /* Perform two comparisons between each source
2368 and target ban, conflicts are resolved by
2369 keeping the broader ban and copying the later
2370 expiration and triggered time. */
2371 if(match_ircglobs(tbData->mask, sbData->mask))
2373 /* There is a broader ban in the target channel that
2374 overrides one in the source channel; remove the
2375 source ban and break. */
2376 if(sbData->expires > tbData->expires)
2377 tbData->expires = sbData->expires;
2378 if(sbData->triggered > tbData->triggered)
2379 tbData->triggered = sbData->triggered;
2380 del_channel_ban(sbData);
2383 else if(match_ircglobs(sbData->mask, tbData->mask))
2385 /* There is a broader ban in the source channel that
2386 overrides one in the target channel; remove the
2387 target ban, fall through and move the source over. */
2388 if(tbData->expires > sbData->expires)
2389 sbData->expires = tbData->expires;
2390 if(tbData->triggered > sbData->triggered)
2391 sbData->triggered = tbData->triggered;
2392 if(tbData == tFront)
2394 del_channel_ban(tbData);
2397 /* Source bans can override multiple target bans, so
2398 we allow a source to run through this loop multiple
2399 times, but we can only move it once. */
2404 /* Remove the source ban from the source ban list. */
2406 sbData->next->prev = sbData->prev;
2408 /* Modify the source ban's associated channel. */
2409 sbData->channel = target;
2411 /* Insert the ban into the target channel's linked list. */
2412 sbData->prev = NULL;
2413 sbData->next = target->bans;
2416 target->bans->prev = sbData;
2417 target->bans = sbData;
2419 /* Update the user counts for the target channel. */
2424 /* Possible to assert (source->bans == NULL) here. */
2425 source->bans = NULL;
2429 merge_data(struct chanData *source, struct chanData *target)
2431 /* Use more recent visited and owner-transfer time; use older
2432 * registered time. Bitwise or may_opchan. Use higher max.
2433 * Do not touch last_refresh, ban count or user counts.
2435 if(source->visited > target->visited)
2436 target->visited = source->visited;
2437 if(source->registered < target->registered)
2438 target->registered = source->registered;
2439 if(source->ownerTransfer > target->ownerTransfer)
2440 target->ownerTransfer = source->ownerTransfer;
2441 if(source->may_opchan)
2442 target->may_opchan = 1;
2443 if(source->max > target->max)
2444 target->max = source->max;
2448 merge_channel(struct chanData *source, struct chanData *target)
2450 merge_users(source, target);
2451 merge_bans(source, target);
2452 merge_data(source, target);
2455 static CHANSERV_FUNC(cmd_merge)
2457 struct userData *target_user;
2458 struct chanNode *target;
2459 char reason[MAXLEN];
2463 /* Make sure the target channel exists and is registered to the user
2464 performing the command. */
2465 if(!(target = GetChannel(argv[1])))
2467 reply("MSG_INVALID_CHANNEL");
2471 if(!target->channel_info)
2473 reply("CSMSG_NOT_REGISTERED", target->name);
2477 if(IsProtected(channel->channel_info))
2479 reply("CSMSG_MERGE_NODELETE");
2483 if(IsSuspended(target->channel_info))
2485 reply("CSMSG_MERGE_SUSPENDED");
2489 if(channel == target)
2491 reply("CSMSG_MERGE_SELF");
2495 target_user = GetChannelUser(target->channel_info, user->handle_info);
2496 if(!target_user || (target_user->access < UL_OWNER))
2498 reply("CSMSG_MERGE_NOT_OWNER");
2502 /* Merge the channel structures and associated data. */
2503 merge_channel(channel->channel_info, target->channel_info);
2504 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2505 unregister_channel(channel->channel_info, reason);
2506 reply("CSMSG_MERGE_SUCCESS", target->name);
2510 static CHANSERV_FUNC(cmd_opchan)
2512 struct mod_chanmode change;
2513 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2515 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2518 channel->channel_info->may_opchan = 0;
2519 mod_chanmode_init(&change);
2521 change.args[0].mode = MODE_CHANOP;
2522 change.args[0].u.member = GetUserMode(channel, chanserv);
2523 mod_chanmode_announce(chanserv, channel, &change);
2524 reply("CSMSG_OPCHAN_DONE", channel->name);
2528 static CHANSERV_FUNC(cmd_adduser)
2530 struct userData *actee;
2531 struct userData *actor, *real_actor;
2532 struct handle_info *handle;
2533 unsigned short access, override = 0;
2537 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2539 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2543 access = user_level_from_name(argv[2], UL_OWNER);
2546 reply("CSMSG_INVALID_ACCESS", argv[2]);
2550 actor = GetChannelUser(channel->channel_info, user->handle_info);
2551 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2553 if(actor->access <= access)
2555 reply("CSMSG_NO_BUMP_ACCESS");
2559 /* Trying to add someone with equal/more access? */
2560 if (!real_actor || real_actor->access <= access)
2561 override = CMD_LOG_OVERRIDE;
2563 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2566 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2568 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2572 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2573 scan_user_presence(actee, NULL);
2574 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2575 return 1 | override;
2578 static CHANSERV_FUNC(cmd_clvl)
2580 struct handle_info *handle;
2581 struct userData *victim;
2582 struct userData *actor, *real_actor;
2583 unsigned short new_access, override = 0;
2584 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2588 actor = GetChannelUser(channel->channel_info, user->handle_info);
2589 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2591 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2594 if(handle == user->handle_info && !privileged)
2596 reply("CSMSG_NO_SELF_CLVL");
2600 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2602 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2606 if(actor->access <= victim->access && !privileged)
2608 reply("MSG_USER_OUTRANKED", handle->handle);
2612 new_access = user_level_from_name(argv[2], UL_OWNER);
2616 reply("CSMSG_INVALID_ACCESS", argv[2]);
2620 if(new_access >= actor->access && !privileged)
2622 reply("CSMSG_NO_BUMP_ACCESS");
2626 /* Trying to clvl a equal/higher user? */
2627 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2628 override = CMD_LOG_OVERRIDE;
2629 /* Trying to clvl someone to equal/higher access? */
2630 if(!real_actor || new_access >= real_actor->access)
2631 override = CMD_LOG_OVERRIDE;
2632 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2633 * If they lower their own access it's not a big problem.
2636 victim->access = new_access;
2637 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2638 return 1 | override;
2641 static CHANSERV_FUNC(cmd_deluser)
2643 struct handle_info *handle;
2644 struct userData *victim;
2645 struct userData *actor, *real_actor;
2646 unsigned short access, override = 0;
2651 actor = GetChannelUser(channel->channel_info, user->handle_info);
2652 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2654 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2657 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2659 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2665 access = user_level_from_name(argv[1], UL_OWNER);
2668 reply("CSMSG_INVALID_ACCESS", argv[1]);
2671 if(access != victim->access)
2673 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2679 access = victim->access;
2682 if((actor->access <= victim->access) && !IsHelping(user))
2684 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2688 /* If people delete themselves it is an override, but they
2689 * could've used deleteme so we don't log it as an override
2691 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2692 override = CMD_LOG_OVERRIDE;
2694 chan_name = strdup(channel->name);
2695 del_channel_user(victim, 1);
2696 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2698 return 1 | override;
2702 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2704 struct userData *actor, *real_actor, *uData, *next;
2705 unsigned int override = 0;
2707 actor = GetChannelUser(channel->channel_info, user->handle_info);
2708 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2710 if(min_access > max_access)
2712 reply("CSMSG_BAD_RANGE", min_access, max_access);
2716 if((actor->access <= max_access) && !IsHelping(user))
2718 reply("CSMSG_NO_ACCESS");
2722 if(!real_actor || real_actor->access <= max_access)
2723 override = CMD_LOG_OVERRIDE;
2725 for(uData = channel->channel_info->users; uData; uData = next)
2729 if((uData->access >= min_access)
2730 && (uData->access <= max_access)
2731 && match_ircglob(uData->handle->handle, mask))
2732 del_channel_user(uData, 1);
2735 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2736 return 1 | override;
2739 static CHANSERV_FUNC(cmd_mdelowner)
2741 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2744 static CHANSERV_FUNC(cmd_mdelcoowner)
2746 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2749 static CHANSERV_FUNC(cmd_mdelmaster)
2751 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2754 static CHANSERV_FUNC(cmd_mdelop)
2756 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2759 static CHANSERV_FUNC(cmd_mdelpeon)
2761 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2765 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2767 struct banData *bData, *next;
2768 char interval[INTERVALLEN];
2773 limit = now - duration;
2774 for(bData = channel->channel_info->bans; bData; bData = next)
2778 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2781 del_channel_ban(bData);
2785 intervalString(interval, duration, user->handle_info);
2786 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2791 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2793 struct userData *actor, *uData, *next;
2794 char interval[INTERVALLEN];
2798 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2799 if(min_access > max_access)
2801 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2805 if(!actor || actor->access <= max_access)
2807 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2812 limit = now - duration;
2813 for(uData = channel->channel_info->users; uData; uData = next)
2817 if((uData->seen > limit)
2819 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2822 if(((uData->access >= min_access) && (uData->access <= max_access))
2823 || (!max_access && (uData->access < actor->access)))
2825 del_channel_user(uData, 1);
2833 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2835 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2839 static CHANSERV_FUNC(cmd_trim)
2841 unsigned long duration;
2842 unsigned short min_level, max_level;
2847 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2848 duration = ParseInterval(argv[2]);
2851 reply("CSMSG_CANNOT_TRIM");
2855 if(!irccasecmp(argv[1], "bans"))
2857 cmd_trim_bans(user, channel, duration);
2860 else if(!irccasecmp(argv[1], "users"))
2862 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2865 else if(parse_level_range(&min_level, &max_level, argv[1]))
2867 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2870 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2872 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2877 reply("CSMSG_INVALID_TRIM", argv[1]);
2882 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2883 to the user. cmd_all takes advantage of this. */
2884 static CHANSERV_FUNC(cmd_up)
2886 struct mod_chanmode change;
2887 struct userData *uData;
2890 mod_chanmode_init(&change);
2892 change.args[0].u.member = GetUserMode(channel, user);
2893 if(!change.args[0].u.member)
2896 reply("MSG_CHANNEL_ABSENT", channel->name);
2900 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2904 reply("CSMSG_GODMODE_UP", argv[0]);
2907 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2909 change.args[0].mode = MODE_CHANOP;
2910 errmsg = "CSMSG_ALREADY_OPPED";
2912 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2914 change.args[0].mode = MODE_VOICE;
2915 errmsg = "CSMSG_ALREADY_VOICED";
2920 reply("CSMSG_NO_ACCESS");
2923 change.args[0].mode &= ~change.args[0].u.member->modes;
2924 if(!change.args[0].mode)
2927 reply(errmsg, channel->name);
2930 modcmd_chanmode_announce(&change);
2934 static CHANSERV_FUNC(cmd_down)
2936 struct mod_chanmode change;
2938 mod_chanmode_init(&change);
2940 change.args[0].u.member = GetUserMode(channel, user);
2941 if(!change.args[0].u.member)
2944 reply("MSG_CHANNEL_ABSENT", channel->name);
2948 if(!change.args[0].u.member->modes)
2951 reply("CSMSG_ALREADY_DOWN", channel->name);
2955 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2956 modcmd_chanmode_announce(&change);
2960 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)
2962 struct userData *cList;
2964 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2966 if(IsSuspended(cList->channel)
2967 || IsUserSuspended(cList)
2968 || !GetUserMode(cList->channel->channel, user))
2971 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2977 static CHANSERV_FUNC(cmd_upall)
2979 return cmd_all(CSFUNC_ARGS, cmd_up);
2982 static CHANSERV_FUNC(cmd_downall)
2984 return cmd_all(CSFUNC_ARGS, cmd_down);
2987 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2988 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2991 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)
2993 unsigned int ii, valid;
2994 struct userNode *victim;
2995 struct mod_chanmode *change;
2997 change = mod_chanmode_alloc(argc - 1);
2999 for(ii=valid=0; ++ii < argc; )
3001 if(!(victim = GetUserH(argv[ii])))
3003 change->args[valid].mode = mode;
3004 change->args[valid].u.member = GetUserMode(channel, victim);
3005 if(!change->args[valid].u.member)
3007 if(validate && !validate(user, channel, victim))
3012 change->argc = valid;
3013 if(valid < (argc-1))
3014 reply("CSMSG_PROCESS_FAILED");
3017 modcmd_chanmode_announce(change);
3018 reply(action, channel->name);
3020 mod_chanmode_free(change);
3024 static CHANSERV_FUNC(cmd_op)
3026 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3029 static CHANSERV_FUNC(cmd_deop)
3031 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3034 static CHANSERV_FUNC(cmd_voice)
3036 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3039 static CHANSERV_FUNC(cmd_devoice)
3041 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3045 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3051 for(ii=0; ii<channel->members.used; ii++)
3053 struct modeNode *mn = channel->members.list[ii];
3055 if(IsService(mn->user))
3058 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3061 if(protect_user(mn->user, user, channel->channel_info))
3065 victims[(*victimCount)++] = mn;
3071 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3073 struct userNode *victim;
3074 struct modeNode **victims;
3075 unsigned int offset, n, victimCount, duration = 0;
3076 char *reason = "Bye.", *ban, *name;
3077 char interval[INTERVALLEN];
3079 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3080 REQUIRE_PARAMS(offset);
3083 reason = unsplit_string(argv + offset, argc - offset, NULL);
3084 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3086 /* Truncate the reason to a length of TOPICLEN, as
3087 the ircd does; however, leave room for an ellipsis
3088 and the kicker's nick. */
3089 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3093 if((victim = GetUserH(argv[1])))
3095 victims = alloca(sizeof(victims[0]));
3096 victims[0] = GetUserMode(channel, victim);
3097 /* XXX: The comparison with ACTION_KICK is just because all
3098 * other actions can work on users outside the channel, and we
3099 * want to allow those (e.g. unbans) in that case. If we add
3100 * some other ejection action for in-channel users, change
3102 victimCount = victims[0] ? 1 : 0;
3104 if(IsService(victim))
3106 reply("MSG_SERVICE_IMMUNE", victim->nick);
3110 if((action == ACTION_KICK) && !victimCount)
3112 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3116 if(protect_user(victim, user, channel->channel_info))
3118 reply("CSMSG_USER_PROTECTED", victim->nick);
3122 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3123 name = victim->nick;
3125 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3127 struct handle_info *hi;
3128 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3129 const char *accountname = argv[1] + 1;
3131 if(!(hi = get_handle_info(accountname)))
3133 reply("MSG_HANDLE_UNKNOWN", accountname);
3137 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3138 victims = alloca(sizeof(victims[0]) * channel->members.used);
3140 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3142 reply("CSMSG_MASK_PROTECTED", banmask);
3146 if((action == ACTION_KICK) && (victimCount == 0))
3148 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3152 name = ban = strdup(banmask);
3156 if(!is_ircmask(argv[1]))
3158 reply("MSG_NICK_UNKNOWN", argv[1]);
3162 victims = alloca(sizeof(victims[0]) * channel->members.used);
3164 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3166 reply("CSMSG_MASK_PROTECTED", argv[1]);
3170 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3172 reply("CSMSG_LAME_MASK", argv[1]);
3176 if((action == ACTION_KICK) && (victimCount == 0))
3178 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3182 name = ban = strdup(argv[1]);
3185 /* Truncate the ban in place if necessary; we must ensure
3186 that 'ban' is a valid ban mask before sanitizing it. */
3187 sanitize_ircmask(ban);
3189 if(action & ACTION_ADD_BAN)
3191 struct banData *bData, *next;
3193 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3195 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3200 if(action & ACTION_ADD_TIMED_BAN)
3202 duration = ParseInterval(argv[2]);
3206 reply("CSMSG_DURATION_TOO_LOW");
3210 else if(duration > (86400 * 365 * 2))
3212 reply("CSMSG_DURATION_TOO_HIGH");
3218 for(bData = channel->channel_info->bans; bData; bData = next)
3220 if(match_ircglobs(bData->mask, ban))
3222 int exact = !irccasecmp(bData->mask, ban);
3224 /* The ban is redundant; there is already a ban
3225 with the same effect in place. */
3229 free(bData->reason);
3230 bData->reason = strdup(reason);
3231 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3233 reply("CSMSG_REASON_CHANGE", ban);
3237 if(exact && bData->expires)
3241 /* If the ban matches an existing one exactly,
3242 extend the expiration time if the provided
3243 duration is longer. */
3244 if(duration && ((time_t)(now + duration) > bData->expires))
3246 bData->expires = now + duration;
3257 /* Delete the expiration timeq entry and
3258 requeue if necessary. */
3259 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3262 timeq_add(bData->expires, expire_ban, bData);
3266 /* automated kickban */
3269 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3271 reply("CSMSG_BAN_ADDED", name, channel->name);
3277 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3284 if(match_ircglobs(ban, bData->mask))
3286 /* The ban we are adding makes previously existing
3287 bans redundant; silently remove them. */
3288 del_channel_ban(bData);
3292 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);
3294 name = ban = strdup(bData->mask);
3298 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3300 extern const char *hidden_host_suffix;
3301 const char *old_name = chanserv_conf.old_ban_names->list[n];
3303 unsigned int l1, l2;
3306 l2 = strlen(old_name);
3309 if(irccasecmp(ban + l1 - l2, old_name))
3311 new_mask = malloc(MAXLEN);
3312 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3314 name = ban = new_mask;
3319 if(action & ACTION_BAN)
3321 unsigned int exists;
3322 struct mod_chanmode *change;
3324 if(channel->banlist.used >= MAXBANS)
3327 reply("CSMSG_BANLIST_FULL", channel->name);
3332 exists = ChannelBanExists(channel, ban);
3333 change = mod_chanmode_alloc(victimCount + 1);
3334 for(n = 0; n < victimCount; ++n)
3336 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3337 change->args[n].u.member = victims[n];
3341 change->args[n].mode = MODE_BAN;
3342 change->args[n++].u.hostmask = ban;
3346 modcmd_chanmode_announce(change);
3348 mod_chanmode_announce(chanserv, channel, change);
3349 mod_chanmode_free(change);
3351 if(exists && (action == ACTION_BAN))
3354 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3360 if(action & ACTION_KICK)
3362 char kick_reason[MAXLEN];
3363 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3365 for(n = 0; n < victimCount; n++)
3366 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3371 /* No response, since it was automated. */
3373 else if(action & ACTION_ADD_BAN)
3376 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3378 reply("CSMSG_BAN_ADDED", name, channel->name);
3380 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3381 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3382 else if(action & ACTION_BAN)
3383 reply("CSMSG_BAN_DONE", name, channel->name);
3384 else if(action & ACTION_KICK && victimCount)
3385 reply("CSMSG_KICK_DONE", name, channel->name);
3391 static CHANSERV_FUNC(cmd_kickban)
3393 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3396 static CHANSERV_FUNC(cmd_kick)
3398 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3401 static CHANSERV_FUNC(cmd_ban)
3403 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3406 static CHANSERV_FUNC(cmd_addban)
3408 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3411 static CHANSERV_FUNC(cmd_addtimedban)
3413 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3416 static struct mod_chanmode *
3417 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3419 struct mod_chanmode *change;
3420 unsigned char *match;
3421 unsigned int ii, count;
3423 match = alloca(bans->used);
3426 for(ii = count = 0; ii < bans->used; ++ii)
3428 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3429 MATCH_USENICK | MATCH_VISIBLE);
3436 for(ii = count = 0; ii < bans->used; ++ii)
3438 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3445 change = mod_chanmode_alloc(count);
3446 for(ii = count = 0; ii < bans->used; ++ii)
3450 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3451 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3453 assert(count == change->argc);
3458 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3460 struct userNode *actee;
3466 /* may want to allow a comma delimited list of users... */
3467 if(!(actee = GetUserH(argv[1])))
3469 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3471 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3472 const char *accountname = argv[1] + 1;
3474 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3475 mask = strdup(banmask);
3477 else if(!is_ircmask(argv[1]))
3479 reply("MSG_NICK_UNKNOWN", argv[1]);
3484 mask = strdup(argv[1]);
3488 /* We don't sanitize the mask here because ircu
3490 if(action & ACTION_UNBAN)
3492 struct mod_chanmode *change;
3493 change = find_matching_bans(&channel->banlist, actee, mask);
3498 modcmd_chanmode_announce(change);
3499 for(ii = 0; ii < change->argc; ++ii)
3500 free((char*)change->args[ii].u.hostmask);
3501 mod_chanmode_free(change);
3506 if(action & ACTION_DEL_BAN)
3508 struct banData *ban, *next;
3510 ban = channel->channel_info->bans;
3514 for( ; ban && !user_matches_glob(actee, ban->mask,
3515 MATCH_USENICK | MATCH_VISIBLE);
3518 for( ; ban && !match_ircglobs(mask, ban->mask);
3523 del_channel_ban(ban);
3530 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3532 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3538 static CHANSERV_FUNC(cmd_unban)
3540 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3543 static CHANSERV_FUNC(cmd_delban)
3545 /* it doesn't necessarily have to remove the channel ban - may want
3546 to make that an option. */
3547 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3550 static CHANSERV_FUNC(cmd_unbanme)
3552 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3553 long flags = ACTION_UNBAN;
3555 /* remove permanent bans if the user has the proper access. */
3556 if(uData->access >= UL_MASTER)
3557 flags |= ACTION_DEL_BAN;
3559 argv[1] = user->nick;
3560 return unban_user(user, channel, 2, argv, cmd, flags);
3563 static CHANSERV_FUNC(cmd_unbanall)
3565 struct mod_chanmode *change;
3568 if(!channel->banlist.used)
3570 reply("CSMSG_NO_BANS", channel->name);
3574 change = mod_chanmode_alloc(channel->banlist.used);
3575 for(ii=0; ii<channel->banlist.used; ii++)
3577 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3578 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3580 modcmd_chanmode_announce(change);
3581 for(ii = 0; ii < change->argc; ++ii)
3582 free((char*)change->args[ii].u.hostmask);
3583 mod_chanmode_free(change);
3584 reply("CSMSG_BANS_REMOVED", channel->name);
3588 static CHANSERV_FUNC(cmd_open)
3590 struct mod_chanmode *change;
3593 change = find_matching_bans(&channel->banlist, user, NULL);
3595 change = mod_chanmode_alloc(0);
3596 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3597 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3598 && channel->channel_info->modes.modes_set)
3599 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3600 modcmd_chanmode_announce(change);
3601 reply("CSMSG_CHANNEL_OPENED", channel->name);
3602 for(ii = 0; ii < change->argc; ++ii)
3603 free((char*)change->args[ii].u.hostmask);
3604 mod_chanmode_free(change);
3608 static CHANSERV_FUNC(cmd_myaccess)
3610 static struct string_buffer sbuf;
3611 struct handle_info *target_handle;
3612 struct userData *uData;
3615 target_handle = user->handle_info;
3616 else if(!IsHelping(user))
3618 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3621 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3624 if(!target_handle->channels)
3626 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3630 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3631 for(uData = target_handle->channels; uData; uData = uData->u_next)
3633 struct chanData *cData = uData->channel;
3635 if(uData->access > UL_OWNER)
3637 if(IsProtected(cData)
3638 && (target_handle != user->handle_info)
3639 && !GetTrueChannelAccess(cData, user->handle_info))
3642 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3643 if(uData->flags != USER_AUTO_OP)
3644 string_buffer_append(&sbuf, ',');
3645 if(IsUserSuspended(uData))
3646 string_buffer_append(&sbuf, 's');
3647 if(IsUserAutoOp(uData))
3649 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3650 string_buffer_append(&sbuf, 'o');
3651 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3652 string_buffer_append(&sbuf, 'v');
3654 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3655 string_buffer_append(&sbuf, 'i');
3657 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3659 string_buffer_append_string(&sbuf, ")]");
3660 string_buffer_append(&sbuf, '\0');
3661 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3667 static CHANSERV_FUNC(cmd_access)
3669 struct userNode *target;
3670 struct handle_info *target_handle;
3671 struct userData *uData;
3673 char prefix[MAXLEN];
3678 target_handle = target->handle_info;
3680 else if((target = GetUserH(argv[1])))
3682 target_handle = target->handle_info;
3684 else if(argv[1][0] == '*')
3686 if(!(target_handle = get_handle_info(argv[1]+1)))
3688 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3694 reply("MSG_NICK_UNKNOWN", argv[1]);
3698 assert(target || target_handle);
3700 if(target == chanserv)
3702 reply("CSMSG_IS_CHANSERV");
3710 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3715 reply("MSG_USER_AUTHENTICATE", target->nick);
3718 reply("MSG_AUTHENTICATE");
3724 const char *epithet = NULL, *type = NULL;
3727 epithet = chanserv_conf.irc_operator_epithet;
3728 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3730 else if(IsNetworkHelper(target))
3732 epithet = chanserv_conf.network_helper_epithet;
3733 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3735 else if(IsSupportHelper(target))
3737 epithet = chanserv_conf.support_helper_epithet;
3738 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3742 if(target_handle->epithet)
3743 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3745 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3747 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3751 sprintf(prefix, "%s", target_handle->handle);
3754 if(!channel->channel_info)
3756 reply("CSMSG_NOT_REGISTERED", channel->name);
3760 helping = HANDLE_FLAGGED(target_handle, HELPING)
3761 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3762 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3764 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3765 /* To prevent possible information leaks, only show infolines
3766 * if the requestor is in the channel or it's their own
3768 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3770 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3772 /* Likewise, only say it's suspended if the user has active
3773 * access in that channel or it's their own entry. */
3774 if(IsUserSuspended(uData)
3775 && (GetChannelUser(channel->channel_info, user->handle_info)
3776 || (user->handle_info == uData->handle)))
3778 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3783 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3790 zoot_list(struct listData *list)
3792 struct userData *uData;
3793 unsigned int start, curr, highest, lowest;
3794 struct helpfile_table tmp_table;
3795 const char **temp, *msg;
3797 if(list->table.length == 1)
3800 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3802 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3803 msg = user_find_message(list->user, "MSG_NONE");
3804 send_message_type(4, list->user, list->bot, " %s", msg);
3806 tmp_table.width = list->table.width;
3807 tmp_table.flags = list->table.flags;
3808 list->table.contents[0][0] = " ";
3809 highest = list->highest;
3810 if(list->lowest != 0)
3811 lowest = list->lowest;
3812 else if(highest < 100)
3815 lowest = highest - 100;
3816 for(start = curr = 1; curr < list->table.length; )
3818 uData = list->users[curr-1];
3819 list->table.contents[curr++][0] = " ";
3820 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3823 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3825 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3826 temp = list->table.contents[--start];
3827 list->table.contents[start] = list->table.contents[0];
3828 tmp_table.contents = list->table.contents + start;
3829 tmp_table.length = curr - start;
3830 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3831 list->table.contents[start] = temp;
3833 highest = lowest - 1;
3834 lowest = (highest < 100) ? 0 : (highest - 99);
3840 def_list(struct listData *list)
3844 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3846 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3847 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3848 if(list->table.length == 1)
3850 msg = user_find_message(list->user, "MSG_NONE");
3851 send_message_type(4, list->user, list->bot, " %s", msg);
3856 userData_access_comp(const void *arg_a, const void *arg_b)
3858 const struct userData *a = *(struct userData**)arg_a;
3859 const struct userData *b = *(struct userData**)arg_b;
3861 if(a->access != b->access)
3862 res = b->access - a->access;
3864 res = irccasecmp(a->handle->handle, b->handle->handle);
3869 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3871 void (*send_list)(struct listData *);
3872 struct userData *uData;
3873 struct listData lData;
3874 unsigned int matches;
3878 lData.bot = cmd->parent->bot;
3879 lData.channel = channel;
3880 lData.lowest = lowest;
3881 lData.highest = highest;
3882 lData.search = (argc > 1) ? argv[1] : NULL;
3883 send_list = def_list;
3884 (void)zoot_list; /* since it doesn't show user levels */
3886 if(user->handle_info)
3888 switch(user->handle_info->userlist_style)
3890 case HI_STYLE_DEF: send_list = def_list; break;
3891 case HI_STYLE_ZOOT: send_list = def_list; break;
3895 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3897 for(uData = channel->channel_info->users; uData; uData = uData->next)
3899 if((uData->access < lowest)
3900 || (uData->access > highest)
3901 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3903 lData.users[matches++] = uData;
3905 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3907 lData.table.length = matches+1;
3908 lData.table.width = 4;
3909 lData.table.flags = TABLE_NO_FREE;
3910 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3911 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3912 lData.table.contents[0] = ary;
3915 ary[2] = "Last Seen";
3917 for(matches = 1; matches < lData.table.length; ++matches)
3919 struct userData *uData = lData.users[matches-1];
3920 char seen[INTERVALLEN];
3922 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3923 lData.table.contents[matches] = ary;
3924 ary[0] = strtab(uData->access);
3925 ary[1] = uData->handle->handle;
3928 else if(!uData->seen)
3931 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3932 ary[2] = strdup(ary[2]);
3933 if(IsUserSuspended(uData))
3934 ary[3] = "Suspended";
3935 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3936 ary[3] = "Vacation";
3941 for(matches = 1; matches < lData.table.length; ++matches)
3943 free((char*)lData.table.contents[matches][2]);
3944 free(lData.table.contents[matches]);
3946 free(lData.table.contents[0]);
3947 free(lData.table.contents);
3951 static CHANSERV_FUNC(cmd_users)
3953 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3956 static CHANSERV_FUNC(cmd_wlist)
3958 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3961 static CHANSERV_FUNC(cmd_clist)
3963 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3966 static CHANSERV_FUNC(cmd_mlist)
3968 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3971 static CHANSERV_FUNC(cmd_olist)
3973 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3976 static CHANSERV_FUNC(cmd_plist)
3978 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3981 static CHANSERV_FUNC(cmd_bans)
3983 struct userNode *search_u = NULL;
3984 struct helpfile_table tbl;
3985 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3986 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3987 const char *msg_never, *triggered, *expires;
3988 struct banData *ban, **bans;
3992 else if(strchr(search = argv[1], '!'))
3995 search_wilds = search[strcspn(search, "?*")];
3997 else if(!(search_u = GetUserH(search)))
3998 reply("MSG_NICK_UNKNOWN", search);
4000 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4002 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4006 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4011 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4014 bans[matches++] = ban;
4019 tbl.length = matches + 1;
4020 tbl.width = 4 + timed;
4022 tbl.flags = TABLE_NO_FREE;
4023 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4024 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4025 tbl.contents[0][0] = "Mask";
4026 tbl.contents[0][1] = "Set By";
4027 tbl.contents[0][2] = "Triggered";
4030 tbl.contents[0][3] = "Expires";
4031 tbl.contents[0][4] = "Reason";
4034 tbl.contents[0][3] = "Reason";
4037 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4039 free(tbl.contents[0]);
4044 msg_never = user_find_message(user, "MSG_NEVER");
4045 for(ii = 0; ii < matches; )
4051 else if(ban->expires)
4052 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4054 expires = msg_never;
4057 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4059 triggered = msg_never;
4061 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4062 tbl.contents[ii][0] = ban->mask;
4063 tbl.contents[ii][1] = ban->owner;
4064 tbl.contents[ii][2] = strdup(triggered);
4067 tbl.contents[ii][3] = strdup(expires);
4068 tbl.contents[ii][4] = ban->reason;
4071 tbl.contents[ii][3] = ban->reason;
4073 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4074 reply("MSG_MATCH_COUNT", matches);
4075 for(ii = 1; ii < tbl.length; ++ii)
4077 free((char*)tbl.contents[ii][2]);
4079 free((char*)tbl.contents[ii][3]);
4080 free(tbl.contents[ii]);
4082 free(tbl.contents[0]);
4088 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4090 struct chanData *cData = channel->channel_info;
4091 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4093 if(cData->topic_mask)
4094 return !match_ircglob(new_topic, cData->topic_mask);
4095 else if(cData->topic)
4096 return irccasecmp(new_topic, cData->topic);
4101 static CHANSERV_FUNC(cmd_topic)
4103 struct chanData *cData;
4106 cData = channel->channel_info;
4111 SetChannelTopic(channel, chanserv, cData->topic, 1);
4112 reply("CSMSG_TOPIC_SET", cData->topic);
4116 reply("CSMSG_NO_TOPIC", channel->name);
4120 topic = unsplit_string(argv + 1, argc - 1, NULL);
4121 /* If they say "!topic *", use an empty topic. */
4122 if((topic[0] == '*') && (topic[1] == 0))
4124 if(bad_topic(channel, user, topic))
4126 char *topic_mask = cData->topic_mask;
4129 char new_topic[TOPICLEN+1], tchar;
4130 int pos=0, starpos=-1, dpos=0, len;
4132 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4139 len = strlen(topic);
4140 if((dpos + len) > TOPICLEN)
4141 len = TOPICLEN + 1 - dpos;
4142 memcpy(new_topic+dpos, topic, len);
4146 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4147 default: new_topic[dpos++] = tchar; break;
4150 if((dpos > TOPICLEN) || tchar)
4153 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4154 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4157 new_topic[dpos] = 0;
4158 SetChannelTopic(channel, chanserv, new_topic, 1);
4160 reply("CSMSG_TOPIC_LOCKED", channel->name);
4165 SetChannelTopic(channel, chanserv, topic, 1);
4167 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4169 /* Grab the topic and save it as the default topic. */
4171 cData->topic = strdup(channel->topic);
4177 static CHANSERV_FUNC(cmd_mode)
4179 struct userData *uData;
4180 struct mod_chanmode *change;
4185 change = &channel->channel_info->modes;
4186 if(change->modes_set || change->modes_clear) {
4187 modcmd_chanmode_announce(change);
4188 reply("CSMSG_DEFAULTED_MODES", channel->name);
4190 reply("CSMSG_NO_MODES", channel->name);
4194 uData = GetChannelUser(channel->channel_info, user->handle_info);
4196 base_oplevel = MAXOPLEVEL;
4197 else if (uData->access >= UL_OWNER)
4200 base_oplevel = 1 + UL_OWNER - uData->access;
4201 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4204 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4208 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4209 && mode_lock_violated(&channel->channel_info->modes, change))
4212 mod_chanmode_format(&channel->channel_info->modes, modes);
4213 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4217 modcmd_chanmode_announce(change);
4218 mod_chanmode_free(change);
4219 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4223 static CHANSERV_FUNC(cmd_invite)
4225 struct userData *uData;
4226 struct userNode *invite;
4228 uData = GetChannelUser(channel->channel_info, user->handle_info);
4232 if(!(invite = GetUserH(argv[1])))
4234 reply("MSG_NICK_UNKNOWN", argv[1]);
4241 if(GetUserMode(channel, invite))
4243 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4251 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4252 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4255 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4257 irc_invite(chanserv, invite, channel);
4259 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4264 static CHANSERV_FUNC(cmd_inviteme)
4266 if(GetUserMode(channel, user))
4268 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4271 if(channel->channel_info
4272 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4274 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4277 irc_invite(cmd->parent->bot, user, channel);
4282 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4285 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4287 /* We display things based on two dimensions:
4288 * - Issue time: present or absent
4289 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4290 * (in order of precedence, so something both expired and revoked
4291 * only counts as revoked)
4293 combo = (suspended->issued ? 4 : 0)
4294 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4296 case 0: /* no issue time, indefinite expiration */
4297 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4299 case 1: /* no issue time, expires in future */
4300 intervalString(buf1, suspended->expires-now, user->handle_info);
4301 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4303 case 2: /* no issue time, expired */
4304 intervalString(buf1, now-suspended->expires, user->handle_info);
4305 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4307 case 3: /* no issue time, revoked */
4308 intervalString(buf1, now-suspended->revoked, user->handle_info);
4309 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4311 case 4: /* issue time set, indefinite expiration */
4312 intervalString(buf1, now-suspended->issued, user->handle_info);
4313 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4315 case 5: /* issue time set, expires in future */
4316 intervalString(buf1, now-suspended->issued, user->handle_info);
4317 intervalString(buf2, suspended->expires-now, user->handle_info);
4318 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4320 case 6: /* issue time set, expired */
4321 intervalString(buf1, now-suspended->issued, user->handle_info);
4322 intervalString(buf2, now-suspended->expires, user->handle_info);
4323 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4325 case 7: /* issue time set, revoked */
4326 intervalString(buf1, now-suspended->issued, user->handle_info);
4327 intervalString(buf2, now-suspended->revoked, user->handle_info);
4328 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4331 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4336 static CHANSERV_FUNC(cmd_info)
4338 char modes[MAXLEN], buffer[INTERVALLEN];
4339 struct userData *uData, *owner;
4340 struct chanData *cData;
4341 struct do_not_register *dnr;
4346 cData = channel->channel_info;
4347 reply("CSMSG_CHANNEL_INFO", channel->name);
4349 uData = GetChannelUser(cData, user->handle_info);
4350 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4352 mod_chanmode_format(&cData->modes, modes);
4353 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4354 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4357 for(it = dict_first(cData->notes); it; it = iter_next(it))
4361 note = iter_data(it);
4362 if(!note_type_visible_to_user(cData, note->type, user))
4365 padding = PADLEN - 1 - strlen(iter_key(it));
4366 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4369 reply("CSMSG_CHANNEL_MAX", cData->max);
4370 for(owner = cData->users; owner; owner = owner->next)
4371 if(owner->access == UL_OWNER)
4372 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4373 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4374 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4375 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4377 privileged = IsStaff(user);
4379 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4380 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4381 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4383 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4384 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4386 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4388 struct suspended *suspended;
4389 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4390 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4391 show_suspension_info(cmd, user, suspended);
4393 else if(IsSuspended(cData))
4395 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4396 show_suspension_info(cmd, user, cData->suspended);
4401 static CHANSERV_FUNC(cmd_netinfo)
4403 extern time_t boot_time;
4404 extern unsigned long burst_length;
4405 char interval[INTERVALLEN];
4407 reply("CSMSG_NETWORK_INFO");
4408 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4409 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4410 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4411 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4412 reply("CSMSG_NETWORK_BANS", banCount);
4413 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4414 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4415 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4420 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4422 struct helpfile_table table;
4424 struct userNode *user;
4429 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4430 table.contents = alloca(list->used*sizeof(*table.contents));
4431 for(nn=0; nn<list->used; nn++)
4433 user = list->list[nn];
4434 if(user->modes & skip_flags)
4438 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4441 nick = alloca(strlen(user->nick)+3);
4442 sprintf(nick, "(%s)", user->nick);
4446 table.contents[table.length][0] = nick;
4449 table_send(chanserv, to->nick, 0, NULL, table);
4452 static CHANSERV_FUNC(cmd_ircops)
4454 reply("CSMSG_STAFF_OPERS");
4455 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4459 static CHANSERV_FUNC(cmd_helpers)
4461 reply("CSMSG_STAFF_HELPERS");
4462 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4466 static CHANSERV_FUNC(cmd_staff)
4468 reply("CSMSG_NETWORK_STAFF");
4469 cmd_ircops(CSFUNC_ARGS);
4470 cmd_helpers(CSFUNC_ARGS);
4474 static CHANSERV_FUNC(cmd_peek)
4476 struct modeNode *mn;
4477 char modes[MODELEN];
4479 struct helpfile_table table;
4481 irc_make_chanmode(channel, modes);
4483 reply("CSMSG_PEEK_INFO", channel->name);
4484 reply("CSMSG_PEEK_TOPIC", channel->topic);
4485 reply("CSMSG_PEEK_MODES", modes);
4486 reply("CSMSG_PEEK_USERS", channel->members.used);
4490 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4491 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4492 for(n = 0; n < channel->members.used; n++)
4494 mn = channel->members.list[n];
4495 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4497 table.contents[table.length] = alloca(sizeof(**table.contents));
4498 table.contents[table.length][0] = mn->user->nick;
4503 reply("CSMSG_PEEK_OPS");
4504 table_send(chanserv, user->nick, 0, NULL, table);
4507 reply("CSMSG_PEEK_NO_OPS");
4511 static MODCMD_FUNC(cmd_wipeinfo)
4513 struct handle_info *victim;
4514 struct userData *ud, *actor, *real_actor;
4515 unsigned int override = 0;
4518 actor = GetChannelUser(channel->channel_info, user->handle_info);
4519 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4520 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4522 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4524 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4527 if((ud->access >= actor->access) && (ud != actor))
4529 reply("MSG_USER_OUTRANKED", victim->handle);
4532 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4533 override = CMD_LOG_OVERRIDE;
4537 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4538 return 1 | override;
4541 static CHANSERV_FUNC(cmd_resync)
4543 struct mod_chanmode *changes;
4544 struct chanData *cData = channel->channel_info;
4545 unsigned int ii, used;
4547 changes = mod_chanmode_alloc(channel->members.used * 2);
4548 for(ii = used = 0; ii < channel->members.used; ++ii)
4550 struct modeNode *mn = channel->members.list[ii];
4551 struct userData *uData;
4553 if(IsService(mn->user))
4556 uData = GetChannelAccess(cData, mn->user->handle_info);
4557 if(!cData->lvlOpts[lvlGiveOps]
4558 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4560 if(!(mn->modes & MODE_CHANOP))
4562 changes->args[used].mode = MODE_CHANOP;
4563 changes->args[used++].u.member = mn;
4566 else if(!cData->lvlOpts[lvlGiveVoice]
4567 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4569 if(mn->modes & MODE_CHANOP)
4571 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4572 changes->args[used++].u.member = mn;
4574 if(!(mn->modes & MODE_VOICE))
4576 changes->args[used].mode = MODE_VOICE;
4577 changes->args[used++].u.member = mn;
4584 changes->args[used].mode = MODE_REMOVE | mn->modes;
4585 changes->args[used++].u.member = mn;
4589 changes->argc = used;
4590 modcmd_chanmode_announce(changes);
4591 mod_chanmode_free(changes);
4592 reply("CSMSG_RESYNCED_USERS", channel->name);
4596 static CHANSERV_FUNC(cmd_seen)
4598 struct userData *uData;
4599 struct handle_info *handle;
4600 char seen[INTERVALLEN];
4604 if(!irccasecmp(argv[1], chanserv->nick))
4606 reply("CSMSG_IS_CHANSERV");
4610 if(!(handle = get_handle_info(argv[1])))
4612 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4616 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4618 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4623 reply("CSMSG_USER_PRESENT", handle->handle);
4624 else if(uData->seen)
4625 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4627 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4629 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4630 reply("CSMSG_USER_VACATION", handle->handle);
4635 static MODCMD_FUNC(cmd_names)
4637 struct userNode *targ;
4638 struct userData *targData;
4639 unsigned int ii, pos;
4642 for(ii=pos=0; ii<channel->members.used; ++ii)
4644 targ = channel->members.list[ii]->user;
4645 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4648 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4651 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4655 if(IsUserSuspended(targData))
4657 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4660 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4661 reply("CSMSG_END_NAMES", channel->name);
4666 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4668 switch(ntype->visible_type)
4670 case NOTE_VIS_ALL: return 1;
4671 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4672 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4677 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4679 struct userData *uData;
4681 switch(ntype->set_access_type)
4683 case NOTE_SET_CHANNEL_ACCESS:
4684 if(!user->handle_info)
4686 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4688 return uData->access >= ntype->set_access.min_ulevel;
4689 case NOTE_SET_CHANNEL_SETTER:
4690 return check_user_level(channel, user, lvlSetters, 1, 0);
4691 case NOTE_SET_PRIVILEGED: default:
4692 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4696 static CHANSERV_FUNC(cmd_note)
4698 struct chanData *cData;
4700 struct note_type *ntype;
4702 cData = channel->channel_info;
4705 reply("CSMSG_NOT_REGISTERED", channel->name);
4709 /* If no arguments, show all visible notes for the channel. */
4715 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4717 note = iter_data(it);
4718 if(!note_type_visible_to_user(cData, note->type, user))
4721 reply("CSMSG_NOTELIST_HEADER", channel->name);
4722 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4725 reply("CSMSG_NOTELIST_END", channel->name);
4727 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4729 /* If one argument, show the named note. */
4732 if((note = dict_find(cData->notes, argv[1], NULL))
4733 && note_type_visible_to_user(cData, note->type, user))
4735 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4737 else if((ntype = dict_find(note_types, argv[1], NULL))
4738 && note_type_visible_to_user(NULL, ntype, user))
4740 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4745 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4749 /* Assume they're trying to set a note. */
4753 ntype = dict_find(note_types, argv[1], NULL);
4756 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4759 else if(note_type_settable_by_user(channel, ntype, user))
4761 note_text = unsplit_string(argv+2, argc-2, NULL);
4762 if((note = dict_find(cData->notes, argv[1], NULL)))
4763 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4764 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4765 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4767 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4769 /* The note is viewable to staff only, so return 0
4770 to keep the invocation from getting logged (or
4771 regular users can see it in !events). */
4777 reply("CSMSG_NO_ACCESS");
4784 static CHANSERV_FUNC(cmd_delnote)
4789 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4790 || !note_type_settable_by_user(channel, note->type, user))
4792 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4795 dict_remove(channel->channel_info->notes, note->type->name);
4796 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4800 static CHANSERV_FUNC(cmd_events)
4802 struct logSearch discrim;
4803 struct logReport report;
4804 unsigned int matches, limit;
4806 limit = (argc > 1) ? atoi(argv[1]) : 10;
4807 if(limit < 1 || limit > 200)
4810 memset(&discrim, 0, sizeof(discrim));
4811 discrim.masks.bot = chanserv;
4812 discrim.masks.channel_name = channel->name;
4814 discrim.masks.command = argv[2];
4815 discrim.limit = limit;
4816 discrim.max_time = INT_MAX;
4817 discrim.severities = 1 << LOG_COMMAND;
4818 report.reporter = chanserv;
4820 reply("CSMSG_EVENT_SEARCH_RESULTS");
4821 matches = log_entry_search(&discrim, log_report_entry, &report);
4823 reply("MSG_MATCH_COUNT", matches);
4825 reply("MSG_NO_MATCHES");
4829 static CHANSERV_FUNC(cmd_say)
4835 msg = unsplit_string(argv + 1, argc - 1, NULL);
4836 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4838 else if(GetUserH(argv[1]))
4841 msg = unsplit_string(argv + 2, argc - 2, NULL);
4842 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4846 reply("MSG_NOT_TARGET_NAME");
4852 static CHANSERV_FUNC(cmd_emote)
4858 /* CTCP is so annoying. */
4859 msg = unsplit_string(argv + 1, argc - 1, NULL);
4860 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4862 else if(GetUserH(argv[1]))
4864 msg = unsplit_string(argv + 2, argc - 2, NULL);
4865 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4869 reply("MSG_NOT_TARGET_NAME");
4875 struct channelList *
4876 chanserv_support_channels(void)
4878 return &chanserv_conf.support_channels;
4881 static CHANSERV_FUNC(cmd_expire)
4883 int channel_count = registered_channels;
4884 expire_channels(NULL);
4885 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4890 chanserv_expire_suspension(void *data)
4892 struct suspended *suspended = data;
4893 struct chanNode *channel;
4895 if(!suspended->expires || (now < suspended->expires))
4896 suspended->revoked = now;
4897 channel = suspended->cData->channel;
4898 suspended->cData->channel = channel;
4899 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4900 if(!IsOffChannel(suspended->cData))
4902 struct mod_chanmode change;
4903 mod_chanmode_init(&change);
4905 change.args[0].mode = MODE_CHANOP;
4906 change.args[0].u.member = AddChannelUser(chanserv, channel);
4907 mod_chanmode_announce(chanserv, channel, &change);
4911 static CHANSERV_FUNC(cmd_csuspend)
4913 struct suspended *suspended;
4914 char reason[MAXLEN];
4915 time_t expiry, duration;
4916 struct userData *uData;
4920 if(IsProtected(channel->channel_info))
4922 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4926 if(argv[1][0] == '!')
4928 else if(IsSuspended(channel->channel_info))
4930 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4931 show_suspension_info(cmd, user, channel->channel_info->suspended);
4935 if(!strcmp(argv[1], "0"))
4937 else if((duration = ParseInterval(argv[1])))
4938 expiry = now + duration;
4941 reply("MSG_INVALID_DURATION", argv[1]);
4945 unsplit_string(argv + 2, argc - 2, reason);
4947 suspended = calloc(1, sizeof(*suspended));
4948 suspended->revoked = 0;
4949 suspended->issued = now;
4950 suspended->suspender = strdup(user->handle_info->handle);
4951 suspended->expires = expiry;
4952 suspended->reason = strdup(reason);
4953 suspended->cData = channel->channel_info;
4954 suspended->previous = suspended->cData->suspended;
4955 suspended->cData->suspended = suspended;
4957 if(suspended->expires)
4958 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4960 if(IsSuspended(channel->channel_info))
4962 suspended->previous->revoked = now;
4963 if(suspended->previous->expires)
4964 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4965 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4966 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4970 /* Mark all users in channel as absent. */
4971 for(uData = channel->channel_info->users; uData; uData = uData->next)
4980 /* Mark the channel as suspended, then part. */
4981 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4982 DelChannelUser(chanserv, channel, suspended->reason, 0);
4983 reply("CSMSG_SUSPENDED", channel->name);
4984 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4985 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4990 static CHANSERV_FUNC(cmd_cunsuspend)
4992 struct suspended *suspended;
4993 char message[MAXLEN];
4995 if(!IsSuspended(channel->channel_info))
4997 reply("CSMSG_NOT_SUSPENDED", channel->name);
5001 suspended = channel->channel_info->suspended;
5003 /* Expire the suspension and join ChanServ to the channel. */
5004 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5005 chanserv_expire_suspension(suspended);
5006 reply("CSMSG_UNSUSPENDED", channel->name);
5007 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5008 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5012 typedef struct chanservSearch
5020 unsigned long flags;
5024 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5027 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5032 search = malloc(sizeof(struct chanservSearch));
5033 memset(search, 0, sizeof(*search));
5036 for(i = 0; i < argc; i++)
5038 /* Assume all criteria require arguments. */
5041 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5045 if(!irccasecmp(argv[i], "name"))
5046 search->name = argv[++i];
5047 else if(!irccasecmp(argv[i], "registrar"))
5048 search->registrar = argv[++i];
5049 else if(!irccasecmp(argv[i], "unvisited"))
5050 search->unvisited = ParseInterval(argv[++i]);
5051 else if(!irccasecmp(argv[i], "registered"))
5052 search->registered = ParseInterval(argv[++i]);
5053 else if(!irccasecmp(argv[i], "flags"))
5056 if(!irccasecmp(argv[i], "nodelete"))
5057 search->flags |= CHANNEL_NODELETE;
5058 else if(!irccasecmp(argv[i], "suspended"))
5059 search->flags |= CHANNEL_SUSPENDED;
5060 else if(!irccasecmp(argv[i], "unreviewed"))
5061 search->flags |= CHANNEL_UNREVIEWED;
5064 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5068 else if(!irccasecmp(argv[i], "limit"))
5069 search->limit = strtoul(argv[++i], NULL, 10);
5072 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5077 if(search->name && !strcmp(search->name, "*"))
5079 if(search->registrar && !strcmp(search->registrar, "*"))
5080 search->registrar = 0;
5089 chanserv_channel_match(struct chanData *channel, search_t search)
5091 const char *name = channel->channel->name;
5092 if((search->name && !match_ircglob(name, search->name)) ||
5093 (search->registrar && !channel->registrar) ||
5094 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5095 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5096 (search->registered && (now - channel->registered) > search->registered) ||
5097 (search->flags && ((search->flags & channel->flags) != search->flags)))
5104 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5106 struct chanData *channel;
5107 unsigned int matches = 0;
5109 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5111 if(!chanserv_channel_match(channel, search))
5121 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5126 search_print(struct chanData *channel, void *data)
5128 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5131 static CHANSERV_FUNC(cmd_search)
5134 unsigned int matches;
5135 channel_search_func action;
5139 if(!irccasecmp(argv[1], "count"))
5140 action = search_count;
5141 else if(!irccasecmp(argv[1], "print"))
5142 action = search_print;
5145 reply("CSMSG_ACTION_INVALID", argv[1]);
5149 search = chanserv_search_create(user, argc - 2, argv + 2);
5153 if(action == search_count)
5154 search->limit = INT_MAX;
5156 if(action == search_print)
5157 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5159 matches = chanserv_channel_search(search, action, user);
5162 reply("MSG_MATCH_COUNT", matches);
5164 reply("MSG_NO_MATCHES");
5170 static CHANSERV_FUNC(cmd_unvisited)
5172 struct chanData *cData;
5173 time_t interval = chanserv_conf.channel_expire_delay;
5174 char buffer[INTERVALLEN];
5175 unsigned int limit = 25, matches = 0;
5179 interval = ParseInterval(argv[1]);
5181 limit = atoi(argv[2]);
5184 intervalString(buffer, interval, user->handle_info);
5185 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5187 for(cData = channelList; cData && matches < limit; cData = cData->next)
5189 if((now - cData->visited) < interval)
5192 intervalString(buffer, now - cData->visited, user->handle_info);
5193 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5200 static MODCMD_FUNC(chan_opt_defaulttopic)
5206 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5208 reply("CSMSG_TOPIC_LOCKED", channel->name);
5212 topic = unsplit_string(argv+1, argc-1, NULL);
5214 free(channel->channel_info->topic);
5215 if(topic[0] == '*' && topic[1] == 0)
5217 topic = channel->channel_info->topic = NULL;
5221 topic = channel->channel_info->topic = strdup(topic);
5222 if(channel->channel_info->topic_mask
5223 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5224 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5226 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5229 if(channel->channel_info->topic)
5230 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5232 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5236 static MODCMD_FUNC(chan_opt_topicmask)
5240 struct chanData *cData = channel->channel_info;
5243 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5245 reply("CSMSG_TOPIC_LOCKED", channel->name);
5249 mask = unsplit_string(argv+1, argc-1, NULL);
5251 if(cData->topic_mask)
5252 free(cData->topic_mask);
5253 if(mask[0] == '*' && mask[1] == 0)
5255 cData->topic_mask = 0;
5259 cData->topic_mask = strdup(mask);
5261 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5262 else if(!match_ircglob(cData->topic, cData->topic_mask))
5263 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5267 if(channel->channel_info->topic_mask)
5268 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5270 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5274 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5278 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5282 if(greeting[0] == '*' && greeting[1] == 0)
5286 unsigned int length = strlen(greeting);
5287 if(length > chanserv_conf.greeting_length)
5289 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5292 *data = strdup(greeting);
5301 reply(name, user_find_message(user, "MSG_NONE"));
5305 static MODCMD_FUNC(chan_opt_greeting)
5307 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5310 static MODCMD_FUNC(chan_opt_usergreeting)
5312 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5315 static MODCMD_FUNC(chan_opt_modes)
5317 struct mod_chanmode *new_modes;
5318 char modes[MODELEN];
5322 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5324 reply("CSMSG_NO_ACCESS");
5327 if(argv[1][0] == '*' && argv[1][1] == 0)
5329 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5331 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5333 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5336 else if(new_modes->argc > 1)
5338 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5339 mod_chanmode_free(new_modes);
5344 channel->channel_info->modes = *new_modes;
5345 modcmd_chanmode_announce(new_modes);
5346 mod_chanmode_free(new_modes);
5350 mod_chanmode_format(&channel->channel_info->modes, modes);
5352 reply("CSMSG_SET_MODES", modes);
5354 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5358 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5360 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5362 struct chanData *cData = channel->channel_info;
5367 /* Set flag according to value. */
5368 if(enabled_string(argv[1]))
5370 cData->flags |= mask;
5373 else if(disabled_string(argv[1]))
5375 cData->flags &= ~mask;
5380 reply("MSG_INVALID_BINARY", argv[1]);
5386 /* Find current option value. */
5387 value = (cData->flags & mask) ? 1 : 0;
5391 reply(name, user_find_message(user, "MSG_ON"));
5393 reply(name, user_find_message(user, "MSG_OFF"));
5397 static MODCMD_FUNC(chan_opt_nodelete)
5399 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5401 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5405 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5408 static MODCMD_FUNC(chan_opt_dynlimit)
5410 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5413 static MODCMD_FUNC(chan_opt_offchannel)
5415 struct chanData *cData = channel->channel_info;
5420 /* Set flag according to value. */
5421 if(enabled_string(argv[1]))
5423 if(!IsOffChannel(cData))
5424 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5425 cData->flags |= CHANNEL_OFFCHANNEL;
5428 else if(disabled_string(argv[1]))
5430 if(IsOffChannel(cData))
5432 struct mod_chanmode change;
5433 mod_chanmode_init(&change);
5435 change.args[0].mode = MODE_CHANOP;
5436 change.args[0].u.member = AddChannelUser(chanserv, channel);
5437 mod_chanmode_announce(chanserv, channel, &change);
5439 cData->flags &= ~CHANNEL_OFFCHANNEL;
5444 reply("MSG_INVALID_BINARY", argv[1]);
5450 /* Find current option value. */
5451 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5455 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5457 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5461 static MODCMD_FUNC(chan_opt_unreviewed)
5463 struct chanData *cData = channel->channel_info;
5464 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5470 /* The two directions can have different ACLs. */
5471 if(enabled_string(argv[1]))
5473 else if(disabled_string(argv[1]))
5477 reply("MSG_INVALID_BINARY", argv[1]);
5481 if (new_value != value)
5483 struct svccmd *subcmd;
5484 char subcmd_name[32];
5486 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5487 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5490 reply("MSG_COMMAND_DISABLED", subcmd_name);
5493 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5497 cData->flags |= CHANNEL_UNREVIEWED;
5500 free(cData->registrar);
5501 cData->registrar = strdup(user->handle_info->handle);
5502 cData->flags &= ~CHANNEL_UNREVIEWED;
5509 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5511 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5515 static MODCMD_FUNC(chan_opt_defaults)
5517 struct userData *uData;
5518 struct chanData *cData;
5519 const char *confirm;
5520 enum levelOption lvlOpt;
5521 enum charOption chOpt;
5523 cData = channel->channel_info;
5524 uData = GetChannelUser(cData, user->handle_info);
5525 if(!uData || (uData->access < UL_OWNER))
5527 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5530 confirm = make_confirmation_string(uData);
5531 if((argc < 2) || strcmp(argv[1], confirm))
5533 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5536 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5537 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5538 cData->modes = chanserv_conf.default_modes;
5539 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5540 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5541 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5542 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5543 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5548 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5550 struct chanData *cData = channel->channel_info;
5551 struct userData *uData;
5552 unsigned short value;
5556 if(!check_user_level(channel, user, option, 1, 1))
5558 reply("CSMSG_CANNOT_SET");
5561 value = user_level_from_name(argv[1], UL_OWNER+1);
5562 if(!value && strcmp(argv[1], "0"))
5564 reply("CSMSG_INVALID_ACCESS", argv[1]);
5567 uData = GetChannelUser(cData, user->handle_info);
5568 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5570 reply("CSMSG_BAD_SETLEVEL");
5576 if(value > cData->lvlOpts[lvlGiveOps])
5578 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5583 if(value < cData->lvlOpts[lvlGiveVoice])
5585 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5590 /* This test only applies to owners, since non-owners
5591 * trying to set an option to above their level get caught
5592 * by the CSMSG_BAD_SETLEVEL test above.
5594 if(value > uData->access)
5596 reply("CSMSG_BAD_SETTERS");
5603 cData->lvlOpts[option] = value;
5605 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5609 static MODCMD_FUNC(chan_opt_enfops)
5611 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5614 static MODCMD_FUNC(chan_opt_giveops)
5616 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5619 static MODCMD_FUNC(chan_opt_enfmodes)
5621 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5624 static MODCMD_FUNC(chan_opt_enftopic)
5626 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5629 static MODCMD_FUNC(chan_opt_pubcmd)
5631 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5634 static MODCMD_FUNC(chan_opt_setters)
5636 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5639 static MODCMD_FUNC(chan_opt_ctcpusers)
5641 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5644 static MODCMD_FUNC(chan_opt_userinfo)
5646 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5649 static MODCMD_FUNC(chan_opt_givevoice)
5651 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5654 static MODCMD_FUNC(chan_opt_topicsnarf)
5656 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5659 static MODCMD_FUNC(chan_opt_inviteme)
5661 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5665 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5667 struct chanData *cData = channel->channel_info;
5668 int count = charOptions[option].count, index;
5672 index = atoi(argv[1]);
5674 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5676 reply("CSMSG_INVALID_NUMERIC", index);
5677 /* Show possible values. */
5678 for(index = 0; index < count; index++)
5679 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5683 cData->chOpts[option] = charOptions[option].values[index].value;
5687 /* Find current option value. */
5690 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5694 /* Somehow, the option value is corrupt; reset it to the default. */
5695 cData->chOpts[option] = charOptions[option].default_value;
5700 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5704 static MODCMD_FUNC(chan_opt_protect)
5706 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5709 static MODCMD_FUNC(chan_opt_toys)
5711 return channel_multiple_option(chToys, CSFUNC_ARGS);
5714 static MODCMD_FUNC(chan_opt_ctcpreaction)
5716 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5719 static MODCMD_FUNC(chan_opt_topicrefresh)
5721 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5724 static struct svccmd_list set_shows_list;
5727 handle_svccmd_unbind(struct svccmd *target) {
5729 for(ii=0; ii<set_shows_list.used; ++ii)
5730 if(target == set_shows_list.list[ii])
5731 set_shows_list.used = 0;
5734 static CHANSERV_FUNC(cmd_set)
5736 struct svccmd *subcmd;
5740 /* Check if we need to (re-)initialize set_shows_list. */
5741 if(!set_shows_list.used)
5743 if(!set_shows_list.size)
5745 set_shows_list.size = chanserv_conf.set_shows->used;
5746 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5748 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5750 const char *name = chanserv_conf.set_shows->list[ii];
5751 sprintf(buf, "%s %s", argv[0], name);
5752 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5755 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5758 svccmd_list_append(&set_shows_list, subcmd);
5764 reply("CSMSG_CHANNEL_OPTIONS");
5765 for(ii = 0; ii < set_shows_list.used; ii++)
5767 subcmd = set_shows_list.list[ii];
5768 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5773 sprintf(buf, "%s %s", argv[0], argv[1]);
5774 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5777 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5780 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5782 reply("CSMSG_NO_ACCESS");
5788 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5792 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5794 struct userData *uData;
5796 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5799 reply("CSMSG_NOT_USER", channel->name);
5805 /* Just show current option value. */
5807 else if(enabled_string(argv[1]))
5809 uData->flags |= mask;
5811 else if(disabled_string(argv[1]))
5813 uData->flags &= ~mask;
5817 reply("MSG_INVALID_BINARY", argv[1]);
5821 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5825 static MODCMD_FUNC(user_opt_noautoop)
5827 struct userData *uData;
5829 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5832 reply("CSMSG_NOT_USER", channel->name);
5835 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5836 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5838 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5841 static MODCMD_FUNC(user_opt_autoinvite)
5843 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5846 static MODCMD_FUNC(user_opt_info)
5848 struct userData *uData;
5851 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5855 /* If they got past the command restrictions (which require access)
5856 * but fail this test, we have some fool with security override on.
5858 reply("CSMSG_NOT_USER", channel->name);
5865 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5866 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5868 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5871 bp = strcspn(infoline, "\001");
5874 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5879 if(infoline[0] == '*' && infoline[1] == 0)
5882 uData->info = strdup(infoline);
5885 reply("CSMSG_USET_INFO", uData->info);
5887 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5891 struct svccmd_list uset_shows_list;
5893 static CHANSERV_FUNC(cmd_uset)
5895 struct svccmd *subcmd;
5899 /* Check if we need to (re-)initialize uset_shows_list. */
5900 if(!uset_shows_list.used)
5904 "NoAutoOp", "AutoInvite", "Info"
5907 if(!uset_shows_list.size)
5909 uset_shows_list.size = ArrayLength(options);
5910 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5912 for(ii = 0; ii < ArrayLength(options); ii++)
5914 const char *name = options[ii];
5915 sprintf(buf, "%s %s", argv[0], name);
5916 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5919 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5922 svccmd_list_append(&uset_shows_list, subcmd);
5928 /* Do this so options are presented in a consistent order. */
5929 reply("CSMSG_USER_OPTIONS");
5930 for(ii = 0; ii < uset_shows_list.used; ii++)
5931 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5935 sprintf(buf, "%s %s", argv[0], argv[1]);
5936 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5939 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5943 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5946 static CHANSERV_FUNC(cmd_giveownership)
5948 struct handle_info *new_owner_hi;
5949 struct userData *new_owner;
5950 struct userData *curr_user;
5951 struct userData *invoker;
5952 struct chanData *cData = channel->channel_info;
5953 struct do_not_register *dnr;
5954 const char *confirm;
5956 unsigned short co_access;
5957 char reason[MAXLEN];
5960 curr_user = GetChannelAccess(cData, user->handle_info);
5961 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5962 if(!curr_user || (curr_user->access != UL_OWNER))
5964 struct userData *owner = NULL;
5965 for(curr_user = channel->channel_info->users;
5967 curr_user = curr_user->next)
5969 if(curr_user->access != UL_OWNER)
5973 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5980 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5982 char delay[INTERVALLEN];
5983 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5984 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5987 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5989 if(new_owner_hi == user->handle_info)
5991 reply("CSMSG_NO_TRANSFER_SELF");
5994 new_owner = GetChannelAccess(cData, new_owner_hi);
5999 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6003 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6007 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6009 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6012 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6013 if(!IsHelping(user))
6014 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6016 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6019 invoker = GetChannelUser(cData, user->handle_info);
6020 if(invoker->access <= UL_OWNER)
6022 confirm = make_confirmation_string(curr_user);
6023 if((argc < 3) || strcmp(argv[2], confirm))
6025 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6029 if(new_owner->access >= UL_COOWNER)
6030 co_access = new_owner->access;
6032 co_access = UL_COOWNER;
6033 new_owner->access = UL_OWNER;
6035 curr_user->access = co_access;
6036 cData->ownerTransfer = now;
6037 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6038 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6039 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6043 static CHANSERV_FUNC(cmd_suspend)
6045 struct handle_info *hi;
6046 struct userData *self, *real_self, *target;
6047 unsigned int override = 0;
6050 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6051 self = GetChannelUser(channel->channel_info, user->handle_info);
6052 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6053 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6055 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6058 if(target->access >= self->access)
6060 reply("MSG_USER_OUTRANKED", hi->handle);
6063 if(target->flags & USER_SUSPENDED)
6065 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6070 target->present = 0;
6073 if(!real_self || target->access >= real_self->access)
6074 override = CMD_LOG_OVERRIDE;
6075 target->flags |= USER_SUSPENDED;
6076 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6077 return 1 | override;
6080 static CHANSERV_FUNC(cmd_unsuspend)
6082 struct handle_info *hi;
6083 struct userData *self, *real_self, *target;
6084 unsigned int override = 0;
6087 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6088 self = GetChannelUser(channel->channel_info, user->handle_info);
6089 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6090 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6092 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6095 if(target->access >= self->access)
6097 reply("MSG_USER_OUTRANKED", hi->handle);
6100 if(!(target->flags & USER_SUSPENDED))
6102 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6105 if(!real_self || target->access >= real_self->access)
6106 override = CMD_LOG_OVERRIDE;
6107 target->flags &= ~USER_SUSPENDED;
6108 scan_user_presence(target, NULL);
6109 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6110 return 1 | override;
6113 static MODCMD_FUNC(cmd_deleteme)
6115 struct handle_info *hi;
6116 struct userData *target;
6117 const char *confirm_string;
6118 unsigned short access;
6121 hi = user->handle_info;
6122 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6124 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6127 if(target->access == UL_OWNER)
6129 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6132 confirm_string = make_confirmation_string(target);
6133 if((argc < 2) || strcmp(argv[1], confirm_string))
6135 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6138 access = target->access;
6139 channel_name = strdup(channel->name);
6140 del_channel_user(target, 1);
6141 reply("CSMSG_DELETED_YOU", access, channel_name);
6147 chanserv_refresh_topics(UNUSED_ARG(void *data))
6149 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6150 struct chanData *cData;
6153 for(cData = channelList; cData; cData = cData->next)
6155 if(IsSuspended(cData))
6157 opt = cData->chOpts[chTopicRefresh];
6160 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6163 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6164 cData->last_refresh = refresh_num;
6166 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6169 static CHANSERV_FUNC(cmd_unf)
6173 char response[MAXLEN];
6174 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6175 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6176 irc_privmsg(cmd->parent->bot, channel->name, response);
6179 reply("CSMSG_UNF_RESPONSE");
6183 static CHANSERV_FUNC(cmd_ping)
6187 char response[MAXLEN];
6188 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6189 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6190 irc_privmsg(cmd->parent->bot, channel->name, response);
6193 reply("CSMSG_PING_RESPONSE");
6197 static CHANSERV_FUNC(cmd_wut)
6201 char response[MAXLEN];
6202 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6203 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6204 irc_privmsg(cmd->parent->bot, channel->name, response);
6207 reply("CSMSG_WUT_RESPONSE");
6211 static CHANSERV_FUNC(cmd_8ball)
6213 unsigned int i, j, accum;
6218 for(i=1; i<argc; i++)
6219 for(j=0; argv[i][j]; j++)
6220 accum = (accum << 5) - accum + toupper(argv[i][j]);
6221 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6224 char response[MAXLEN];
6225 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6226 irc_privmsg(cmd->parent->bot, channel->name, response);
6229 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6233 static CHANSERV_FUNC(cmd_d)
6235 unsigned long sides, count, modifier, ii, total;
6236 char response[MAXLEN], *sep;
6240 if((count = strtoul(argv[1], &sep, 10)) < 1)
6250 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6251 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6255 else if((sep[0] == '-') && isdigit(sep[1]))
6256 modifier = strtoul(sep, NULL, 10);
6257 else if((sep[0] == '+') && isdigit(sep[1]))
6258 modifier = strtoul(sep+1, NULL, 10);
6265 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6270 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6273 for(total = ii = 0; ii < count; ++ii)
6274 total += (rand() % sides) + 1;
6277 if((count > 1) || modifier)
6279 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6280 sprintf(response, fmt, total, count, sides, modifier);
6284 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6285 sprintf(response, fmt, total, sides);
6288 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6290 send_message_type(4, user, cmd->parent->bot, "%s", response);
6294 static CHANSERV_FUNC(cmd_huggle)
6296 /* CTCP must be via PRIVMSG, never notice */
6298 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6300 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6305 chanserv_adjust_limit(void *data)
6307 struct mod_chanmode change;
6308 struct chanData *cData = data;
6309 struct chanNode *channel = cData->channel;
6312 if(IsSuspended(cData))
6315 cData->limitAdjusted = now;
6316 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6317 if(cData->modes.modes_set & MODE_LIMIT)
6319 if(limit > cData->modes.new_limit)
6320 limit = cData->modes.new_limit;
6321 else if(limit == cData->modes.new_limit)
6325 mod_chanmode_init(&change);
6326 change.modes_set = MODE_LIMIT;
6327 change.new_limit = limit;
6328 mod_chanmode_announce(chanserv, channel, &change);
6332 handle_new_channel(struct chanNode *channel)
6334 struct chanData *cData;
6336 if(!(cData = channel->channel_info))
6339 if(cData->modes.modes_set || cData->modes.modes_clear)
6340 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6342 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6343 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6346 /* Welcome to my worst nightmare. Warning: Read (or modify)
6347 the code below at your own risk. */
6349 handle_join(struct modeNode *mNode)
6351 struct mod_chanmode change;
6352 struct userNode *user = mNode->user;
6353 struct chanNode *channel = mNode->channel;
6354 struct chanData *cData;
6355 struct userData *uData = NULL;
6356 struct banData *bData;
6357 struct handle_info *handle;
6358 unsigned int modes = 0, info = 0;
6361 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6364 cData = channel->channel_info;
6365 if(channel->members.used > cData->max)
6366 cData->max = channel->members.used;
6368 /* Check for bans. If they're joining through a ban, one of two
6370 * 1: Join during a netburst, by riding the break. Kick them
6371 * unless they have ops or voice in the channel.
6372 * 2: They're allowed to join through the ban (an invite in
6373 * ircu2.10, or a +e on Hybrid, or something).
6374 * If they're not joining through a ban, and the banlist is not
6375 * full, see if they're on the banlist for the channel. If so,
6378 if(user->uplink->burst && !mNode->modes)
6381 for(ii = 0; ii < channel->banlist.used; ii++)
6383 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6385 /* Riding a netburst. Naughty. */
6386 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6392 mod_chanmode_init(&change);
6394 if(channel->banlist.used < MAXBANS)
6396 /* Not joining through a ban. */
6397 for(bData = cData->bans;
6398 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6399 bData = bData->next);
6403 char kick_reason[MAXLEN];
6404 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6406 bData->triggered = now;
6407 if(bData != cData->bans)
6409 /* Shuffle the ban to the head of the list. */
6411 bData->next->prev = bData->prev;
6413 bData->prev->next = bData->next;
6416 bData->next = cData->bans;
6419 cData->bans->prev = bData;
6420 cData->bans = bData;
6423 change.args[0].mode = MODE_BAN;
6424 change.args[0].u.hostmask = bData->mask;
6425 mod_chanmode_announce(chanserv, channel, &change);
6426 KickChannelUser(user, channel, chanserv, kick_reason);
6431 /* ChanServ will not modify the limits in join-flooded channels.
6432 It will also skip DynLimit processing when the user (or srvx)
6433 is bursting in, because there are likely more incoming. */
6434 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6435 && !user->uplink->burst
6436 && !channel->join_flooded
6437 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6439 /* The user count has begun "bumping" into the channel limit,
6440 so set a timer to raise the limit a bit. Any previous
6441 timers are removed so three incoming users within the delay
6442 results in one limit change, not three. */
6444 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6445 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6448 if(channel->join_flooded)
6450 /* don't automatically give ops or voice during a join flood */
6452 else if(cData->lvlOpts[lvlGiveOps] == 0)
6453 modes |= MODE_CHANOP;
6454 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6455 modes |= MODE_VOICE;
6457 greeting = cData->greeting;
6458 if(user->handle_info)
6460 handle = user->handle_info;
6462 if(IsHelper(user) && !IsHelping(user))
6465 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6467 if(channel == chanserv_conf.support_channels.list[ii])
6469 HANDLE_SET_FLAG(user->handle_info, HELPING);
6475 uData = GetTrueChannelAccess(cData, handle);
6476 if(uData && !IsUserSuspended(uData))
6478 /* Ops and above were handled by the above case. */
6479 if(IsUserAutoOp(uData))
6481 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6482 modes |= MODE_CHANOP;
6483 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6484 modes |= MODE_VOICE;
6486 if(uData->access >= UL_PRESENT)
6487 cData->visited = now;
6488 if(cData->user_greeting)
6489 greeting = cData->user_greeting;
6491 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6492 && ((now - uData->seen) >= chanserv_conf.info_delay)
6500 /* If user joining normally (not during burst), apply op or voice,
6501 * and send greeting/userinfo as appropriate.
6503 if(!user->uplink->burst)
6507 if(modes & MODE_CHANOP)
6508 modes &= ~MODE_VOICE;
6509 change.args[0].mode = modes;
6510 change.args[0].u.member = mNode;
6511 mod_chanmode_announce(chanserv, channel, &change);
6514 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6516 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6522 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6524 struct mod_chanmode change;
6525 struct userData *channel;
6526 unsigned int ii, jj;
6528 if(!user->handle_info)
6531 mod_chanmode_init(&change);
6533 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6535 struct chanNode *cn;
6536 struct modeNode *mn;
6537 if(IsUserSuspended(channel)
6538 || IsSuspended(channel->channel)
6539 || !(cn = channel->channel->channel))
6542 mn = GetUserMode(cn, user);
6545 if(!IsUserSuspended(channel)
6546 && IsUserAutoInvite(channel)
6547 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6549 && !user->uplink->burst)
6550 irc_invite(chanserv, user, cn);
6554 if(channel->access >= UL_PRESENT)
6555 channel->channel->visited = now;
6557 if(IsUserAutoOp(channel))
6559 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6560 change.args[0].mode = MODE_CHANOP;
6561 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6562 change.args[0].mode = MODE_VOICE;
6564 change.args[0].mode = 0;
6565 change.args[0].u.member = mn;
6566 if(change.args[0].mode)
6567 mod_chanmode_announce(chanserv, cn, &change);
6570 channel->seen = now;
6571 channel->present = 1;
6574 for(ii = 0; ii < user->channels.used; ++ii)
6576 struct chanNode *channel = user->channels.list[ii]->channel;
6577 struct banData *ban;
6579 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6580 || !channel->channel_info
6581 || IsSuspended(channel->channel_info))
6583 for(jj = 0; jj < channel->banlist.used; ++jj)
6584 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6586 if(jj < channel->banlist.used)
6588 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6590 char kick_reason[MAXLEN];
6591 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6593 change.args[0].mode = MODE_BAN;
6594 change.args[0].u.hostmask = ban->mask;
6595 mod_chanmode_announce(chanserv, channel, &change);
6596 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6597 KickChannelUser(user, channel, chanserv, kick_reason);
6598 ban->triggered = now;
6603 if(IsSupportHelper(user))
6605 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6607 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6609 HANDLE_SET_FLAG(user->handle_info, HELPING);
6617 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6619 struct chanData *cData;
6620 struct userData *uData;
6622 cData = mn->channel->channel_info;
6623 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6626 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6628 /* Allow for a bit of padding so that the limit doesn't
6629 track the user count exactly, which could get annoying. */
6630 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6632 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6633 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6637 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6639 scan_user_presence(uData, mn->user);
6641 if (uData->access >= UL_PRESENT)
6642 cData->visited = now;
6645 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6647 unsigned int ii, jj;
6648 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6650 for(jj = 0; jj < mn->user->channels.used; ++jj)
6651 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6653 if(jj < mn->user->channels.used)
6656 if(ii == chanserv_conf.support_channels.used)
6657 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6662 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6664 struct userData *uData;
6666 if(!channel->channel_info || !kicker || IsService(kicker)
6667 || (kicker == victim) || IsSuspended(channel->channel_info)
6668 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6671 if(protect_user(victim, kicker, channel->channel_info))
6673 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6674 KickChannelUser(kicker, channel, chanserv, reason);
6677 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6682 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6684 struct chanData *cData;
6686 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6689 cData = channel->channel_info;
6690 if(bad_topic(channel, user, channel->topic))
6692 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6693 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6694 SetChannelTopic(channel, chanserv, old_topic, 1);
6695 else if(cData->topic)
6696 SetChannelTopic(channel, chanserv, cData->topic, 1);
6699 /* With topicsnarf, grab the topic and save it as the default topic. */
6700 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6703 cData->topic = strdup(channel->topic);
6709 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6711 struct mod_chanmode *bounce = NULL;
6712 unsigned int bnc, ii;
6715 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6718 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6719 && mode_lock_violated(&channel->channel_info->modes, change))
6721 char correct[MAXLEN];
6722 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6723 mod_chanmode_format(&channel->channel_info->modes, correct);
6724 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6726 for(ii = bnc = 0; ii < change->argc; ++ii)
6728 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6730 const struct userNode *victim = change->args[ii].u.member->user;
6731 if(!protect_user(victim, user, channel->channel_info))
6734 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6737 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6738 bounce->args[bnc].u.member = GetUserMode(channel, user);
6739 if(bounce->args[bnc].u.member)
6743 bounce->args[bnc].mode = MODE_CHANOP;
6744 bounce->args[bnc].u.member = change->args[ii].u.member;
6746 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6748 else if(change->args[ii].mode & MODE_CHANOP)
6750 const struct userNode *victim = change->args[ii].u.member->user;
6751 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6754 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6755 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6756 bounce->args[bnc].u.member = change->args[ii].u.member;
6759 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6761 const char *ban = change->args[ii].u.hostmask;
6762 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6765 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6766 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6767 bounce->args[bnc].u.hostmask = strdup(ban);
6769 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6774 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6775 mod_chanmode_announce(chanserv, channel, bounce);
6776 for(ii = 0; ii < change->argc; ++ii)
6777 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6778 free((char*)bounce->args[ii].u.hostmask);
6779 mod_chanmode_free(bounce);
6784 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6786 struct chanNode *channel;
6787 struct banData *bData;
6788 struct mod_chanmode change;
6789 unsigned int ii, jj;
6790 char kick_reason[MAXLEN];
6792 mod_chanmode_init(&change);
6794 change.args[0].mode = MODE_BAN;
6795 for(ii = 0; ii < user->channels.used; ++ii)
6797 channel = user->channels.list[ii]->channel;
6798 /* Need not check for bans if they're opped or voiced. */
6799 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6801 /* Need not check for bans unless channel registration is active. */
6802 if(!channel->channel_info || IsSuspended(channel->channel_info))
6804 /* Look for a matching ban already on the channel. */
6805 for(jj = 0; jj < channel->banlist.used; ++jj)
6806 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6808 /* Need not act if we found one. */
6809 if(jj < channel->banlist.used)
6811 /* Look for a matching ban in this channel. */
6812 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6814 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6816 change.args[0].u.hostmask = bData->mask;
6817 mod_chanmode_announce(chanserv, channel, &change);
6818 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6819 KickChannelUser(user, channel, chanserv, kick_reason);
6820 bData->triggered = now;
6821 break; /* we don't need to check any more bans in the channel */
6826 static void handle_rename(struct handle_info *handle, const char *old_handle)
6828 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6832 dict_remove2(handle_dnrs, old_handle, 1);
6833 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6834 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6839 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6841 struct userNode *h_user;
6843 if(handle->channels)
6845 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6846 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6848 while(handle->channels)
6849 del_channel_user(handle->channels, 1);
6854 handle_server_link(UNUSED_ARG(struct server *server))
6856 struct chanData *cData;
6858 for(cData = channelList; cData; cData = cData->next)
6860 if(!IsSuspended(cData))
6861 cData->may_opchan = 1;
6862 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6863 && !cData->channel->join_flooded
6864 && ((cData->channel->limit - cData->channel->members.used)
6865 < chanserv_conf.adjust_threshold))
6867 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6868 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6874 chanserv_conf_read(void)
6878 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6879 struct mod_chanmode *change;
6880 struct string_list *strlist;
6881 struct chanNode *chan;
6884 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6886 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6889 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6890 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6891 chanserv_conf.support_channels.used = 0;
6892 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6894 for(ii = 0; ii < strlist->used; ++ii)
6896 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6899 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6901 channelList_append(&chanserv_conf.support_channels, chan);
6904 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6907 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6910 chan = AddChannel(str, now, str2, NULL);
6912 channelList_append(&chanserv_conf.support_channels, chan);
6914 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6915 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6916 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6917 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6918 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6919 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6920 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6921 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6922 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6923 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6924 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6925 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6926 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6927 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6928 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6929 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6930 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6931 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6932 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6933 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6934 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6935 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6936 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6937 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6938 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6940 NickChange(chanserv, str, 0);
6941 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6942 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6943 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6944 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6945 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6946 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6947 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6948 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6949 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6950 chanserv_conf.max_owned = str ? atoi(str) : 5;
6951 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6952 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6953 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6954 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6955 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6956 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6957 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6960 safestrncpy(mode_line, str, sizeof(mode_line));
6961 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6962 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6963 && (change->argc < 2))
6965 chanserv_conf.default_modes = *change;
6966 mod_chanmode_free(change);
6968 free_string_list(chanserv_conf.set_shows);
6969 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6971 strlist = string_list_copy(strlist);
6974 static const char *list[] = {
6975 /* free form text */
6976 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6977 /* options based on user level */
6978 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6979 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6980 /* multiple choice options */
6981 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6982 /* binary options */
6983 "DynLimit", "NoDelete",
6988 strlist = alloc_string_list(ArrayLength(list)-1);
6989 for(ii=0; list[ii]; ii++)
6990 string_list_append(strlist, strdup(list[ii]));
6992 chanserv_conf.set_shows = strlist;
6993 /* We don't look things up now, in case the list refers to options
6994 * defined by modules initialized after this point. Just mark the
6995 * function list as invalid, so it will be initialized.
6997 set_shows_list.used = 0;
6998 free_string_list(chanserv_conf.eightball);
6999 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7002 strlist = string_list_copy(strlist);
7006 strlist = alloc_string_list(4);
7007 string_list_append(strlist, strdup("Yes."));
7008 string_list_append(strlist, strdup("No."));
7009 string_list_append(strlist, strdup("Maybe so."));
7011 chanserv_conf.eightball = strlist;
7012 free_string_list(chanserv_conf.old_ban_names);
7013 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7015 strlist = string_list_copy(strlist);
7017 strlist = alloc_string_list(2);
7018 chanserv_conf.old_ban_names = strlist;
7019 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7020 off_channel = str ? atoi(str) : 0;
7024 chanserv_note_type_read(const char *key, struct record_data *rd)
7027 struct note_type *ntype;
7030 if(!(obj = GET_RECORD_OBJECT(rd)))
7032 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7035 if(!(ntype = chanserv_create_note_type(key)))
7037 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7041 /* Figure out set access */
7042 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7044 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7045 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7047 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7049 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7050 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7052 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7054 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7058 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7059 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7060 ntype->set_access.min_opserv = 0;
7063 /* Figure out visibility */
7064 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7065 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7066 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7067 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7068 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7069 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7070 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7071 ntype->visible_type = NOTE_VIS_ALL;
7073 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7075 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7076 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7080 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7082 struct handle_info *handle;
7083 struct userData *uData;
7084 char *seen, *inf, *flags;
7086 unsigned short access;
7088 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7090 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7094 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7095 if(access > UL_OWNER)
7097 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7101 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7102 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7103 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7104 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7105 handle = get_handle_info(key);
7108 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7112 uData = add_channel_user(chan, handle, access, last_seen, inf);
7113 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7117 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7119 struct banData *bData;
7120 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7121 time_t set_time, triggered_time, expires_time;
7123 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7125 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7129 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7130 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7131 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7132 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7133 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7134 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7135 if (!reason || !owner)
7138 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7139 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7141 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7143 expires_time = set_time + atoi(s_duration);
7147 if(!reason || (expires_time && (expires_time < now)))
7150 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7153 static struct suspended *
7154 chanserv_read_suspended(dict_t obj)
7156 struct suspended *suspended = calloc(1, sizeof(*suspended));
7160 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7161 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7162 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7163 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7164 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7165 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7166 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7167 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7168 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7169 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7174 chanserv_channel_read(const char *key, struct record_data *hir)
7176 struct suspended *suspended;
7177 struct mod_chanmode *modes;
7178 struct chanNode *cNode;
7179 struct chanData *cData;
7180 struct dict *channel, *obj;
7181 char *str, *argv[10];
7185 channel = hir->d.object;
7187 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7190 cNode = AddChannel(key, now, NULL, NULL);
7193 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7196 cData = register_channel(cNode, str);
7199 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7203 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7205 enum levelOption lvlOpt;
7206 enum charOption chOpt;
7208 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7209 cData->flags = atoi(str);
7211 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7213 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7215 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7216 else if(levelOptions[lvlOpt].old_flag)
7218 if(cData->flags & levelOptions[lvlOpt].old_flag)
7219 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7221 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7225 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7227 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7229 cData->chOpts[chOpt] = str[0];
7232 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7234 enum levelOption lvlOpt;
7235 enum charOption chOpt;
7238 cData->flags = base64toint(str, 5);
7239 count = strlen(str += 5);
7240 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7243 if(levelOptions[lvlOpt].old_flag)
7245 if(cData->flags & levelOptions[lvlOpt].old_flag)
7246 lvl = levelOptions[lvlOpt].flag_value;
7248 lvl = levelOptions[lvlOpt].default_value;
7250 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7252 case 'c': lvl = UL_COOWNER; break;
7253 case 'm': lvl = UL_MASTER; break;
7254 case 'n': lvl = UL_OWNER+1; break;
7255 case 'o': lvl = UL_OP; break;
7256 case 'p': lvl = UL_PEON; break;
7257 case 'w': lvl = UL_OWNER; break;
7258 default: lvl = 0; break;
7260 cData->lvlOpts[lvlOpt] = lvl;
7262 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7263 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7266 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7268 suspended = chanserv_read_suspended(obj);
7269 cData->suspended = suspended;
7270 suspended->cData = cData;
7271 /* We could use suspended->expires and suspended->revoked to
7272 * set the CHANNEL_SUSPENDED flag, but we don't. */
7274 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7276 suspended = calloc(1, sizeof(*suspended));
7277 suspended->issued = 0;
7278 suspended->revoked = 0;
7279 suspended->suspender = strdup(str);
7280 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7281 suspended->expires = str ? atoi(str) : 0;
7282 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7283 suspended->reason = strdup(str ? str : "No reason");
7284 suspended->previous = NULL;
7285 cData->suspended = suspended;
7286 suspended->cData = cData;
7290 cData->flags &= ~CHANNEL_SUSPENDED;
7291 suspended = NULL; /* to squelch a warning */
7294 if(IsSuspended(cData)) {
7295 if(suspended->expires > now)
7296 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7297 else if(suspended->expires)
7298 cData->flags &= ~CHANNEL_SUSPENDED;
7301 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7302 struct mod_chanmode change;
7303 mod_chanmode_init(&change);
7305 change.args[0].mode = MODE_CHANOP;
7306 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7307 mod_chanmode_announce(chanserv, cNode, &change);
7310 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7311 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7312 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7313 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7314 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7315 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7316 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7317 cData->max = str ? atoi(str) : 0;
7318 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7319 cData->greeting = str ? strdup(str) : NULL;
7320 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7321 cData->user_greeting = str ? strdup(str) : NULL;
7322 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7323 cData->topic_mask = str ? strdup(str) : NULL;
7324 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7325 cData->topic = str ? strdup(str) : NULL;
7327 if(!IsSuspended(cData)
7328 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7329 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7330 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7331 cData->modes = *modes;
7333 cData->modes.modes_set |= MODE_REGISTERED;
7334 if(cData->modes.argc > 1)
7335 cData->modes.argc = 1;
7336 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7337 mod_chanmode_free(modes);
7340 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7341 for(it = dict_first(obj); it; it = iter_next(it))
7342 user_read_helper(iter_key(it), iter_data(it), cData);
7344 if(!cData->users && !IsProtected(cData))
7346 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7347 unregister_channel(cData, "has empty user list.");
7351 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7352 for(it = dict_first(obj); it; it = iter_next(it))
7353 ban_read_helper(iter_key(it), iter_data(it), cData);
7355 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7356 for(it = dict_first(obj); it; it = iter_next(it))
7358 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7359 struct record_data *rd = iter_data(it);
7360 const char *note, *setter;
7362 if(rd->type != RECDB_OBJECT)
7364 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7368 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7370 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7372 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7376 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7377 if(!setter) setter = "<unknown>";
7378 chanserv_add_channel_note(cData, ntype, setter, note);
7386 chanserv_dnr_read(const char *key, struct record_data *hir)
7388 const char *setter, *reason, *str;
7389 struct do_not_register *dnr;
7392 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7395 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7398 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7401 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7404 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7405 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7406 if(expiry && expiry <= now)
7408 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7411 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7413 dnr->set = atoi(str);
7419 chanserv_saxdb_read(struct dict *database)
7421 struct dict *section;
7424 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7425 for(it = dict_first(section); it; it = iter_next(it))
7426 chanserv_note_type_read(iter_key(it), iter_data(it));
7428 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7429 for(it = dict_first(section); it; it = iter_next(it))
7430 chanserv_channel_read(iter_key(it), iter_data(it));
7432 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7433 for(it = dict_first(section); it; it = iter_next(it))
7434 chanserv_dnr_read(iter_key(it), iter_data(it));
7440 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7442 int high_present = 0;
7443 saxdb_start_record(ctx, KEY_USERS, 1);
7444 for(; uData; uData = uData->next)
7446 if((uData->access >= UL_PRESENT) && uData->present)
7448 saxdb_start_record(ctx, uData->handle->handle, 0);
7449 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7450 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7452 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7454 saxdb_write_string(ctx, KEY_INFO, uData->info);
7455 saxdb_end_record(ctx);
7457 saxdb_end_record(ctx);
7458 return high_present;
7462 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7466 saxdb_start_record(ctx, KEY_BANS, 1);
7467 for(; bData; bData = bData->next)
7469 saxdb_start_record(ctx, bData->mask, 0);
7470 saxdb_write_int(ctx, KEY_SET, bData->set);
7471 if(bData->triggered)
7472 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7474 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7476 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7478 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7479 saxdb_end_record(ctx);
7481 saxdb_end_record(ctx);
7485 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7487 saxdb_start_record(ctx, name, 0);
7488 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7489 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7491 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7493 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7495 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7497 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7498 saxdb_end_record(ctx);
7502 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7506 enum levelOption lvlOpt;
7507 enum charOption chOpt;
7509 saxdb_start_record(ctx, channel->channel->name, 1);
7511 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7512 saxdb_write_int(ctx, KEY_MAX, channel->max);
7514 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7515 if(channel->registrar)
7516 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7517 if(channel->greeting)
7518 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7519 if(channel->user_greeting)
7520 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7521 if(channel->topic_mask)
7522 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7523 if(channel->suspended)
7524 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7526 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7527 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7528 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7529 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7530 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7532 buf[0] = channel->chOpts[chOpt];
7534 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7536 saxdb_end_record(ctx);
7538 if(channel->modes.modes_set || channel->modes.modes_clear)
7540 mod_chanmode_format(&channel->modes, buf);
7541 saxdb_write_string(ctx, KEY_MODES, buf);
7544 high_present = chanserv_write_users(ctx, channel->users);
7545 chanserv_write_bans(ctx, channel->bans);
7547 if(dict_size(channel->notes))
7551 saxdb_start_record(ctx, KEY_NOTES, 1);
7552 for(it = dict_first(channel->notes); it; it = iter_next(it))
7554 struct note *note = iter_data(it);
7555 saxdb_start_record(ctx, iter_key(it), 0);
7556 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7557 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7558 saxdb_end_record(ctx);
7560 saxdb_end_record(ctx);
7563 if(channel->ownerTransfer)
7564 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7565 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7566 saxdb_end_record(ctx);
7570 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7574 saxdb_start_record(ctx, ntype->name, 0);
7575 switch(ntype->set_access_type)
7577 case NOTE_SET_CHANNEL_ACCESS:
7578 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7580 case NOTE_SET_CHANNEL_SETTER:
7581 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7583 case NOTE_SET_PRIVILEGED: default:
7584 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7587 switch(ntype->visible_type)
7589 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7590 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7591 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7593 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7594 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7595 saxdb_end_record(ctx);
7599 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7601 struct do_not_register *dnr;
7602 dict_iterator_t it, next;
7604 for(it = dict_first(dnrs); it; it = next)
7606 next = iter_next(it);
7607 dnr = iter_data(it);
7608 if(dnr->expires && dnr->expires <= now)
7610 dict_remove(dnrs, iter_key(it));
7613 saxdb_start_record(ctx, dnr->chan_name, 0);
7615 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7617 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7618 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7619 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7620 saxdb_end_record(ctx);
7625 chanserv_saxdb_write(struct saxdb_context *ctx)
7628 struct chanData *channel;
7631 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7632 for(it = dict_first(note_types); it; it = iter_next(it))
7633 chanserv_write_note_type(ctx, iter_data(it));
7634 saxdb_end_record(ctx);
7637 saxdb_start_record(ctx, KEY_DNR, 1);
7638 write_dnrs_helper(ctx, handle_dnrs);
7639 write_dnrs_helper(ctx, plain_dnrs);
7640 write_dnrs_helper(ctx, mask_dnrs);
7641 saxdb_end_record(ctx);
7644 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7645 for(channel = channelList; channel; channel = channel->next)
7646 chanserv_write_channel(ctx, channel);
7647 saxdb_end_record(ctx);
7653 chanserv_db_cleanup(void) {
7655 unreg_part_func(handle_part);
7657 unregister_channel(channelList, "terminating.");
7658 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7659 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7660 free(chanserv_conf.support_channels.list);
7661 dict_delete(handle_dnrs);
7662 dict_delete(plain_dnrs);
7663 dict_delete(mask_dnrs);
7664 dict_delete(note_types);
7665 free_string_list(chanserv_conf.eightball);
7666 free_string_list(chanserv_conf.old_ban_names);
7667 free_string_list(chanserv_conf.set_shows);
7668 free(set_shows_list.list);
7669 free(uset_shows_list.list);
7672 struct userData *helper = helperList;
7673 helperList = helperList->next;
7678 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7679 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7680 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7683 init_chanserv(const char *nick)
7685 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7686 conf_register_reload(chanserv_conf_read);
7690 reg_server_link_func(handle_server_link);
7691 reg_new_channel_func(handle_new_channel);
7692 reg_join_func(handle_join);
7693 reg_part_func(handle_part);
7694 reg_kick_func(handle_kick);
7695 reg_topic_func(handle_topic);
7696 reg_mode_change_func(handle_mode);
7697 reg_nick_change_func(handle_nick_change);
7698 reg_auth_func(handle_auth);
7701 reg_handle_rename_func(handle_rename);
7702 reg_unreg_func(handle_unreg);
7704 handle_dnrs = dict_new();
7705 dict_set_free_data(handle_dnrs, free);
7706 plain_dnrs = dict_new();
7707 dict_set_free_data(plain_dnrs, free);
7708 mask_dnrs = dict_new();
7709 dict_set_free_data(mask_dnrs, free);
7711 reg_svccmd_unbind_func(handle_svccmd_unbind);
7712 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7713 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7714 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7715 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7716 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7717 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7718 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7719 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7720 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7721 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7722 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7723 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7724 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7726 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7727 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7729 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7730 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7731 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7732 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7733 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7735 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7736 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7737 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7738 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7739 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7741 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7742 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7743 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7744 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7746 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7747 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7748 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7749 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7750 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7751 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7752 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7753 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7755 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7756 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7757 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7758 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7759 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7760 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7761 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7762 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7763 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7764 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7765 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7766 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7767 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7768 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7770 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7771 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7772 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7773 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7774 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7776 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7777 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7779 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7780 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7781 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7782 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7783 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7784 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7785 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7786 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7787 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7788 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7789 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7791 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7792 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7794 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7795 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7796 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7797 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7799 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7800 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7801 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7802 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7803 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7805 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7806 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7807 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7808 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7809 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7810 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7812 /* Channel options */
7813 DEFINE_CHANNEL_OPTION(defaulttopic);
7814 DEFINE_CHANNEL_OPTION(topicmask);
7815 DEFINE_CHANNEL_OPTION(greeting);
7816 DEFINE_CHANNEL_OPTION(usergreeting);
7817 DEFINE_CHANNEL_OPTION(modes);
7818 DEFINE_CHANNEL_OPTION(enfops);
7819 DEFINE_CHANNEL_OPTION(giveops);
7820 DEFINE_CHANNEL_OPTION(protect);
7821 DEFINE_CHANNEL_OPTION(enfmodes);
7822 DEFINE_CHANNEL_OPTION(enftopic);
7823 DEFINE_CHANNEL_OPTION(pubcmd);
7824 DEFINE_CHANNEL_OPTION(givevoice);
7825 DEFINE_CHANNEL_OPTION(userinfo);
7826 DEFINE_CHANNEL_OPTION(dynlimit);
7827 DEFINE_CHANNEL_OPTION(topicsnarf);
7828 DEFINE_CHANNEL_OPTION(nodelete);
7829 DEFINE_CHANNEL_OPTION(toys);
7830 DEFINE_CHANNEL_OPTION(setters);
7831 DEFINE_CHANNEL_OPTION(topicrefresh);
7832 DEFINE_CHANNEL_OPTION(ctcpusers);
7833 DEFINE_CHANNEL_OPTION(ctcpreaction);
7834 DEFINE_CHANNEL_OPTION(inviteme);
7835 DEFINE_CHANNEL_OPTION(unreviewed);
7836 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7837 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7839 DEFINE_CHANNEL_OPTION(offchannel);
7840 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7842 /* Alias set topic to set defaulttopic for compatibility. */
7843 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7846 DEFINE_USER_OPTION(noautoop);
7847 DEFINE_USER_OPTION(autoinvite);
7848 DEFINE_USER_OPTION(info);
7850 /* Alias uset autovoice to uset autoop. */
7851 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7853 note_types = dict_new();
7854 dict_set_free_data(note_types, chanserv_deref_note_type);
7857 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7858 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7859 service_register(chanserv)->trigger = '!';
7860 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7862 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7864 if(chanserv_conf.channel_expire_frequency)
7865 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7867 if(chanserv_conf.dnr_expire_frequency)
7868 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7870 if(chanserv_conf.refresh_period)
7872 time_t next_refresh;
7873 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7874 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7877 reg_exit_func(chanserv_db_cleanup);
7878 message_register_table(msgtab);