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;
500 unsigned long info_delay;
501 unsigned long adjust_delay;
502 unsigned 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, unsigned long 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, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1186 unsigned int ii, l1, l2;
1191 bd = malloc(sizeof(struct banData));
1193 bd->channel = channel;
1195 bd->triggered = triggered;
1196 bd->expires = expires;
1198 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1200 extern const char *hidden_host_suffix;
1201 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1205 l2 = strlen(old_name);
1208 if(irccasecmp(mask + l1 - l2, old_name))
1210 new_mask = alloca(MAXLEN);
1211 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1214 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1216 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1217 bd->reason = strdup(reason);
1220 timeq_add(expires, expire_ban, bd);
1223 bd->next = channel->bans;
1225 channel->bans->prev = bd;
1227 channel->banCount++;
1234 del_channel_ban(struct banData *ban)
1236 ban->channel->banCount--;
1240 ban->prev->next = ban->next;
1242 ban->channel->bans = ban->next;
1245 ban->next->prev = ban->prev;
1248 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1257 expire_ban(void *data)
1259 struct banData *bd = data;
1260 if(!IsSuspended(bd->channel))
1262 struct banList bans;
1263 struct mod_chanmode change;
1265 bans = bd->channel->channel->banlist;
1266 mod_chanmode_init(&change);
1267 for(ii=0; ii<bans.used; ii++)
1269 if(!strcmp(bans.list[ii]->ban, bd->mask))
1272 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1273 change.args[0].u.hostmask = bd->mask;
1274 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1280 del_channel_ban(bd);
1283 static void chanserv_expire_suspension(void *data);
1286 unregister_channel(struct chanData *channel, const char *reason)
1288 struct mod_chanmode change;
1289 char msgbuf[MAXLEN];
1291 /* After channel unregistration, the following must be cleaned
1293 - Channel information.
1296 - Channel suspension data.
1297 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1303 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1307 mod_chanmode_init(&change);
1308 change.modes_clear |= MODE_REGISTERED;
1309 mod_chanmode_announce(chanserv, channel->channel, &change);
1312 while(channel->users)
1313 del_channel_user(channel->users, 0);
1315 while(channel->bans)
1316 del_channel_ban(channel->bans);
1318 free(channel->topic);
1319 free(channel->registrar);
1320 free(channel->greeting);
1321 free(channel->user_greeting);
1322 free(channel->topic_mask);
1325 channel->prev->next = channel->next;
1327 channelList = channel->next;
1330 channel->next->prev = channel->prev;
1332 if(channel->suspended)
1334 struct chanNode *cNode = channel->channel;
1335 struct suspended *suspended, *next_suspended;
1337 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1339 next_suspended = suspended->previous;
1340 free(suspended->suspender);
1341 free(suspended->reason);
1342 if(suspended->expires)
1343 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1348 cNode->channel_info = NULL;
1350 channel->channel->channel_info = NULL;
1352 dict_delete(channel->notes);
1353 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1354 if(!IsSuspended(channel))
1355 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1356 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1357 UnlockChannel(channel->channel);
1359 registered_channels--;
1363 expire_channels(UNUSED_ARG(void *data))
1365 struct chanData *channel, *next;
1366 struct userData *user;
1367 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1369 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1370 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1372 for(channel = channelList; channel; channel = next)
1374 next = channel->next;
1376 /* See if the channel can be expired. */
1377 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1378 || IsProtected(channel))
1381 /* Make sure there are no high-ranking users still in the channel. */
1382 for(user=channel->users; user; user=user->next)
1383 if(user->present && (user->access >= UL_PRESENT))
1388 /* Unregister the channel */
1389 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1390 unregister_channel(channel, "registration expired.");
1393 if(chanserv_conf.channel_expire_frequency)
1394 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1398 expire_dnrs(UNUSED_ARG(void *data))
1400 dict_iterator_t it, next;
1401 struct do_not_register *dnr;
1403 for(it = dict_first(handle_dnrs); it; it = next)
1405 dnr = iter_data(it);
1406 next = iter_next(it);
1407 if(dnr->expires && dnr->expires <= now)
1408 dict_remove(handle_dnrs, dnr->chan_name + 1);
1410 for(it = dict_first(plain_dnrs); it; it = next)
1412 dnr = iter_data(it);
1413 next = iter_next(it);
1414 if(dnr->expires && dnr->expires <= now)
1415 dict_remove(plain_dnrs, dnr->chan_name + 1);
1417 for(it = dict_first(mask_dnrs); it; it = next)
1419 dnr = iter_data(it);
1420 next = iter_next(it);
1421 if(dnr->expires && dnr->expires <= now)
1422 dict_remove(mask_dnrs, dnr->chan_name + 1);
1425 if(chanserv_conf.dnr_expire_frequency)
1426 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1430 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1432 char protect = channel->chOpts[chProtect];
1433 struct userData *cs_victim, *cs_aggressor;
1435 /* Don't protect if no one is to be protected, someone is attacking
1436 himself, or if the aggressor is an IRC Operator. */
1437 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1440 /* Don't protect if the victim isn't authenticated (because they
1441 can't be a channel user), unless we are to protect non-users
1443 cs_victim = GetChannelAccess(channel, victim->handle_info);
1444 if(protect != 'a' && !cs_victim)
1447 /* Protect if the aggressor isn't a user because at this point,
1448 the aggressor can only be less than or equal to the victim. */
1449 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1453 /* If the aggressor was a user, then the victim can't be helped. */
1460 if(cs_victim->access > cs_aggressor->access)
1465 if(cs_victim->access >= cs_aggressor->access)
1474 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1476 struct chanData *cData = channel->channel_info;
1477 struct userData *cs_victim;
1479 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1480 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1481 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1483 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1491 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1493 if(IsService(victim))
1495 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1499 if(protect_user(victim, user, channel->channel_info))
1501 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1508 static struct do_not_register *
1509 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long 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];
1580 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1585 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1586 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1590 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1593 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1598 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1600 struct dnrList list;
1603 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1604 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1605 dnr_print_func(list.list[ii], user);
1607 reply("CSMSG_MORE_DNRS", list.used - ii);
1612 struct do_not_register *
1613 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1615 struct dnrList list;
1616 struct do_not_register *dnr;
1618 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1619 dnr = list.used ? list.list[0] : NULL;
1624 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1626 struct do_not_register *dnr;
1627 dict_iterator_t it, next;
1628 unsigned int matches = 0;
1630 for(it = dict_first(dict); it; it = next)
1632 dnr = iter_data(it);
1633 next = iter_next(it);
1634 if(dnr->expires && dnr->expires <= now)
1636 dict_remove(dict, iter_key(it));
1639 dnr_print_func(dnr, user);
1646 static CHANSERV_FUNC(cmd_noregister)
1649 unsigned long expiry, duration;
1650 unsigned int matches;
1654 reply("CSMSG_DNR_SEARCH_RESULTS");
1655 matches = send_dnrs(user, handle_dnrs);
1656 matches += send_dnrs(user, plain_dnrs);
1657 matches += send_dnrs(user, mask_dnrs);
1659 reply("MSG_MATCH_COUNT", matches);
1661 reply("MSG_NO_MATCHES");
1667 if(!IsChannelName(target) && (*target != '*'))
1669 reply("CSMSG_NOT_DNR", target);
1677 reply("MSG_INVALID_DURATION", argv[2]);
1681 if(!strcmp(argv[2], "0"))
1683 else if((duration = ParseInterval(argv[2])))
1684 expiry = now + duration;
1687 reply("MSG_INVALID_DURATION", argv[2]);
1691 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1692 if((*target == '*') && !get_handle_info(target + 1))
1694 reply("MSG_HANDLE_UNKNOWN", target + 1);
1697 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1698 reply("CSMSG_NOREGISTER_CHANNEL", target);
1702 reply("CSMSG_DNR_SEARCH_RESULTS");
1704 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1706 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1708 reply("MSG_NO_MATCHES");
1712 static CHANSERV_FUNC(cmd_allowregister)
1714 const char *chan_name = argv[1];
1716 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1717 || dict_remove(plain_dnrs, chan_name)
1718 || dict_remove(mask_dnrs, chan_name))
1720 reply("CSMSG_DNR_REMOVED", chan_name);
1723 reply("CSMSG_NO_SUCH_DNR", chan_name);
1728 struct userNode *source;
1732 unsigned long min_set, max_set;
1733 unsigned long min_expires, max_expires;
1738 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1740 return !((dnr->set < search->min_set)
1741 || (dnr->set > search->max_set)
1742 || (dnr->expires < search->min_expires)
1743 || (search->max_expires
1744 && ((dnr->expires == 0)
1745 || (dnr->expires > search->max_expires)))
1746 || (search->chan_mask
1747 && !match_ircglob(dnr->chan_name, search->chan_mask))
1748 || (search->setter_mask
1749 && !match_ircglob(dnr->setter, search->setter_mask))
1750 || (search->reason_mask
1751 && !match_ircglob(dnr->reason, search->reason_mask)));
1754 static struct dnr_search *
1755 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1757 struct dnr_search *discrim;
1760 discrim = calloc(1, sizeof(*discrim));
1761 discrim->source = user;
1762 discrim->chan_mask = NULL;
1763 discrim->setter_mask = NULL;
1764 discrim->reason_mask = NULL;
1765 discrim->max_set = INT_MAX;
1766 discrim->limit = 50;
1768 for(ii=0; ii<argc; ++ii)
1772 reply("MSG_MISSING_PARAMS", argv[ii]);
1775 else if(0 == irccasecmp(argv[ii], "channel"))
1777 discrim->chan_mask = argv[++ii];
1779 else if(0 == irccasecmp(argv[ii], "setter"))
1781 discrim->setter_mask = argv[++ii];
1783 else if(0 == irccasecmp(argv[ii], "reason"))
1785 discrim->reason_mask = argv[++ii];
1787 else if(0 == irccasecmp(argv[ii], "limit"))
1789 discrim->limit = strtoul(argv[++ii], NULL, 0);
1791 else if(0 == irccasecmp(argv[ii], "set"))
1793 const char *cmp = argv[++ii];
1796 discrim->min_set = now - ParseInterval(cmp + 2);
1798 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1799 } else if(cmp[0] == '=') {
1800 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1801 } else if(cmp[0] == '>') {
1803 discrim->max_set = now - ParseInterval(cmp + 2);
1805 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1807 discrim->max_set = now - (ParseInterval(cmp) - 1);
1810 else if(0 == irccasecmp(argv[ii], "expires"))
1812 const char *cmp = argv[++ii];
1815 discrim->max_expires = now + ParseInterval(cmp + 2);
1817 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1818 } else if(cmp[0] == '=') {
1819 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1820 } else if(cmp[0] == '>') {
1822 discrim->min_expires = now + ParseInterval(cmp + 2);
1824 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1826 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1831 reply("MSG_INVALID_CRITERIA", argv[ii]);
1842 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1845 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1847 struct do_not_register *dnr;
1848 dict_iterator_t next;
1853 /* Initialize local variables. */
1856 if(discrim->chan_mask)
1858 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1859 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1863 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1865 /* Check against account-based DNRs. */
1866 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1867 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1870 else if(target_fixed)
1872 /* Check against channel-based DNRs. */
1873 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1874 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1879 /* Exhaustively search account DNRs. */
1880 for(it = dict_first(handle_dnrs); it; it = next)
1882 next = iter_next(it);
1883 dnr = iter_data(it);
1884 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1888 /* Do the same for channel DNRs. */
1889 for(it = dict_first(plain_dnrs); it; it = next)
1891 next = iter_next(it);
1892 dnr = iter_data(it);
1893 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1897 /* Do the same for wildcarded channel DNRs. */
1898 for(it = dict_first(mask_dnrs); it; it = next)
1900 next = iter_next(it);
1901 dnr = iter_data(it);
1902 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1910 dnr_remove_func(struct do_not_register *match, void *extra)
1912 struct userNode *user;
1915 chan_name = alloca(strlen(match->chan_name) + 1);
1916 strcpy(chan_name, match->chan_name);
1918 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1919 || dict_remove(plain_dnrs, chan_name)
1920 || dict_remove(mask_dnrs, chan_name))
1922 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1928 dnr_count_func(struct do_not_register *match, void *extra)
1930 return 0; (void)match; (void)extra;
1933 static MODCMD_FUNC(cmd_dnrsearch)
1935 struct dnr_search *discrim;
1936 dnr_search_func action;
1937 struct svccmd *subcmd;
1938 unsigned int matches;
1941 sprintf(buf, "dnrsearch %s", argv[1]);
1942 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1945 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1948 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1950 if(!irccasecmp(argv[1], "print"))
1951 action = dnr_print_func;
1952 else if(!irccasecmp(argv[1], "remove"))
1953 action = dnr_remove_func;
1954 else if(!irccasecmp(argv[1], "count"))
1955 action = dnr_count_func;
1958 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1962 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1966 if(action == dnr_print_func)
1967 reply("CSMSG_DNR_SEARCH_RESULTS");
1968 matches = dnr_search(discrim, action, user);
1970 reply("MSG_MATCH_COUNT", matches);
1972 reply("MSG_NO_MATCHES");
1978 chanserv_get_owned_count(struct handle_info *hi)
1980 struct userData *cList;
1983 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1984 if(cList->access == UL_OWNER)
1989 static CHANSERV_FUNC(cmd_register)
1991 struct handle_info *handle;
1992 struct chanData *cData;
1993 struct modeNode *mn;
1994 char reason[MAXLEN];
1996 unsigned int new_channel, force=0;
1997 struct do_not_register *dnr;
2001 if(channel->channel_info)
2003 reply("CSMSG_ALREADY_REGGED", channel->name);
2007 if(channel->bad_channel)
2009 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2014 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2016 reply("CSMSG_MUST_BE_OPPED", channel->name);
2021 chan_name = channel->name;
2025 if((argc < 2) || !IsChannelName(argv[1]))
2027 reply("MSG_NOT_CHANNEL_NAME");
2031 if(opserv_bad_channel(argv[1]))
2033 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2038 chan_name = argv[1];
2041 if(argc >= (new_channel+2))
2043 if(!IsHelping(user))
2045 reply("CSMSG_PROXY_FORBIDDEN");
2049 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2051 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2052 dnr = chanserv_is_dnr(chan_name, handle);
2056 handle = user->handle_info;
2057 dnr = chanserv_is_dnr(chan_name, handle);
2061 if(!IsHelping(user))
2062 reply("CSMSG_DNR_CHANNEL", chan_name);
2064 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2068 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2070 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2075 channel = AddChannel(argv[1], now, NULL, NULL);
2077 cData = register_channel(channel, user->handle_info->handle);
2078 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2079 cData->modes = chanserv_conf.default_modes;
2081 cData->modes.modes_set |= MODE_REGISTERED;
2082 if (IsOffChannel(cData))
2084 mod_chanmode_announce(chanserv, channel, &cData->modes);
2088 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2089 change->args[change->argc].mode = MODE_CHANOP;
2090 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2092 mod_chanmode_announce(chanserv, channel, change);
2093 mod_chanmode_free(change);
2096 /* Initialize the channel's max user record. */
2097 cData->max = channel->members.used;
2099 if(handle != user->handle_info)
2100 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2102 reply("CSMSG_REG_SUCCESS", channel->name);
2104 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2105 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2110 make_confirmation_string(struct userData *uData)
2112 static char strbuf[16];
2117 for(src = uData->handle->handle; *src; )
2118 accum = accum * 31 + toupper(*src++);
2120 for(src = uData->channel->channel->name; *src; )
2121 accum = accum * 31 + toupper(*src++);
2122 sprintf(strbuf, "%08x", accum);
2126 static CHANSERV_FUNC(cmd_unregister)
2129 char reason[MAXLEN];
2130 struct chanData *cData;
2131 struct userData *uData;
2133 cData = channel->channel_info;
2136 reply("CSMSG_NOT_REGISTERED", channel->name);
2140 uData = GetChannelUser(cData, user->handle_info);
2141 if(!uData || (uData->access < UL_OWNER))
2143 reply("CSMSG_NO_ACCESS");
2147 if(IsProtected(cData))
2149 reply("CSMSG_UNREG_NODELETE", channel->name);
2153 if(!IsHelping(user))
2155 const char *confirm_string;
2156 if(IsSuspended(cData))
2158 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2161 confirm_string = make_confirmation_string(uData);
2162 if((argc < 2) || strcmp(argv[1], confirm_string))
2164 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2169 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2170 name = strdup(channel->name);
2171 unregister_channel(cData, reason);
2172 reply("CSMSG_UNREG_SUCCESS", name);
2177 static CHANSERV_FUNC(cmd_move)
2179 struct mod_chanmode change;
2180 struct chanNode *target;
2181 struct modeNode *mn;
2182 struct userData *uData;
2183 char reason[MAXLEN];
2184 struct do_not_register *dnr;
2188 if(IsProtected(channel->channel_info))
2190 reply("CSMSG_MOVE_NODELETE", channel->name);
2194 if(!IsChannelName(argv[1]))
2196 reply("MSG_NOT_CHANNEL_NAME");
2200 if(opserv_bad_channel(argv[1]))
2202 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2206 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2208 for(uData = channel->channel_info->users; uData; uData = uData->next)
2210 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2212 if(!IsHelping(user))
2213 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2215 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2221 mod_chanmode_init(&change);
2222 if(!(target = GetChannel(argv[1])))
2224 target = AddChannel(argv[1], now, NULL, NULL);
2225 if(!IsSuspended(channel->channel_info))
2226 AddChannelUser(chanserv, target);
2228 else if(target->channel_info)
2230 reply("CSMSG_ALREADY_REGGED", target->name);
2233 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2234 && !IsHelping(user))
2236 reply("CSMSG_MUST_BE_OPPED", target->name);
2239 else if(!IsSuspended(channel->channel_info))
2242 change.args[0].mode = MODE_CHANOP;
2243 change.args[0].u.member = AddChannelUser(chanserv, target);
2244 mod_chanmode_announce(chanserv, target, &change);
2249 /* Clear MODE_REGISTERED from old channel, add it to new. */
2251 change.modes_clear = MODE_REGISTERED;
2252 mod_chanmode_announce(chanserv, channel, &change);
2253 change.modes_clear = 0;
2254 change.modes_set = MODE_REGISTERED;
2255 mod_chanmode_announce(chanserv, target, &change);
2258 /* Move the channel_info to the target channel; it
2259 shouldn't be necessary to clear timeq callbacks
2260 for the old channel. */
2261 target->channel_info = channel->channel_info;
2262 target->channel_info->channel = target;
2263 channel->channel_info = NULL;
2265 reply("CSMSG_MOVE_SUCCESS", target->name);
2267 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2268 if(!IsSuspended(target->channel_info))
2270 char reason2[MAXLEN];
2271 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2272 DelChannelUser(chanserv, channel, reason2, 0);
2274 UnlockChannel(channel);
2275 LockChannel(target);
2276 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2281 merge_users(struct chanData *source, struct chanData *target)
2283 struct userData *suData, *tuData, *next;
2289 /* Insert the source's users into the scratch area. */
2290 for(suData = source->users; suData; suData = suData->next)
2291 dict_insert(merge, suData->handle->handle, suData);
2293 /* Iterate through the target's users, looking for
2294 users common to both channels. The lower access is
2295 removed from either the scratch area or target user
2297 for(tuData = target->users; tuData; tuData = next)
2299 struct userData *choice;
2301 next = tuData->next;
2303 /* If a source user exists with the same handle as a target
2304 channel's user, resolve the conflict by removing one. */
2305 suData = dict_find(merge, tuData->handle->handle, NULL);
2309 /* Pick the data we want to keep. */
2310 /* If the access is the same, use the later seen time. */
2311 if(suData->access == tuData->access)
2312 choice = (suData->seen > tuData->seen) ? suData : tuData;
2313 else /* Otherwise, keep the higher access level. */
2314 choice = (suData->access > tuData->access) ? suData : tuData;
2316 /* Remove the user that wasn't picked. */
2317 if(choice == tuData)
2319 dict_remove(merge, suData->handle->handle);
2320 del_channel_user(suData, 0);
2323 del_channel_user(tuData, 0);
2326 /* Move the remaining users to the target channel. */
2327 for(it = dict_first(merge); it; it = iter_next(it))
2329 suData = iter_data(it);
2331 /* Insert the user into the target channel's linked list. */
2332 suData->prev = NULL;
2333 suData->next = target->users;
2334 suData->channel = target;
2337 target->users->prev = suData;
2338 target->users = suData;
2340 /* Update the user counts for the target channel; the
2341 source counts are left alone. */
2342 target->userCount++;
2345 /* Possible to assert (source->users == NULL) here. */
2346 source->users = NULL;
2351 merge_bans(struct chanData *source, struct chanData *target)
2353 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2355 /* Hold on to the original head of the target ban list
2356 to avoid comparing source bans with source bans. */
2357 tFront = target->bans;
2359 /* Perform a totally expensive O(n*m) merge, ick. */
2360 for(sbData = source->bans; sbData; sbData = sNext)
2362 /* Flag to track whether the ban's been moved
2363 to the destination yet. */
2366 /* Possible to assert (sbData->prev == NULL) here. */
2367 sNext = sbData->next;
2369 for(tbData = tFront; tbData; tbData = tNext)
2371 tNext = tbData->next;
2373 /* Perform two comparisons between each source
2374 and target ban, conflicts are resolved by
2375 keeping the broader ban and copying the later
2376 expiration and triggered time. */
2377 if(match_ircglobs(tbData->mask, sbData->mask))
2379 /* There is a broader ban in the target channel that
2380 overrides one in the source channel; remove the
2381 source ban and break. */
2382 if(sbData->expires > tbData->expires)
2383 tbData->expires = sbData->expires;
2384 if(sbData->triggered > tbData->triggered)
2385 tbData->triggered = sbData->triggered;
2386 del_channel_ban(sbData);
2389 else if(match_ircglobs(sbData->mask, tbData->mask))
2391 /* There is a broader ban in the source channel that
2392 overrides one in the target channel; remove the
2393 target ban, fall through and move the source over. */
2394 if(tbData->expires > sbData->expires)
2395 sbData->expires = tbData->expires;
2396 if(tbData->triggered > sbData->triggered)
2397 sbData->triggered = tbData->triggered;
2398 if(tbData == tFront)
2400 del_channel_ban(tbData);
2403 /* Source bans can override multiple target bans, so
2404 we allow a source to run through this loop multiple
2405 times, but we can only move it once. */
2410 /* Remove the source ban from the source ban list. */
2412 sbData->next->prev = sbData->prev;
2414 /* Modify the source ban's associated channel. */
2415 sbData->channel = target;
2417 /* Insert the ban into the target channel's linked list. */
2418 sbData->prev = NULL;
2419 sbData->next = target->bans;
2422 target->bans->prev = sbData;
2423 target->bans = sbData;
2425 /* Update the user counts for the target channel. */
2430 /* Possible to assert (source->bans == NULL) here. */
2431 source->bans = NULL;
2435 merge_data(struct chanData *source, struct chanData *target)
2437 /* Use more recent visited and owner-transfer time; use older
2438 * registered time. Bitwise or may_opchan. Use higher max.
2439 * Do not touch last_refresh, ban count or user counts.
2441 if(source->visited > target->visited)
2442 target->visited = source->visited;
2443 if(source->registered < target->registered)
2444 target->registered = source->registered;
2445 if(source->ownerTransfer > target->ownerTransfer)
2446 target->ownerTransfer = source->ownerTransfer;
2447 if(source->may_opchan)
2448 target->may_opchan = 1;
2449 if(source->max > target->max)
2450 target->max = source->max;
2454 merge_channel(struct chanData *source, struct chanData *target)
2456 merge_users(source, target);
2457 merge_bans(source, target);
2458 merge_data(source, target);
2461 static CHANSERV_FUNC(cmd_merge)
2463 struct userData *target_user;
2464 struct chanNode *target;
2465 char reason[MAXLEN];
2469 /* Make sure the target channel exists and is registered to the user
2470 performing the command. */
2471 if(!(target = GetChannel(argv[1])))
2473 reply("MSG_INVALID_CHANNEL");
2477 if(!target->channel_info)
2479 reply("CSMSG_NOT_REGISTERED", target->name);
2483 if(IsProtected(channel->channel_info))
2485 reply("CSMSG_MERGE_NODELETE");
2489 if(IsSuspended(target->channel_info))
2491 reply("CSMSG_MERGE_SUSPENDED");
2495 if(channel == target)
2497 reply("CSMSG_MERGE_SELF");
2501 target_user = GetChannelUser(target->channel_info, user->handle_info);
2502 if(!target_user || (target_user->access < UL_OWNER))
2504 reply("CSMSG_MERGE_NOT_OWNER");
2508 /* Merge the channel structures and associated data. */
2509 merge_channel(channel->channel_info, target->channel_info);
2510 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2511 unregister_channel(channel->channel_info, reason);
2512 reply("CSMSG_MERGE_SUCCESS", target->name);
2516 static CHANSERV_FUNC(cmd_opchan)
2518 struct mod_chanmode change;
2519 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2521 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2524 channel->channel_info->may_opchan = 0;
2525 mod_chanmode_init(&change);
2527 change.args[0].mode = MODE_CHANOP;
2528 change.args[0].u.member = GetUserMode(channel, chanserv);
2529 mod_chanmode_announce(chanserv, channel, &change);
2530 reply("CSMSG_OPCHAN_DONE", channel->name);
2534 static CHANSERV_FUNC(cmd_adduser)
2536 struct userData *actee;
2537 struct userData *actor, *real_actor;
2538 struct handle_info *handle;
2539 unsigned short access, override = 0;
2543 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2545 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2549 access = user_level_from_name(argv[2], UL_OWNER);
2552 reply("CSMSG_INVALID_ACCESS", argv[2]);
2556 actor = GetChannelUser(channel->channel_info, user->handle_info);
2557 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2559 if(actor->access <= access)
2561 reply("CSMSG_NO_BUMP_ACCESS");
2565 /* Trying to add someone with equal/more access? */
2566 if (!real_actor || real_actor->access <= access)
2567 override = CMD_LOG_OVERRIDE;
2569 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2572 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2574 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2578 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2579 scan_user_presence(actee, NULL);
2580 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2581 return 1 | override;
2584 static CHANSERV_FUNC(cmd_clvl)
2586 struct handle_info *handle;
2587 struct userData *victim;
2588 struct userData *actor, *real_actor;
2589 unsigned short new_access, override = 0;
2590 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2594 actor = GetChannelUser(channel->channel_info, user->handle_info);
2595 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2597 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2600 if(handle == user->handle_info && !privileged)
2602 reply("CSMSG_NO_SELF_CLVL");
2606 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2608 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2612 if(actor->access <= victim->access && !privileged)
2614 reply("MSG_USER_OUTRANKED", handle->handle);
2618 new_access = user_level_from_name(argv[2], UL_OWNER);
2622 reply("CSMSG_INVALID_ACCESS", argv[2]);
2626 if(new_access >= actor->access && !privileged)
2628 reply("CSMSG_NO_BUMP_ACCESS");
2632 /* Trying to clvl a equal/higher user? */
2633 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2634 override = CMD_LOG_OVERRIDE;
2635 /* Trying to clvl someone to equal/higher access? */
2636 if(!real_actor || new_access >= real_actor->access)
2637 override = CMD_LOG_OVERRIDE;
2638 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2639 * If they lower their own access it's not a big problem.
2642 victim->access = new_access;
2643 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2644 return 1 | override;
2647 static CHANSERV_FUNC(cmd_deluser)
2649 struct handle_info *handle;
2650 struct userData *victim;
2651 struct userData *actor, *real_actor;
2652 unsigned short access, override = 0;
2657 actor = GetChannelUser(channel->channel_info, user->handle_info);
2658 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2660 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2663 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2665 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2671 access = user_level_from_name(argv[1], UL_OWNER);
2674 reply("CSMSG_INVALID_ACCESS", argv[1]);
2677 if(access != victim->access)
2679 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2685 access = victim->access;
2688 if((actor->access <= victim->access) && !IsHelping(user))
2690 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2694 /* If people delete themselves it is an override, but they
2695 * could've used deleteme so we don't log it as an override
2697 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2698 override = CMD_LOG_OVERRIDE;
2700 chan_name = strdup(channel->name);
2701 del_channel_user(victim, 1);
2702 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2704 return 1 | override;
2708 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2710 struct userData *actor, *real_actor, *uData, *next;
2711 unsigned int override = 0;
2713 actor = GetChannelUser(channel->channel_info, user->handle_info);
2714 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2716 if(min_access > max_access)
2718 reply("CSMSG_BAD_RANGE", min_access, max_access);
2722 if((actor->access <= max_access) && !IsHelping(user))
2724 reply("CSMSG_NO_ACCESS");
2728 if(!real_actor || real_actor->access <= max_access)
2729 override = CMD_LOG_OVERRIDE;
2731 for(uData = channel->channel_info->users; uData; uData = next)
2735 if((uData->access >= min_access)
2736 && (uData->access <= max_access)
2737 && match_ircglob(uData->handle->handle, mask))
2738 del_channel_user(uData, 1);
2741 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2742 return 1 | override;
2745 static CHANSERV_FUNC(cmd_mdelowner)
2747 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2750 static CHANSERV_FUNC(cmd_mdelcoowner)
2752 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2755 static CHANSERV_FUNC(cmd_mdelmaster)
2757 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2760 static CHANSERV_FUNC(cmd_mdelop)
2762 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2765 static CHANSERV_FUNC(cmd_mdelpeon)
2767 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2771 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2773 struct banData *bData, *next;
2774 char interval[INTERVALLEN];
2776 unsigned long limit;
2779 limit = now - duration;
2780 for(bData = channel->channel_info->bans; bData; bData = next)
2784 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2787 del_channel_ban(bData);
2791 intervalString(interval, duration, user->handle_info);
2792 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2797 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2799 struct userData *actor, *uData, *next;
2800 char interval[INTERVALLEN];
2802 unsigned long limit;
2804 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2805 if(min_access > max_access)
2807 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2811 if(!actor || actor->access <= max_access)
2813 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2818 limit = now - duration;
2819 for(uData = channel->channel_info->users; uData; uData = next)
2823 if((uData->seen > limit)
2825 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2828 if(((uData->access >= min_access) && (uData->access <= max_access))
2829 || (!max_access && (uData->access < actor->access)))
2831 del_channel_user(uData, 1);
2839 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2841 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2845 static CHANSERV_FUNC(cmd_trim)
2847 unsigned long duration;
2848 unsigned short min_level, max_level;
2853 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2854 duration = ParseInterval(argv[2]);
2857 reply("CSMSG_CANNOT_TRIM");
2861 if(!irccasecmp(argv[1], "bans"))
2863 cmd_trim_bans(user, channel, duration);
2866 else if(!irccasecmp(argv[1], "users"))
2868 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2871 else if(parse_level_range(&min_level, &max_level, argv[1]))
2873 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2876 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2878 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2883 reply("CSMSG_INVALID_TRIM", argv[1]);
2888 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2889 to the user. cmd_all takes advantage of this. */
2890 static CHANSERV_FUNC(cmd_up)
2892 struct mod_chanmode change;
2893 struct userData *uData;
2896 mod_chanmode_init(&change);
2898 change.args[0].u.member = GetUserMode(channel, user);
2899 if(!change.args[0].u.member)
2902 reply("MSG_CHANNEL_ABSENT", channel->name);
2906 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2910 reply("CSMSG_GODMODE_UP", argv[0]);
2913 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2915 change.args[0].mode = MODE_CHANOP;
2916 errmsg = "CSMSG_ALREADY_OPPED";
2918 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2920 change.args[0].mode = MODE_VOICE;
2921 errmsg = "CSMSG_ALREADY_VOICED";
2926 reply("CSMSG_NO_ACCESS");
2929 change.args[0].mode &= ~change.args[0].u.member->modes;
2930 if(!change.args[0].mode)
2933 reply(errmsg, channel->name);
2936 modcmd_chanmode_announce(&change);
2940 static CHANSERV_FUNC(cmd_down)
2942 struct mod_chanmode change;
2944 mod_chanmode_init(&change);
2946 change.args[0].u.member = GetUserMode(channel, user);
2947 if(!change.args[0].u.member)
2950 reply("MSG_CHANNEL_ABSENT", channel->name);
2954 if(!change.args[0].u.member->modes)
2957 reply("CSMSG_ALREADY_DOWN", channel->name);
2961 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2962 modcmd_chanmode_announce(&change);
2966 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)
2968 struct userData *cList;
2970 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2972 if(IsSuspended(cList->channel)
2973 || IsUserSuspended(cList)
2974 || !GetUserMode(cList->channel->channel, user))
2977 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2983 static CHANSERV_FUNC(cmd_upall)
2985 return cmd_all(CSFUNC_ARGS, cmd_up);
2988 static CHANSERV_FUNC(cmd_downall)
2990 return cmd_all(CSFUNC_ARGS, cmd_down);
2993 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2994 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2997 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)
2999 unsigned int ii, valid;
3000 struct userNode *victim;
3001 struct mod_chanmode *change;
3003 change = mod_chanmode_alloc(argc - 1);
3005 for(ii=valid=0; ++ii < argc; )
3007 if(!(victim = GetUserH(argv[ii])))
3009 change->args[valid].mode = mode;
3010 change->args[valid].u.member = GetUserMode(channel, victim);
3011 if(!change->args[valid].u.member)
3013 if(validate && !validate(user, channel, victim))
3018 change->argc = valid;
3019 if(valid < (argc-1))
3020 reply("CSMSG_PROCESS_FAILED");
3023 modcmd_chanmode_announce(change);
3024 reply(action, channel->name);
3026 mod_chanmode_free(change);
3030 static CHANSERV_FUNC(cmd_op)
3032 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3035 static CHANSERV_FUNC(cmd_deop)
3037 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3040 static CHANSERV_FUNC(cmd_voice)
3042 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3045 static CHANSERV_FUNC(cmd_devoice)
3047 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3051 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3057 for(ii=0; ii<channel->members.used; ii++)
3059 struct modeNode *mn = channel->members.list[ii];
3061 if(IsService(mn->user))
3064 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3067 if(protect_user(mn->user, user, channel->channel_info))
3071 victims[(*victimCount)++] = mn;
3077 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3079 struct userNode *victim;
3080 struct modeNode **victims;
3081 unsigned int offset, n, victimCount, duration = 0;
3082 char *reason = "Bye.", *ban, *name;
3083 char interval[INTERVALLEN];
3085 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3086 REQUIRE_PARAMS(offset);
3089 reason = unsplit_string(argv + offset, argc - offset, NULL);
3090 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3092 /* Truncate the reason to a length of TOPICLEN, as
3093 the ircd does; however, leave room for an ellipsis
3094 and the kicker's nick. */
3095 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3099 if((victim = GetUserH(argv[1])))
3101 victims = alloca(sizeof(victims[0]));
3102 victims[0] = GetUserMode(channel, victim);
3103 /* XXX: The comparison with ACTION_KICK is just because all
3104 * other actions can work on users outside the channel, and we
3105 * want to allow those (e.g. unbans) in that case. If we add
3106 * some other ejection action for in-channel users, change
3108 victimCount = victims[0] ? 1 : 0;
3110 if(IsService(victim))
3112 reply("MSG_SERVICE_IMMUNE", victim->nick);
3116 if((action == ACTION_KICK) && !victimCount)
3118 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3122 if(protect_user(victim, user, channel->channel_info))
3124 reply("CSMSG_USER_PROTECTED", victim->nick);
3128 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3129 name = victim->nick;
3131 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3133 struct handle_info *hi;
3134 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3135 const char *accountname = argv[1] + 1;
3137 if(!(hi = get_handle_info(accountname)))
3139 reply("MSG_HANDLE_UNKNOWN", accountname);
3143 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3144 victims = alloca(sizeof(victims[0]) * channel->members.used);
3146 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3148 reply("CSMSG_MASK_PROTECTED", banmask);
3152 if((action == ACTION_KICK) && (victimCount == 0))
3154 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3158 name = ban = strdup(banmask);
3162 if(!is_ircmask(argv[1]))
3164 reply("MSG_NICK_UNKNOWN", argv[1]);
3168 victims = alloca(sizeof(victims[0]) * channel->members.used);
3170 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3172 reply("CSMSG_MASK_PROTECTED", argv[1]);
3176 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3178 reply("CSMSG_LAME_MASK", argv[1]);
3182 if((action == ACTION_KICK) && (victimCount == 0))
3184 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3188 name = ban = strdup(argv[1]);
3191 /* Truncate the ban in place if necessary; we must ensure
3192 that 'ban' is a valid ban mask before sanitizing it. */
3193 sanitize_ircmask(ban);
3195 if(action & ACTION_ADD_BAN)
3197 struct banData *bData, *next;
3199 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3201 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3206 if(action & ACTION_ADD_TIMED_BAN)
3208 duration = ParseInterval(argv[2]);
3212 reply("CSMSG_DURATION_TOO_LOW");
3216 else if(duration > (86400 * 365 * 2))
3218 reply("CSMSG_DURATION_TOO_HIGH");
3224 for(bData = channel->channel_info->bans; bData; bData = next)
3226 if(match_ircglobs(bData->mask, ban))
3228 int exact = !irccasecmp(bData->mask, ban);
3230 /* The ban is redundant; there is already a ban
3231 with the same effect in place. */
3235 free(bData->reason);
3236 bData->reason = strdup(reason);
3237 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3239 reply("CSMSG_REASON_CHANGE", ban);
3243 if(exact && bData->expires)
3247 /* If the ban matches an existing one exactly,
3248 extend the expiration time if the provided
3249 duration is longer. */
3250 if(duration && (now + duration > bData->expires))
3252 bData->expires = now + duration;
3263 /* Delete the expiration timeq entry and
3264 requeue if necessary. */
3265 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3268 timeq_add(bData->expires, expire_ban, bData);
3272 /* automated kickban */
3275 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3277 reply("CSMSG_BAN_ADDED", name, channel->name);
3283 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3290 if(match_ircglobs(ban, bData->mask))
3292 /* The ban we are adding makes previously existing
3293 bans redundant; silently remove them. */
3294 del_channel_ban(bData);
3298 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);
3300 name = ban = strdup(bData->mask);
3304 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3306 extern const char *hidden_host_suffix;
3307 const char *old_name = chanserv_conf.old_ban_names->list[n];
3309 unsigned int l1, l2;
3312 l2 = strlen(old_name);
3315 if(irccasecmp(ban + l1 - l2, old_name))
3317 new_mask = malloc(MAXLEN);
3318 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3320 name = ban = new_mask;
3325 if(action & ACTION_BAN)
3327 unsigned int exists;
3328 struct mod_chanmode *change;
3330 if(channel->banlist.used >= MAXBANS)
3333 reply("CSMSG_BANLIST_FULL", channel->name);
3338 exists = ChannelBanExists(channel, ban);
3339 change = mod_chanmode_alloc(victimCount + 1);
3340 for(n = 0; n < victimCount; ++n)
3342 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3343 change->args[n].u.member = victims[n];
3347 change->args[n].mode = MODE_BAN;
3348 change->args[n++].u.hostmask = ban;
3352 modcmd_chanmode_announce(change);
3354 mod_chanmode_announce(chanserv, channel, change);
3355 mod_chanmode_free(change);
3357 if(exists && (action == ACTION_BAN))
3360 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3366 if(action & ACTION_KICK)
3368 char kick_reason[MAXLEN];
3369 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3371 for(n = 0; n < victimCount; n++)
3372 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3377 /* No response, since it was automated. */
3379 else if(action & ACTION_ADD_BAN)
3382 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3384 reply("CSMSG_BAN_ADDED", name, channel->name);
3386 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3387 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3388 else if(action & ACTION_BAN)
3389 reply("CSMSG_BAN_DONE", name, channel->name);
3390 else if(action & ACTION_KICK && victimCount)
3391 reply("CSMSG_KICK_DONE", name, channel->name);
3397 static CHANSERV_FUNC(cmd_kickban)
3399 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3402 static CHANSERV_FUNC(cmd_kick)
3404 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3407 static CHANSERV_FUNC(cmd_ban)
3409 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3412 static CHANSERV_FUNC(cmd_addban)
3414 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3417 static CHANSERV_FUNC(cmd_addtimedban)
3419 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3422 static struct mod_chanmode *
3423 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3425 struct mod_chanmode *change;
3426 unsigned char *match;
3427 unsigned int ii, count;
3429 match = alloca(bans->used);
3432 for(ii = count = 0; ii < bans->used; ++ii)
3434 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3435 MATCH_USENICK | MATCH_VISIBLE);
3442 for(ii = count = 0; ii < bans->used; ++ii)
3444 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3451 change = mod_chanmode_alloc(count);
3452 for(ii = count = 0; ii < bans->used; ++ii)
3456 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3457 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3459 assert(count == change->argc);
3464 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3466 struct userNode *actee;
3472 /* may want to allow a comma delimited list of users... */
3473 if(!(actee = GetUserH(argv[1])))
3475 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3477 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3478 const char *accountname = argv[1] + 1;
3480 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3481 mask = strdup(banmask);
3483 else if(!is_ircmask(argv[1]))
3485 reply("MSG_NICK_UNKNOWN", argv[1]);
3490 mask = strdup(argv[1]);
3494 /* We don't sanitize the mask here because ircu
3496 if(action & ACTION_UNBAN)
3498 struct mod_chanmode *change;
3499 change = find_matching_bans(&channel->banlist, actee, mask);
3504 modcmd_chanmode_announce(change);
3505 for(ii = 0; ii < change->argc; ++ii)
3506 free((char*)change->args[ii].u.hostmask);
3507 mod_chanmode_free(change);
3512 if(action & ACTION_DEL_BAN)
3514 struct banData *ban, *next;
3516 ban = channel->channel_info->bans;
3520 for( ; ban && !user_matches_glob(actee, ban->mask,
3521 MATCH_USENICK | MATCH_VISIBLE);
3524 for( ; ban && !match_ircglobs(mask, ban->mask);
3529 del_channel_ban(ban);
3536 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3538 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3544 static CHANSERV_FUNC(cmd_unban)
3546 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3549 static CHANSERV_FUNC(cmd_delban)
3551 /* it doesn't necessarily have to remove the channel ban - may want
3552 to make that an option. */
3553 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3556 static CHANSERV_FUNC(cmd_unbanme)
3558 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3559 long flags = ACTION_UNBAN;
3561 /* remove permanent bans if the user has the proper access. */
3562 if(uData->access >= UL_MASTER)
3563 flags |= ACTION_DEL_BAN;
3565 argv[1] = user->nick;
3566 return unban_user(user, channel, 2, argv, cmd, flags);
3569 static CHANSERV_FUNC(cmd_unbanall)
3571 struct mod_chanmode *change;
3574 if(!channel->banlist.used)
3576 reply("CSMSG_NO_BANS", channel->name);
3580 change = mod_chanmode_alloc(channel->banlist.used);
3581 for(ii=0; ii<channel->banlist.used; ii++)
3583 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3584 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3586 modcmd_chanmode_announce(change);
3587 for(ii = 0; ii < change->argc; ++ii)
3588 free((char*)change->args[ii].u.hostmask);
3589 mod_chanmode_free(change);
3590 reply("CSMSG_BANS_REMOVED", channel->name);
3594 static CHANSERV_FUNC(cmd_open)
3596 struct mod_chanmode *change;
3599 change = find_matching_bans(&channel->banlist, user, NULL);
3601 change = mod_chanmode_alloc(0);
3602 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3603 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3604 && channel->channel_info->modes.modes_set)
3605 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3606 modcmd_chanmode_announce(change);
3607 reply("CSMSG_CHANNEL_OPENED", channel->name);
3608 for(ii = 0; ii < change->argc; ++ii)
3609 free((char*)change->args[ii].u.hostmask);
3610 mod_chanmode_free(change);
3614 static CHANSERV_FUNC(cmd_myaccess)
3616 static struct string_buffer sbuf;
3617 struct handle_info *target_handle;
3618 struct userData *uData;
3621 target_handle = user->handle_info;
3622 else if(!IsHelping(user))
3624 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3627 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3630 if(!target_handle->channels)
3632 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3636 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3637 for(uData = target_handle->channels; uData; uData = uData->u_next)
3639 struct chanData *cData = uData->channel;
3641 if(uData->access > UL_OWNER)
3643 if(IsProtected(cData)
3644 && (target_handle != user->handle_info)
3645 && !GetTrueChannelAccess(cData, user->handle_info))
3648 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3649 if(uData->flags != USER_AUTO_OP)
3650 string_buffer_append(&sbuf, ',');
3651 if(IsUserSuspended(uData))
3652 string_buffer_append(&sbuf, 's');
3653 if(IsUserAutoOp(uData))
3655 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3656 string_buffer_append(&sbuf, 'o');
3657 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3658 string_buffer_append(&sbuf, 'v');
3660 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3661 string_buffer_append(&sbuf, 'i');
3663 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3665 string_buffer_append_string(&sbuf, ")]");
3666 string_buffer_append(&sbuf, '\0');
3667 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3673 static CHANSERV_FUNC(cmd_access)
3675 struct userNode *target;
3676 struct handle_info *target_handle;
3677 struct userData *uData;
3679 char prefix[MAXLEN];
3684 target_handle = target->handle_info;
3686 else if((target = GetUserH(argv[1])))
3688 target_handle = target->handle_info;
3690 else if(argv[1][0] == '*')
3692 if(!(target_handle = get_handle_info(argv[1]+1)))
3694 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3700 reply("MSG_NICK_UNKNOWN", argv[1]);
3704 assert(target || target_handle);
3706 if(target == chanserv)
3708 reply("CSMSG_IS_CHANSERV");
3716 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3721 reply("MSG_USER_AUTHENTICATE", target->nick);
3724 reply("MSG_AUTHENTICATE");
3730 const char *epithet = NULL, *type = NULL;
3733 epithet = chanserv_conf.irc_operator_epithet;
3734 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3736 else if(IsNetworkHelper(target))
3738 epithet = chanserv_conf.network_helper_epithet;
3739 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3741 else if(IsSupportHelper(target))
3743 epithet = chanserv_conf.support_helper_epithet;
3744 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3748 if(target_handle->epithet)
3749 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3751 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3753 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3757 sprintf(prefix, "%s", target_handle->handle);
3760 if(!channel->channel_info)
3762 reply("CSMSG_NOT_REGISTERED", channel->name);
3766 helping = HANDLE_FLAGGED(target_handle, HELPING)
3767 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3768 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3770 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3771 /* To prevent possible information leaks, only show infolines
3772 * if the requestor is in the channel or it's their own
3774 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3776 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3778 /* Likewise, only say it's suspended if the user has active
3779 * access in that channel or it's their own entry. */
3780 if(IsUserSuspended(uData)
3781 && (GetChannelUser(channel->channel_info, user->handle_info)
3782 || (user->handle_info == uData->handle)))
3784 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3789 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3796 zoot_list(struct listData *list)
3798 struct userData *uData;
3799 unsigned int start, curr, highest, lowest;
3800 struct helpfile_table tmp_table;
3801 const char **temp, *msg;
3803 if(list->table.length == 1)
3806 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3808 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3809 msg = user_find_message(list->user, "MSG_NONE");
3810 send_message_type(4, list->user, list->bot, " %s", msg);
3812 tmp_table.width = list->table.width;
3813 tmp_table.flags = list->table.flags;
3814 list->table.contents[0][0] = " ";
3815 highest = list->highest;
3816 if(list->lowest != 0)
3817 lowest = list->lowest;
3818 else if(highest < 100)
3821 lowest = highest - 100;
3822 for(start = curr = 1; curr < list->table.length; )
3824 uData = list->users[curr-1];
3825 list->table.contents[curr++][0] = " ";
3826 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3829 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3831 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3832 temp = list->table.contents[--start];
3833 list->table.contents[start] = list->table.contents[0];
3834 tmp_table.contents = list->table.contents + start;
3835 tmp_table.length = curr - start;
3836 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3837 list->table.contents[start] = temp;
3839 highest = lowest - 1;
3840 lowest = (highest < 100) ? 0 : (highest - 99);
3846 def_list(struct listData *list)
3850 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3852 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3853 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3854 if(list->table.length == 1)
3856 msg = user_find_message(list->user, "MSG_NONE");
3857 send_message_type(4, list->user, list->bot, " %s", msg);
3862 userData_access_comp(const void *arg_a, const void *arg_b)
3864 const struct userData *a = *(struct userData**)arg_a;
3865 const struct userData *b = *(struct userData**)arg_b;
3867 if(a->access != b->access)
3868 res = b->access - a->access;
3870 res = irccasecmp(a->handle->handle, b->handle->handle);
3875 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3877 void (*send_list)(struct listData *);
3878 struct userData *uData;
3879 struct listData lData;
3880 unsigned int matches;
3884 lData.bot = cmd->parent->bot;
3885 lData.channel = channel;
3886 lData.lowest = lowest;
3887 lData.highest = highest;
3888 lData.search = (argc > 1) ? argv[1] : NULL;
3889 send_list = def_list;
3890 (void)zoot_list; /* since it doesn't show user levels */
3892 if(user->handle_info)
3894 switch(user->handle_info->userlist_style)
3896 case HI_STYLE_DEF: send_list = def_list; break;
3897 case HI_STYLE_ZOOT: send_list = def_list; break;
3901 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3903 for(uData = channel->channel_info->users; uData; uData = uData->next)
3905 if((uData->access < lowest)
3906 || (uData->access > highest)
3907 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3909 lData.users[matches++] = uData;
3911 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3913 lData.table.length = matches+1;
3914 lData.table.width = 4;
3915 lData.table.flags = TABLE_NO_FREE;
3916 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3917 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3918 lData.table.contents[0] = ary;
3921 ary[2] = "Last Seen";
3923 for(matches = 1; matches < lData.table.length; ++matches)
3925 struct userData *uData = lData.users[matches-1];
3926 char seen[INTERVALLEN];
3928 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3929 lData.table.contents[matches] = ary;
3930 ary[0] = strtab(uData->access);
3931 ary[1] = uData->handle->handle;
3934 else if(!uData->seen)
3937 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3938 ary[2] = strdup(ary[2]);
3939 if(IsUserSuspended(uData))
3940 ary[3] = "Suspended";
3941 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3942 ary[3] = "Vacation";
3947 for(matches = 1; matches < lData.table.length; ++matches)
3949 free((char*)lData.table.contents[matches][2]);
3950 free(lData.table.contents[matches]);
3952 free(lData.table.contents[0]);
3953 free(lData.table.contents);
3957 static CHANSERV_FUNC(cmd_users)
3959 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3962 static CHANSERV_FUNC(cmd_wlist)
3964 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3967 static CHANSERV_FUNC(cmd_clist)
3969 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3972 static CHANSERV_FUNC(cmd_mlist)
3974 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3977 static CHANSERV_FUNC(cmd_olist)
3979 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3982 static CHANSERV_FUNC(cmd_plist)
3984 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3987 static CHANSERV_FUNC(cmd_bans)
3989 struct userNode *search_u = NULL;
3990 struct helpfile_table tbl;
3991 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3992 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3993 const char *msg_never, *triggered, *expires;
3994 struct banData *ban, **bans;
3998 else if(strchr(search = argv[1], '!'))
4001 search_wilds = search[strcspn(search, "?*")];
4003 else if(!(search_u = GetUserH(search)))
4004 reply("MSG_NICK_UNKNOWN", search);
4006 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4008 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4012 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4017 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4020 bans[matches++] = ban;
4025 tbl.length = matches + 1;
4026 tbl.width = 4 + timed;
4028 tbl.flags = TABLE_NO_FREE;
4029 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4030 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4031 tbl.contents[0][0] = "Mask";
4032 tbl.contents[0][1] = "Set By";
4033 tbl.contents[0][2] = "Triggered";
4036 tbl.contents[0][3] = "Expires";
4037 tbl.contents[0][4] = "Reason";
4040 tbl.contents[0][3] = "Reason";
4043 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4045 free(tbl.contents[0]);
4050 msg_never = user_find_message(user, "MSG_NEVER");
4051 for(ii = 0; ii < matches; )
4057 else if(ban->expires)
4058 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4060 expires = msg_never;
4063 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4065 triggered = msg_never;
4067 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4068 tbl.contents[ii][0] = ban->mask;
4069 tbl.contents[ii][1] = ban->owner;
4070 tbl.contents[ii][2] = strdup(triggered);
4073 tbl.contents[ii][3] = strdup(expires);
4074 tbl.contents[ii][4] = ban->reason;
4077 tbl.contents[ii][3] = ban->reason;
4079 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4080 reply("MSG_MATCH_COUNT", matches);
4081 for(ii = 1; ii < tbl.length; ++ii)
4083 free((char*)tbl.contents[ii][2]);
4085 free((char*)tbl.contents[ii][3]);
4086 free(tbl.contents[ii]);
4088 free(tbl.contents[0]);
4094 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4096 struct chanData *cData = channel->channel_info;
4097 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4099 if(cData->topic_mask)
4100 return !match_ircglob(new_topic, cData->topic_mask);
4101 else if(cData->topic)
4102 return irccasecmp(new_topic, cData->topic);
4107 static CHANSERV_FUNC(cmd_topic)
4109 struct chanData *cData;
4112 cData = channel->channel_info;
4117 SetChannelTopic(channel, chanserv, cData->topic, 1);
4118 reply("CSMSG_TOPIC_SET", cData->topic);
4122 reply("CSMSG_NO_TOPIC", channel->name);
4126 topic = unsplit_string(argv + 1, argc - 1, NULL);
4127 /* If they say "!topic *", use an empty topic. */
4128 if((topic[0] == '*') && (topic[1] == 0))
4130 if(bad_topic(channel, user, topic))
4132 char *topic_mask = cData->topic_mask;
4135 char new_topic[TOPICLEN+1], tchar;
4136 int pos=0, starpos=-1, dpos=0, len;
4138 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4145 len = strlen(topic);
4146 if((dpos + len) > TOPICLEN)
4147 len = TOPICLEN + 1 - dpos;
4148 memcpy(new_topic+dpos, topic, len);
4152 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4153 default: new_topic[dpos++] = tchar; break;
4156 if((dpos > TOPICLEN) || tchar)
4159 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4160 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4163 new_topic[dpos] = 0;
4164 SetChannelTopic(channel, chanserv, new_topic, 1);
4166 reply("CSMSG_TOPIC_LOCKED", channel->name);
4171 SetChannelTopic(channel, chanserv, topic, 1);
4173 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4175 /* Grab the topic and save it as the default topic. */
4177 cData->topic = strdup(channel->topic);
4183 static CHANSERV_FUNC(cmd_mode)
4185 struct userData *uData;
4186 struct mod_chanmode *change;
4191 change = &channel->channel_info->modes;
4192 if(change->modes_set || change->modes_clear) {
4193 modcmd_chanmode_announce(change);
4194 reply("CSMSG_DEFAULTED_MODES", channel->name);
4196 reply("CSMSG_NO_MODES", channel->name);
4200 uData = GetChannelUser(channel->channel_info, user->handle_info);
4202 base_oplevel = MAXOPLEVEL;
4203 else if (uData->access >= UL_OWNER)
4206 base_oplevel = 1 + UL_OWNER - uData->access;
4207 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4210 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4214 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4215 && mode_lock_violated(&channel->channel_info->modes, change))
4218 mod_chanmode_format(&channel->channel_info->modes, modes);
4219 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4223 modcmd_chanmode_announce(change);
4224 mod_chanmode_free(change);
4225 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4229 static CHANSERV_FUNC(cmd_invite)
4231 struct userData *uData;
4232 struct userNode *invite;
4234 uData = GetChannelUser(channel->channel_info, user->handle_info);
4238 if(!(invite = GetUserH(argv[1])))
4240 reply("MSG_NICK_UNKNOWN", argv[1]);
4247 if(GetUserMode(channel, invite))
4249 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4257 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4258 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4261 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4263 irc_invite(chanserv, invite, channel);
4265 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4270 static CHANSERV_FUNC(cmd_inviteme)
4272 if(GetUserMode(channel, user))
4274 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4277 if(channel->channel_info
4278 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4280 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4283 irc_invite(cmd->parent->bot, user, channel);
4288 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4291 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4293 /* We display things based on two dimensions:
4294 * - Issue time: present or absent
4295 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4296 * (in order of precedence, so something both expired and revoked
4297 * only counts as revoked)
4299 combo = (suspended->issued ? 4 : 0)
4300 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4302 case 0: /* no issue time, indefinite expiration */
4303 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4305 case 1: /* no issue time, expires in future */
4306 intervalString(buf1, suspended->expires-now, user->handle_info);
4307 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4309 case 2: /* no issue time, expired */
4310 intervalString(buf1, now-suspended->expires, user->handle_info);
4311 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4313 case 3: /* no issue time, revoked */
4314 intervalString(buf1, now-suspended->revoked, user->handle_info);
4315 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4317 case 4: /* issue time set, indefinite expiration */
4318 intervalString(buf1, now-suspended->issued, user->handle_info);
4319 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4321 case 5: /* issue time set, expires in future */
4322 intervalString(buf1, now-suspended->issued, user->handle_info);
4323 intervalString(buf2, suspended->expires-now, user->handle_info);
4324 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4326 case 6: /* issue time set, expired */
4327 intervalString(buf1, now-suspended->issued, user->handle_info);
4328 intervalString(buf2, now-suspended->expires, user->handle_info);
4329 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4331 case 7: /* issue time set, revoked */
4332 intervalString(buf1, now-suspended->issued, user->handle_info);
4333 intervalString(buf2, now-suspended->revoked, user->handle_info);
4334 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4337 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4342 static CHANSERV_FUNC(cmd_info)
4344 char modes[MAXLEN], buffer[INTERVALLEN];
4345 struct userData *uData, *owner;
4346 struct chanData *cData;
4347 struct do_not_register *dnr;
4352 cData = channel->channel_info;
4353 reply("CSMSG_CHANNEL_INFO", channel->name);
4355 uData = GetChannelUser(cData, user->handle_info);
4356 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4358 mod_chanmode_format(&cData->modes, modes);
4359 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4360 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4363 for(it = dict_first(cData->notes); it; it = iter_next(it))
4367 note = iter_data(it);
4368 if(!note_type_visible_to_user(cData, note->type, user))
4371 padding = PADLEN - 1 - strlen(iter_key(it));
4372 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4375 reply("CSMSG_CHANNEL_MAX", cData->max);
4376 for(owner = cData->users; owner; owner = owner->next)
4377 if(owner->access == UL_OWNER)
4378 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4379 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4380 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4381 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4383 privileged = IsStaff(user);
4385 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4386 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4387 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4389 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4390 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4392 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4394 struct suspended *suspended;
4395 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4396 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4397 show_suspension_info(cmd, user, suspended);
4399 else if(IsSuspended(cData))
4401 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4402 show_suspension_info(cmd, user, cData->suspended);
4407 static CHANSERV_FUNC(cmd_netinfo)
4409 extern unsigned long boot_time;
4410 extern unsigned long burst_length;
4411 char interval[INTERVALLEN];
4413 reply("CSMSG_NETWORK_INFO");
4414 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4415 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4416 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4417 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4418 reply("CSMSG_NETWORK_BANS", banCount);
4419 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4420 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4421 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4426 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4428 struct helpfile_table table;
4430 struct userNode *user;
4435 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4436 table.contents = alloca(list->used*sizeof(*table.contents));
4437 for(nn=0; nn<list->used; nn++)
4439 user = list->list[nn];
4440 if(user->modes & skip_flags)
4444 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4447 nick = alloca(strlen(user->nick)+3);
4448 sprintf(nick, "(%s)", user->nick);
4452 table.contents[table.length][0] = nick;
4455 table_send(chanserv, to->nick, 0, NULL, table);
4458 static CHANSERV_FUNC(cmd_ircops)
4460 reply("CSMSG_STAFF_OPERS");
4461 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4465 static CHANSERV_FUNC(cmd_helpers)
4467 reply("CSMSG_STAFF_HELPERS");
4468 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4472 static CHANSERV_FUNC(cmd_staff)
4474 reply("CSMSG_NETWORK_STAFF");
4475 cmd_ircops(CSFUNC_ARGS);
4476 cmd_helpers(CSFUNC_ARGS);
4480 static CHANSERV_FUNC(cmd_peek)
4482 struct modeNode *mn;
4483 char modes[MODELEN];
4485 struct helpfile_table table;
4487 irc_make_chanmode(channel, modes);
4489 reply("CSMSG_PEEK_INFO", channel->name);
4490 reply("CSMSG_PEEK_TOPIC", channel->topic);
4491 reply("CSMSG_PEEK_MODES", modes);
4492 reply("CSMSG_PEEK_USERS", channel->members.used);
4496 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4497 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4498 for(n = 0; n < channel->members.used; n++)
4500 mn = channel->members.list[n];
4501 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4503 table.contents[table.length] = alloca(sizeof(**table.contents));
4504 table.contents[table.length][0] = mn->user->nick;
4509 reply("CSMSG_PEEK_OPS");
4510 table_send(chanserv, user->nick, 0, NULL, table);
4513 reply("CSMSG_PEEK_NO_OPS");
4517 static MODCMD_FUNC(cmd_wipeinfo)
4519 struct handle_info *victim;
4520 struct userData *ud, *actor, *real_actor;
4521 unsigned int override = 0;
4524 actor = GetChannelUser(channel->channel_info, user->handle_info);
4525 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4526 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4528 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4530 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4533 if((ud->access >= actor->access) && (ud != actor))
4535 reply("MSG_USER_OUTRANKED", victim->handle);
4538 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4539 override = CMD_LOG_OVERRIDE;
4543 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4544 return 1 | override;
4547 static CHANSERV_FUNC(cmd_resync)
4549 struct mod_chanmode *changes;
4550 struct chanData *cData = channel->channel_info;
4551 unsigned int ii, used;
4553 changes = mod_chanmode_alloc(channel->members.used * 2);
4554 for(ii = used = 0; ii < channel->members.used; ++ii)
4556 struct modeNode *mn = channel->members.list[ii];
4557 struct userData *uData;
4559 if(IsService(mn->user))
4562 uData = GetChannelAccess(cData, mn->user->handle_info);
4563 if(!cData->lvlOpts[lvlGiveOps]
4564 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4566 if(!(mn->modes & MODE_CHANOP))
4568 changes->args[used].mode = MODE_CHANOP;
4569 changes->args[used++].u.member = mn;
4572 else if(!cData->lvlOpts[lvlGiveVoice]
4573 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4575 if(mn->modes & MODE_CHANOP)
4577 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4578 changes->args[used++].u.member = mn;
4580 if(!(mn->modes & MODE_VOICE))
4582 changes->args[used].mode = MODE_VOICE;
4583 changes->args[used++].u.member = mn;
4590 changes->args[used].mode = MODE_REMOVE | mn->modes;
4591 changes->args[used++].u.member = mn;
4595 changes->argc = used;
4596 modcmd_chanmode_announce(changes);
4597 mod_chanmode_free(changes);
4598 reply("CSMSG_RESYNCED_USERS", channel->name);
4602 static CHANSERV_FUNC(cmd_seen)
4604 struct userData *uData;
4605 struct handle_info *handle;
4606 char seen[INTERVALLEN];
4610 if(!irccasecmp(argv[1], chanserv->nick))
4612 reply("CSMSG_IS_CHANSERV");
4616 if(!(handle = get_handle_info(argv[1])))
4618 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4622 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4624 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4629 reply("CSMSG_USER_PRESENT", handle->handle);
4630 else if(uData->seen)
4631 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4633 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4635 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4636 reply("CSMSG_USER_VACATION", handle->handle);
4641 static MODCMD_FUNC(cmd_names)
4643 struct userNode *targ;
4644 struct userData *targData;
4645 unsigned int ii, pos;
4648 for(ii=pos=0; ii<channel->members.used; ++ii)
4650 targ = channel->members.list[ii]->user;
4651 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4654 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4657 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4661 if(IsUserSuspended(targData))
4663 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4666 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4667 reply("CSMSG_END_NAMES", channel->name);
4672 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4674 switch(ntype->visible_type)
4676 case NOTE_VIS_ALL: return 1;
4677 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4678 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4683 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4685 struct userData *uData;
4687 switch(ntype->set_access_type)
4689 case NOTE_SET_CHANNEL_ACCESS:
4690 if(!user->handle_info)
4692 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4694 return uData->access >= ntype->set_access.min_ulevel;
4695 case NOTE_SET_CHANNEL_SETTER:
4696 return check_user_level(channel, user, lvlSetters, 1, 0);
4697 case NOTE_SET_PRIVILEGED: default:
4698 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4702 static CHANSERV_FUNC(cmd_note)
4704 struct chanData *cData;
4706 struct note_type *ntype;
4708 cData = channel->channel_info;
4711 reply("CSMSG_NOT_REGISTERED", channel->name);
4715 /* If no arguments, show all visible notes for the channel. */
4721 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4723 note = iter_data(it);
4724 if(!note_type_visible_to_user(cData, note->type, user))
4727 reply("CSMSG_NOTELIST_HEADER", channel->name);
4728 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4731 reply("CSMSG_NOTELIST_END", channel->name);
4733 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4735 /* If one argument, show the named note. */
4738 if((note = dict_find(cData->notes, argv[1], NULL))
4739 && note_type_visible_to_user(cData, note->type, user))
4741 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4743 else if((ntype = dict_find(note_types, argv[1], NULL))
4744 && note_type_visible_to_user(NULL, ntype, user))
4746 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4751 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4755 /* Assume they're trying to set a note. */
4759 ntype = dict_find(note_types, argv[1], NULL);
4762 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4765 else if(note_type_settable_by_user(channel, ntype, user))
4767 note_text = unsplit_string(argv+2, argc-2, NULL);
4768 if((note = dict_find(cData->notes, argv[1], NULL)))
4769 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4770 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4771 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4773 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4775 /* The note is viewable to staff only, so return 0
4776 to keep the invocation from getting logged (or
4777 regular users can see it in !events). */
4783 reply("CSMSG_NO_ACCESS");
4790 static CHANSERV_FUNC(cmd_delnote)
4795 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4796 || !note_type_settable_by_user(channel, note->type, user))
4798 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4801 dict_remove(channel->channel_info->notes, note->type->name);
4802 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4806 static CHANSERV_FUNC(cmd_events)
4808 struct logSearch discrim;
4809 struct logReport report;
4810 unsigned int matches, limit;
4812 limit = (argc > 1) ? atoi(argv[1]) : 10;
4813 if(limit < 1 || limit > 200)
4816 memset(&discrim, 0, sizeof(discrim));
4817 discrim.masks.bot = chanserv;
4818 discrim.masks.channel_name = channel->name;
4820 discrim.masks.command = argv[2];
4821 discrim.limit = limit;
4822 discrim.max_time = INT_MAX;
4823 discrim.severities = 1 << LOG_COMMAND;
4824 report.reporter = chanserv;
4826 reply("CSMSG_EVENT_SEARCH_RESULTS");
4827 matches = log_entry_search(&discrim, log_report_entry, &report);
4829 reply("MSG_MATCH_COUNT", matches);
4831 reply("MSG_NO_MATCHES");
4835 static CHANSERV_FUNC(cmd_say)
4841 msg = unsplit_string(argv + 1, argc - 1, NULL);
4842 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4844 else if(GetUserH(argv[1]))
4847 msg = unsplit_string(argv + 2, argc - 2, NULL);
4848 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4852 reply("MSG_NOT_TARGET_NAME");
4858 static CHANSERV_FUNC(cmd_emote)
4864 /* CTCP is so annoying. */
4865 msg = unsplit_string(argv + 1, argc - 1, NULL);
4866 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4868 else if(GetUserH(argv[1]))
4870 msg = unsplit_string(argv + 2, argc - 2, NULL);
4871 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4875 reply("MSG_NOT_TARGET_NAME");
4881 struct channelList *
4882 chanserv_support_channels(void)
4884 return &chanserv_conf.support_channels;
4887 static CHANSERV_FUNC(cmd_expire)
4889 int channel_count = registered_channels;
4890 expire_channels(NULL);
4891 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4896 chanserv_expire_suspension(void *data)
4898 struct suspended *suspended = data;
4899 struct chanNode *channel;
4901 if(!suspended->expires || (now < suspended->expires))
4902 suspended->revoked = now;
4903 channel = suspended->cData->channel;
4904 suspended->cData->channel = channel;
4905 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4906 if(!IsOffChannel(suspended->cData))
4908 struct mod_chanmode change;
4909 mod_chanmode_init(&change);
4911 change.args[0].mode = MODE_CHANOP;
4912 change.args[0].u.member = AddChannelUser(chanserv, channel);
4913 mod_chanmode_announce(chanserv, channel, &change);
4917 static CHANSERV_FUNC(cmd_csuspend)
4919 struct suspended *suspended;
4920 char reason[MAXLEN];
4921 unsigned long expiry, duration;
4922 struct userData *uData;
4926 if(IsProtected(channel->channel_info))
4928 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4932 if(argv[1][0] == '!')
4934 else if(IsSuspended(channel->channel_info))
4936 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4937 show_suspension_info(cmd, user, channel->channel_info->suspended);
4941 if(!strcmp(argv[1], "0"))
4943 else if((duration = ParseInterval(argv[1])))
4944 expiry = now + duration;
4947 reply("MSG_INVALID_DURATION", argv[1]);
4951 unsplit_string(argv + 2, argc - 2, reason);
4953 suspended = calloc(1, sizeof(*suspended));
4954 suspended->revoked = 0;
4955 suspended->issued = now;
4956 suspended->suspender = strdup(user->handle_info->handle);
4957 suspended->expires = expiry;
4958 suspended->reason = strdup(reason);
4959 suspended->cData = channel->channel_info;
4960 suspended->previous = suspended->cData->suspended;
4961 suspended->cData->suspended = suspended;
4963 if(suspended->expires)
4964 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4966 if(IsSuspended(channel->channel_info))
4968 suspended->previous->revoked = now;
4969 if(suspended->previous->expires)
4970 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4971 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4972 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4976 /* Mark all users in channel as absent. */
4977 for(uData = channel->channel_info->users; uData; uData = uData->next)
4986 /* Mark the channel as suspended, then part. */
4987 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4988 DelChannelUser(chanserv, channel, suspended->reason, 0);
4989 reply("CSMSG_SUSPENDED", channel->name);
4990 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4991 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4996 static CHANSERV_FUNC(cmd_cunsuspend)
4998 struct suspended *suspended;
4999 char message[MAXLEN];
5001 if(!IsSuspended(channel->channel_info))
5003 reply("CSMSG_NOT_SUSPENDED", channel->name);
5007 suspended = channel->channel_info->suspended;
5009 /* Expire the suspension and join ChanServ to the channel. */
5010 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5011 chanserv_expire_suspension(suspended);
5012 reply("CSMSG_UNSUSPENDED", channel->name);
5013 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5014 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5018 typedef struct chanservSearch
5023 unsigned long unvisited;
5024 unsigned long registered;
5026 unsigned long flags;
5030 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5033 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5038 search = malloc(sizeof(struct chanservSearch));
5039 memset(search, 0, sizeof(*search));
5042 for(i = 0; i < argc; i++)
5044 /* Assume all criteria require arguments. */
5047 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5051 if(!irccasecmp(argv[i], "name"))
5052 search->name = argv[++i];
5053 else if(!irccasecmp(argv[i], "registrar"))
5054 search->registrar = argv[++i];
5055 else if(!irccasecmp(argv[i], "unvisited"))
5056 search->unvisited = ParseInterval(argv[++i]);
5057 else if(!irccasecmp(argv[i], "registered"))
5058 search->registered = ParseInterval(argv[++i]);
5059 else if(!irccasecmp(argv[i], "flags"))
5062 if(!irccasecmp(argv[i], "nodelete"))
5063 search->flags |= CHANNEL_NODELETE;
5064 else if(!irccasecmp(argv[i], "suspended"))
5065 search->flags |= CHANNEL_SUSPENDED;
5066 else if(!irccasecmp(argv[i], "unreviewed"))
5067 search->flags |= CHANNEL_UNREVIEWED;
5070 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5074 else if(!irccasecmp(argv[i], "limit"))
5075 search->limit = strtoul(argv[++i], NULL, 10);
5078 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5083 if(search->name && !strcmp(search->name, "*"))
5085 if(search->registrar && !strcmp(search->registrar, "*"))
5086 search->registrar = 0;
5095 chanserv_channel_match(struct chanData *channel, search_t search)
5097 const char *name = channel->channel->name;
5098 if((search->name && !match_ircglob(name, search->name)) ||
5099 (search->registrar && !channel->registrar) ||
5100 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5101 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5102 (search->registered && (now - channel->registered) > search->registered) ||
5103 (search->flags && ((search->flags & channel->flags) != search->flags)))
5110 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5112 struct chanData *channel;
5113 unsigned int matches = 0;
5115 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5117 if(!chanserv_channel_match(channel, search))
5127 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5132 search_print(struct chanData *channel, void *data)
5134 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5137 static CHANSERV_FUNC(cmd_search)
5140 unsigned int matches;
5141 channel_search_func action;
5145 if(!irccasecmp(argv[1], "count"))
5146 action = search_count;
5147 else if(!irccasecmp(argv[1], "print"))
5148 action = search_print;
5151 reply("CSMSG_ACTION_INVALID", argv[1]);
5155 search = chanserv_search_create(user, argc - 2, argv + 2);
5159 if(action == search_count)
5160 search->limit = INT_MAX;
5162 if(action == search_print)
5163 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5165 matches = chanserv_channel_search(search, action, user);
5168 reply("MSG_MATCH_COUNT", matches);
5170 reply("MSG_NO_MATCHES");
5176 static CHANSERV_FUNC(cmd_unvisited)
5178 struct chanData *cData;
5179 unsigned long interval = chanserv_conf.channel_expire_delay;
5180 char buffer[INTERVALLEN];
5181 unsigned int limit = 25, matches = 0;
5185 interval = ParseInterval(argv[1]);
5187 limit = atoi(argv[2]);
5190 intervalString(buffer, interval, user->handle_info);
5191 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5193 for(cData = channelList; cData && matches < limit; cData = cData->next)
5195 if((now - cData->visited) < interval)
5198 intervalString(buffer, now - cData->visited, user->handle_info);
5199 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5206 static MODCMD_FUNC(chan_opt_defaulttopic)
5212 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5214 reply("CSMSG_TOPIC_LOCKED", channel->name);
5218 topic = unsplit_string(argv+1, argc-1, NULL);
5220 free(channel->channel_info->topic);
5221 if(topic[0] == '*' && topic[1] == 0)
5223 topic = channel->channel_info->topic = NULL;
5227 topic = channel->channel_info->topic = strdup(topic);
5228 if(channel->channel_info->topic_mask
5229 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5230 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5232 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5235 if(channel->channel_info->topic)
5236 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5238 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5242 static MODCMD_FUNC(chan_opt_topicmask)
5246 struct chanData *cData = channel->channel_info;
5249 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5251 reply("CSMSG_TOPIC_LOCKED", channel->name);
5255 mask = unsplit_string(argv+1, argc-1, NULL);
5257 if(cData->topic_mask)
5258 free(cData->topic_mask);
5259 if(mask[0] == '*' && mask[1] == 0)
5261 cData->topic_mask = 0;
5265 cData->topic_mask = strdup(mask);
5267 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5268 else if(!match_ircglob(cData->topic, cData->topic_mask))
5269 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5273 if(channel->channel_info->topic_mask)
5274 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5276 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5280 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5284 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5288 if(greeting[0] == '*' && greeting[1] == 0)
5292 unsigned int length = strlen(greeting);
5293 if(length > chanserv_conf.greeting_length)
5295 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5298 *data = strdup(greeting);
5307 reply(name, user_find_message(user, "MSG_NONE"));
5311 static MODCMD_FUNC(chan_opt_greeting)
5313 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5316 static MODCMD_FUNC(chan_opt_usergreeting)
5318 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5321 static MODCMD_FUNC(chan_opt_modes)
5323 struct mod_chanmode *new_modes;
5324 char modes[MODELEN];
5328 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5330 reply("CSMSG_NO_ACCESS");
5333 if(argv[1][0] == '*' && argv[1][1] == 0)
5335 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5337 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5339 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5342 else if(new_modes->argc > 1)
5344 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5345 mod_chanmode_free(new_modes);
5350 channel->channel_info->modes = *new_modes;
5351 modcmd_chanmode_announce(new_modes);
5352 mod_chanmode_free(new_modes);
5356 mod_chanmode_format(&channel->channel_info->modes, modes);
5358 reply("CSMSG_SET_MODES", modes);
5360 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5364 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5366 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5368 struct chanData *cData = channel->channel_info;
5373 /* Set flag according to value. */
5374 if(enabled_string(argv[1]))
5376 cData->flags |= mask;
5379 else if(disabled_string(argv[1]))
5381 cData->flags &= ~mask;
5386 reply("MSG_INVALID_BINARY", argv[1]);
5392 /* Find current option value. */
5393 value = (cData->flags & mask) ? 1 : 0;
5397 reply(name, user_find_message(user, "MSG_ON"));
5399 reply(name, user_find_message(user, "MSG_OFF"));
5403 static MODCMD_FUNC(chan_opt_nodelete)
5405 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5407 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5411 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5414 static MODCMD_FUNC(chan_opt_dynlimit)
5416 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5419 static MODCMD_FUNC(chan_opt_offchannel)
5421 struct chanData *cData = channel->channel_info;
5426 /* Set flag according to value. */
5427 if(enabled_string(argv[1]))
5429 if(!IsOffChannel(cData))
5430 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5431 cData->flags |= CHANNEL_OFFCHANNEL;
5434 else if(disabled_string(argv[1]))
5436 if(IsOffChannel(cData))
5438 struct mod_chanmode change;
5439 mod_chanmode_init(&change);
5441 change.args[0].mode = MODE_CHANOP;
5442 change.args[0].u.member = AddChannelUser(chanserv, channel);
5443 mod_chanmode_announce(chanserv, channel, &change);
5445 cData->flags &= ~CHANNEL_OFFCHANNEL;
5450 reply("MSG_INVALID_BINARY", argv[1]);
5456 /* Find current option value. */
5457 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5461 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5463 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5467 static MODCMD_FUNC(chan_opt_unreviewed)
5469 struct chanData *cData = channel->channel_info;
5470 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5476 /* The two directions can have different ACLs. */
5477 if(enabled_string(argv[1]))
5479 else if(disabled_string(argv[1]))
5483 reply("MSG_INVALID_BINARY", argv[1]);
5487 if (new_value != value)
5489 struct svccmd *subcmd;
5490 char subcmd_name[32];
5492 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5493 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5496 reply("MSG_COMMAND_DISABLED", subcmd_name);
5499 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5503 cData->flags |= CHANNEL_UNREVIEWED;
5506 free(cData->registrar);
5507 cData->registrar = strdup(user->handle_info->handle);
5508 cData->flags &= ~CHANNEL_UNREVIEWED;
5515 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5517 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5521 static MODCMD_FUNC(chan_opt_defaults)
5523 struct userData *uData;
5524 struct chanData *cData;
5525 const char *confirm;
5526 enum levelOption lvlOpt;
5527 enum charOption chOpt;
5529 cData = channel->channel_info;
5530 uData = GetChannelUser(cData, user->handle_info);
5531 if(!uData || (uData->access < UL_OWNER))
5533 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5536 confirm = make_confirmation_string(uData);
5537 if((argc < 2) || strcmp(argv[1], confirm))
5539 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5542 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5543 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5544 cData->modes = chanserv_conf.default_modes;
5545 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5546 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5547 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5548 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5549 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5554 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5556 struct chanData *cData = channel->channel_info;
5557 struct userData *uData;
5558 unsigned short value;
5562 if(!check_user_level(channel, user, option, 1, 1))
5564 reply("CSMSG_CANNOT_SET");
5567 value = user_level_from_name(argv[1], UL_OWNER+1);
5568 if(!value && strcmp(argv[1], "0"))
5570 reply("CSMSG_INVALID_ACCESS", argv[1]);
5573 uData = GetChannelUser(cData, user->handle_info);
5574 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5576 reply("CSMSG_BAD_SETLEVEL");
5582 if(value > cData->lvlOpts[lvlGiveOps])
5584 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5589 if(value < cData->lvlOpts[lvlGiveVoice])
5591 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5596 /* This test only applies to owners, since non-owners
5597 * trying to set an option to above their level get caught
5598 * by the CSMSG_BAD_SETLEVEL test above.
5600 if(value > uData->access)
5602 reply("CSMSG_BAD_SETTERS");
5609 cData->lvlOpts[option] = value;
5611 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5615 static MODCMD_FUNC(chan_opt_enfops)
5617 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5620 static MODCMD_FUNC(chan_opt_giveops)
5622 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5625 static MODCMD_FUNC(chan_opt_enfmodes)
5627 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5630 static MODCMD_FUNC(chan_opt_enftopic)
5632 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5635 static MODCMD_FUNC(chan_opt_pubcmd)
5637 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5640 static MODCMD_FUNC(chan_opt_setters)
5642 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5645 static MODCMD_FUNC(chan_opt_ctcpusers)
5647 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5650 static MODCMD_FUNC(chan_opt_userinfo)
5652 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5655 static MODCMD_FUNC(chan_opt_givevoice)
5657 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5660 static MODCMD_FUNC(chan_opt_topicsnarf)
5662 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5665 static MODCMD_FUNC(chan_opt_inviteme)
5667 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5671 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5673 struct chanData *cData = channel->channel_info;
5674 int count = charOptions[option].count, index;
5678 index = atoi(argv[1]);
5680 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5682 reply("CSMSG_INVALID_NUMERIC", index);
5683 /* Show possible values. */
5684 for(index = 0; index < count; index++)
5685 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5689 cData->chOpts[option] = charOptions[option].values[index].value;
5693 /* Find current option value. */
5696 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5700 /* Somehow, the option value is corrupt; reset it to the default. */
5701 cData->chOpts[option] = charOptions[option].default_value;
5706 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5710 static MODCMD_FUNC(chan_opt_protect)
5712 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5715 static MODCMD_FUNC(chan_opt_toys)
5717 return channel_multiple_option(chToys, CSFUNC_ARGS);
5720 static MODCMD_FUNC(chan_opt_ctcpreaction)
5722 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5725 static MODCMD_FUNC(chan_opt_topicrefresh)
5727 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5730 static struct svccmd_list set_shows_list;
5733 handle_svccmd_unbind(struct svccmd *target) {
5735 for(ii=0; ii<set_shows_list.used; ++ii)
5736 if(target == set_shows_list.list[ii])
5737 set_shows_list.used = 0;
5740 static CHANSERV_FUNC(cmd_set)
5742 struct svccmd *subcmd;
5746 /* Check if we need to (re-)initialize set_shows_list. */
5747 if(!set_shows_list.used)
5749 if(!set_shows_list.size)
5751 set_shows_list.size = chanserv_conf.set_shows->used;
5752 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5754 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5756 const char *name = chanserv_conf.set_shows->list[ii];
5757 sprintf(buf, "%s %s", argv[0], name);
5758 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5761 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5764 svccmd_list_append(&set_shows_list, subcmd);
5770 reply("CSMSG_CHANNEL_OPTIONS");
5771 for(ii = 0; ii < set_shows_list.used; ii++)
5773 subcmd = set_shows_list.list[ii];
5774 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5779 sprintf(buf, "%s %s", argv[0], argv[1]);
5780 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5783 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5786 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5788 reply("CSMSG_NO_ACCESS");
5794 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5798 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5800 struct userData *uData;
5802 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5805 reply("CSMSG_NOT_USER", channel->name);
5811 /* Just show current option value. */
5813 else if(enabled_string(argv[1]))
5815 uData->flags |= mask;
5817 else if(disabled_string(argv[1]))
5819 uData->flags &= ~mask;
5823 reply("MSG_INVALID_BINARY", argv[1]);
5827 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5831 static MODCMD_FUNC(user_opt_noautoop)
5833 struct userData *uData;
5835 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5838 reply("CSMSG_NOT_USER", channel->name);
5841 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5842 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5844 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5847 static MODCMD_FUNC(user_opt_autoinvite)
5849 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5852 static MODCMD_FUNC(user_opt_info)
5854 struct userData *uData;
5857 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5861 /* If they got past the command restrictions (which require access)
5862 * but fail this test, we have some fool with security override on.
5864 reply("CSMSG_NOT_USER", channel->name);
5871 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5872 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5874 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5877 bp = strcspn(infoline, "\001");
5880 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5885 if(infoline[0] == '*' && infoline[1] == 0)
5888 uData->info = strdup(infoline);
5891 reply("CSMSG_USET_INFO", uData->info);
5893 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5897 struct svccmd_list uset_shows_list;
5899 static CHANSERV_FUNC(cmd_uset)
5901 struct svccmd *subcmd;
5905 /* Check if we need to (re-)initialize uset_shows_list. */
5906 if(!uset_shows_list.used)
5910 "NoAutoOp", "AutoInvite", "Info"
5913 if(!uset_shows_list.size)
5915 uset_shows_list.size = ArrayLength(options);
5916 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5918 for(ii = 0; ii < ArrayLength(options); ii++)
5920 const char *name = options[ii];
5921 sprintf(buf, "%s %s", argv[0], name);
5922 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5925 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5928 svccmd_list_append(&uset_shows_list, subcmd);
5934 /* Do this so options are presented in a consistent order. */
5935 reply("CSMSG_USER_OPTIONS");
5936 for(ii = 0; ii < uset_shows_list.used; ii++)
5937 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5941 sprintf(buf, "%s %s", argv[0], argv[1]);
5942 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5945 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5949 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5952 static CHANSERV_FUNC(cmd_giveownership)
5954 struct handle_info *new_owner_hi;
5955 struct userData *new_owner;
5956 struct userData *curr_user;
5957 struct userData *invoker;
5958 struct chanData *cData = channel->channel_info;
5959 struct do_not_register *dnr;
5960 const char *confirm;
5962 unsigned short co_access;
5963 char reason[MAXLEN];
5966 curr_user = GetChannelAccess(cData, user->handle_info);
5967 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5968 if(!curr_user || (curr_user->access != UL_OWNER))
5970 struct userData *owner = NULL;
5971 for(curr_user = channel->channel_info->users;
5973 curr_user = curr_user->next)
5975 if(curr_user->access != UL_OWNER)
5979 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5986 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
5988 char delay[INTERVALLEN];
5989 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5990 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5993 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5995 if(new_owner_hi == user->handle_info)
5997 reply("CSMSG_NO_TRANSFER_SELF");
6000 new_owner = GetChannelAccess(cData, new_owner_hi);
6005 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6009 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6013 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6015 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6018 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6019 if(!IsHelping(user))
6020 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6022 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6025 invoker = GetChannelUser(cData, user->handle_info);
6026 if(invoker->access <= UL_OWNER)
6028 confirm = make_confirmation_string(curr_user);
6029 if((argc < 3) || strcmp(argv[2], confirm))
6031 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6035 if(new_owner->access >= UL_COOWNER)
6036 co_access = new_owner->access;
6038 co_access = UL_COOWNER;
6039 new_owner->access = UL_OWNER;
6041 curr_user->access = co_access;
6042 cData->ownerTransfer = now;
6043 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6044 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6045 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6049 static CHANSERV_FUNC(cmd_suspend)
6051 struct handle_info *hi;
6052 struct userData *self, *real_self, *target;
6053 unsigned int override = 0;
6056 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6057 self = GetChannelUser(channel->channel_info, user->handle_info);
6058 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6059 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6061 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6064 if(target->access >= self->access)
6066 reply("MSG_USER_OUTRANKED", hi->handle);
6069 if(target->flags & USER_SUSPENDED)
6071 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6076 target->present = 0;
6079 if(!real_self || target->access >= real_self->access)
6080 override = CMD_LOG_OVERRIDE;
6081 target->flags |= USER_SUSPENDED;
6082 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6083 return 1 | override;
6086 static CHANSERV_FUNC(cmd_unsuspend)
6088 struct handle_info *hi;
6089 struct userData *self, *real_self, *target;
6090 unsigned int override = 0;
6093 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6094 self = GetChannelUser(channel->channel_info, user->handle_info);
6095 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6096 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6098 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6101 if(target->access >= self->access)
6103 reply("MSG_USER_OUTRANKED", hi->handle);
6106 if(!(target->flags & USER_SUSPENDED))
6108 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6111 if(!real_self || target->access >= real_self->access)
6112 override = CMD_LOG_OVERRIDE;
6113 target->flags &= ~USER_SUSPENDED;
6114 scan_user_presence(target, NULL);
6115 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6116 return 1 | override;
6119 static MODCMD_FUNC(cmd_deleteme)
6121 struct handle_info *hi;
6122 struct userData *target;
6123 const char *confirm_string;
6124 unsigned short access;
6127 hi = user->handle_info;
6128 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6130 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6133 if(target->access == UL_OWNER)
6135 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6138 confirm_string = make_confirmation_string(target);
6139 if((argc < 2) || strcmp(argv[1], confirm_string))
6141 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6144 access = target->access;
6145 channel_name = strdup(channel->name);
6146 del_channel_user(target, 1);
6147 reply("CSMSG_DELETED_YOU", access, channel_name);
6153 chanserv_refresh_topics(UNUSED_ARG(void *data))
6155 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6156 struct chanData *cData;
6159 for(cData = channelList; cData; cData = cData->next)
6161 if(IsSuspended(cData))
6163 opt = cData->chOpts[chTopicRefresh];
6166 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6169 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6170 cData->last_refresh = refresh_num;
6172 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6175 static CHANSERV_FUNC(cmd_unf)
6179 char response[MAXLEN];
6180 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6181 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6182 irc_privmsg(cmd->parent->bot, channel->name, response);
6185 reply("CSMSG_UNF_RESPONSE");
6189 static CHANSERV_FUNC(cmd_ping)
6193 char response[MAXLEN];
6194 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6195 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6196 irc_privmsg(cmd->parent->bot, channel->name, response);
6199 reply("CSMSG_PING_RESPONSE");
6203 static CHANSERV_FUNC(cmd_wut)
6207 char response[MAXLEN];
6208 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6209 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6210 irc_privmsg(cmd->parent->bot, channel->name, response);
6213 reply("CSMSG_WUT_RESPONSE");
6217 static CHANSERV_FUNC(cmd_8ball)
6219 unsigned int i, j, accum;
6224 for(i=1; i<argc; i++)
6225 for(j=0; argv[i][j]; j++)
6226 accum = (accum << 5) - accum + toupper(argv[i][j]);
6227 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6230 char response[MAXLEN];
6231 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6232 irc_privmsg(cmd->parent->bot, channel->name, response);
6235 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6239 static CHANSERV_FUNC(cmd_d)
6241 unsigned long sides, count, modifier, ii, total;
6242 char response[MAXLEN], *sep;
6246 if((count = strtoul(argv[1], &sep, 10)) < 1)
6256 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6257 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6261 else if((sep[0] == '-') && isdigit(sep[1]))
6262 modifier = strtoul(sep, NULL, 10);
6263 else if((sep[0] == '+') && isdigit(sep[1]))
6264 modifier = strtoul(sep+1, NULL, 10);
6271 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6276 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6279 for(total = ii = 0; ii < count; ++ii)
6280 total += (rand() % sides) + 1;
6283 if((count > 1) || modifier)
6285 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6286 sprintf(response, fmt, total, count, sides, modifier);
6290 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6291 sprintf(response, fmt, total, sides);
6294 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6296 send_message_type(4, user, cmd->parent->bot, "%s", response);
6300 static CHANSERV_FUNC(cmd_huggle)
6302 /* CTCP must be via PRIVMSG, never notice */
6304 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6306 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6311 chanserv_adjust_limit(void *data)
6313 struct mod_chanmode change;
6314 struct chanData *cData = data;
6315 struct chanNode *channel = cData->channel;
6318 if(IsSuspended(cData))
6321 cData->limitAdjusted = now;
6322 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6323 if(cData->modes.modes_set & MODE_LIMIT)
6325 if(limit > cData->modes.new_limit)
6326 limit = cData->modes.new_limit;
6327 else if(limit == cData->modes.new_limit)
6331 mod_chanmode_init(&change);
6332 change.modes_set = MODE_LIMIT;
6333 change.new_limit = limit;
6334 mod_chanmode_announce(chanserv, channel, &change);
6338 handle_new_channel(struct chanNode *channel)
6340 struct chanData *cData;
6342 if(!(cData = channel->channel_info))
6345 if(cData->modes.modes_set || cData->modes.modes_clear)
6346 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6348 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6349 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6352 /* Welcome to my worst nightmare. Warning: Read (or modify)
6353 the code below at your own risk. */
6355 handle_join(struct modeNode *mNode)
6357 struct mod_chanmode change;
6358 struct userNode *user = mNode->user;
6359 struct chanNode *channel = mNode->channel;
6360 struct chanData *cData;
6361 struct userData *uData = NULL;
6362 struct banData *bData;
6363 struct handle_info *handle;
6364 unsigned int modes = 0, info = 0;
6367 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6370 cData = channel->channel_info;
6371 if(channel->members.used > cData->max)
6372 cData->max = channel->members.used;
6374 /* Check for bans. If they're joining through a ban, one of two
6376 * 1: Join during a netburst, by riding the break. Kick them
6377 * unless they have ops or voice in the channel.
6378 * 2: They're allowed to join through the ban (an invite in
6379 * ircu2.10, or a +e on Hybrid, or something).
6380 * If they're not joining through a ban, and the banlist is not
6381 * full, see if they're on the banlist for the channel. If so,
6384 if(user->uplink->burst && !mNode->modes)
6387 for(ii = 0; ii < channel->banlist.used; ii++)
6389 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6391 /* Riding a netburst. Naughty. */
6392 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6398 mod_chanmode_init(&change);
6400 if(channel->banlist.used < MAXBANS)
6402 /* Not joining through a ban. */
6403 for(bData = cData->bans;
6404 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6405 bData = bData->next);
6409 char kick_reason[MAXLEN];
6410 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6412 bData->triggered = now;
6413 if(bData != cData->bans)
6415 /* Shuffle the ban to the head of the list. */
6417 bData->next->prev = bData->prev;
6419 bData->prev->next = bData->next;
6422 bData->next = cData->bans;
6425 cData->bans->prev = bData;
6426 cData->bans = bData;
6429 change.args[0].mode = MODE_BAN;
6430 change.args[0].u.hostmask = bData->mask;
6431 mod_chanmode_announce(chanserv, channel, &change);
6432 KickChannelUser(user, channel, chanserv, kick_reason);
6437 /* ChanServ will not modify the limits in join-flooded channels.
6438 It will also skip DynLimit processing when the user (or srvx)
6439 is bursting in, because there are likely more incoming. */
6440 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6441 && !user->uplink->burst
6442 && !channel->join_flooded
6443 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6445 /* The user count has begun "bumping" into the channel limit,
6446 so set a timer to raise the limit a bit. Any previous
6447 timers are removed so three incoming users within the delay
6448 results in one limit change, not three. */
6450 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6451 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6454 if(channel->join_flooded)
6456 /* don't automatically give ops or voice during a join flood */
6458 else if(cData->lvlOpts[lvlGiveOps] == 0)
6459 modes |= MODE_CHANOP;
6460 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6461 modes |= MODE_VOICE;
6463 greeting = cData->greeting;
6464 if(user->handle_info)
6466 handle = user->handle_info;
6468 if(IsHelper(user) && !IsHelping(user))
6471 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6473 if(channel == chanserv_conf.support_channels.list[ii])
6475 HANDLE_SET_FLAG(user->handle_info, HELPING);
6481 uData = GetTrueChannelAccess(cData, handle);
6482 if(uData && !IsUserSuspended(uData))
6484 /* Ops and above were handled by the above case. */
6485 if(IsUserAutoOp(uData))
6487 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6488 modes |= MODE_CHANOP;
6489 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6490 modes |= MODE_VOICE;
6492 if(uData->access >= UL_PRESENT)
6493 cData->visited = now;
6494 if(cData->user_greeting)
6495 greeting = cData->user_greeting;
6497 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6498 && ((now - uData->seen) >= chanserv_conf.info_delay)
6506 /* If user joining normally (not during burst), apply op or voice,
6507 * and send greeting/userinfo as appropriate.
6509 if(!user->uplink->burst)
6513 if(modes & MODE_CHANOP)
6514 modes &= ~MODE_VOICE;
6515 change.args[0].mode = modes;
6516 change.args[0].u.member = mNode;
6517 mod_chanmode_announce(chanserv, channel, &change);
6520 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6522 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6528 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6530 struct mod_chanmode change;
6531 struct userData *channel;
6532 unsigned int ii, jj;
6534 if(!user->handle_info)
6537 mod_chanmode_init(&change);
6539 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6541 struct chanNode *cn;
6542 struct modeNode *mn;
6543 if(IsUserSuspended(channel)
6544 || IsSuspended(channel->channel)
6545 || !(cn = channel->channel->channel))
6548 mn = GetUserMode(cn, user);
6551 if(!IsUserSuspended(channel)
6552 && IsUserAutoInvite(channel)
6553 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6555 && !user->uplink->burst)
6556 irc_invite(chanserv, user, cn);
6560 if(channel->access >= UL_PRESENT)
6561 channel->channel->visited = now;
6563 if(IsUserAutoOp(channel))
6565 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6566 change.args[0].mode = MODE_CHANOP;
6567 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6568 change.args[0].mode = MODE_VOICE;
6570 change.args[0].mode = 0;
6571 change.args[0].u.member = mn;
6572 if(change.args[0].mode)
6573 mod_chanmode_announce(chanserv, cn, &change);
6576 channel->seen = now;
6577 channel->present = 1;
6580 for(ii = 0; ii < user->channels.used; ++ii)
6582 struct chanNode *channel = user->channels.list[ii]->channel;
6583 struct banData *ban;
6585 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6586 || !channel->channel_info
6587 || IsSuspended(channel->channel_info))
6589 for(jj = 0; jj < channel->banlist.used; ++jj)
6590 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6592 if(jj < channel->banlist.used)
6594 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6596 char kick_reason[MAXLEN];
6597 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6599 change.args[0].mode = MODE_BAN;
6600 change.args[0].u.hostmask = ban->mask;
6601 mod_chanmode_announce(chanserv, channel, &change);
6602 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6603 KickChannelUser(user, channel, chanserv, kick_reason);
6604 ban->triggered = now;
6609 if(IsSupportHelper(user))
6611 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6613 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6615 HANDLE_SET_FLAG(user->handle_info, HELPING);
6623 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6625 struct chanData *cData;
6626 struct userData *uData;
6628 cData = mn->channel->channel_info;
6629 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6632 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6634 /* Allow for a bit of padding so that the limit doesn't
6635 track the user count exactly, which could get annoying. */
6636 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6638 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6639 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6643 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6645 scan_user_presence(uData, mn->user);
6647 if (uData->access >= UL_PRESENT)
6648 cData->visited = now;
6651 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6653 unsigned int ii, jj;
6654 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6656 for(jj = 0; jj < mn->user->channels.used; ++jj)
6657 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6659 if(jj < mn->user->channels.used)
6662 if(ii == chanserv_conf.support_channels.used)
6663 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6668 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6670 struct userData *uData;
6672 if(!channel->channel_info || !kicker || IsService(kicker)
6673 || (kicker == victim) || IsSuspended(channel->channel_info)
6674 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6677 if(protect_user(victim, kicker, channel->channel_info))
6679 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6680 KickChannelUser(kicker, channel, chanserv, reason);
6683 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6688 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6690 struct chanData *cData;
6692 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6695 cData = channel->channel_info;
6696 if(bad_topic(channel, user, channel->topic))
6698 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6699 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6700 SetChannelTopic(channel, chanserv, old_topic, 1);
6701 else if(cData->topic)
6702 SetChannelTopic(channel, chanserv, cData->topic, 1);
6705 /* With topicsnarf, grab the topic and save it as the default topic. */
6706 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6709 cData->topic = strdup(channel->topic);
6715 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6717 struct mod_chanmode *bounce = NULL;
6718 unsigned int bnc, ii;
6721 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6724 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6725 && mode_lock_violated(&channel->channel_info->modes, change))
6727 char correct[MAXLEN];
6728 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6729 mod_chanmode_format(&channel->channel_info->modes, correct);
6730 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6732 for(ii = bnc = 0; ii < change->argc; ++ii)
6734 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6736 const struct userNode *victim = change->args[ii].u.member->user;
6737 if(!protect_user(victim, user, channel->channel_info))
6740 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6743 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6744 bounce->args[bnc].u.member = GetUserMode(channel, user);
6745 if(bounce->args[bnc].u.member)
6749 bounce->args[bnc].mode = MODE_CHANOP;
6750 bounce->args[bnc].u.member = change->args[ii].u.member;
6752 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6754 else if(change->args[ii].mode & MODE_CHANOP)
6756 const struct userNode *victim = change->args[ii].u.member->user;
6757 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6760 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6761 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6762 bounce->args[bnc].u.member = change->args[ii].u.member;
6765 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6767 const char *ban = change->args[ii].u.hostmask;
6768 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6771 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6772 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6773 bounce->args[bnc].u.hostmask = strdup(ban);
6775 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6780 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6781 mod_chanmode_announce(chanserv, channel, bounce);
6782 for(ii = 0; ii < change->argc; ++ii)
6783 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6784 free((char*)bounce->args[ii].u.hostmask);
6785 mod_chanmode_free(bounce);
6790 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6792 struct chanNode *channel;
6793 struct banData *bData;
6794 struct mod_chanmode change;
6795 unsigned int ii, jj;
6796 char kick_reason[MAXLEN];
6798 mod_chanmode_init(&change);
6800 change.args[0].mode = MODE_BAN;
6801 for(ii = 0; ii < user->channels.used; ++ii)
6803 channel = user->channels.list[ii]->channel;
6804 /* Need not check for bans if they're opped or voiced. */
6805 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6807 /* Need not check for bans unless channel registration is active. */
6808 if(!channel->channel_info || IsSuspended(channel->channel_info))
6810 /* Look for a matching ban already on the channel. */
6811 for(jj = 0; jj < channel->banlist.used; ++jj)
6812 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6814 /* Need not act if we found one. */
6815 if(jj < channel->banlist.used)
6817 /* Look for a matching ban in this channel. */
6818 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6820 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6822 change.args[0].u.hostmask = bData->mask;
6823 mod_chanmode_announce(chanserv, channel, &change);
6824 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6825 KickChannelUser(user, channel, chanserv, kick_reason);
6826 bData->triggered = now;
6827 break; /* we don't need to check any more bans in the channel */
6832 static void handle_rename(struct handle_info *handle, const char *old_handle)
6834 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6838 dict_remove2(handle_dnrs, old_handle, 1);
6839 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6840 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6845 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6847 struct userNode *h_user;
6849 if(handle->channels)
6851 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6852 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6854 while(handle->channels)
6855 del_channel_user(handle->channels, 1);
6860 handle_server_link(UNUSED_ARG(struct server *server))
6862 struct chanData *cData;
6864 for(cData = channelList; cData; cData = cData->next)
6866 if(!IsSuspended(cData))
6867 cData->may_opchan = 1;
6868 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6869 && !cData->channel->join_flooded
6870 && ((cData->channel->limit - cData->channel->members.used)
6871 < chanserv_conf.adjust_threshold))
6873 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6874 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6880 chanserv_conf_read(void)
6884 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6885 struct mod_chanmode *change;
6886 struct string_list *strlist;
6887 struct chanNode *chan;
6890 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6892 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6895 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6896 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6897 chanserv_conf.support_channels.used = 0;
6898 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6900 for(ii = 0; ii < strlist->used; ++ii)
6902 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6905 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6907 channelList_append(&chanserv_conf.support_channels, chan);
6910 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6913 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6916 chan = AddChannel(str, now, str2, NULL);
6918 channelList_append(&chanserv_conf.support_channels, chan);
6920 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6921 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6922 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6923 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6924 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6925 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6926 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6927 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6928 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6929 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6930 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6931 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6932 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6933 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6934 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6935 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6936 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6937 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6938 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6939 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6940 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6941 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6942 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6943 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6944 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6946 NickChange(chanserv, str, 0);
6947 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6948 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6949 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6950 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6951 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6952 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6953 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6954 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6955 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6956 chanserv_conf.max_owned = str ? atoi(str) : 5;
6957 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6958 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6959 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6960 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6961 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6962 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6963 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6966 safestrncpy(mode_line, str, sizeof(mode_line));
6967 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6968 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6969 && (change->argc < 2))
6971 chanserv_conf.default_modes = *change;
6972 mod_chanmode_free(change);
6974 free_string_list(chanserv_conf.set_shows);
6975 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6977 strlist = string_list_copy(strlist);
6980 static const char *list[] = {
6981 /* free form text */
6982 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6983 /* options based on user level */
6984 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6985 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6986 /* multiple choice options */
6987 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6988 /* binary options */
6989 "DynLimit", "NoDelete",
6994 strlist = alloc_string_list(ArrayLength(list)-1);
6995 for(ii=0; list[ii]; ii++)
6996 string_list_append(strlist, strdup(list[ii]));
6998 chanserv_conf.set_shows = strlist;
6999 /* We don't look things up now, in case the list refers to options
7000 * defined by modules initialized after this point. Just mark the
7001 * function list as invalid, so it will be initialized.
7003 set_shows_list.used = 0;
7004 free_string_list(chanserv_conf.eightball);
7005 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7008 strlist = string_list_copy(strlist);
7012 strlist = alloc_string_list(4);
7013 string_list_append(strlist, strdup("Yes."));
7014 string_list_append(strlist, strdup("No."));
7015 string_list_append(strlist, strdup("Maybe so."));
7017 chanserv_conf.eightball = strlist;
7018 free_string_list(chanserv_conf.old_ban_names);
7019 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7021 strlist = string_list_copy(strlist);
7023 strlist = alloc_string_list(2);
7024 chanserv_conf.old_ban_names = strlist;
7025 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7026 off_channel = str ? atoi(str) : 0;
7030 chanserv_note_type_read(const char *key, struct record_data *rd)
7033 struct note_type *ntype;
7036 if(!(obj = GET_RECORD_OBJECT(rd)))
7038 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7041 if(!(ntype = chanserv_create_note_type(key)))
7043 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7047 /* Figure out set access */
7048 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7050 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7051 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7053 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7055 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7056 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7058 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7060 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7064 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7065 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7066 ntype->set_access.min_opserv = 0;
7069 /* Figure out visibility */
7070 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7071 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7072 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7073 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7074 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7075 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7076 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7077 ntype->visible_type = NOTE_VIS_ALL;
7079 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7081 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7082 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7086 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7088 struct handle_info *handle;
7089 struct userData *uData;
7090 char *seen, *inf, *flags;
7091 unsigned long last_seen;
7092 unsigned short access;
7094 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7096 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7100 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7101 if(access > UL_OWNER)
7103 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7107 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7108 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7109 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7110 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7111 handle = get_handle_info(key);
7114 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7118 uData = add_channel_user(chan, handle, access, last_seen, inf);
7119 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7123 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7125 struct banData *bData;
7126 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7127 unsigned long set_time, triggered_time, expires_time;
7129 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7131 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7135 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7136 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7137 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7138 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7139 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7140 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7141 if (!reason || !owner)
7144 set_time = set ? strtoul(set, NULL, 0) : now;
7145 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7147 expires_time = strtoul(s_expires, NULL, 0);
7149 expires_time = set_time + atoi(s_duration);
7153 if(!reason || (expires_time && (expires_time < now)))
7156 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7159 static struct suspended *
7160 chanserv_read_suspended(dict_t obj)
7162 struct suspended *suspended = calloc(1, sizeof(*suspended));
7166 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7167 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7168 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7169 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7170 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7171 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7172 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7173 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7174 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7175 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7180 chanserv_channel_read(const char *key, struct record_data *hir)
7182 struct suspended *suspended;
7183 struct mod_chanmode *modes;
7184 struct chanNode *cNode;
7185 struct chanData *cData;
7186 struct dict *channel, *obj;
7187 char *str, *argv[10];
7191 channel = hir->d.object;
7193 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7196 cNode = AddChannel(key, now, NULL, NULL);
7199 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7202 cData = register_channel(cNode, str);
7205 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7209 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7211 enum levelOption lvlOpt;
7212 enum charOption chOpt;
7214 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7215 cData->flags = atoi(str);
7217 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7219 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7221 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7222 else if(levelOptions[lvlOpt].old_flag)
7224 if(cData->flags & levelOptions[lvlOpt].old_flag)
7225 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7227 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7231 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7233 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7235 cData->chOpts[chOpt] = str[0];
7238 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7240 enum levelOption lvlOpt;
7241 enum charOption chOpt;
7244 cData->flags = base64toint(str, 5);
7245 count = strlen(str += 5);
7246 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7249 if(levelOptions[lvlOpt].old_flag)
7251 if(cData->flags & levelOptions[lvlOpt].old_flag)
7252 lvl = levelOptions[lvlOpt].flag_value;
7254 lvl = levelOptions[lvlOpt].default_value;
7256 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7258 case 'c': lvl = UL_COOWNER; break;
7259 case 'm': lvl = UL_MASTER; break;
7260 case 'n': lvl = UL_OWNER+1; break;
7261 case 'o': lvl = UL_OP; break;
7262 case 'p': lvl = UL_PEON; break;
7263 case 'w': lvl = UL_OWNER; break;
7264 default: lvl = 0; break;
7266 cData->lvlOpts[lvlOpt] = lvl;
7268 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7269 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7272 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7274 suspended = chanserv_read_suspended(obj);
7275 cData->suspended = suspended;
7276 suspended->cData = cData;
7277 /* We could use suspended->expires and suspended->revoked to
7278 * set the CHANNEL_SUSPENDED flag, but we don't. */
7280 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7282 suspended = calloc(1, sizeof(*suspended));
7283 suspended->issued = 0;
7284 suspended->revoked = 0;
7285 suspended->suspender = strdup(str);
7286 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7287 suspended->expires = str ? atoi(str) : 0;
7288 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7289 suspended->reason = strdup(str ? str : "No reason");
7290 suspended->previous = NULL;
7291 cData->suspended = suspended;
7292 suspended->cData = cData;
7296 cData->flags &= ~CHANNEL_SUSPENDED;
7297 suspended = NULL; /* to squelch a warning */
7300 if(IsSuspended(cData)) {
7301 if(suspended->expires > now)
7302 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7303 else if(suspended->expires)
7304 cData->flags &= ~CHANNEL_SUSPENDED;
7307 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7308 struct mod_chanmode change;
7309 mod_chanmode_init(&change);
7311 change.args[0].mode = MODE_CHANOP;
7312 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7313 mod_chanmode_announce(chanserv, cNode, &change);
7316 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7317 cData->registered = str ? strtoul(str, NULL, 0) : now;
7318 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7319 cData->visited = str ? strtoul(str, NULL, 0) : now;
7320 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7321 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7322 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7323 cData->max = str ? atoi(str) : 0;
7324 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7325 cData->greeting = str ? strdup(str) : NULL;
7326 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7327 cData->user_greeting = str ? strdup(str) : NULL;
7328 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7329 cData->topic_mask = str ? strdup(str) : NULL;
7330 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7331 cData->topic = str ? strdup(str) : NULL;
7333 if(!IsSuspended(cData)
7334 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7335 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7336 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7337 cData->modes = *modes;
7339 cData->modes.modes_set |= MODE_REGISTERED;
7340 if(cData->modes.argc > 1)
7341 cData->modes.argc = 1;
7342 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7343 mod_chanmode_free(modes);
7346 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7347 for(it = dict_first(obj); it; it = iter_next(it))
7348 user_read_helper(iter_key(it), iter_data(it), cData);
7350 if(!cData->users && !IsProtected(cData))
7352 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7353 unregister_channel(cData, "has empty user list.");
7357 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7358 for(it = dict_first(obj); it; it = iter_next(it))
7359 ban_read_helper(iter_key(it), iter_data(it), cData);
7361 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7362 for(it = dict_first(obj); it; it = iter_next(it))
7364 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7365 struct record_data *rd = iter_data(it);
7366 const char *note, *setter;
7368 if(rd->type != RECDB_OBJECT)
7370 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7374 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7376 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7378 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7382 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7383 if(!setter) setter = "<unknown>";
7384 chanserv_add_channel_note(cData, ntype, setter, note);
7392 chanserv_dnr_read(const char *key, struct record_data *hir)
7394 const char *setter, *reason, *str;
7395 struct do_not_register *dnr;
7396 unsigned long expiry;
7398 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7401 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7404 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7407 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7410 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7411 expiry = str ? strtoul(str, NULL, 0) : 0;
7412 if(expiry && expiry <= now)
7414 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7417 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7419 dnr->set = atoi(str);
7425 chanserv_saxdb_read(struct dict *database)
7427 struct dict *section;
7430 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7431 for(it = dict_first(section); it; it = iter_next(it))
7432 chanserv_note_type_read(iter_key(it), iter_data(it));
7434 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7435 for(it = dict_first(section); it; it = iter_next(it))
7436 chanserv_channel_read(iter_key(it), iter_data(it));
7438 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7439 for(it = dict_first(section); it; it = iter_next(it))
7440 chanserv_dnr_read(iter_key(it), iter_data(it));
7446 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7448 int high_present = 0;
7449 saxdb_start_record(ctx, KEY_USERS, 1);
7450 for(; uData; uData = uData->next)
7452 if((uData->access >= UL_PRESENT) && uData->present)
7454 saxdb_start_record(ctx, uData->handle->handle, 0);
7455 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7456 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7458 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7460 saxdb_write_string(ctx, KEY_INFO, uData->info);
7461 saxdb_end_record(ctx);
7463 saxdb_end_record(ctx);
7464 return high_present;
7468 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7472 saxdb_start_record(ctx, KEY_BANS, 1);
7473 for(; bData; bData = bData->next)
7475 saxdb_start_record(ctx, bData->mask, 0);
7476 saxdb_write_int(ctx, KEY_SET, bData->set);
7477 if(bData->triggered)
7478 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7480 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7482 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7484 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7485 saxdb_end_record(ctx);
7487 saxdb_end_record(ctx);
7491 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7493 saxdb_start_record(ctx, name, 0);
7494 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7495 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7497 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7499 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7501 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7503 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7504 saxdb_end_record(ctx);
7508 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7512 enum levelOption lvlOpt;
7513 enum charOption chOpt;
7515 saxdb_start_record(ctx, channel->channel->name, 1);
7517 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7518 saxdb_write_int(ctx, KEY_MAX, channel->max);
7520 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7521 if(channel->registrar)
7522 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7523 if(channel->greeting)
7524 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7525 if(channel->user_greeting)
7526 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7527 if(channel->topic_mask)
7528 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7529 if(channel->suspended)
7530 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7532 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7533 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7534 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7535 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7536 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7538 buf[0] = channel->chOpts[chOpt];
7540 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7542 saxdb_end_record(ctx);
7544 if(channel->modes.modes_set || channel->modes.modes_clear)
7546 mod_chanmode_format(&channel->modes, buf);
7547 saxdb_write_string(ctx, KEY_MODES, buf);
7550 high_present = chanserv_write_users(ctx, channel->users);
7551 chanserv_write_bans(ctx, channel->bans);
7553 if(dict_size(channel->notes))
7557 saxdb_start_record(ctx, KEY_NOTES, 1);
7558 for(it = dict_first(channel->notes); it; it = iter_next(it))
7560 struct note *note = iter_data(it);
7561 saxdb_start_record(ctx, iter_key(it), 0);
7562 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7563 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7564 saxdb_end_record(ctx);
7566 saxdb_end_record(ctx);
7569 if(channel->ownerTransfer)
7570 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7571 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7572 saxdb_end_record(ctx);
7576 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7580 saxdb_start_record(ctx, ntype->name, 0);
7581 switch(ntype->set_access_type)
7583 case NOTE_SET_CHANNEL_ACCESS:
7584 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7586 case NOTE_SET_CHANNEL_SETTER:
7587 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7589 case NOTE_SET_PRIVILEGED: default:
7590 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7593 switch(ntype->visible_type)
7595 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7596 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7597 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7599 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7600 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7601 saxdb_end_record(ctx);
7605 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7607 struct do_not_register *dnr;
7608 dict_iterator_t it, next;
7610 for(it = dict_first(dnrs); it; it = next)
7612 next = iter_next(it);
7613 dnr = iter_data(it);
7614 if(dnr->expires && dnr->expires <= now)
7616 dict_remove(dnrs, iter_key(it));
7619 saxdb_start_record(ctx, dnr->chan_name, 0);
7621 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7623 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7624 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7625 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7626 saxdb_end_record(ctx);
7631 chanserv_saxdb_write(struct saxdb_context *ctx)
7634 struct chanData *channel;
7637 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7638 for(it = dict_first(note_types); it; it = iter_next(it))
7639 chanserv_write_note_type(ctx, iter_data(it));
7640 saxdb_end_record(ctx);
7643 saxdb_start_record(ctx, KEY_DNR, 1);
7644 write_dnrs_helper(ctx, handle_dnrs);
7645 write_dnrs_helper(ctx, plain_dnrs);
7646 write_dnrs_helper(ctx, mask_dnrs);
7647 saxdb_end_record(ctx);
7650 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7651 for(channel = channelList; channel; channel = channel->next)
7652 chanserv_write_channel(ctx, channel);
7653 saxdb_end_record(ctx);
7659 chanserv_db_cleanup(void) {
7661 unreg_part_func(handle_part);
7663 unregister_channel(channelList, "terminating.");
7664 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7665 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7666 free(chanserv_conf.support_channels.list);
7667 dict_delete(handle_dnrs);
7668 dict_delete(plain_dnrs);
7669 dict_delete(mask_dnrs);
7670 dict_delete(note_types);
7671 free_string_list(chanserv_conf.eightball);
7672 free_string_list(chanserv_conf.old_ban_names);
7673 free_string_list(chanserv_conf.set_shows);
7674 free(set_shows_list.list);
7675 free(uset_shows_list.list);
7678 struct userData *helper = helperList;
7679 helperList = helperList->next;
7684 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7685 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7686 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7689 init_chanserv(const char *nick)
7691 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7692 conf_register_reload(chanserv_conf_read);
7696 reg_server_link_func(handle_server_link);
7697 reg_new_channel_func(handle_new_channel);
7698 reg_join_func(handle_join);
7699 reg_part_func(handle_part);
7700 reg_kick_func(handle_kick);
7701 reg_topic_func(handle_topic);
7702 reg_mode_change_func(handle_mode);
7703 reg_nick_change_func(handle_nick_change);
7704 reg_auth_func(handle_auth);
7707 reg_handle_rename_func(handle_rename);
7708 reg_unreg_func(handle_unreg);
7710 handle_dnrs = dict_new();
7711 dict_set_free_data(handle_dnrs, free);
7712 plain_dnrs = dict_new();
7713 dict_set_free_data(plain_dnrs, free);
7714 mask_dnrs = dict_new();
7715 dict_set_free_data(mask_dnrs, free);
7717 reg_svccmd_unbind_func(handle_svccmd_unbind);
7718 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7719 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7720 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7721 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7722 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7723 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7724 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7725 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7726 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7727 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7728 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7729 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7730 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7732 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7733 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7735 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7736 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7737 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7738 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7739 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7741 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7742 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7743 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7744 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7745 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7747 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7748 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7749 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7750 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7752 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7753 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7754 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7755 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7756 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7757 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7758 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7759 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7761 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7762 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7763 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7764 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7765 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7766 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7767 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7768 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7769 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7770 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7771 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7772 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7773 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7774 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7776 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7777 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7778 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7779 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7780 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7782 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7783 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7785 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7786 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7787 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7788 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7789 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7790 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7791 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7792 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7793 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7794 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7795 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7797 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7798 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7800 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7801 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7802 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7803 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7805 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7806 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7807 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7808 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7809 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7811 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7812 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7813 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7814 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7815 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7816 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7818 /* Channel options */
7819 DEFINE_CHANNEL_OPTION(defaulttopic);
7820 DEFINE_CHANNEL_OPTION(topicmask);
7821 DEFINE_CHANNEL_OPTION(greeting);
7822 DEFINE_CHANNEL_OPTION(usergreeting);
7823 DEFINE_CHANNEL_OPTION(modes);
7824 DEFINE_CHANNEL_OPTION(enfops);
7825 DEFINE_CHANNEL_OPTION(giveops);
7826 DEFINE_CHANNEL_OPTION(protect);
7827 DEFINE_CHANNEL_OPTION(enfmodes);
7828 DEFINE_CHANNEL_OPTION(enftopic);
7829 DEFINE_CHANNEL_OPTION(pubcmd);
7830 DEFINE_CHANNEL_OPTION(givevoice);
7831 DEFINE_CHANNEL_OPTION(userinfo);
7832 DEFINE_CHANNEL_OPTION(dynlimit);
7833 DEFINE_CHANNEL_OPTION(topicsnarf);
7834 DEFINE_CHANNEL_OPTION(nodelete);
7835 DEFINE_CHANNEL_OPTION(toys);
7836 DEFINE_CHANNEL_OPTION(setters);
7837 DEFINE_CHANNEL_OPTION(topicrefresh);
7838 DEFINE_CHANNEL_OPTION(ctcpusers);
7839 DEFINE_CHANNEL_OPTION(ctcpreaction);
7840 DEFINE_CHANNEL_OPTION(inviteme);
7841 DEFINE_CHANNEL_OPTION(unreviewed);
7842 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7843 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7845 DEFINE_CHANNEL_OPTION(offchannel);
7846 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7848 /* Alias set topic to set defaulttopic for compatibility. */
7849 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7852 DEFINE_USER_OPTION(noautoop);
7853 DEFINE_USER_OPTION(autoinvite);
7854 DEFINE_USER_OPTION(info);
7856 /* Alias uset autovoice to uset autoop. */
7857 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7859 note_types = dict_new();
7860 dict_set_free_data(note_types, chanserv_deref_note_type);
7863 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7864 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7865 service_register(chanserv)->trigger = '!';
7866 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7868 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7870 if(chanserv_conf.channel_expire_frequency)
7871 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7873 if(chanserv_conf.dnr_expire_frequency)
7874 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7876 if(chanserv_conf.refresh_period)
7878 unsigned long next_refresh;
7879 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7880 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7883 reg_exit_func(chanserv_db_cleanup);
7884 message_register_table(msgtab);