1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 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 for $S to invite you." },
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), UNUSED_ARG(unsigned int is_notice))
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)
1650 unsigned long expiry, duration;
1651 unsigned int matches;
1655 reply("CSMSG_DNR_SEARCH_RESULTS");
1656 matches = send_dnrs(user, handle_dnrs);
1657 matches += send_dnrs(user, plain_dnrs);
1658 matches += send_dnrs(user, mask_dnrs);
1660 reply("MSG_MATCH_COUNT", matches);
1662 reply("MSG_NO_MATCHES");
1668 if(!IsChannelName(target) && (*target != '*'))
1670 reply("CSMSG_NOT_DNR", target);
1678 reply("MSG_INVALID_DURATION", argv[2]);
1682 if(!strcmp(argv[2], "0"))
1684 else if((duration = ParseInterval(argv[2])))
1685 expiry = now + duration;
1688 reply("MSG_INVALID_DURATION", argv[2]);
1692 reason = unsplit_string(argv + 3, argc - 3, NULL);
1693 if((*target == '*') && !get_handle_info(target + 1))
1695 reply("MSG_HANDLE_UNKNOWN", target + 1);
1698 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1699 reply("CSMSG_NOREGISTER_CHANNEL", target);
1703 reply("CSMSG_DNR_SEARCH_RESULTS");
1705 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1707 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1709 reply("MSG_NO_MATCHES");
1713 static CHANSERV_FUNC(cmd_allowregister)
1715 const char *chan_name = argv[1];
1717 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1718 || dict_remove(plain_dnrs, chan_name)
1719 || dict_remove(mask_dnrs, chan_name))
1721 reply("CSMSG_DNR_REMOVED", chan_name);
1724 reply("CSMSG_NO_SUCH_DNR", chan_name);
1729 struct userNode *source;
1733 unsigned long min_set, max_set;
1734 unsigned long min_expires, max_expires;
1739 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1741 return !((dnr->set < search->min_set)
1742 || (dnr->set > search->max_set)
1743 || (dnr->expires < search->min_expires)
1744 || (search->max_expires
1745 && ((dnr->expires == 0)
1746 || (dnr->expires > search->max_expires)))
1747 || (search->chan_mask
1748 && !match_ircglob(dnr->chan_name, search->chan_mask))
1749 || (search->setter_mask
1750 && !match_ircglob(dnr->setter, search->setter_mask))
1751 || (search->reason_mask
1752 && !match_ircglob(dnr->reason, search->reason_mask)));
1755 static struct dnr_search *
1756 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1758 struct dnr_search *discrim;
1761 discrim = calloc(1, sizeof(*discrim));
1762 discrim->source = user;
1763 discrim->chan_mask = NULL;
1764 discrim->setter_mask = NULL;
1765 discrim->reason_mask = NULL;
1766 discrim->max_set = INT_MAX;
1767 discrim->limit = 50;
1769 for(ii=0; ii<argc; ++ii)
1773 reply("MSG_MISSING_PARAMS", argv[ii]);
1776 else if(0 == irccasecmp(argv[ii], "channel"))
1778 discrim->chan_mask = argv[++ii];
1780 else if(0 == irccasecmp(argv[ii], "setter"))
1782 discrim->setter_mask = argv[++ii];
1784 else if(0 == irccasecmp(argv[ii], "reason"))
1786 discrim->reason_mask = argv[++ii];
1788 else if(0 == irccasecmp(argv[ii], "limit"))
1790 discrim->limit = strtoul(argv[++ii], NULL, 0);
1792 else if(0 == irccasecmp(argv[ii], "set"))
1794 const char *cmp = argv[++ii];
1797 discrim->min_set = now - ParseInterval(cmp + 2);
1799 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1800 } else if(cmp[0] == '=') {
1801 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1802 } else if(cmp[0] == '>') {
1804 discrim->max_set = now - ParseInterval(cmp + 2);
1806 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1808 discrim->max_set = now - (ParseInterval(cmp) - 1);
1811 else if(0 == irccasecmp(argv[ii], "expires"))
1813 const char *cmp = argv[++ii];
1816 discrim->max_expires = now + ParseInterval(cmp + 2);
1818 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1819 } else if(cmp[0] == '=') {
1820 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1821 } else if(cmp[0] == '>') {
1823 discrim->min_expires = now + ParseInterval(cmp + 2);
1825 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1827 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1832 reply("MSG_INVALID_CRITERIA", argv[ii]);
1843 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1846 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1848 struct do_not_register *dnr;
1849 dict_iterator_t next;
1854 /* Initialize local variables. */
1857 if(discrim->chan_mask)
1859 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1860 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1864 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1866 /* Check against account-based DNRs. */
1867 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1868 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1871 else if(target_fixed)
1873 /* Check against channel-based DNRs. */
1874 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1875 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1880 /* Exhaustively search account DNRs. */
1881 for(it = dict_first(handle_dnrs); it; it = next)
1883 next = iter_next(it);
1884 dnr = iter_data(it);
1885 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1889 /* Do the same for channel DNRs. */
1890 for(it = dict_first(plain_dnrs); it; it = next)
1892 next = iter_next(it);
1893 dnr = iter_data(it);
1894 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1898 /* Do the same for wildcarded channel DNRs. */
1899 for(it = dict_first(mask_dnrs); it; it = next)
1901 next = iter_next(it);
1902 dnr = iter_data(it);
1903 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1911 dnr_remove_func(struct do_not_register *match, void *extra)
1913 struct userNode *user;
1916 chan_name = alloca(strlen(match->chan_name) + 1);
1917 strcpy(chan_name, match->chan_name);
1919 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1920 || dict_remove(plain_dnrs, chan_name)
1921 || dict_remove(mask_dnrs, chan_name))
1923 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1929 dnr_count_func(struct do_not_register *match, void *extra)
1931 return 0; (void)match; (void)extra;
1934 static MODCMD_FUNC(cmd_dnrsearch)
1936 struct dnr_search *discrim;
1937 dnr_search_func action;
1938 struct svccmd *subcmd;
1939 unsigned int matches;
1942 sprintf(buf, "dnrsearch %s", argv[1]);
1943 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1946 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1949 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1951 if(!irccasecmp(argv[1], "print"))
1952 action = dnr_print_func;
1953 else if(!irccasecmp(argv[1], "remove"))
1954 action = dnr_remove_func;
1955 else if(!irccasecmp(argv[1], "count"))
1956 action = dnr_count_func;
1959 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1963 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1967 if(action == dnr_print_func)
1968 reply("CSMSG_DNR_SEARCH_RESULTS");
1969 matches = dnr_search(discrim, action, user);
1971 reply("MSG_MATCH_COUNT", matches);
1973 reply("MSG_NO_MATCHES");
1979 chanserv_get_owned_count(struct handle_info *hi)
1981 struct userData *cList;
1984 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1985 if(cList->access == UL_OWNER)
1990 static CHANSERV_FUNC(cmd_register)
1992 struct handle_info *handle;
1993 struct chanData *cData;
1994 struct modeNode *mn;
1995 char reason[MAXLEN];
1997 unsigned int new_channel, force=0;
1998 struct do_not_register *dnr;
2002 if(channel->channel_info)
2004 reply("CSMSG_ALREADY_REGGED", channel->name);
2008 if(channel->bad_channel)
2010 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2015 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2017 reply("CSMSG_MUST_BE_OPPED", channel->name);
2022 chan_name = channel->name;
2026 if((argc < 2) || !IsChannelName(argv[1]))
2028 reply("MSG_NOT_CHANNEL_NAME");
2032 if(opserv_bad_channel(argv[1]))
2034 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2039 chan_name = argv[1];
2042 if(argc >= (new_channel+2))
2044 if(!IsHelping(user))
2046 reply("CSMSG_PROXY_FORBIDDEN");
2050 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2052 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2053 dnr = chanserv_is_dnr(chan_name, handle);
2057 handle = user->handle_info;
2058 dnr = chanserv_is_dnr(chan_name, handle);
2062 if(!IsHelping(user))
2063 reply("CSMSG_DNR_CHANNEL", chan_name);
2065 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2069 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2071 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2076 channel = AddChannel(argv[1], now, NULL, NULL);
2078 cData = register_channel(channel, user->handle_info->handle);
2079 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2080 cData->modes = chanserv_conf.default_modes;
2082 cData->modes.modes_set |= MODE_REGISTERED;
2083 if (IsOffChannel(cData))
2085 mod_chanmode_announce(chanserv, channel, &cData->modes);
2089 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2090 change->args[change->argc].mode = MODE_CHANOP;
2091 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2093 mod_chanmode_announce(chanserv, channel, change);
2094 mod_chanmode_free(change);
2097 /* Initialize the channel's max user record. */
2098 cData->max = channel->members.used;
2100 if(handle != user->handle_info)
2101 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2103 reply("CSMSG_REG_SUCCESS", channel->name);
2105 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2106 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2111 make_confirmation_string(struct userData *uData)
2113 static char strbuf[16];
2118 for(src = uData->handle->handle; *src; )
2119 accum = accum * 31 + toupper(*src++);
2121 for(src = uData->channel->channel->name; *src; )
2122 accum = accum * 31 + toupper(*src++);
2123 sprintf(strbuf, "%08x", accum);
2127 static CHANSERV_FUNC(cmd_unregister)
2130 char reason[MAXLEN];
2131 struct chanData *cData;
2132 struct userData *uData;
2134 cData = channel->channel_info;
2137 reply("CSMSG_NOT_REGISTERED", channel->name);
2141 uData = GetChannelUser(cData, user->handle_info);
2142 if(!uData || (uData->access < UL_OWNER))
2144 reply("CSMSG_NO_ACCESS");
2148 if(IsProtected(cData))
2150 reply("CSMSG_UNREG_NODELETE", channel->name);
2154 if(!IsHelping(user))
2156 const char *confirm_string;
2157 if(IsSuspended(cData))
2159 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2162 confirm_string = make_confirmation_string(uData);
2163 if((argc < 2) || strcmp(argv[1], confirm_string))
2165 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2170 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2171 name = strdup(channel->name);
2172 unregister_channel(cData, reason);
2173 reply("CSMSG_UNREG_SUCCESS", name);
2178 static CHANSERV_FUNC(cmd_move)
2180 struct mod_chanmode change;
2181 struct chanNode *target;
2182 struct modeNode *mn;
2183 struct userData *uData;
2184 char reason[MAXLEN];
2185 struct do_not_register *dnr;
2189 if(IsProtected(channel->channel_info))
2191 reply("CSMSG_MOVE_NODELETE", channel->name);
2195 if(!IsChannelName(argv[1]))
2197 reply("MSG_NOT_CHANNEL_NAME");
2201 if(opserv_bad_channel(argv[1]))
2203 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2207 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2209 for(uData = channel->channel_info->users; uData; uData = uData->next)
2211 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2213 if(!IsHelping(user))
2214 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2216 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2222 mod_chanmode_init(&change);
2223 if(!(target = GetChannel(argv[1])))
2225 target = AddChannel(argv[1], now, NULL, NULL);
2226 if(!IsSuspended(channel->channel_info))
2227 AddChannelUser(chanserv, target);
2229 else if(target->channel_info)
2231 reply("CSMSG_ALREADY_REGGED", target->name);
2234 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2235 && !IsHelping(user))
2237 reply("CSMSG_MUST_BE_OPPED", target->name);
2240 else if(!IsSuspended(channel->channel_info))
2243 change.args[0].mode = MODE_CHANOP;
2244 change.args[0].u.member = AddChannelUser(chanserv, target);
2245 mod_chanmode_announce(chanserv, target, &change);
2250 /* Clear MODE_REGISTERED from old channel, add it to new. */
2252 change.modes_clear = MODE_REGISTERED;
2253 mod_chanmode_announce(chanserv, channel, &change);
2254 change.modes_clear = 0;
2255 change.modes_set = MODE_REGISTERED;
2256 mod_chanmode_announce(chanserv, target, &change);
2259 /* Move the channel_info to the target channel; it
2260 shouldn't be necessary to clear timeq callbacks
2261 for the old channel. */
2262 target->channel_info = channel->channel_info;
2263 target->channel_info->channel = target;
2264 channel->channel_info = NULL;
2266 reply("CSMSG_MOVE_SUCCESS", target->name);
2268 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2269 if(!IsSuspended(target->channel_info))
2271 char reason2[MAXLEN];
2272 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2273 DelChannelUser(chanserv, channel, reason2, 0);
2275 UnlockChannel(channel);
2276 LockChannel(target);
2277 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2282 merge_users(struct chanData *source, struct chanData *target)
2284 struct userData *suData, *tuData, *next;
2290 /* Insert the source's users into the scratch area. */
2291 for(suData = source->users; suData; suData = suData->next)
2292 dict_insert(merge, suData->handle->handle, suData);
2294 /* Iterate through the target's users, looking for
2295 users common to both channels. The lower access is
2296 removed from either the scratch area or target user
2298 for(tuData = target->users; tuData; tuData = next)
2300 struct userData *choice;
2302 next = tuData->next;
2304 /* If a source user exists with the same handle as a target
2305 channel's user, resolve the conflict by removing one. */
2306 suData = dict_find(merge, tuData->handle->handle, NULL);
2310 /* Pick the data we want to keep. */
2311 /* If the access is the same, use the later seen time. */
2312 if(suData->access == tuData->access)
2313 choice = (suData->seen > tuData->seen) ? suData : tuData;
2314 else /* Otherwise, keep the higher access level. */
2315 choice = (suData->access > tuData->access) ? suData : tuData;
2317 /* Remove the user that wasn't picked. */
2318 if(choice == tuData)
2320 dict_remove(merge, suData->handle->handle);
2321 del_channel_user(suData, 0);
2324 del_channel_user(tuData, 0);
2327 /* Move the remaining users to the target channel. */
2328 for(it = dict_first(merge); it; it = iter_next(it))
2330 suData = iter_data(it);
2332 /* Insert the user into the target channel's linked list. */
2333 suData->prev = NULL;
2334 suData->next = target->users;
2335 suData->channel = target;
2338 target->users->prev = suData;
2339 target->users = suData;
2341 /* Update the user counts for the target channel; the
2342 source counts are left alone. */
2343 target->userCount++;
2346 /* Possible to assert (source->users == NULL) here. */
2347 source->users = NULL;
2352 merge_bans(struct chanData *source, struct chanData *target)
2354 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2356 /* Hold on to the original head of the target ban list
2357 to avoid comparing source bans with source bans. */
2358 tFront = target->bans;
2360 /* Perform a totally expensive O(n*m) merge, ick. */
2361 for(sbData = source->bans; sbData; sbData = sNext)
2363 /* Flag to track whether the ban's been moved
2364 to the destination yet. */
2367 /* Possible to assert (sbData->prev == NULL) here. */
2368 sNext = sbData->next;
2370 for(tbData = tFront; tbData; tbData = tNext)
2372 tNext = tbData->next;
2374 /* Perform two comparisons between each source
2375 and target ban, conflicts are resolved by
2376 keeping the broader ban and copying the later
2377 expiration and triggered time. */
2378 if(match_ircglobs(tbData->mask, sbData->mask))
2380 /* There is a broader ban in the target channel that
2381 overrides one in the source channel; remove the
2382 source ban and break. */
2383 if(sbData->expires > tbData->expires)
2384 tbData->expires = sbData->expires;
2385 if(sbData->triggered > tbData->triggered)
2386 tbData->triggered = sbData->triggered;
2387 del_channel_ban(sbData);
2390 else if(match_ircglobs(sbData->mask, tbData->mask))
2392 /* There is a broader ban in the source channel that
2393 overrides one in the target channel; remove the
2394 target ban, fall through and move the source over. */
2395 if(tbData->expires > sbData->expires)
2396 sbData->expires = tbData->expires;
2397 if(tbData->triggered > sbData->triggered)
2398 sbData->triggered = tbData->triggered;
2399 if(tbData == tFront)
2401 del_channel_ban(tbData);
2404 /* Source bans can override multiple target bans, so
2405 we allow a source to run through this loop multiple
2406 times, but we can only move it once. */
2411 /* Remove the source ban from the source ban list. */
2413 sbData->next->prev = sbData->prev;
2415 /* Modify the source ban's associated channel. */
2416 sbData->channel = target;
2418 /* Insert the ban into the target channel's linked list. */
2419 sbData->prev = NULL;
2420 sbData->next = target->bans;
2423 target->bans->prev = sbData;
2424 target->bans = sbData;
2426 /* Update the user counts for the target channel. */
2431 /* Possible to assert (source->bans == NULL) here. */
2432 source->bans = NULL;
2436 merge_data(struct chanData *source, struct chanData *target)
2438 /* Use more recent visited and owner-transfer time; use older
2439 * registered time. Bitwise or may_opchan. Use higher max.
2440 * Do not touch last_refresh, ban count or user counts.
2442 if(source->visited > target->visited)
2443 target->visited = source->visited;
2444 if(source->registered < target->registered)
2445 target->registered = source->registered;
2446 if(source->ownerTransfer > target->ownerTransfer)
2447 target->ownerTransfer = source->ownerTransfer;
2448 if(source->may_opchan)
2449 target->may_opchan = 1;
2450 if(source->max > target->max)
2451 target->max = source->max;
2455 merge_channel(struct chanData *source, struct chanData *target)
2457 merge_users(source, target);
2458 merge_bans(source, target);
2459 merge_data(source, target);
2462 static CHANSERV_FUNC(cmd_merge)
2464 struct userData *target_user;
2465 struct chanNode *target;
2466 char reason[MAXLEN];
2470 /* Make sure the target channel exists and is registered to the user
2471 performing the command. */
2472 if(!(target = GetChannel(argv[1])))
2474 reply("MSG_INVALID_CHANNEL");
2478 if(!target->channel_info)
2480 reply("CSMSG_NOT_REGISTERED", target->name);
2484 if(IsProtected(channel->channel_info))
2486 reply("CSMSG_MERGE_NODELETE");
2490 if(IsSuspended(target->channel_info))
2492 reply("CSMSG_MERGE_SUSPENDED");
2496 if(channel == target)
2498 reply("CSMSG_MERGE_SELF");
2502 target_user = GetChannelUser(target->channel_info, user->handle_info);
2503 if(!target_user || (target_user->access < UL_OWNER))
2505 reply("CSMSG_MERGE_NOT_OWNER");
2509 /* Merge the channel structures and associated data. */
2510 merge_channel(channel->channel_info, target->channel_info);
2511 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2512 unregister_channel(channel->channel_info, reason);
2513 reply("CSMSG_MERGE_SUCCESS", target->name);
2517 static CHANSERV_FUNC(cmd_opchan)
2519 struct mod_chanmode change;
2520 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2522 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2525 channel->channel_info->may_opchan = 0;
2526 mod_chanmode_init(&change);
2528 change.args[0].mode = MODE_CHANOP;
2529 change.args[0].u.member = GetUserMode(channel, chanserv);
2530 mod_chanmode_announce(chanserv, channel, &change);
2531 reply("CSMSG_OPCHAN_DONE", channel->name);
2535 static CHANSERV_FUNC(cmd_adduser)
2537 struct userData *actee;
2538 struct userData *actor, *real_actor;
2539 struct handle_info *handle;
2540 unsigned short access, override = 0;
2544 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2546 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2550 access = user_level_from_name(argv[2], UL_OWNER);
2553 reply("CSMSG_INVALID_ACCESS", argv[2]);
2557 actor = GetChannelUser(channel->channel_info, user->handle_info);
2558 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2560 if(actor->access <= access)
2562 reply("CSMSG_NO_BUMP_ACCESS");
2566 /* Trying to add someone with equal/more access? */
2567 if (!real_actor || real_actor->access <= access)
2568 override = CMD_LOG_OVERRIDE;
2570 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2573 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2575 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2579 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2580 scan_user_presence(actee, NULL);
2581 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2582 return 1 | override;
2585 static CHANSERV_FUNC(cmd_clvl)
2587 struct handle_info *handle;
2588 struct userData *victim;
2589 struct userData *actor, *real_actor;
2590 unsigned short new_access, override = 0;
2591 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2595 actor = GetChannelUser(channel->channel_info, user->handle_info);
2596 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2598 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2601 if(handle == user->handle_info && !privileged)
2603 reply("CSMSG_NO_SELF_CLVL");
2607 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2609 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2613 if(actor->access <= victim->access && !privileged)
2615 reply("MSG_USER_OUTRANKED", handle->handle);
2619 new_access = user_level_from_name(argv[2], UL_OWNER);
2623 reply("CSMSG_INVALID_ACCESS", argv[2]);
2627 if(new_access >= actor->access && !privileged)
2629 reply("CSMSG_NO_BUMP_ACCESS");
2633 /* Trying to clvl a equal/higher user? */
2634 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2635 override = CMD_LOG_OVERRIDE;
2636 /* Trying to clvl someone to equal/higher access? */
2637 if(!real_actor || new_access >= real_actor->access)
2638 override = CMD_LOG_OVERRIDE;
2639 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2640 * If they lower their own access it's not a big problem.
2643 victim->access = new_access;
2644 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2645 return 1 | override;
2648 static CHANSERV_FUNC(cmd_deluser)
2650 struct handle_info *handle;
2651 struct userData *victim;
2652 struct userData *actor, *real_actor;
2653 unsigned short access, override = 0;
2658 actor = GetChannelUser(channel->channel_info, user->handle_info);
2659 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2661 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2664 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2666 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2672 access = user_level_from_name(argv[1], UL_OWNER);
2675 reply("CSMSG_INVALID_ACCESS", argv[1]);
2678 if(access != victim->access)
2680 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2686 access = victim->access;
2689 if((actor->access <= victim->access) && !IsHelping(user))
2691 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2695 /* If people delete themselves it is an override, but they
2696 * could've used deleteme so we don't log it as an override
2698 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2699 override = CMD_LOG_OVERRIDE;
2701 chan_name = strdup(channel->name);
2702 del_channel_user(victim, 1);
2703 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2705 return 1 | override;
2709 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2711 struct userData *actor, *real_actor, *uData, *next;
2712 unsigned int override = 0;
2714 actor = GetChannelUser(channel->channel_info, user->handle_info);
2715 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2717 if(min_access > max_access)
2719 reply("CSMSG_BAD_RANGE", min_access, max_access);
2723 if((actor->access <= max_access) && !IsHelping(user))
2725 reply("CSMSG_NO_ACCESS");
2729 if(!real_actor || real_actor->access <= max_access)
2730 override = CMD_LOG_OVERRIDE;
2732 for(uData = channel->channel_info->users; uData; uData = next)
2736 if((uData->access >= min_access)
2737 && (uData->access <= max_access)
2738 && match_ircglob(uData->handle->handle, mask))
2739 del_channel_user(uData, 1);
2742 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2743 return 1 | override;
2746 static CHANSERV_FUNC(cmd_mdelowner)
2748 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2751 static CHANSERV_FUNC(cmd_mdelcoowner)
2753 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2756 static CHANSERV_FUNC(cmd_mdelmaster)
2758 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2761 static CHANSERV_FUNC(cmd_mdelop)
2763 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2766 static CHANSERV_FUNC(cmd_mdelpeon)
2768 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2772 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2774 struct banData *bData, *next;
2775 char interval[INTERVALLEN];
2777 unsigned long limit;
2780 limit = now - duration;
2781 for(bData = channel->channel_info->bans; bData; bData = next)
2785 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2788 del_channel_ban(bData);
2792 intervalString(interval, duration, user->handle_info);
2793 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2798 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2800 struct userData *actor, *uData, *next;
2801 char interval[INTERVALLEN];
2803 unsigned long limit;
2805 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2806 if(min_access > max_access)
2808 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2812 if(!actor || actor->access <= max_access)
2814 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2819 limit = now - duration;
2820 for(uData = channel->channel_info->users; uData; uData = next)
2824 if((uData->seen > limit)
2826 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2829 if(((uData->access >= min_access) && (uData->access <= max_access))
2830 || (!max_access && (uData->access < actor->access)))
2832 del_channel_user(uData, 1);
2840 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2842 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2846 static CHANSERV_FUNC(cmd_trim)
2848 unsigned long duration;
2849 unsigned short min_level, max_level;
2854 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2855 duration = ParseInterval(argv[2]);
2858 reply("CSMSG_CANNOT_TRIM");
2862 if(!irccasecmp(argv[1], "bans"))
2864 cmd_trim_bans(user, channel, duration);
2867 else if(!irccasecmp(argv[1], "users"))
2869 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2872 else if(parse_level_range(&min_level, &max_level, argv[1]))
2874 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2877 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2879 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2884 reply("CSMSG_INVALID_TRIM", argv[1]);
2889 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2890 to the user. cmd_all takes advantage of this. */
2891 static CHANSERV_FUNC(cmd_up)
2893 struct mod_chanmode change;
2894 struct userData *uData;
2897 mod_chanmode_init(&change);
2899 change.args[0].u.member = GetUserMode(channel, user);
2900 if(!change.args[0].u.member)
2903 reply("MSG_CHANNEL_ABSENT", channel->name);
2907 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2911 reply("CSMSG_GODMODE_UP", argv[0]);
2914 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2916 change.args[0].mode = MODE_CHANOP;
2917 errmsg = "CSMSG_ALREADY_OPPED";
2919 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2921 change.args[0].mode = MODE_VOICE;
2922 errmsg = "CSMSG_ALREADY_VOICED";
2927 reply("CSMSG_NO_ACCESS");
2930 change.args[0].mode &= ~change.args[0].u.member->modes;
2931 if(!change.args[0].mode)
2934 reply(errmsg, channel->name);
2937 modcmd_chanmode_announce(&change);
2941 static CHANSERV_FUNC(cmd_down)
2943 struct mod_chanmode change;
2945 mod_chanmode_init(&change);
2947 change.args[0].u.member = GetUserMode(channel, user);
2948 if(!change.args[0].u.member)
2951 reply("MSG_CHANNEL_ABSENT", channel->name);
2955 if(!change.args[0].u.member->modes)
2958 reply("CSMSG_ALREADY_DOWN", channel->name);
2962 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2963 modcmd_chanmode_announce(&change);
2967 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)
2969 struct userData *cList;
2971 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2973 if(IsSuspended(cList->channel)
2974 || IsUserSuspended(cList)
2975 || !GetUserMode(cList->channel->channel, user))
2978 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2984 static CHANSERV_FUNC(cmd_upall)
2986 return cmd_all(CSFUNC_ARGS, cmd_up);
2989 static CHANSERV_FUNC(cmd_downall)
2991 return cmd_all(CSFUNC_ARGS, cmd_down);
2994 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2995 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2998 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)
3000 unsigned int ii, valid;
3001 struct userNode *victim;
3002 struct mod_chanmode *change;
3004 change = mod_chanmode_alloc(argc - 1);
3006 for(ii=valid=0; ++ii < argc; )
3008 if(!(victim = GetUserH(argv[ii])))
3010 change->args[valid].mode = mode;
3011 change->args[valid].u.member = GetUserMode(channel, victim);
3012 if(!change->args[valid].u.member)
3014 if(validate && !validate(user, channel, victim))
3019 change->argc = valid;
3020 if(valid < (argc-1))
3021 reply("CSMSG_PROCESS_FAILED");
3024 modcmd_chanmode_announce(change);
3025 reply(action, channel->name);
3027 mod_chanmode_free(change);
3031 static CHANSERV_FUNC(cmd_op)
3033 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3036 static CHANSERV_FUNC(cmd_deop)
3038 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3041 static CHANSERV_FUNC(cmd_voice)
3043 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3046 static CHANSERV_FUNC(cmd_devoice)
3048 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3052 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3058 for(ii=0; ii<channel->members.used; ii++)
3060 struct modeNode *mn = channel->members.list[ii];
3062 if(IsService(mn->user))
3065 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3068 if(protect_user(mn->user, user, channel->channel_info))
3072 victims[(*victimCount)++] = mn;
3078 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3080 struct userNode *victim;
3081 struct modeNode **victims;
3082 unsigned int offset, n, victimCount, duration = 0;
3083 char *reason = "Bye.", *ban, *name;
3084 char interval[INTERVALLEN];
3086 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3087 REQUIRE_PARAMS(offset);
3090 reason = unsplit_string(argv + offset, argc - offset, NULL);
3091 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3093 /* Truncate the reason to a length of TOPICLEN, as
3094 the ircd does; however, leave room for an ellipsis
3095 and the kicker's nick. */
3096 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3100 if((victim = GetUserH(argv[1])))
3102 victims = alloca(sizeof(victims[0]));
3103 victims[0] = GetUserMode(channel, victim);
3104 /* XXX: The comparison with ACTION_KICK is just because all
3105 * other actions can work on users outside the channel, and we
3106 * want to allow those (e.g. unbans) in that case. If we add
3107 * some other ejection action for in-channel users, change
3109 victimCount = victims[0] ? 1 : 0;
3111 if(IsService(victim))
3113 reply("MSG_SERVICE_IMMUNE", victim->nick);
3117 if((action == ACTION_KICK) && !victimCount)
3119 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3123 if(protect_user(victim, user, channel->channel_info))
3125 reply("CSMSG_USER_PROTECTED", victim->nick);
3129 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3130 name = victim->nick;
3132 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3134 struct handle_info *hi;
3135 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3136 const char *accountname = argv[1] + 1;
3138 if(!(hi = get_handle_info(accountname)))
3140 reply("MSG_HANDLE_UNKNOWN", accountname);
3144 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3145 victims = alloca(sizeof(victims[0]) * channel->members.used);
3147 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3149 reply("CSMSG_MASK_PROTECTED", banmask);
3153 if((action == ACTION_KICK) && (victimCount == 0))
3155 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3159 name = ban = strdup(banmask);
3163 if(!is_ircmask(argv[1]))
3165 reply("MSG_NICK_UNKNOWN", argv[1]);
3169 victims = alloca(sizeof(victims[0]) * channel->members.used);
3171 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3173 reply("CSMSG_MASK_PROTECTED", argv[1]);
3177 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3179 reply("CSMSG_LAME_MASK", argv[1]);
3183 if((action == ACTION_KICK) && (victimCount == 0))
3185 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3189 name = ban = strdup(argv[1]);
3192 /* Truncate the ban in place if necessary; we must ensure
3193 that 'ban' is a valid ban mask before sanitizing it. */
3194 sanitize_ircmask(ban);
3196 if(action & ACTION_ADD_BAN)
3198 struct banData *bData, *next;
3200 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3202 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3207 if(action & ACTION_ADD_TIMED_BAN)
3209 duration = ParseInterval(argv[2]);
3213 reply("CSMSG_DURATION_TOO_LOW");
3217 else if(duration > (86400 * 365 * 2))
3219 reply("CSMSG_DURATION_TOO_HIGH");
3225 for(bData = channel->channel_info->bans; bData; bData = next)
3227 if(match_ircglobs(bData->mask, ban))
3229 int exact = !irccasecmp(bData->mask, ban);
3231 /* The ban is redundant; there is already a ban
3232 with the same effect in place. */
3236 free(bData->reason);
3237 bData->reason = strdup(reason);
3238 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3240 reply("CSMSG_REASON_CHANGE", ban);
3244 if(exact && bData->expires)
3248 /* If the ban matches an existing one exactly,
3249 extend the expiration time if the provided
3250 duration is longer. */
3251 if(duration && (now + duration > bData->expires))
3253 bData->expires = now + duration;
3264 /* Delete the expiration timeq entry and
3265 requeue if necessary. */
3266 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3269 timeq_add(bData->expires, expire_ban, bData);
3273 /* automated kickban */
3276 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3278 reply("CSMSG_BAN_ADDED", name, channel->name);
3284 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3291 if(match_ircglobs(ban, bData->mask))
3293 /* The ban we are adding makes previously existing
3294 bans redundant; silently remove them. */
3295 del_channel_ban(bData);
3299 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);
3301 name = ban = strdup(bData->mask);
3305 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3307 extern const char *hidden_host_suffix;
3308 const char *old_name = chanserv_conf.old_ban_names->list[n];
3310 unsigned int l1, l2;
3313 l2 = strlen(old_name);
3316 if(irccasecmp(ban + l1 - l2, old_name))
3318 new_mask = malloc(MAXLEN);
3319 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3321 name = ban = new_mask;
3326 if(action & ACTION_BAN)
3328 unsigned int exists;
3329 struct mod_chanmode *change;
3331 if(channel->banlist.used >= MAXBANS)
3334 reply("CSMSG_BANLIST_FULL", channel->name);
3339 exists = ChannelBanExists(channel, ban);
3340 change = mod_chanmode_alloc(victimCount + 1);
3341 for(n = 0; n < victimCount; ++n)
3343 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3344 change->args[n].u.member = victims[n];
3348 change->args[n].mode = MODE_BAN;
3349 change->args[n++].u.hostmask = ban;
3353 modcmd_chanmode_announce(change);
3355 mod_chanmode_announce(chanserv, channel, change);
3356 mod_chanmode_free(change);
3358 if(exists && (action == ACTION_BAN))
3361 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3367 if(action & ACTION_KICK)
3369 char kick_reason[MAXLEN];
3370 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3372 for(n = 0; n < victimCount; n++)
3373 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3378 /* No response, since it was automated. */
3380 else if(action & ACTION_ADD_BAN)
3383 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3385 reply("CSMSG_BAN_ADDED", name, channel->name);
3387 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3388 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3389 else if(action & ACTION_BAN)
3390 reply("CSMSG_BAN_DONE", name, channel->name);
3391 else if(action & ACTION_KICK && victimCount)
3392 reply("CSMSG_KICK_DONE", name, channel->name);
3398 static CHANSERV_FUNC(cmd_kickban)
3400 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3403 static CHANSERV_FUNC(cmd_kick)
3405 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3408 static CHANSERV_FUNC(cmd_ban)
3410 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3413 static CHANSERV_FUNC(cmd_addban)
3415 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3418 static CHANSERV_FUNC(cmd_addtimedban)
3420 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3423 static struct mod_chanmode *
3424 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3426 struct mod_chanmode *change;
3427 unsigned char *match;
3428 unsigned int ii, count;
3430 match = alloca(bans->used);
3433 for(ii = count = 0; ii < bans->used; ++ii)
3435 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3436 MATCH_USENICK | MATCH_VISIBLE);
3443 for(ii = count = 0; ii < bans->used; ++ii)
3445 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3452 change = mod_chanmode_alloc(count);
3453 for(ii = count = 0; ii < bans->used; ++ii)
3457 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3458 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3460 assert(count == change->argc);
3465 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3467 struct userNode *actee;
3473 /* may want to allow a comma delimited list of users... */
3474 if(!(actee = GetUserH(argv[1])))
3476 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3478 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3479 const char *accountname = argv[1] + 1;
3481 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3482 mask = strdup(banmask);
3484 else if(!is_ircmask(argv[1]))
3486 reply("MSG_NICK_UNKNOWN", argv[1]);
3491 mask = strdup(argv[1]);
3495 /* We don't sanitize the mask here because ircu
3497 if(action & ACTION_UNBAN)
3499 struct mod_chanmode *change;
3500 change = find_matching_bans(&channel->banlist, actee, mask);
3505 modcmd_chanmode_announce(change);
3506 for(ii = 0; ii < change->argc; ++ii)
3507 free((char*)change->args[ii].u.hostmask);
3508 mod_chanmode_free(change);
3513 if(action & ACTION_DEL_BAN)
3515 struct banData *ban, *next;
3517 ban = channel->channel_info->bans;
3521 for( ; ban && !user_matches_glob(actee, ban->mask,
3522 MATCH_USENICK | MATCH_VISIBLE);
3525 for( ; ban && !match_ircglobs(mask, ban->mask);
3530 del_channel_ban(ban);
3537 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3539 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3545 static CHANSERV_FUNC(cmd_unban)
3547 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3550 static CHANSERV_FUNC(cmd_delban)
3552 /* it doesn't necessarily have to remove the channel ban - may want
3553 to make that an option. */
3554 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3557 static CHANSERV_FUNC(cmd_unbanme)
3559 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3560 long flags = ACTION_UNBAN;
3562 /* remove permanent bans if the user has the proper access. */
3563 if(uData->access >= UL_MASTER)
3564 flags |= ACTION_DEL_BAN;
3566 argv[1] = user->nick;
3567 return unban_user(user, channel, 2, argv, cmd, flags);
3570 static CHANSERV_FUNC(cmd_unbanall)
3572 struct mod_chanmode *change;
3575 if(!channel->banlist.used)
3577 reply("CSMSG_NO_BANS", channel->name);
3581 change = mod_chanmode_alloc(channel->banlist.used);
3582 for(ii=0; ii<channel->banlist.used; ii++)
3584 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3585 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3587 modcmd_chanmode_announce(change);
3588 for(ii = 0; ii < change->argc; ++ii)
3589 free((char*)change->args[ii].u.hostmask);
3590 mod_chanmode_free(change);
3591 reply("CSMSG_BANS_REMOVED", channel->name);
3595 static CHANSERV_FUNC(cmd_open)
3597 struct mod_chanmode *change;
3600 change = find_matching_bans(&channel->banlist, user, NULL);
3602 change = mod_chanmode_alloc(0);
3603 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3604 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3605 && channel->channel_info->modes.modes_set)
3606 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3607 modcmd_chanmode_announce(change);
3608 reply("CSMSG_CHANNEL_OPENED", channel->name);
3609 for(ii = 0; ii < change->argc; ++ii)
3610 free((char*)change->args[ii].u.hostmask);
3611 mod_chanmode_free(change);
3615 static CHANSERV_FUNC(cmd_myaccess)
3617 static struct string_buffer sbuf;
3618 struct handle_info *target_handle;
3619 struct userData *uData;
3622 target_handle = user->handle_info;
3623 else if(!IsHelping(user))
3625 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3628 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3631 if(!target_handle->channels)
3633 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3637 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3638 for(uData = target_handle->channels; uData; uData = uData->u_next)
3640 struct chanData *cData = uData->channel;
3642 if(uData->access > UL_OWNER)
3644 if(IsProtected(cData)
3645 && (target_handle != user->handle_info)
3646 && !GetTrueChannelAccess(cData, user->handle_info))
3649 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3650 if(uData->flags != USER_AUTO_OP)
3651 string_buffer_append(&sbuf, ',');
3652 if(IsUserSuspended(uData))
3653 string_buffer_append(&sbuf, 's');
3654 if(IsUserAutoOp(uData))
3656 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3657 string_buffer_append(&sbuf, 'o');
3658 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3659 string_buffer_append(&sbuf, 'v');
3661 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3662 string_buffer_append(&sbuf, 'i');
3664 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3666 string_buffer_append_string(&sbuf, ")]");
3667 string_buffer_append(&sbuf, '\0');
3668 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3674 static CHANSERV_FUNC(cmd_access)
3676 struct userNode *target;
3677 struct handle_info *target_handle;
3678 struct userData *uData;
3680 char prefix[MAXLEN];
3685 target_handle = target->handle_info;
3687 else if((target = GetUserH(argv[1])))
3689 target_handle = target->handle_info;
3691 else if(argv[1][0] == '*')
3693 if(!(target_handle = get_handle_info(argv[1]+1)))
3695 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3701 reply("MSG_NICK_UNKNOWN", argv[1]);
3705 assert(target || target_handle);
3707 if(target == chanserv)
3709 reply("CSMSG_IS_CHANSERV");
3717 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3722 reply("MSG_USER_AUTHENTICATE", target->nick);
3725 reply("MSG_AUTHENTICATE");
3731 const char *epithet = NULL, *type = NULL;
3734 epithet = chanserv_conf.irc_operator_epithet;
3735 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3737 else if(IsNetworkHelper(target))
3739 epithet = chanserv_conf.network_helper_epithet;
3740 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3742 else if(IsSupportHelper(target))
3744 epithet = chanserv_conf.support_helper_epithet;
3745 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3749 if(target_handle->epithet)
3750 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3752 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3754 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3758 sprintf(prefix, "%s", target_handle->handle);
3761 if(!channel->channel_info)
3763 reply("CSMSG_NOT_REGISTERED", channel->name);
3767 helping = HANDLE_FLAGGED(target_handle, HELPING)
3768 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3769 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3771 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3772 /* To prevent possible information leaks, only show infolines
3773 * if the requestor is in the channel or it's their own
3775 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3777 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3779 /* Likewise, only say it's suspended if the user has active
3780 * access in that channel or it's their own entry. */
3781 if(IsUserSuspended(uData)
3782 && (GetChannelUser(channel->channel_info, user->handle_info)
3783 || (user->handle_info == uData->handle)))
3785 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3790 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3797 zoot_list(struct listData *list)
3799 struct userData *uData;
3800 unsigned int start, curr, highest, lowest;
3801 struct helpfile_table tmp_table;
3802 const char **temp, *msg;
3804 if(list->table.length == 1)
3807 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3809 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3810 msg = user_find_message(list->user, "MSG_NONE");
3811 send_message_type(4, list->user, list->bot, " %s", msg);
3813 tmp_table.width = list->table.width;
3814 tmp_table.flags = list->table.flags;
3815 list->table.contents[0][0] = " ";
3816 highest = list->highest;
3817 if(list->lowest != 0)
3818 lowest = list->lowest;
3819 else if(highest < 100)
3822 lowest = highest - 100;
3823 for(start = curr = 1; curr < list->table.length; )
3825 uData = list->users[curr-1];
3826 list->table.contents[curr++][0] = " ";
3827 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3830 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3832 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3833 temp = list->table.contents[--start];
3834 list->table.contents[start] = list->table.contents[0];
3835 tmp_table.contents = list->table.contents + start;
3836 tmp_table.length = curr - start;
3837 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3838 list->table.contents[start] = temp;
3840 highest = lowest - 1;
3841 lowest = (highest < 100) ? 0 : (highest - 99);
3847 def_list(struct listData *list)
3851 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3853 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3854 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3855 if(list->table.length == 1)
3857 msg = user_find_message(list->user, "MSG_NONE");
3858 send_message_type(4, list->user, list->bot, " %s", msg);
3863 userData_access_comp(const void *arg_a, const void *arg_b)
3865 const struct userData *a = *(struct userData**)arg_a;
3866 const struct userData *b = *(struct userData**)arg_b;
3868 if(a->access != b->access)
3869 res = b->access - a->access;
3871 res = irccasecmp(a->handle->handle, b->handle->handle);
3876 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3878 void (*send_list)(struct listData *);
3879 struct userData *uData;
3880 struct listData lData;
3881 unsigned int matches;
3885 lData.bot = cmd->parent->bot;
3886 lData.channel = channel;
3887 lData.lowest = lowest;
3888 lData.highest = highest;
3889 lData.search = (argc > 1) ? argv[1] : NULL;
3890 send_list = def_list;
3891 (void)zoot_list; /* since it doesn't show user levels */
3893 if(user->handle_info)
3895 switch(user->handle_info->userlist_style)
3897 case HI_STYLE_DEF: send_list = def_list; break;
3898 case HI_STYLE_ZOOT: send_list = def_list; break;
3902 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3904 for(uData = channel->channel_info->users; uData; uData = uData->next)
3906 if((uData->access < lowest)
3907 || (uData->access > highest)
3908 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3910 lData.users[matches++] = uData;
3912 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3914 lData.table.length = matches+1;
3915 lData.table.width = 4;
3916 lData.table.flags = TABLE_NO_FREE;
3917 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3918 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3919 lData.table.contents[0] = ary;
3922 ary[2] = "Last Seen";
3924 for(matches = 1; matches < lData.table.length; ++matches)
3926 struct userData *uData = lData.users[matches-1];
3927 char seen[INTERVALLEN];
3929 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3930 lData.table.contents[matches] = ary;
3931 ary[0] = strtab(uData->access);
3932 ary[1] = uData->handle->handle;
3935 else if(!uData->seen)
3938 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3939 ary[2] = strdup(ary[2]);
3940 if(IsUserSuspended(uData))
3941 ary[3] = "Suspended";
3942 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3943 ary[3] = "Vacation";
3948 for(matches = 1; matches < lData.table.length; ++matches)
3950 free((char*)lData.table.contents[matches][2]);
3951 free(lData.table.contents[matches]);
3953 free(lData.table.contents[0]);
3954 free(lData.table.contents);
3958 static CHANSERV_FUNC(cmd_users)
3960 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3963 static CHANSERV_FUNC(cmd_wlist)
3965 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3968 static CHANSERV_FUNC(cmd_clist)
3970 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3973 static CHANSERV_FUNC(cmd_mlist)
3975 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3978 static CHANSERV_FUNC(cmd_olist)
3980 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3983 static CHANSERV_FUNC(cmd_plist)
3985 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3988 static CHANSERV_FUNC(cmd_bans)
3990 struct userNode *search_u = NULL;
3991 struct helpfile_table tbl;
3992 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3993 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3994 const char *msg_never, *triggered, *expires;
3995 struct banData *ban, **bans;
3999 else if(strchr(search = argv[1], '!'))
4002 search_wilds = search[strcspn(search, "?*")];
4004 else if(!(search_u = GetUserH(search)))
4005 reply("MSG_NICK_UNKNOWN", search);
4007 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4009 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4013 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4018 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4021 bans[matches++] = ban;
4026 tbl.length = matches + 1;
4027 tbl.width = 4 + timed;
4029 tbl.flags = TABLE_NO_FREE;
4030 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4031 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4032 tbl.contents[0][0] = "Mask";
4033 tbl.contents[0][1] = "Set By";
4034 tbl.contents[0][2] = "Triggered";
4037 tbl.contents[0][3] = "Expires";
4038 tbl.contents[0][4] = "Reason";
4041 tbl.contents[0][3] = "Reason";
4044 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4046 free(tbl.contents[0]);
4051 msg_never = user_find_message(user, "MSG_NEVER");
4052 for(ii = 0; ii < matches; )
4058 else if(ban->expires)
4059 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4061 expires = msg_never;
4064 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4066 triggered = msg_never;
4068 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4069 tbl.contents[ii][0] = ban->mask;
4070 tbl.contents[ii][1] = ban->owner;
4071 tbl.contents[ii][2] = strdup(triggered);
4074 tbl.contents[ii][3] = strdup(expires);
4075 tbl.contents[ii][4] = ban->reason;
4078 tbl.contents[ii][3] = ban->reason;
4080 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4081 reply("MSG_MATCH_COUNT", matches);
4082 for(ii = 1; ii < tbl.length; ++ii)
4084 free((char*)tbl.contents[ii][2]);
4086 free((char*)tbl.contents[ii][3]);
4087 free(tbl.contents[ii]);
4089 free(tbl.contents[0]);
4095 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4097 struct chanData *cData = channel->channel_info;
4098 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4100 if(cData->topic_mask)
4101 return !match_ircglob(new_topic, cData->topic_mask);
4102 else if(cData->topic)
4103 return irccasecmp(new_topic, cData->topic);
4108 static CHANSERV_FUNC(cmd_topic)
4110 struct chanData *cData;
4113 cData = channel->channel_info;
4118 SetChannelTopic(channel, chanserv, cData->topic, 1);
4119 reply("CSMSG_TOPIC_SET", cData->topic);
4123 reply("CSMSG_NO_TOPIC", channel->name);
4127 topic = unsplit_string(argv + 1, argc - 1, NULL);
4128 /* If they say "!topic *", use an empty topic. */
4129 if((topic[0] == '*') && (topic[1] == 0))
4131 if(bad_topic(channel, user, topic))
4133 char *topic_mask = cData->topic_mask;
4136 char new_topic[TOPICLEN+1], tchar;
4137 int pos=0, starpos=-1, dpos=0, len;
4139 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4146 len = strlen(topic);
4147 if((dpos + len) > TOPICLEN)
4148 len = TOPICLEN + 1 - dpos;
4149 memcpy(new_topic+dpos, topic, len);
4153 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4154 default: new_topic[dpos++] = tchar; break;
4157 if((dpos > TOPICLEN) || tchar)
4160 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4161 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4164 new_topic[dpos] = 0;
4165 SetChannelTopic(channel, chanserv, new_topic, 1);
4167 reply("CSMSG_TOPIC_LOCKED", channel->name);
4172 SetChannelTopic(channel, chanserv, topic, 1);
4174 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4176 /* Grab the topic and save it as the default topic. */
4178 cData->topic = strdup(channel->topic);
4184 static CHANSERV_FUNC(cmd_mode)
4186 struct userData *uData;
4187 struct mod_chanmode *change;
4192 change = &channel->channel_info->modes;
4193 if(change->modes_set || change->modes_clear) {
4194 modcmd_chanmode_announce(change);
4195 reply("CSMSG_DEFAULTED_MODES", channel->name);
4197 reply("CSMSG_NO_MODES", channel->name);
4201 uData = GetChannelUser(channel->channel_info, user->handle_info);
4203 base_oplevel = MAXOPLEVEL;
4204 else if (uData->access >= UL_OWNER)
4207 base_oplevel = 1 + UL_OWNER - uData->access;
4208 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4211 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4215 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4216 && mode_lock_violated(&channel->channel_info->modes, change))
4219 mod_chanmode_format(&channel->channel_info->modes, modes);
4220 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4224 modcmd_chanmode_announce(change);
4225 mod_chanmode_free(change);
4226 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4230 static CHANSERV_FUNC(cmd_invite)
4232 struct userData *uData;
4233 struct userNode *invite;
4235 uData = GetChannelUser(channel->channel_info, user->handle_info);
4239 if(!(invite = GetUserH(argv[1])))
4241 reply("MSG_NICK_UNKNOWN", argv[1]);
4248 if(GetUserMode(channel, invite))
4250 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4258 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4259 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4262 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4264 irc_invite(chanserv, invite, channel);
4266 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4271 static CHANSERV_FUNC(cmd_inviteme)
4273 if(GetUserMode(channel, user))
4275 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4278 if(channel->channel_info
4279 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4281 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4284 irc_invite(cmd->parent->bot, user, channel);
4289 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4292 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4294 /* We display things based on two dimensions:
4295 * - Issue time: present or absent
4296 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4297 * (in order of precedence, so something both expired and revoked
4298 * only counts as revoked)
4300 combo = (suspended->issued ? 4 : 0)
4301 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4303 case 0: /* no issue time, indefinite expiration */
4304 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4306 case 1: /* no issue time, expires in future */
4307 intervalString(buf1, suspended->expires-now, user->handle_info);
4308 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4310 case 2: /* no issue time, expired */
4311 intervalString(buf1, now-suspended->expires, user->handle_info);
4312 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4314 case 3: /* no issue time, revoked */
4315 intervalString(buf1, now-suspended->revoked, user->handle_info);
4316 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4318 case 4: /* issue time set, indefinite expiration */
4319 intervalString(buf1, now-suspended->issued, user->handle_info);
4320 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4322 case 5: /* issue time set, expires in future */
4323 intervalString(buf1, now-suspended->issued, user->handle_info);
4324 intervalString(buf2, suspended->expires-now, user->handle_info);
4325 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4327 case 6: /* issue time set, expired */
4328 intervalString(buf1, now-suspended->issued, user->handle_info);
4329 intervalString(buf2, now-suspended->expires, user->handle_info);
4330 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4332 case 7: /* issue time set, revoked */
4333 intervalString(buf1, now-suspended->issued, user->handle_info);
4334 intervalString(buf2, now-suspended->revoked, user->handle_info);
4335 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4338 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4343 static CHANSERV_FUNC(cmd_info)
4345 char modes[MAXLEN], buffer[INTERVALLEN];
4346 struct userData *uData, *owner;
4347 struct chanData *cData;
4348 struct do_not_register *dnr;
4353 cData = channel->channel_info;
4354 reply("CSMSG_CHANNEL_INFO", channel->name);
4356 uData = GetChannelUser(cData, user->handle_info);
4357 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4359 mod_chanmode_format(&cData->modes, modes);
4360 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4361 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4364 for(it = dict_first(cData->notes); it; it = iter_next(it))
4368 note = iter_data(it);
4369 if(!note_type_visible_to_user(cData, note->type, user))
4372 padding = PADLEN - 1 - strlen(iter_key(it));
4373 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4376 reply("CSMSG_CHANNEL_MAX", cData->max);
4377 for(owner = cData->users; owner; owner = owner->next)
4378 if(owner->access == UL_OWNER)
4379 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4380 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4381 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4382 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4384 privileged = IsStaff(user);
4386 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4387 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4388 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4390 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4391 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4393 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4395 struct suspended *suspended;
4396 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4397 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4398 show_suspension_info(cmd, user, suspended);
4400 else if(IsSuspended(cData))
4402 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4403 show_suspension_info(cmd, user, cData->suspended);
4408 static CHANSERV_FUNC(cmd_netinfo)
4410 extern unsigned long boot_time;
4411 extern unsigned long burst_length;
4412 char interval[INTERVALLEN];
4414 reply("CSMSG_NETWORK_INFO");
4415 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4416 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4417 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4418 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4419 reply("CSMSG_NETWORK_BANS", banCount);
4420 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4421 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4422 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4427 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4429 struct helpfile_table table;
4431 struct userNode *user;
4436 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4437 table.contents = alloca(list->used*sizeof(*table.contents));
4438 for(nn=0; nn<list->used; nn++)
4440 user = list->list[nn];
4441 if(user->modes & skip_flags)
4445 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4448 nick = alloca(strlen(user->nick)+3);
4449 sprintf(nick, "(%s)", user->nick);
4453 table.contents[table.length][0] = nick;
4456 table_send(chanserv, to->nick, 0, NULL, table);
4459 static CHANSERV_FUNC(cmd_ircops)
4461 reply("CSMSG_STAFF_OPERS");
4462 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4466 static CHANSERV_FUNC(cmd_helpers)
4468 reply("CSMSG_STAFF_HELPERS");
4469 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4473 static CHANSERV_FUNC(cmd_staff)
4475 reply("CSMSG_NETWORK_STAFF");
4476 cmd_ircops(CSFUNC_ARGS);
4477 cmd_helpers(CSFUNC_ARGS);
4481 static CHANSERV_FUNC(cmd_peek)
4483 struct modeNode *mn;
4484 char modes[MODELEN];
4486 struct helpfile_table table;
4488 irc_make_chanmode(channel, modes);
4490 reply("CSMSG_PEEK_INFO", channel->name);
4491 reply("CSMSG_PEEK_TOPIC", channel->topic);
4492 reply("CSMSG_PEEK_MODES", modes);
4493 reply("CSMSG_PEEK_USERS", channel->members.used);
4497 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4498 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4499 for(n = 0; n < channel->members.used; n++)
4501 mn = channel->members.list[n];
4502 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4504 table.contents[table.length] = alloca(sizeof(**table.contents));
4505 table.contents[table.length][0] = mn->user->nick;
4510 reply("CSMSG_PEEK_OPS");
4511 table_send(chanserv, user->nick, 0, NULL, table);
4514 reply("CSMSG_PEEK_NO_OPS");
4518 static MODCMD_FUNC(cmd_wipeinfo)
4520 struct handle_info *victim;
4521 struct userData *ud, *actor, *real_actor;
4522 unsigned int override = 0;
4525 actor = GetChannelUser(channel->channel_info, user->handle_info);
4526 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4527 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4529 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4531 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4534 if((ud->access >= actor->access) && (ud != actor))
4536 reply("MSG_USER_OUTRANKED", victim->handle);
4539 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4540 override = CMD_LOG_OVERRIDE;
4544 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4545 return 1 | override;
4548 static CHANSERV_FUNC(cmd_resync)
4550 struct mod_chanmode *changes;
4551 struct chanData *cData = channel->channel_info;
4552 unsigned int ii, used;
4554 changes = mod_chanmode_alloc(channel->members.used * 2);
4555 for(ii = used = 0; ii < channel->members.used; ++ii)
4557 struct modeNode *mn = channel->members.list[ii];
4558 struct userData *uData;
4560 if(IsService(mn->user))
4563 uData = GetChannelAccess(cData, mn->user->handle_info);
4564 if(!cData->lvlOpts[lvlGiveOps]
4565 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4567 if(!(mn->modes & MODE_CHANOP))
4569 changes->args[used].mode = MODE_CHANOP;
4570 changes->args[used++].u.member = mn;
4573 else if(!cData->lvlOpts[lvlGiveVoice]
4574 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4576 if(mn->modes & MODE_CHANOP)
4578 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4579 changes->args[used++].u.member = mn;
4581 if(!(mn->modes & MODE_VOICE))
4583 changes->args[used].mode = MODE_VOICE;
4584 changes->args[used++].u.member = mn;
4591 changes->args[used].mode = MODE_REMOVE | mn->modes;
4592 changes->args[used++].u.member = mn;
4596 changes->argc = used;
4597 modcmd_chanmode_announce(changes);
4598 mod_chanmode_free(changes);
4599 reply("CSMSG_RESYNCED_USERS", channel->name);
4603 static CHANSERV_FUNC(cmd_seen)
4605 struct userData *uData;
4606 struct handle_info *handle;
4607 char seen[INTERVALLEN];
4611 if(!irccasecmp(argv[1], chanserv->nick))
4613 reply("CSMSG_IS_CHANSERV");
4617 if(!(handle = get_handle_info(argv[1])))
4619 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4623 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4625 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4630 reply("CSMSG_USER_PRESENT", handle->handle);
4631 else if(uData->seen)
4632 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4634 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4636 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4637 reply("CSMSG_USER_VACATION", handle->handle);
4642 static MODCMD_FUNC(cmd_names)
4644 struct userNode *targ;
4645 struct userData *targData;
4646 unsigned int ii, pos;
4649 for(ii=pos=0; ii<channel->members.used; ++ii)
4651 targ = channel->members.list[ii]->user;
4652 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4655 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4658 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4662 if(IsUserSuspended(targData))
4664 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4667 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4668 reply("CSMSG_END_NAMES", channel->name);
4673 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4675 switch(ntype->visible_type)
4677 case NOTE_VIS_ALL: return 1;
4678 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4679 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4684 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4686 struct userData *uData;
4688 switch(ntype->set_access_type)
4690 case NOTE_SET_CHANNEL_ACCESS:
4691 if(!user->handle_info)
4693 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4695 return uData->access >= ntype->set_access.min_ulevel;
4696 case NOTE_SET_CHANNEL_SETTER:
4697 return check_user_level(channel, user, lvlSetters, 1, 0);
4698 case NOTE_SET_PRIVILEGED: default:
4699 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4703 static CHANSERV_FUNC(cmd_note)
4705 struct chanData *cData;
4707 struct note_type *ntype;
4709 cData = channel->channel_info;
4712 reply("CSMSG_NOT_REGISTERED", channel->name);
4716 /* If no arguments, show all visible notes for the channel. */
4722 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4724 note = iter_data(it);
4725 if(!note_type_visible_to_user(cData, note->type, user))
4728 reply("CSMSG_NOTELIST_HEADER", channel->name);
4729 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4732 reply("CSMSG_NOTELIST_END", channel->name);
4734 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4736 /* If one argument, show the named note. */
4739 if((note = dict_find(cData->notes, argv[1], NULL))
4740 && note_type_visible_to_user(cData, note->type, user))
4742 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4744 else if((ntype = dict_find(note_types, argv[1], NULL))
4745 && note_type_visible_to_user(NULL, ntype, user))
4747 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4752 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4756 /* Assume they're trying to set a note. */
4760 ntype = dict_find(note_types, argv[1], NULL);
4763 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4766 else if(note_type_settable_by_user(channel, ntype, user))
4768 note_text = unsplit_string(argv+2, argc-2, NULL);
4769 if((note = dict_find(cData->notes, argv[1], NULL)))
4770 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4771 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4772 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4774 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4776 /* The note is viewable to staff only, so return 0
4777 to keep the invocation from getting logged (or
4778 regular users can see it in !events). */
4784 reply("CSMSG_NO_ACCESS");
4791 static CHANSERV_FUNC(cmd_delnote)
4796 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4797 || !note_type_settable_by_user(channel, note->type, user))
4799 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4802 dict_remove(channel->channel_info->notes, note->type->name);
4803 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4807 static CHANSERV_FUNC(cmd_events)
4809 struct logSearch discrim;
4810 struct logReport report;
4811 unsigned int matches, limit;
4813 limit = (argc > 1) ? atoi(argv[1]) : 10;
4814 if(limit < 1 || limit > 200)
4817 memset(&discrim, 0, sizeof(discrim));
4818 discrim.masks.bot = chanserv;
4819 discrim.masks.channel_name = channel->name;
4821 discrim.masks.command = argv[2];
4822 discrim.limit = limit;
4823 discrim.max_time = INT_MAX;
4824 discrim.severities = 1 << LOG_COMMAND;
4825 report.reporter = chanserv;
4827 reply("CSMSG_EVENT_SEARCH_RESULTS");
4828 matches = log_entry_search(&discrim, log_report_entry, &report);
4830 reply("MSG_MATCH_COUNT", matches);
4832 reply("MSG_NO_MATCHES");
4836 static CHANSERV_FUNC(cmd_say)
4842 msg = unsplit_string(argv + 1, argc - 1, NULL);
4843 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4845 else if(*argv[1] == '*' && argv[1][1] != '\0')
4847 struct handle_info *hi;
4848 struct userNode *authed;
4851 msg = unsplit_string(argv + 2, argc - 2, NULL);
4853 if (!(hi = get_handle_info(argv[1] + 1)))
4855 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4859 for (authed = hi->users; authed; authed = authed->next_authed)
4860 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4862 else if(GetUserH(argv[1]))
4865 msg = unsplit_string(argv + 2, argc - 2, NULL);
4866 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4870 reply("MSG_NOT_TARGET_NAME");
4876 static CHANSERV_FUNC(cmd_emote)
4882 /* CTCP is so annoying. */
4883 msg = unsplit_string(argv + 1, argc - 1, NULL);
4884 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4886 else if(*argv[1] == '*' && argv[1][1] != '\0')
4888 struct handle_info *hi;
4889 struct userNode *authed;
4892 msg = unsplit_string(argv + 2, argc - 2, NULL);
4894 if (!(hi = get_handle_info(argv[1] + 1)))
4896 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4900 for (authed = hi->users; authed; authed = authed->next_authed)
4901 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4903 else if(GetUserH(argv[1]))
4905 msg = unsplit_string(argv + 2, argc - 2, NULL);
4906 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4910 reply("MSG_NOT_TARGET_NAME");
4916 struct channelList *
4917 chanserv_support_channels(void)
4919 return &chanserv_conf.support_channels;
4922 static CHANSERV_FUNC(cmd_expire)
4924 int channel_count = registered_channels;
4925 expire_channels(NULL);
4926 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4931 chanserv_expire_suspension(void *data)
4933 struct suspended *suspended = data;
4934 struct chanNode *channel;
4936 if(!suspended->expires || (now < suspended->expires))
4937 suspended->revoked = now;
4938 channel = suspended->cData->channel;
4939 suspended->cData->channel = channel;
4940 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4941 if(!IsOffChannel(suspended->cData))
4943 struct mod_chanmode change;
4944 mod_chanmode_init(&change);
4946 change.args[0].mode = MODE_CHANOP;
4947 change.args[0].u.member = AddChannelUser(chanserv, channel);
4948 mod_chanmode_announce(chanserv, channel, &change);
4952 static CHANSERV_FUNC(cmd_csuspend)
4954 struct suspended *suspended;
4955 char reason[MAXLEN];
4956 unsigned long expiry, duration;
4957 struct userData *uData;
4961 if(IsProtected(channel->channel_info))
4963 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4967 if(argv[1][0] == '!')
4969 else if(IsSuspended(channel->channel_info))
4971 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4972 show_suspension_info(cmd, user, channel->channel_info->suspended);
4976 if(!strcmp(argv[1], "0"))
4978 else if((duration = ParseInterval(argv[1])))
4979 expiry = now + duration;
4982 reply("MSG_INVALID_DURATION", argv[1]);
4986 unsplit_string(argv + 2, argc - 2, reason);
4988 suspended = calloc(1, sizeof(*suspended));
4989 suspended->revoked = 0;
4990 suspended->issued = now;
4991 suspended->suspender = strdup(user->handle_info->handle);
4992 suspended->expires = expiry;
4993 suspended->reason = strdup(reason);
4994 suspended->cData = channel->channel_info;
4995 suspended->previous = suspended->cData->suspended;
4996 suspended->cData->suspended = suspended;
4998 if(suspended->expires)
4999 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5001 if(IsSuspended(channel->channel_info))
5003 suspended->previous->revoked = now;
5004 if(suspended->previous->expires)
5005 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5006 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5007 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5011 /* Mark all users in channel as absent. */
5012 for(uData = channel->channel_info->users; uData; uData = uData->next)
5021 /* Mark the channel as suspended, then part. */
5022 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5023 DelChannelUser(chanserv, channel, suspended->reason, 0);
5024 reply("CSMSG_SUSPENDED", channel->name);
5025 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5026 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5031 static CHANSERV_FUNC(cmd_cunsuspend)
5033 struct suspended *suspended;
5034 char message[MAXLEN];
5036 if(!IsSuspended(channel->channel_info))
5038 reply("CSMSG_NOT_SUSPENDED", channel->name);
5042 suspended = channel->channel_info->suspended;
5044 /* Expire the suspension and join ChanServ to the channel. */
5045 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5046 chanserv_expire_suspension(suspended);
5047 reply("CSMSG_UNSUSPENDED", channel->name);
5048 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5049 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5053 typedef struct chanservSearch
5058 unsigned long unvisited;
5059 unsigned long registered;
5061 unsigned long flags;
5065 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5068 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5073 search = malloc(sizeof(struct chanservSearch));
5074 memset(search, 0, sizeof(*search));
5077 for(i = 0; i < argc; i++)
5079 /* Assume all criteria require arguments. */
5082 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5086 if(!irccasecmp(argv[i], "name"))
5087 search->name = argv[++i];
5088 else if(!irccasecmp(argv[i], "registrar"))
5089 search->registrar = argv[++i];
5090 else if(!irccasecmp(argv[i], "unvisited"))
5091 search->unvisited = ParseInterval(argv[++i]);
5092 else if(!irccasecmp(argv[i], "registered"))
5093 search->registered = ParseInterval(argv[++i]);
5094 else if(!irccasecmp(argv[i], "flags"))
5097 if(!irccasecmp(argv[i], "nodelete"))
5098 search->flags |= CHANNEL_NODELETE;
5099 else if(!irccasecmp(argv[i], "suspended"))
5100 search->flags |= CHANNEL_SUSPENDED;
5101 else if(!irccasecmp(argv[i], "unreviewed"))
5102 search->flags |= CHANNEL_UNREVIEWED;
5105 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5109 else if(!irccasecmp(argv[i], "limit"))
5110 search->limit = strtoul(argv[++i], NULL, 10);
5113 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5118 if(search->name && !strcmp(search->name, "*"))
5120 if(search->registrar && !strcmp(search->registrar, "*"))
5121 search->registrar = 0;
5130 chanserv_channel_match(struct chanData *channel, search_t search)
5132 const char *name = channel->channel->name;
5133 if((search->name && !match_ircglob(name, search->name)) ||
5134 (search->registrar && !channel->registrar) ||
5135 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5136 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5137 (search->registered && (now - channel->registered) > search->registered) ||
5138 (search->flags && ((search->flags & channel->flags) != search->flags)))
5145 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5147 struct chanData *channel;
5148 unsigned int matches = 0;
5150 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5152 if(!chanserv_channel_match(channel, search))
5162 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5167 search_print(struct chanData *channel, void *data)
5169 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5172 static CHANSERV_FUNC(cmd_search)
5175 unsigned int matches;
5176 channel_search_func action;
5180 if(!irccasecmp(argv[1], "count"))
5181 action = search_count;
5182 else if(!irccasecmp(argv[1], "print"))
5183 action = search_print;
5186 reply("CSMSG_ACTION_INVALID", argv[1]);
5190 search = chanserv_search_create(user, argc - 2, argv + 2);
5194 if(action == search_count)
5195 search->limit = INT_MAX;
5197 if(action == search_print)
5198 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5200 matches = chanserv_channel_search(search, action, user);
5203 reply("MSG_MATCH_COUNT", matches);
5205 reply("MSG_NO_MATCHES");
5211 static CHANSERV_FUNC(cmd_unvisited)
5213 struct chanData *cData;
5214 unsigned long interval = chanserv_conf.channel_expire_delay;
5215 char buffer[INTERVALLEN];
5216 unsigned int limit = 25, matches = 0;
5220 interval = ParseInterval(argv[1]);
5222 limit = atoi(argv[2]);
5225 intervalString(buffer, interval, user->handle_info);
5226 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5228 for(cData = channelList; cData && matches < limit; cData = cData->next)
5230 if((now - cData->visited) < interval)
5233 intervalString(buffer, now - cData->visited, user->handle_info);
5234 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5241 static MODCMD_FUNC(chan_opt_defaulttopic)
5247 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5249 reply("CSMSG_TOPIC_LOCKED", channel->name);
5253 topic = unsplit_string(argv+1, argc-1, NULL);
5255 free(channel->channel_info->topic);
5256 if(topic[0] == '*' && topic[1] == 0)
5258 topic = channel->channel_info->topic = NULL;
5262 topic = channel->channel_info->topic = strdup(topic);
5263 if(channel->channel_info->topic_mask
5264 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5265 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5267 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5270 if(channel->channel_info->topic)
5271 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5273 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5277 static MODCMD_FUNC(chan_opt_topicmask)
5281 struct chanData *cData = channel->channel_info;
5284 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5286 reply("CSMSG_TOPIC_LOCKED", channel->name);
5290 mask = unsplit_string(argv+1, argc-1, NULL);
5292 if(cData->topic_mask)
5293 free(cData->topic_mask);
5294 if(mask[0] == '*' && mask[1] == 0)
5296 cData->topic_mask = 0;
5300 cData->topic_mask = strdup(mask);
5302 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5303 else if(!match_ircglob(cData->topic, cData->topic_mask))
5304 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5308 if(channel->channel_info->topic_mask)
5309 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5311 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5315 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5319 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5323 if(greeting[0] == '*' && greeting[1] == 0)
5327 unsigned int length = strlen(greeting);
5328 if(length > chanserv_conf.greeting_length)
5330 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5333 *data = strdup(greeting);
5342 reply(name, user_find_message(user, "MSG_NONE"));
5346 static MODCMD_FUNC(chan_opt_greeting)
5348 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5351 static MODCMD_FUNC(chan_opt_usergreeting)
5353 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5356 static MODCMD_FUNC(chan_opt_modes)
5358 struct mod_chanmode *new_modes;
5359 char modes[MODELEN];
5363 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5365 reply("CSMSG_NO_ACCESS");
5368 if(argv[1][0] == '*' && argv[1][1] == 0)
5370 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5372 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5374 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5377 else if(new_modes->argc > 1)
5379 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5380 mod_chanmode_free(new_modes);
5385 channel->channel_info->modes = *new_modes;
5386 modcmd_chanmode_announce(new_modes);
5387 mod_chanmode_free(new_modes);
5391 mod_chanmode_format(&channel->channel_info->modes, modes);
5393 reply("CSMSG_SET_MODES", modes);
5395 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5399 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5401 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5403 struct chanData *cData = channel->channel_info;
5408 /* Set flag according to value. */
5409 if(enabled_string(argv[1]))
5411 cData->flags |= mask;
5414 else if(disabled_string(argv[1]))
5416 cData->flags &= ~mask;
5421 reply("MSG_INVALID_BINARY", argv[1]);
5427 /* Find current option value. */
5428 value = (cData->flags & mask) ? 1 : 0;
5432 reply(name, user_find_message(user, "MSG_ON"));
5434 reply(name, user_find_message(user, "MSG_OFF"));
5438 static MODCMD_FUNC(chan_opt_nodelete)
5440 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5442 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5446 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5449 static MODCMD_FUNC(chan_opt_dynlimit)
5451 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5454 static MODCMD_FUNC(chan_opt_offchannel)
5456 struct chanData *cData = channel->channel_info;
5461 /* Set flag according to value. */
5462 if(enabled_string(argv[1]))
5464 if(!IsOffChannel(cData))
5465 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5466 cData->flags |= CHANNEL_OFFCHANNEL;
5469 else if(disabled_string(argv[1]))
5471 if(IsOffChannel(cData))
5473 struct mod_chanmode change;
5474 mod_chanmode_init(&change);
5476 change.args[0].mode = MODE_CHANOP;
5477 change.args[0].u.member = AddChannelUser(chanserv, channel);
5478 mod_chanmode_announce(chanserv, channel, &change);
5480 cData->flags &= ~CHANNEL_OFFCHANNEL;
5485 reply("MSG_INVALID_BINARY", argv[1]);
5491 /* Find current option value. */
5492 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5496 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5498 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5502 static MODCMD_FUNC(chan_opt_unreviewed)
5504 struct chanData *cData = channel->channel_info;
5505 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5511 /* The two directions can have different ACLs. */
5512 if(enabled_string(argv[1]))
5514 else if(disabled_string(argv[1]))
5518 reply("MSG_INVALID_BINARY", argv[1]);
5522 if (new_value != value)
5524 struct svccmd *subcmd;
5525 char subcmd_name[32];
5527 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5528 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5531 reply("MSG_COMMAND_DISABLED", subcmd_name);
5534 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5538 cData->flags |= CHANNEL_UNREVIEWED;
5541 free(cData->registrar);
5542 cData->registrar = strdup(user->handle_info->handle);
5543 cData->flags &= ~CHANNEL_UNREVIEWED;
5550 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5552 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5556 static MODCMD_FUNC(chan_opt_defaults)
5558 struct userData *uData;
5559 struct chanData *cData;
5560 const char *confirm;
5561 enum levelOption lvlOpt;
5562 enum charOption chOpt;
5564 cData = channel->channel_info;
5565 uData = GetChannelUser(cData, user->handle_info);
5566 if(!uData || (uData->access < UL_OWNER))
5568 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5571 confirm = make_confirmation_string(uData);
5572 if((argc < 2) || strcmp(argv[1], confirm))
5574 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5577 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5578 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5579 cData->modes = chanserv_conf.default_modes;
5580 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5581 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5582 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5583 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5584 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5589 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5591 struct chanData *cData = channel->channel_info;
5592 struct userData *uData;
5593 unsigned short value;
5597 if(!check_user_level(channel, user, option, 1, 1))
5599 reply("CSMSG_CANNOT_SET");
5602 value = user_level_from_name(argv[1], UL_OWNER+1);
5603 if(!value && strcmp(argv[1], "0"))
5605 reply("CSMSG_INVALID_ACCESS", argv[1]);
5608 uData = GetChannelUser(cData, user->handle_info);
5609 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5611 reply("CSMSG_BAD_SETLEVEL");
5617 if(value > cData->lvlOpts[lvlGiveOps])
5619 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5624 if(value < cData->lvlOpts[lvlGiveVoice])
5626 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5631 /* This test only applies to owners, since non-owners
5632 * trying to set an option to above their level get caught
5633 * by the CSMSG_BAD_SETLEVEL test above.
5635 if(value > uData->access)
5637 reply("CSMSG_BAD_SETTERS");
5644 cData->lvlOpts[option] = value;
5646 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5650 static MODCMD_FUNC(chan_opt_enfops)
5652 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5655 static MODCMD_FUNC(chan_opt_giveops)
5657 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5660 static MODCMD_FUNC(chan_opt_enfmodes)
5662 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5665 static MODCMD_FUNC(chan_opt_enftopic)
5667 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5670 static MODCMD_FUNC(chan_opt_pubcmd)
5672 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5675 static MODCMD_FUNC(chan_opt_setters)
5677 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5680 static MODCMD_FUNC(chan_opt_ctcpusers)
5682 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5685 static MODCMD_FUNC(chan_opt_userinfo)
5687 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5690 static MODCMD_FUNC(chan_opt_givevoice)
5692 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5695 static MODCMD_FUNC(chan_opt_topicsnarf)
5697 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5700 static MODCMD_FUNC(chan_opt_inviteme)
5702 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5706 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5708 struct chanData *cData = channel->channel_info;
5709 int count = charOptions[option].count, index;
5713 index = atoi(argv[1]);
5715 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5717 reply("CSMSG_INVALID_NUMERIC", index);
5718 /* Show possible values. */
5719 for(index = 0; index < count; index++)
5720 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5724 cData->chOpts[option] = charOptions[option].values[index].value;
5728 /* Find current option value. */
5731 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5735 /* Somehow, the option value is corrupt; reset it to the default. */
5736 cData->chOpts[option] = charOptions[option].default_value;
5741 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5745 static MODCMD_FUNC(chan_opt_protect)
5747 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5750 static MODCMD_FUNC(chan_opt_toys)
5752 return channel_multiple_option(chToys, CSFUNC_ARGS);
5755 static MODCMD_FUNC(chan_opt_ctcpreaction)
5757 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5760 static MODCMD_FUNC(chan_opt_topicrefresh)
5762 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5765 static struct svccmd_list set_shows_list;
5768 handle_svccmd_unbind(struct svccmd *target) {
5770 for(ii=0; ii<set_shows_list.used; ++ii)
5771 if(target == set_shows_list.list[ii])
5772 set_shows_list.used = 0;
5775 static CHANSERV_FUNC(cmd_set)
5777 struct svccmd *subcmd;
5781 /* Check if we need to (re-)initialize set_shows_list. */
5782 if(!set_shows_list.used)
5784 if(!set_shows_list.size)
5786 set_shows_list.size = chanserv_conf.set_shows->used;
5787 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5789 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5791 const char *name = chanserv_conf.set_shows->list[ii];
5792 sprintf(buf, "%s %s", argv[0], name);
5793 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5796 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5799 svccmd_list_append(&set_shows_list, subcmd);
5805 reply("CSMSG_CHANNEL_OPTIONS");
5806 for(ii = 0; ii < set_shows_list.used; ii++)
5808 subcmd = set_shows_list.list[ii];
5809 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5814 sprintf(buf, "%s %s", argv[0], argv[1]);
5815 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5818 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5821 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5823 reply("CSMSG_NO_ACCESS");
5829 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5833 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5835 struct userData *uData;
5837 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5840 reply("CSMSG_NOT_USER", channel->name);
5846 /* Just show current option value. */
5848 else if(enabled_string(argv[1]))
5850 uData->flags |= mask;
5852 else if(disabled_string(argv[1]))
5854 uData->flags &= ~mask;
5858 reply("MSG_INVALID_BINARY", argv[1]);
5862 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5866 static MODCMD_FUNC(user_opt_noautoop)
5868 struct userData *uData;
5870 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5873 reply("CSMSG_NOT_USER", channel->name);
5876 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5877 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5879 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5882 static MODCMD_FUNC(user_opt_autoinvite)
5884 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5886 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5888 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5891 static MODCMD_FUNC(user_opt_info)
5893 struct userData *uData;
5896 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5900 /* If they got past the command restrictions (which require access)
5901 * but fail this test, we have some fool with security override on.
5903 reply("CSMSG_NOT_USER", channel->name);
5910 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5911 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5913 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5916 bp = strcspn(infoline, "\001");
5919 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5924 if(infoline[0] == '*' && infoline[1] == 0)
5927 uData->info = strdup(infoline);
5930 reply("CSMSG_USET_INFO", uData->info);
5932 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5936 struct svccmd_list uset_shows_list;
5938 static CHANSERV_FUNC(cmd_uset)
5940 struct svccmd *subcmd;
5944 /* Check if we need to (re-)initialize uset_shows_list. */
5945 if(!uset_shows_list.used)
5949 "NoAutoOp", "AutoInvite", "Info"
5952 if(!uset_shows_list.size)
5954 uset_shows_list.size = ArrayLength(options);
5955 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5957 for(ii = 0; ii < ArrayLength(options); ii++)
5959 const char *name = options[ii];
5960 sprintf(buf, "%s %s", argv[0], name);
5961 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5964 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5967 svccmd_list_append(&uset_shows_list, subcmd);
5973 /* Do this so options are presented in a consistent order. */
5974 reply("CSMSG_USER_OPTIONS");
5975 for(ii = 0; ii < uset_shows_list.used; ii++)
5976 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5980 sprintf(buf, "%s %s", argv[0], argv[1]);
5981 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5984 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5988 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5991 static CHANSERV_FUNC(cmd_giveownership)
5993 struct handle_info *new_owner_hi;
5994 struct userData *new_owner;
5995 struct userData *curr_user;
5996 struct userData *invoker;
5997 struct chanData *cData = channel->channel_info;
5998 struct do_not_register *dnr;
5999 const char *confirm;
6001 unsigned short co_access;
6002 char reason[MAXLEN];
6005 curr_user = GetChannelAccess(cData, user->handle_info);
6006 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6007 if(!curr_user || (curr_user->access != UL_OWNER))
6009 struct userData *owner = NULL;
6010 for(curr_user = channel->channel_info->users;
6012 curr_user = curr_user->next)
6014 if(curr_user->access != UL_OWNER)
6018 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6025 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6027 char delay[INTERVALLEN];
6028 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6029 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6032 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6034 if(new_owner_hi == user->handle_info)
6036 reply("CSMSG_NO_TRANSFER_SELF");
6039 new_owner = GetChannelAccess(cData, new_owner_hi);
6044 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6048 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6052 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6054 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6057 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6058 if(!IsHelping(user))
6059 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6061 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6064 invoker = GetChannelUser(cData, user->handle_info);
6065 if(invoker->access <= UL_OWNER)
6067 confirm = make_confirmation_string(curr_user);
6068 if((argc < 3) || strcmp(argv[2], confirm))
6070 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6074 if(new_owner->access >= UL_COOWNER)
6075 co_access = new_owner->access;
6077 co_access = UL_COOWNER;
6078 new_owner->access = UL_OWNER;
6080 curr_user->access = co_access;
6081 cData->ownerTransfer = now;
6082 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6083 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6084 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6088 static CHANSERV_FUNC(cmd_suspend)
6090 struct handle_info *hi;
6091 struct userData *self, *real_self, *target;
6092 unsigned int override = 0;
6095 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6096 self = GetChannelUser(channel->channel_info, user->handle_info);
6097 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6098 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6100 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6103 if(target->access >= self->access)
6105 reply("MSG_USER_OUTRANKED", hi->handle);
6108 if(target->flags & USER_SUSPENDED)
6110 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6115 target->present = 0;
6118 if(!real_self || target->access >= real_self->access)
6119 override = CMD_LOG_OVERRIDE;
6120 target->flags |= USER_SUSPENDED;
6121 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6122 return 1 | override;
6125 static CHANSERV_FUNC(cmd_unsuspend)
6127 struct handle_info *hi;
6128 struct userData *self, *real_self, *target;
6129 unsigned int override = 0;
6132 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6133 self = GetChannelUser(channel->channel_info, user->handle_info);
6134 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6135 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6137 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6140 if(target->access >= self->access)
6142 reply("MSG_USER_OUTRANKED", hi->handle);
6145 if(!(target->flags & USER_SUSPENDED))
6147 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6150 if(!real_self || target->access >= real_self->access)
6151 override = CMD_LOG_OVERRIDE;
6152 target->flags &= ~USER_SUSPENDED;
6153 scan_user_presence(target, NULL);
6154 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6155 return 1 | override;
6158 static MODCMD_FUNC(cmd_deleteme)
6160 struct handle_info *hi;
6161 struct userData *target;
6162 const char *confirm_string;
6163 unsigned short access;
6166 hi = user->handle_info;
6167 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6169 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6172 if(target->access == UL_OWNER)
6174 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6177 confirm_string = make_confirmation_string(target);
6178 if((argc < 2) || strcmp(argv[1], confirm_string))
6180 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6183 access = target->access;
6184 channel_name = strdup(channel->name);
6185 del_channel_user(target, 1);
6186 reply("CSMSG_DELETED_YOU", access, channel_name);
6192 chanserv_refresh_topics(UNUSED_ARG(void *data))
6194 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6195 struct chanData *cData;
6198 for(cData = channelList; cData; cData = cData->next)
6200 if(IsSuspended(cData))
6202 opt = cData->chOpts[chTopicRefresh];
6205 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6208 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6209 cData->last_refresh = refresh_num;
6211 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6214 static CHANSERV_FUNC(cmd_unf)
6218 char response[MAXLEN];
6219 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6220 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6221 irc_privmsg(cmd->parent->bot, channel->name, response);
6224 reply("CSMSG_UNF_RESPONSE");
6228 static CHANSERV_FUNC(cmd_ping)
6232 char response[MAXLEN];
6233 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6234 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6235 irc_privmsg(cmd->parent->bot, channel->name, response);
6238 reply("CSMSG_PING_RESPONSE");
6242 static CHANSERV_FUNC(cmd_wut)
6246 char response[MAXLEN];
6247 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6248 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6249 irc_privmsg(cmd->parent->bot, channel->name, response);
6252 reply("CSMSG_WUT_RESPONSE");
6256 static CHANSERV_FUNC(cmd_8ball)
6258 unsigned int i, j, accum;
6263 for(i=1; i<argc; i++)
6264 for(j=0; argv[i][j]; j++)
6265 accum = (accum << 5) - accum + toupper(argv[i][j]);
6266 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6269 char response[MAXLEN];
6270 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6271 irc_privmsg(cmd->parent->bot, channel->name, response);
6274 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6278 static CHANSERV_FUNC(cmd_d)
6280 unsigned long sides, count, modifier, ii, total;
6281 char response[MAXLEN], *sep;
6285 if((count = strtoul(argv[1], &sep, 10)) < 1)
6295 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6296 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6300 else if((sep[0] == '-') && isdigit(sep[1]))
6301 modifier = strtoul(sep, NULL, 10);
6302 else if((sep[0] == '+') && isdigit(sep[1]))
6303 modifier = strtoul(sep+1, NULL, 10);
6310 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6315 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6318 for(total = ii = 0; ii < count; ++ii)
6319 total += (rand() % sides) + 1;
6322 if((count > 1) || modifier)
6324 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6325 sprintf(response, fmt, total, count, sides, modifier);
6329 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6330 sprintf(response, fmt, total, sides);
6333 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6335 send_message_type(4, user, cmd->parent->bot, "%s", response);
6339 static CHANSERV_FUNC(cmd_huggle)
6341 /* CTCP must be via PRIVMSG, never notice */
6343 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6345 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6350 chanserv_adjust_limit(void *data)
6352 struct mod_chanmode change;
6353 struct chanData *cData = data;
6354 struct chanNode *channel = cData->channel;
6357 if(IsSuspended(cData))
6360 cData->limitAdjusted = now;
6361 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6362 if(cData->modes.modes_set & MODE_LIMIT)
6364 if(limit > cData->modes.new_limit)
6365 limit = cData->modes.new_limit;
6366 else if(limit == cData->modes.new_limit)
6370 mod_chanmode_init(&change);
6371 change.modes_set = MODE_LIMIT;
6372 change.new_limit = limit;
6373 mod_chanmode_announce(chanserv, channel, &change);
6377 handle_new_channel(struct chanNode *channel)
6379 struct chanData *cData;
6381 if(!(cData = channel->channel_info))
6384 if(cData->modes.modes_set || cData->modes.modes_clear)
6385 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6387 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6388 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6391 /* Welcome to my worst nightmare. Warning: Read (or modify)
6392 the code below at your own risk. */
6394 handle_join(struct modeNode *mNode)
6396 struct mod_chanmode change;
6397 struct userNode *user = mNode->user;
6398 struct chanNode *channel = mNode->channel;
6399 struct chanData *cData;
6400 struct userData *uData = NULL;
6401 struct banData *bData;
6402 struct handle_info *handle;
6403 unsigned int modes = 0, info = 0;
6406 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6409 cData = channel->channel_info;
6410 if(channel->members.used > cData->max)
6411 cData->max = channel->members.used;
6413 /* Check for bans. If they're joining through a ban, one of two
6415 * 1: Join during a netburst, by riding the break. Kick them
6416 * unless they have ops or voice in the channel.
6417 * 2: They're allowed to join through the ban (an invite in
6418 * ircu2.10, or a +e on Hybrid, or something).
6419 * If they're not joining through a ban, and the banlist is not
6420 * full, see if they're on the banlist for the channel. If so,
6423 if(user->uplink->burst && !mNode->modes)
6426 for(ii = 0; ii < channel->banlist.used; ii++)
6428 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6430 /* Riding a netburst. Naughty. */
6431 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6437 mod_chanmode_init(&change);
6439 if(channel->banlist.used < MAXBANS)
6441 /* Not joining through a ban. */
6442 for(bData = cData->bans;
6443 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6444 bData = bData->next);
6448 char kick_reason[MAXLEN];
6449 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6451 bData->triggered = now;
6452 if(bData != cData->bans)
6454 /* Shuffle the ban to the head of the list. */
6456 bData->next->prev = bData->prev;
6458 bData->prev->next = bData->next;
6461 bData->next = cData->bans;
6464 cData->bans->prev = bData;
6465 cData->bans = bData;
6468 change.args[0].mode = MODE_BAN;
6469 change.args[0].u.hostmask = bData->mask;
6470 mod_chanmode_announce(chanserv, channel, &change);
6471 KickChannelUser(user, channel, chanserv, kick_reason);
6476 /* ChanServ will not modify the limits in join-flooded channels.
6477 It will also skip DynLimit processing when the user (or srvx)
6478 is bursting in, because there are likely more incoming. */
6479 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6480 && !user->uplink->burst
6481 && !channel->join_flooded
6482 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6484 /* The user count has begun "bumping" into the channel limit,
6485 so set a timer to raise the limit a bit. Any previous
6486 timers are removed so three incoming users within the delay
6487 results in one limit change, not three. */
6489 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6490 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6493 if(channel->join_flooded)
6495 /* don't automatically give ops or voice during a join flood */
6497 else if(cData->lvlOpts[lvlGiveOps] == 0)
6498 modes |= MODE_CHANOP;
6499 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6500 modes |= MODE_VOICE;
6502 greeting = cData->greeting;
6503 if(user->handle_info)
6505 handle = user->handle_info;
6507 if(IsHelper(user) && !IsHelping(user))
6510 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6512 if(channel == chanserv_conf.support_channels.list[ii])
6514 HANDLE_SET_FLAG(user->handle_info, HELPING);
6520 uData = GetTrueChannelAccess(cData, handle);
6521 if(uData && !IsUserSuspended(uData))
6523 /* Ops and above were handled by the above case. */
6524 if(IsUserAutoOp(uData))
6526 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6527 modes |= MODE_CHANOP;
6528 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6529 modes |= MODE_VOICE;
6531 if(uData->access >= UL_PRESENT)
6532 cData->visited = now;
6533 if(cData->user_greeting)
6534 greeting = cData->user_greeting;
6536 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6537 && ((now - uData->seen) >= chanserv_conf.info_delay)
6545 /* If user joining normally (not during burst), apply op or voice,
6546 * and send greeting/userinfo as appropriate.
6548 if(!user->uplink->burst)
6552 if(modes & MODE_CHANOP)
6553 modes &= ~MODE_VOICE;
6554 change.args[0].mode = modes;
6555 change.args[0].u.member = mNode;
6556 mod_chanmode_announce(chanserv, channel, &change);
6559 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6561 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6567 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6569 struct mod_chanmode change;
6570 struct userData *channel;
6571 unsigned int ii, jj;
6573 if(!user->handle_info)
6576 mod_chanmode_init(&change);
6578 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6580 struct chanNode *cn;
6581 struct modeNode *mn;
6582 if(IsUserSuspended(channel)
6583 || IsSuspended(channel->channel)
6584 || !(cn = channel->channel->channel))
6587 mn = GetUserMode(cn, user);
6590 if(!IsUserSuspended(channel)
6591 && IsUserAutoInvite(channel)
6592 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6594 && !user->uplink->burst)
6595 irc_invite(chanserv, user, cn);
6599 if(channel->access >= UL_PRESENT)
6600 channel->channel->visited = now;
6602 if(IsUserAutoOp(channel))
6604 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6605 change.args[0].mode = MODE_CHANOP;
6606 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6607 change.args[0].mode = MODE_VOICE;
6609 change.args[0].mode = 0;
6610 change.args[0].u.member = mn;
6611 if(change.args[0].mode)
6612 mod_chanmode_announce(chanserv, cn, &change);
6615 channel->seen = now;
6616 channel->present = 1;
6619 for(ii = 0; ii < user->channels.used; ++ii)
6621 struct chanNode *channel = user->channels.list[ii]->channel;
6622 struct banData *ban;
6624 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6625 || !channel->channel_info
6626 || IsSuspended(channel->channel_info))
6628 for(jj = 0; jj < channel->banlist.used; ++jj)
6629 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6631 if(jj < channel->banlist.used)
6633 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6635 char kick_reason[MAXLEN];
6636 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6638 change.args[0].mode = MODE_BAN;
6639 change.args[0].u.hostmask = ban->mask;
6640 mod_chanmode_announce(chanserv, channel, &change);
6641 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6642 KickChannelUser(user, channel, chanserv, kick_reason);
6643 ban->triggered = now;
6648 if(IsSupportHelper(user))
6650 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6652 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6654 HANDLE_SET_FLAG(user->handle_info, HELPING);
6662 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6664 struct chanData *cData;
6665 struct userData *uData;
6667 cData = mn->channel->channel_info;
6668 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6671 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6673 /* Allow for a bit of padding so that the limit doesn't
6674 track the user count exactly, which could get annoying. */
6675 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6677 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6678 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6682 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6684 scan_user_presence(uData, mn->user);
6686 if (uData->access >= UL_PRESENT)
6687 cData->visited = now;
6690 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6692 unsigned int ii, jj;
6693 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6695 for(jj = 0; jj < mn->user->channels.used; ++jj)
6696 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6698 if(jj < mn->user->channels.used)
6701 if(ii == chanserv_conf.support_channels.used)
6702 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6707 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6709 struct userData *uData;
6711 if(!channel->channel_info || !kicker || IsService(kicker)
6712 || (kicker == victim) || IsSuspended(channel->channel_info)
6713 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6716 if(protect_user(victim, kicker, channel->channel_info))
6718 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6719 KickChannelUser(kicker, channel, chanserv, reason);
6722 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6727 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6729 struct chanData *cData;
6731 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6734 cData = channel->channel_info;
6735 if(bad_topic(channel, user, channel->topic))
6737 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6738 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6739 SetChannelTopic(channel, chanserv, old_topic, 1);
6740 else if(cData->topic)
6741 SetChannelTopic(channel, chanserv, cData->topic, 1);
6744 /* With topicsnarf, grab the topic and save it as the default topic. */
6745 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6748 cData->topic = strdup(channel->topic);
6754 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6756 struct mod_chanmode *bounce = NULL;
6757 unsigned int bnc, ii;
6760 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6763 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6764 && mode_lock_violated(&channel->channel_info->modes, change))
6766 char correct[MAXLEN];
6767 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6768 mod_chanmode_format(&channel->channel_info->modes, correct);
6769 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6771 for(ii = bnc = 0; ii < change->argc; ++ii)
6773 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6775 const struct userNode *victim = change->args[ii].u.member->user;
6776 if(!protect_user(victim, user, channel->channel_info))
6779 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6782 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6783 bounce->args[bnc].u.member = GetUserMode(channel, user);
6784 if(bounce->args[bnc].u.member)
6788 bounce->args[bnc].mode = MODE_CHANOP;
6789 bounce->args[bnc].u.member = change->args[ii].u.member;
6791 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6793 else if(change->args[ii].mode & MODE_CHANOP)
6795 const struct userNode *victim = change->args[ii].u.member->user;
6796 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6799 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6800 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6801 bounce->args[bnc].u.member = change->args[ii].u.member;
6804 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6806 const char *ban = change->args[ii].u.hostmask;
6807 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6810 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6811 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6812 bounce->args[bnc].u.hostmask = strdup(ban);
6814 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6819 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6820 mod_chanmode_announce(chanserv, channel, bounce);
6821 for(ii = 0; ii < change->argc; ++ii)
6822 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6823 free((char*)bounce->args[ii].u.hostmask);
6824 mod_chanmode_free(bounce);
6829 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6831 struct chanNode *channel;
6832 struct banData *bData;
6833 struct mod_chanmode change;
6834 unsigned int ii, jj;
6835 char kick_reason[MAXLEN];
6837 mod_chanmode_init(&change);
6839 change.args[0].mode = MODE_BAN;
6840 for(ii = 0; ii < user->channels.used; ++ii)
6842 channel = user->channels.list[ii]->channel;
6843 /* Need not check for bans if they're opped or voiced. */
6844 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6846 /* Need not check for bans unless channel registration is active. */
6847 if(!channel->channel_info || IsSuspended(channel->channel_info))
6849 /* Look for a matching ban already on the channel. */
6850 for(jj = 0; jj < channel->banlist.used; ++jj)
6851 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6853 /* Need not act if we found one. */
6854 if(jj < channel->banlist.used)
6856 /* Look for a matching ban in this channel. */
6857 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6859 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6861 change.args[0].u.hostmask = bData->mask;
6862 mod_chanmode_announce(chanserv, channel, &change);
6863 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6864 KickChannelUser(user, channel, chanserv, kick_reason);
6865 bData->triggered = now;
6866 break; /* we don't need to check any more bans in the channel */
6871 static void handle_rename(struct handle_info *handle, const char *old_handle)
6873 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6877 dict_remove2(handle_dnrs, old_handle, 1);
6878 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6879 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6884 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6886 struct userNode *h_user;
6888 if(handle->channels)
6890 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6891 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6893 while(handle->channels)
6894 del_channel_user(handle->channels, 1);
6899 handle_server_link(UNUSED_ARG(struct server *server))
6901 struct chanData *cData;
6903 for(cData = channelList; cData; cData = cData->next)
6905 if(!IsSuspended(cData))
6906 cData->may_opchan = 1;
6907 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6908 && !cData->channel->join_flooded
6909 && ((cData->channel->limit - cData->channel->members.used)
6910 < chanserv_conf.adjust_threshold))
6912 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6913 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6919 chanserv_conf_read(void)
6923 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6924 struct mod_chanmode *change;
6925 struct string_list *strlist;
6926 struct chanNode *chan;
6929 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6931 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6934 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6935 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6936 chanserv_conf.support_channels.used = 0;
6937 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6939 for(ii = 0; ii < strlist->used; ++ii)
6941 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6944 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6946 channelList_append(&chanserv_conf.support_channels, chan);
6949 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6952 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6955 chan = AddChannel(str, now, str2, NULL);
6957 channelList_append(&chanserv_conf.support_channels, chan);
6959 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6960 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6961 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6962 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6963 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6964 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6965 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6966 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6967 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6968 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6969 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6970 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6971 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6972 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6973 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6974 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6975 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6976 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6977 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6978 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6979 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6980 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6981 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6982 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6983 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6985 NickChange(chanserv, str, 0);
6986 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6987 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6988 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6989 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6990 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6991 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6992 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6993 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6994 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6995 chanserv_conf.max_owned = str ? atoi(str) : 5;
6996 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6997 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6998 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6999 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7000 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7001 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7002 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7005 safestrncpy(mode_line, str, sizeof(mode_line));
7006 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7007 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7008 && (change->argc < 2))
7010 chanserv_conf.default_modes = *change;
7011 mod_chanmode_free(change);
7013 free_string_list(chanserv_conf.set_shows);
7014 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7016 strlist = string_list_copy(strlist);
7019 static const char *list[] = {
7020 /* free form text */
7021 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7022 /* options based on user level */
7023 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7024 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7025 /* multiple choice options */
7026 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7027 /* binary options */
7028 "DynLimit", "NoDelete",
7033 strlist = alloc_string_list(ArrayLength(list)-1);
7034 for(ii=0; list[ii]; ii++)
7035 string_list_append(strlist, strdup(list[ii]));
7037 chanserv_conf.set_shows = strlist;
7038 /* We don't look things up now, in case the list refers to options
7039 * defined by modules initialized after this point. Just mark the
7040 * function list as invalid, so it will be initialized.
7042 set_shows_list.used = 0;
7043 free_string_list(chanserv_conf.eightball);
7044 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7047 strlist = string_list_copy(strlist);
7051 strlist = alloc_string_list(4);
7052 string_list_append(strlist, strdup("Yes."));
7053 string_list_append(strlist, strdup("No."));
7054 string_list_append(strlist, strdup("Maybe so."));
7056 chanserv_conf.eightball = strlist;
7057 free_string_list(chanserv_conf.old_ban_names);
7058 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7060 strlist = string_list_copy(strlist);
7062 strlist = alloc_string_list(2);
7063 chanserv_conf.old_ban_names = strlist;
7064 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7065 off_channel = str ? atoi(str) : 0;
7069 chanserv_note_type_read(const char *key, struct record_data *rd)
7072 struct note_type *ntype;
7075 if(!(obj = GET_RECORD_OBJECT(rd)))
7077 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7080 if(!(ntype = chanserv_create_note_type(key)))
7082 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7086 /* Figure out set access */
7087 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7089 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7090 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7092 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7094 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7095 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7097 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7099 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7103 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7104 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7105 ntype->set_access.min_opserv = 0;
7108 /* Figure out visibility */
7109 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7110 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7111 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7112 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7113 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7114 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7115 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7116 ntype->visible_type = NOTE_VIS_ALL;
7118 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7120 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7121 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7125 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7127 struct handle_info *handle;
7128 struct userData *uData;
7129 char *seen, *inf, *flags;
7130 unsigned long last_seen;
7131 unsigned short access;
7133 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7135 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7139 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7140 if(access > UL_OWNER)
7142 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7146 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7147 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7148 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7149 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7150 handle = get_handle_info(key);
7153 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7157 uData = add_channel_user(chan, handle, access, last_seen, inf);
7158 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7162 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7164 struct banData *bData;
7165 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7166 unsigned long set_time, triggered_time, expires_time;
7168 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7170 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7174 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7175 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7176 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7177 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7178 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7179 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7180 if (!reason || !owner)
7183 set_time = set ? strtoul(set, NULL, 0) : now;
7184 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7186 expires_time = strtoul(s_expires, NULL, 0);
7188 expires_time = set_time + atoi(s_duration);
7192 if(!reason || (expires_time && (expires_time < now)))
7195 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7198 static struct suspended *
7199 chanserv_read_suspended(dict_t obj)
7201 struct suspended *suspended = calloc(1, sizeof(*suspended));
7205 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7206 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7207 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7208 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7209 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7210 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7211 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7212 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7213 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7214 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7219 chanserv_channel_read(const char *key, struct record_data *hir)
7221 struct suspended *suspended;
7222 struct mod_chanmode *modes;
7223 struct chanNode *cNode;
7224 struct chanData *cData;
7225 struct dict *channel, *obj;
7226 char *str, *argv[10];
7230 channel = hir->d.object;
7232 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7235 cNode = AddChannel(key, now, NULL, NULL);
7238 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7241 cData = register_channel(cNode, str);
7244 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7248 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7250 enum levelOption lvlOpt;
7251 enum charOption chOpt;
7253 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7254 cData->flags = atoi(str);
7256 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7258 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7260 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7261 else if(levelOptions[lvlOpt].old_flag)
7263 if(cData->flags & levelOptions[lvlOpt].old_flag)
7264 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7266 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7270 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7272 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7274 cData->chOpts[chOpt] = str[0];
7277 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7279 enum levelOption lvlOpt;
7280 enum charOption chOpt;
7283 cData->flags = base64toint(str, 5);
7284 count = strlen(str += 5);
7285 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7288 if(levelOptions[lvlOpt].old_flag)
7290 if(cData->flags & levelOptions[lvlOpt].old_flag)
7291 lvl = levelOptions[lvlOpt].flag_value;
7293 lvl = levelOptions[lvlOpt].default_value;
7295 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7297 case 'c': lvl = UL_COOWNER; break;
7298 case 'm': lvl = UL_MASTER; break;
7299 case 'n': lvl = UL_OWNER+1; break;
7300 case 'o': lvl = UL_OP; break;
7301 case 'p': lvl = UL_PEON; break;
7302 case 'w': lvl = UL_OWNER; break;
7303 default: lvl = 0; break;
7305 cData->lvlOpts[lvlOpt] = lvl;
7307 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7308 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7311 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7313 suspended = chanserv_read_suspended(obj);
7314 cData->suspended = suspended;
7315 suspended->cData = cData;
7316 /* We could use suspended->expires and suspended->revoked to
7317 * set the CHANNEL_SUSPENDED flag, but we don't. */
7319 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7321 suspended = calloc(1, sizeof(*suspended));
7322 suspended->issued = 0;
7323 suspended->revoked = 0;
7324 suspended->suspender = strdup(str);
7325 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7326 suspended->expires = str ? atoi(str) : 0;
7327 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7328 suspended->reason = strdup(str ? str : "No reason");
7329 suspended->previous = NULL;
7330 cData->suspended = suspended;
7331 suspended->cData = cData;
7335 cData->flags &= ~CHANNEL_SUSPENDED;
7336 suspended = NULL; /* to squelch a warning */
7339 if(IsSuspended(cData)) {
7340 if(suspended->expires > now)
7341 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7342 else if(suspended->expires)
7343 cData->flags &= ~CHANNEL_SUSPENDED;
7346 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7347 struct mod_chanmode change;
7348 mod_chanmode_init(&change);
7350 change.args[0].mode = MODE_CHANOP;
7351 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7352 mod_chanmode_announce(chanserv, cNode, &change);
7355 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7356 cData->registered = str ? strtoul(str, NULL, 0) : now;
7357 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7358 cData->visited = str ? strtoul(str, NULL, 0) : now;
7359 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7360 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7361 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7362 cData->max = str ? atoi(str) : 0;
7363 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7364 cData->greeting = str ? strdup(str) : NULL;
7365 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7366 cData->user_greeting = str ? strdup(str) : NULL;
7367 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7368 cData->topic_mask = str ? strdup(str) : NULL;
7369 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7370 cData->topic = str ? strdup(str) : NULL;
7372 if(!IsSuspended(cData)
7373 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7374 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7375 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7376 cData->modes = *modes;
7378 cData->modes.modes_set |= MODE_REGISTERED;
7379 if(cData->modes.argc > 1)
7380 cData->modes.argc = 1;
7381 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7382 mod_chanmode_free(modes);
7385 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7386 for(it = dict_first(obj); it; it = iter_next(it))
7387 user_read_helper(iter_key(it), iter_data(it), cData);
7389 if(!cData->users && !IsProtected(cData))
7391 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7392 unregister_channel(cData, "has empty user list.");
7396 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7397 for(it = dict_first(obj); it; it = iter_next(it))
7398 ban_read_helper(iter_key(it), iter_data(it), cData);
7400 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7401 for(it = dict_first(obj); it; it = iter_next(it))
7403 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7404 struct record_data *rd = iter_data(it);
7405 const char *note, *setter;
7407 if(rd->type != RECDB_OBJECT)
7409 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7413 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7415 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7417 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7421 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7422 if(!setter) setter = "<unknown>";
7423 chanserv_add_channel_note(cData, ntype, setter, note);
7431 chanserv_dnr_read(const char *key, struct record_data *hir)
7433 const char *setter, *reason, *str;
7434 struct do_not_register *dnr;
7435 unsigned long expiry;
7437 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7440 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7443 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7446 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7449 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7450 expiry = str ? strtoul(str, NULL, 0) : 0;
7451 if(expiry && expiry <= now)
7453 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7456 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7458 dnr->set = atoi(str);
7464 chanserv_saxdb_read(struct dict *database)
7466 struct dict *section;
7469 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7470 for(it = dict_first(section); it; it = iter_next(it))
7471 chanserv_note_type_read(iter_key(it), iter_data(it));
7473 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7474 for(it = dict_first(section); it; it = iter_next(it))
7475 chanserv_channel_read(iter_key(it), iter_data(it));
7477 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7478 for(it = dict_first(section); it; it = iter_next(it))
7479 chanserv_dnr_read(iter_key(it), iter_data(it));
7485 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7487 int high_present = 0;
7488 saxdb_start_record(ctx, KEY_USERS, 1);
7489 for(; uData; uData = uData->next)
7491 if((uData->access >= UL_PRESENT) && uData->present)
7493 saxdb_start_record(ctx, uData->handle->handle, 0);
7494 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7495 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7497 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7499 saxdb_write_string(ctx, KEY_INFO, uData->info);
7500 saxdb_end_record(ctx);
7502 saxdb_end_record(ctx);
7503 return high_present;
7507 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7511 saxdb_start_record(ctx, KEY_BANS, 1);
7512 for(; bData; bData = bData->next)
7514 saxdb_start_record(ctx, bData->mask, 0);
7515 saxdb_write_int(ctx, KEY_SET, bData->set);
7516 if(bData->triggered)
7517 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7519 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7521 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7523 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7524 saxdb_end_record(ctx);
7526 saxdb_end_record(ctx);
7530 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7532 saxdb_start_record(ctx, name, 0);
7533 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7534 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7536 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7538 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7540 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7542 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7543 saxdb_end_record(ctx);
7547 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7551 enum levelOption lvlOpt;
7552 enum charOption chOpt;
7554 saxdb_start_record(ctx, channel->channel->name, 1);
7556 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7557 saxdb_write_int(ctx, KEY_MAX, channel->max);
7559 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7560 if(channel->registrar)
7561 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7562 if(channel->greeting)
7563 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7564 if(channel->user_greeting)
7565 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7566 if(channel->topic_mask)
7567 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7568 if(channel->suspended)
7569 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7571 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7572 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7573 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7574 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7575 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7577 buf[0] = channel->chOpts[chOpt];
7579 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7581 saxdb_end_record(ctx);
7583 if(channel->modes.modes_set || channel->modes.modes_clear)
7585 mod_chanmode_format(&channel->modes, buf);
7586 saxdb_write_string(ctx, KEY_MODES, buf);
7589 high_present = chanserv_write_users(ctx, channel->users);
7590 chanserv_write_bans(ctx, channel->bans);
7592 if(dict_size(channel->notes))
7596 saxdb_start_record(ctx, KEY_NOTES, 1);
7597 for(it = dict_first(channel->notes); it; it = iter_next(it))
7599 struct note *note = iter_data(it);
7600 saxdb_start_record(ctx, iter_key(it), 0);
7601 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7602 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7603 saxdb_end_record(ctx);
7605 saxdb_end_record(ctx);
7608 if(channel->ownerTransfer)
7609 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7610 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7611 saxdb_end_record(ctx);
7615 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7619 saxdb_start_record(ctx, ntype->name, 0);
7620 switch(ntype->set_access_type)
7622 case NOTE_SET_CHANNEL_ACCESS:
7623 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7625 case NOTE_SET_CHANNEL_SETTER:
7626 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7628 case NOTE_SET_PRIVILEGED: default:
7629 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7632 switch(ntype->visible_type)
7634 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7635 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7636 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7638 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7639 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7640 saxdb_end_record(ctx);
7644 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7646 struct do_not_register *dnr;
7647 dict_iterator_t it, next;
7649 for(it = dict_first(dnrs); it; it = next)
7651 next = iter_next(it);
7652 dnr = iter_data(it);
7653 if(dnr->expires && dnr->expires <= now)
7655 dict_remove(dnrs, iter_key(it));
7658 saxdb_start_record(ctx, dnr->chan_name, 0);
7660 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7662 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7663 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7664 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7665 saxdb_end_record(ctx);
7670 chanserv_saxdb_write(struct saxdb_context *ctx)
7673 struct chanData *channel;
7676 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7677 for(it = dict_first(note_types); it; it = iter_next(it))
7678 chanserv_write_note_type(ctx, iter_data(it));
7679 saxdb_end_record(ctx);
7682 saxdb_start_record(ctx, KEY_DNR, 1);
7683 write_dnrs_helper(ctx, handle_dnrs);
7684 write_dnrs_helper(ctx, plain_dnrs);
7685 write_dnrs_helper(ctx, mask_dnrs);
7686 saxdb_end_record(ctx);
7689 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7690 for(channel = channelList; channel; channel = channel->next)
7691 chanserv_write_channel(ctx, channel);
7692 saxdb_end_record(ctx);
7698 chanserv_db_cleanup(void) {
7700 unreg_part_func(handle_part);
7702 unregister_channel(channelList, "terminating.");
7703 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7704 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7705 free(chanserv_conf.support_channels.list);
7706 dict_delete(handle_dnrs);
7707 dict_delete(plain_dnrs);
7708 dict_delete(mask_dnrs);
7709 dict_delete(note_types);
7710 free_string_list(chanserv_conf.eightball);
7711 free_string_list(chanserv_conf.old_ban_names);
7712 free_string_list(chanserv_conf.set_shows);
7713 free(set_shows_list.list);
7714 free(uset_shows_list.list);
7717 struct userData *helper = helperList;
7718 helperList = helperList->next;
7723 #if defined(GCC_VARMACROS)
7724 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7725 #elif defined(C99_VARMACROS)
7726 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7728 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7729 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7732 init_chanserv(const char *nick)
7734 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7735 conf_register_reload(chanserv_conf_read);
7739 reg_server_link_func(handle_server_link);
7740 reg_new_channel_func(handle_new_channel);
7741 reg_join_func(handle_join);
7742 reg_part_func(handle_part);
7743 reg_kick_func(handle_kick);
7744 reg_topic_func(handle_topic);
7745 reg_mode_change_func(handle_mode);
7746 reg_nick_change_func(handle_nick_change);
7747 reg_auth_func(handle_auth);
7750 reg_handle_rename_func(handle_rename);
7751 reg_unreg_func(handle_unreg);
7753 handle_dnrs = dict_new();
7754 dict_set_free_data(handle_dnrs, free);
7755 plain_dnrs = dict_new();
7756 dict_set_free_data(plain_dnrs, free);
7757 mask_dnrs = dict_new();
7758 dict_set_free_data(mask_dnrs, free);
7760 reg_svccmd_unbind_func(handle_svccmd_unbind);
7761 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7762 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7763 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7764 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7765 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7766 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7767 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7768 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7769 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7770 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7771 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7772 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7773 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7775 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7776 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7778 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7779 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7780 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7781 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7782 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7784 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7785 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7786 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7787 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7788 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7790 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7791 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7792 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7793 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7795 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7796 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7797 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7798 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7799 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7800 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7801 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7802 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7804 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7805 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7806 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7807 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7808 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7809 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7810 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7811 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7812 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7813 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7814 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7815 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7816 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7817 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7819 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7820 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7821 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7822 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7823 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7825 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7826 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7828 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7829 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7830 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7831 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7832 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7833 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7834 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7835 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7836 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7837 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7838 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7840 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7841 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7843 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7844 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7845 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7846 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7848 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7849 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7850 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7851 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7852 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7854 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7855 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7856 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7857 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7858 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7859 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7861 /* Channel options */
7862 DEFINE_CHANNEL_OPTION(defaulttopic);
7863 DEFINE_CHANNEL_OPTION(topicmask);
7864 DEFINE_CHANNEL_OPTION(greeting);
7865 DEFINE_CHANNEL_OPTION(usergreeting);
7866 DEFINE_CHANNEL_OPTION(modes);
7867 DEFINE_CHANNEL_OPTION(enfops);
7868 DEFINE_CHANNEL_OPTION(giveops);
7869 DEFINE_CHANNEL_OPTION(protect);
7870 DEFINE_CHANNEL_OPTION(enfmodes);
7871 DEFINE_CHANNEL_OPTION(enftopic);
7872 DEFINE_CHANNEL_OPTION(pubcmd);
7873 DEFINE_CHANNEL_OPTION(givevoice);
7874 DEFINE_CHANNEL_OPTION(userinfo);
7875 DEFINE_CHANNEL_OPTION(dynlimit);
7876 DEFINE_CHANNEL_OPTION(topicsnarf);
7877 DEFINE_CHANNEL_OPTION(nodelete);
7878 DEFINE_CHANNEL_OPTION(toys);
7879 DEFINE_CHANNEL_OPTION(setters);
7880 DEFINE_CHANNEL_OPTION(topicrefresh);
7881 DEFINE_CHANNEL_OPTION(ctcpusers);
7882 DEFINE_CHANNEL_OPTION(ctcpreaction);
7883 DEFINE_CHANNEL_OPTION(inviteme);
7884 DEFINE_CHANNEL_OPTION(unreviewed);
7885 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7886 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7888 DEFINE_CHANNEL_OPTION(offchannel);
7889 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7891 /* Alias set topic to set defaulttopic for compatibility. */
7892 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7895 DEFINE_USER_OPTION(noautoop);
7896 DEFINE_USER_OPTION(autoinvite);
7897 DEFINE_USER_OPTION(info);
7899 /* Alias uset autovoice to uset autoop. */
7900 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7902 note_types = dict_new();
7903 dict_set_free_data(note_types, chanserv_deref_note_type);
7906 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7907 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7908 service_register(chanserv)->trigger = '!';
7909 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7911 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7913 if(chanserv_conf.channel_expire_frequency)
7914 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7916 if(chanserv_conf.dnr_expire_frequency)
7917 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7919 if(chanserv_conf.refresh_period)
7921 unsigned long next_refresh;
7922 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7923 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7926 reg_exit_func(chanserv_db_cleanup);
7927 message_register_table(msgtab);