1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
103 #define KEY_OWNER_TRANSFER "owner_transfer"
106 #define KEY_LEVEL "level"
107 #define KEY_INFO "info"
108 #define KEY_SEEN "seen"
111 #define KEY_OWNER "owner"
112 #define KEY_REASON "reason"
113 #define KEY_SET "set"
114 #define KEY_DURATION "duration"
115 #define KEY_EXPIRES "expires"
116 #define KEY_TRIGGERED "triggered"
118 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
119 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
120 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
122 /* Administrative messages */
123 static const struct message_entry msgtab[] = {
124 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
126 /* Channel registration */
127 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
128 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
129 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
130 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
131 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
132 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
134 /* Do-not-register channels */
135 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
136 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
137 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
138 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
140 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
141 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
142 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
143 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
144 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
145 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
146 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
147 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
148 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
150 /* Channel unregistration */
151 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
152 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
153 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
154 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
157 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
158 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
160 /* Channel merging */
161 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
162 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
163 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
164 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
165 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
167 /* Handle unregistration */
168 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
171 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
172 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
173 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
174 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
175 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
176 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
177 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
178 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
179 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
185 /* Removing yourself from a channel. */
186 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
187 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
188 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
190 /* User management */
191 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
192 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
193 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
194 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
195 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
196 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
197 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
198 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
200 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
201 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
202 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
203 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
204 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
205 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
206 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
209 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
210 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
211 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
212 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
213 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
214 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
215 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
216 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
217 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
218 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
219 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
220 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
221 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
222 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
223 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
224 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
226 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
228 /* Channel management */
229 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
230 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
231 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
233 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
234 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
235 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
236 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
237 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
238 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
239 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
241 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
242 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
243 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
244 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
245 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
246 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
247 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
248 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
249 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
250 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
251 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
252 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
253 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
254 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
255 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
256 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
257 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
258 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
259 { "CSMSG_SET_MODES", "$bModes $b %s" },
260 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
261 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
262 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
263 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
264 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
265 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
266 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
267 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
268 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
269 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
270 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
271 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
272 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
273 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
274 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
275 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
276 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
277 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
278 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
279 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
280 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
281 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
282 { "CSMSG_USET_INFO", "$bInfo $b %s" },
284 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
285 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
286 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
287 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
288 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
289 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
290 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
291 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
292 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
294 { "CSMSG_PROTECT_NONE", "No users will be protected." },
295 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
296 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
297 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
298 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
299 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
300 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
301 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
302 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
303 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
308 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
309 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
310 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
311 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
312 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
313 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
314 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
315 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
317 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
318 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
319 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
321 /* Channel userlist */
322 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
327 /* Channel note list */
328 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
329 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
330 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
331 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
332 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
333 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
334 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
335 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
336 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
337 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
338 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
339 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
340 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
341 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
342 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
344 /* Channel [un]suspension */
345 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
346 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
347 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
348 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
349 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
350 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
351 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
353 /* Access information */
354 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
355 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
356 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
357 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
358 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
359 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
360 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
361 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
362 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
363 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
364 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
365 { "CSMSG_UC_H_TITLE", "network helper" },
366 { "CSMSG_LC_H_TITLE", "support helper" },
367 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
369 /* Seen information */
370 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
371 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
372 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
373 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
375 /* Names information */
376 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
377 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
379 /* Channel information */
380 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
381 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
382 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
383 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
384 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
385 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
386 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
387 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
388 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
389 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
390 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
391 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
399 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
400 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
402 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
403 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
404 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
405 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
406 { "CSMSG_PEEK_OPS", "$bOps:$b" },
407 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
409 /* Network information */
410 { "CSMSG_NETWORK_INFO", "Network Information:" },
411 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
412 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
413 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
414 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
416 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
417 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
418 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
421 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
422 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
423 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
425 /* Channel searches */
426 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
427 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
428 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
429 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
431 /* Channel configuration */
432 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
433 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
434 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
435 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
438 { "CSMSG_USER_OPTIONS", "User Options:" },
439 { "CSMSG_USER_PROTECTED", "That user is protected." },
442 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
443 { "CSMSG_PING_RESPONSE", "Pong!" },
444 { "CSMSG_WUT_RESPONSE", "wut" },
445 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
446 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
447 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
448 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
449 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
450 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
451 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
454 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
458 /* eject_user and unban_user flags */
459 #define ACTION_KICK 0x0001
460 #define ACTION_BAN 0x0002
461 #define ACTION_ADD_BAN 0x0004
462 #define ACTION_ADD_TIMED_BAN 0x0008
463 #define ACTION_UNBAN 0x0010
464 #define ACTION_DEL_BAN 0x0020
466 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
467 #define MODELEN 40 + KEYLEN
471 #define CSFUNC_ARGS user, channel, argc, argv, cmd
473 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
474 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
475 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
476 reply("MSG_MISSING_PARAMS", argv[0]); \
480 DECLARE_LIST(dnrList, struct do_not_register *);
481 DEFINE_LIST(dnrList, struct do_not_register *);
483 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
485 struct userNode *chanserv;
488 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
489 static struct log_type *CS_LOG;
493 struct channelList support_channels;
494 struct mod_chanmode default_modes;
496 unsigned long db_backup_frequency;
497 unsigned long channel_expire_frequency;
498 unsigned long dnr_expire_frequency;
500 unsigned long info_delay;
501 unsigned long adjust_delay;
502 unsigned long channel_expire_delay;
503 unsigned int nodelete_level;
505 unsigned int adjust_threshold;
506 int join_flood_threshold;
508 unsigned int greeting_length;
509 unsigned int refresh_period;
510 unsigned int giveownership_period;
512 unsigned int max_owned;
513 unsigned int max_chan_users;
514 unsigned int max_chan_bans;
515 unsigned int max_userinfo_length;
517 struct string_list *set_shows;
518 struct string_list *eightball;
519 struct string_list *old_ban_names;
521 const char *ctcp_short_ban_duration;
522 const char *ctcp_long_ban_duration;
524 const char *irc_operator_epithet;
525 const char *network_helper_epithet;
526 const char *support_helper_epithet;
531 struct userNode *user;
532 struct userNode *bot;
533 struct chanNode *channel;
535 unsigned short lowest;
536 unsigned short highest;
537 struct userData **users;
538 struct helpfile_table table;
541 enum note_access_type
543 NOTE_SET_CHANNEL_ACCESS,
544 NOTE_SET_CHANNEL_SETTER,
548 enum note_visible_type
551 NOTE_VIS_CHANNEL_USERS,
557 enum note_access_type set_access_type;
559 unsigned int min_opserv;
560 unsigned short min_ulevel;
562 enum note_visible_type visible_type;
563 unsigned int max_length;
570 struct note_type *type;
571 char setter[NICKSERV_HANDLE_LEN+1];
575 static unsigned int registered_channels;
576 static unsigned int banCount;
578 static const struct {
581 unsigned short level;
584 { "peon", "Peon", UL_PEON, '+' },
585 { "op", "Op", UL_OP, '@' },
586 { "master", "Master", UL_MASTER, '%' },
587 { "coowner", "Coowner", UL_COOWNER, '*' },
588 { "owner", "Owner", UL_OWNER, '!' },
589 { "helper", "BUG:", UL_HELPER, 'X' }
592 static const struct {
595 unsigned short default_value;
596 unsigned int old_idx;
597 unsigned int old_flag;
598 unsigned short flag_value;
600 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
601 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
602 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
603 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
604 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
605 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
606 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
607 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
608 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
609 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
610 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
613 struct charOptionValues {
616 } protectValues[] = {
617 { 'a', "CSMSG_PROTECT_ALL" },
618 { 'e', "CSMSG_PROTECT_EQUAL" },
619 { 'l', "CSMSG_PROTECT_LOWER" },
620 { 'n', "CSMSG_PROTECT_NONE" }
622 { 'd', "CSMSG_TOYS_DISABLED" },
623 { 'n', "CSMSG_TOYS_PRIVATE" },
624 { 'p', "CSMSG_TOYS_PUBLIC" }
625 }, topicRefreshValues[] = {
626 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
627 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
628 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
629 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
630 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
631 }, ctcpReactionValues[] = {
632 { 'k', "CSMSG_CTCPREACTION_KICK" },
633 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
634 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
635 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
638 static const struct {
642 unsigned int old_idx;
644 struct charOptionValues *values;
646 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
647 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
648 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
649 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
652 struct userData *helperList;
653 struct chanData *channelList;
654 static struct module *chanserv_module;
655 static unsigned int userCount;
657 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
658 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
659 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
662 user_level_from_name(const char *name, unsigned short clamp_level)
664 unsigned int level = 0, ii;
666 level = strtoul(name, NULL, 10);
667 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
668 if(!irccasecmp(name, accessLevels[ii].name))
669 level = accessLevels[ii].level;
670 if(level > clamp_level)
676 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
679 *minl = strtoul(arg, &sep, 10);
687 *maxl = strtoul(sep+1, &sep, 10);
695 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
697 struct userData *uData, **head;
699 if(!channel || !handle)
702 if(override && HANDLE_FLAGGED(handle, HELPING)
703 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
705 for(uData = helperList;
706 uData && uData->handle != handle;
707 uData = uData->next);
711 uData = calloc(1, sizeof(struct userData));
712 uData->handle = handle;
714 uData->access = UL_HELPER;
720 uData->next = helperList;
722 helperList->prev = uData;
730 for(uData = channel->users; uData; uData = uData->next)
731 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
734 head = &(channel->users);
737 if(uData && (uData != *head))
739 /* Shuffle the user to the head of whatever list he was in. */
741 uData->next->prev = uData->prev;
743 uData->prev->next = uData->next;
749 (**head).prev = uData;
756 /* Returns non-zero if user has at least the minimum access.
757 * exempt_owner is set when handling !set, so the owner can set things
760 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
762 struct userData *uData;
763 struct chanData *cData = channel->channel_info;
764 unsigned short minimum = cData->lvlOpts[opt];
767 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
770 if(minimum <= uData->access)
772 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
777 /* Scan for other users authenticated to the same handle
778 still in the channel. If so, keep them listed as present.
780 user is optional, if not null, it skips checking that userNode
781 (for the handle_part function) */
783 scan_user_presence(struct userData *uData, struct userNode *user)
787 if(IsSuspended(uData->channel)
788 || IsUserSuspended(uData)
789 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
801 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
803 unsigned int eflags, argc;
805 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
807 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
808 if(!channel->channel_info
809 || IsSuspended(channel->channel_info)
811 || !ircncasecmp(text, "ACTION ", 7))
813 /* Figure out the minimum level needed to CTCP the channel */
814 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
816 /* We need to enforce against them; do so. */
818 argv[0] = (char*)text;
819 argv[1] = user->nick;
821 if(GetUserMode(channel, user))
822 eflags |= ACTION_KICK;
823 switch(channel->channel_info->chOpts[chCTCPReaction]) {
824 default: case 'k': /* just do the kick */ break;
826 eflags |= ACTION_BAN;
829 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
830 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
833 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
834 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
837 argv[argc++] = bad_ctcp_reason;
838 eject_user(chanserv, channel, argc, argv, NULL, eflags);
842 chanserv_create_note_type(const char *name)
844 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
845 strcpy(ntype->name, name);
847 dict_insert(note_types, ntype->name, ntype);
852 chanserv_deref_note_type(void *data)
854 struct note_type *ntype = data;
856 if(--ntype->refs > 0)
862 chanserv_flush_note_type(struct note_type *ntype)
864 struct chanData *cData;
865 for(cData = channelList; cData; cData = cData->next)
866 dict_remove(cData->notes, ntype->name);
870 chanserv_truncate_notes(struct note_type *ntype)
872 struct chanData *cData;
874 unsigned int size = sizeof(*note) + ntype->max_length;
876 for(cData = channelList; cData; cData = cData->next) {
877 note = dict_find(cData->notes, ntype->name, NULL);
880 if(strlen(note->note) <= ntype->max_length)
882 dict_remove2(cData->notes, ntype->name, 1);
883 note = realloc(note, size);
884 note->note[ntype->max_length] = 0;
885 dict_insert(cData->notes, ntype->name, note);
889 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
892 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
895 unsigned int len = strlen(text);
897 if(len > type->max_length) len = type->max_length;
898 note = calloc(1, sizeof(*note) + len);
900 strncpy(note->setter, setter, sizeof(note->setter)-1);
901 memcpy(note->note, text, len);
903 dict_insert(channel->notes, type->name, note);
909 chanserv_free_note(void *data)
911 struct note *note = data;
913 chanserv_deref_note_type(note->type);
914 assert(note->type->refs > 0); /* must use delnote to remove the type */
918 static MODCMD_FUNC(cmd_createnote) {
919 struct note_type *ntype;
920 unsigned int arg = 1, existed = 0, max_length;
922 if((ntype = dict_find(note_types, argv[1], NULL)))
925 ntype = chanserv_create_note_type(argv[arg]);
926 if(!irccasecmp(argv[++arg], "privileged"))
929 ntype->set_access_type = NOTE_SET_PRIVILEGED;
930 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
932 else if(!irccasecmp(argv[arg], "channel"))
934 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
937 reply("CSMSG_INVALID_ACCESS", argv[arg]);
940 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
941 ntype->set_access.min_ulevel = ulvl;
943 else if(!irccasecmp(argv[arg], "setter"))
945 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
949 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
953 if(!irccasecmp(argv[++arg], "privileged"))
954 ntype->visible_type = NOTE_VIS_PRIVILEGED;
955 else if(!irccasecmp(argv[arg], "channel_users"))
956 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
957 else if(!irccasecmp(argv[arg], "all"))
958 ntype->visible_type = NOTE_VIS_ALL;
960 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
964 if((arg+1) >= argc) {
965 reply("MSG_MISSING_PARAMS", argv[0]);
968 max_length = strtoul(argv[++arg], NULL, 0);
969 if(max_length < 20 || max_length > 450)
971 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
974 if(existed && (max_length < ntype->max_length))
976 ntype->max_length = max_length;
977 chanserv_truncate_notes(ntype);
979 ntype->max_length = max_length;
982 reply("CSMSG_NOTE_MODIFIED", ntype->name);
984 reply("CSMSG_NOTE_CREATED", ntype->name);
989 dict_remove(note_types, ntype->name);
993 static MODCMD_FUNC(cmd_removenote) {
994 struct note_type *ntype;
997 ntype = dict_find(note_types, argv[1], NULL);
998 force = (argc > 2) && !irccasecmp(argv[2], "force");
1001 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1008 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1011 chanserv_flush_note_type(ntype);
1013 dict_remove(note_types, argv[1]);
1014 reply("CSMSG_NOTE_DELETED", argv[1]);
1019 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1023 if(orig->modes_set & change->modes_clear)
1025 if(orig->modes_clear & change->modes_set)
1027 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1028 && strcmp(orig->new_key, change->new_key))
1030 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1031 && (orig->new_limit != change->new_limit))
1036 static char max_length_text[MAXLEN+1][16];
1038 static struct helpfile_expansion
1039 chanserv_expand_variable(const char *variable)
1041 struct helpfile_expansion exp;
1043 if(!irccasecmp(variable, "notes"))
1046 exp.type = HF_TABLE;
1047 exp.value.table.length = 1;
1048 exp.value.table.width = 3;
1049 exp.value.table.flags = 0;
1050 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1051 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1052 exp.value.table.contents[0][0] = "Note Type";
1053 exp.value.table.contents[0][1] = "Visibility";
1054 exp.value.table.contents[0][2] = "Max Length";
1055 for(it=dict_first(note_types); it; it=iter_next(it))
1057 struct note_type *ntype = iter_data(it);
1060 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1061 row = exp.value.table.length++;
1062 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1063 exp.value.table.contents[row][0] = ntype->name;
1064 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1065 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1067 if(!max_length_text[ntype->max_length][0])
1068 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1069 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1074 exp.type = HF_STRING;
1075 exp.value.str = NULL;
1079 static struct chanData*
1080 register_channel(struct chanNode *cNode, char *registrar)
1082 struct chanData *channel;
1083 enum levelOption lvlOpt;
1084 enum charOption chOpt;
1086 channel = calloc(1, sizeof(struct chanData));
1088 channel->notes = dict_new();
1089 dict_set_free_data(channel->notes, chanserv_free_note);
1091 channel->registrar = strdup(registrar);
1092 channel->registered = now;
1093 channel->visited = now;
1094 channel->limitAdjusted = now;
1095 channel->ownerTransfer = now;
1096 channel->flags = CHANNEL_DEFAULT_FLAGS;
1097 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1098 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1099 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1100 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1102 channel->prev = NULL;
1103 channel->next = channelList;
1106 channelList->prev = channel;
1107 channelList = channel;
1108 registered_channels++;
1110 channel->channel = cNode;
1112 cNode->channel_info = channel;
1117 static struct userData*
1118 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, unsigned long seen, const char *info)
1120 struct userData *ud;
1122 if(access > UL_OWNER)
1125 ud = calloc(1, sizeof(*ud));
1126 ud->channel = channel;
1127 ud->handle = handle;
1129 ud->access = access;
1130 ud->info = info ? strdup(info) : NULL;
1133 ud->next = channel->users;
1135 channel->users->prev = ud;
1136 channel->users = ud;
1138 channel->userCount++;
1142 ud->u_next = ud->handle->channels;
1144 ud->u_next->u_prev = ud;
1145 ud->handle->channels = ud;
1150 static void unregister_channel(struct chanData *channel, const char *reason);
1153 del_channel_user(struct userData *user, int do_gc)
1155 struct chanData *channel = user->channel;
1157 channel->userCount--;
1161 user->prev->next = user->next;
1163 channel->users = user->next;
1165 user->next->prev = user->prev;
1168 user->u_prev->u_next = user->u_next;
1170 user->handle->channels = user->u_next;
1172 user->u_next->u_prev = user->u_prev;
1176 if(do_gc && !channel->users && !IsProtected(channel))
1177 unregister_channel(channel, "lost all users.");
1180 static void expire_ban(void *data);
1182 static struct banData*
1183 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1186 unsigned int ii, l1, l2;
1191 bd = malloc(sizeof(struct banData));
1193 bd->channel = channel;
1195 bd->triggered = triggered;
1196 bd->expires = expires;
1198 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1200 extern const char *hidden_host_suffix;
1201 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1205 l2 = strlen(old_name);
1208 if(irccasecmp(mask + l1 - l2, old_name))
1210 new_mask = alloca(MAXLEN);
1211 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1214 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1216 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1217 bd->reason = strdup(reason);
1220 timeq_add(expires, expire_ban, bd);
1223 bd->next = channel->bans;
1225 channel->bans->prev = bd;
1227 channel->banCount++;
1234 del_channel_ban(struct banData *ban)
1236 ban->channel->banCount--;
1240 ban->prev->next = ban->next;
1242 ban->channel->bans = ban->next;
1245 ban->next->prev = ban->prev;
1248 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1257 expire_ban(void *data)
1259 struct banData *bd = data;
1260 if(!IsSuspended(bd->channel))
1262 struct banList bans;
1263 struct mod_chanmode change;
1265 bans = bd->channel->channel->banlist;
1266 mod_chanmode_init(&change);
1267 for(ii=0; ii<bans.used; ii++)
1269 if(!strcmp(bans.list[ii]->ban, bd->mask))
1272 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1273 change.args[0].u.hostmask = bd->mask;
1274 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1280 del_channel_ban(bd);
1283 static void chanserv_expire_suspension(void *data);
1286 unregister_channel(struct chanData *channel, const char *reason)
1288 struct mod_chanmode change;
1289 char msgbuf[MAXLEN];
1291 /* After channel unregistration, the following must be cleaned
1293 - Channel information.
1296 - Channel suspension data.
1297 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1303 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1307 mod_chanmode_init(&change);
1308 change.modes_clear |= MODE_REGISTERED;
1309 mod_chanmode_announce(chanserv, channel->channel, &change);
1312 while(channel->users)
1313 del_channel_user(channel->users, 0);
1315 while(channel->bans)
1316 del_channel_ban(channel->bans);
1318 free(channel->topic);
1319 free(channel->registrar);
1320 free(channel->greeting);
1321 free(channel->user_greeting);
1322 free(channel->topic_mask);
1325 channel->prev->next = channel->next;
1327 channelList = channel->next;
1330 channel->next->prev = channel->prev;
1332 if(channel->suspended)
1334 struct chanNode *cNode = channel->channel;
1335 struct suspended *suspended, *next_suspended;
1337 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1339 next_suspended = suspended->previous;
1340 free(suspended->suspender);
1341 free(suspended->reason);
1342 if(suspended->expires)
1343 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1348 cNode->channel_info = NULL;
1350 channel->channel->channel_info = NULL;
1352 dict_delete(channel->notes);
1353 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1354 if(!IsSuspended(channel))
1355 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1356 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1357 UnlockChannel(channel->channel);
1359 registered_channels--;
1363 expire_channels(UNUSED_ARG(void *data))
1365 struct chanData *channel, *next;
1366 struct userData *user;
1367 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1369 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1370 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1372 for(channel = channelList; channel; channel = next)
1374 next = channel->next;
1376 /* See if the channel can be expired. */
1377 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1378 || IsProtected(channel))
1381 /* Make sure there are no high-ranking users still in the channel. */
1382 for(user=channel->users; user; user=user->next)
1383 if(user->present && (user->access >= UL_PRESENT))
1388 /* Unregister the channel */
1389 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1390 unregister_channel(channel, "registration expired.");
1393 if(chanserv_conf.channel_expire_frequency)
1394 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1398 expire_dnrs(UNUSED_ARG(void *data))
1400 dict_iterator_t it, next;
1401 struct do_not_register *dnr;
1403 for(it = dict_first(handle_dnrs); it; it = next)
1405 dnr = iter_data(it);
1406 next = iter_next(it);
1407 if(dnr->expires && dnr->expires <= now)
1408 dict_remove(handle_dnrs, dnr->chan_name + 1);
1410 for(it = dict_first(plain_dnrs); it; it = next)
1412 dnr = iter_data(it);
1413 next = iter_next(it);
1414 if(dnr->expires && dnr->expires <= now)
1415 dict_remove(plain_dnrs, dnr->chan_name + 1);
1417 for(it = dict_first(mask_dnrs); it; it = next)
1419 dnr = iter_data(it);
1420 next = iter_next(it);
1421 if(dnr->expires && dnr->expires <= now)
1422 dict_remove(mask_dnrs, dnr->chan_name + 1);
1425 if(chanserv_conf.dnr_expire_frequency)
1426 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1430 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1432 char protect = channel->chOpts[chProtect];
1433 struct userData *cs_victim, *cs_aggressor;
1435 /* Don't protect if no one is to be protected, someone is attacking
1436 himself, or if the aggressor is an IRC Operator. */
1437 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1440 /* Don't protect if the victim isn't authenticated (because they
1441 can't be a channel user), unless we are to protect non-users
1443 cs_victim = GetChannelAccess(channel, victim->handle_info);
1444 if(protect != 'a' && !cs_victim)
1447 /* Protect if the aggressor isn't a user because at this point,
1448 the aggressor can only be less than or equal to the victim. */
1449 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1453 /* If the aggressor was a user, then the victim can't be helped. */
1460 if(cs_victim->access > cs_aggressor->access)
1465 if(cs_victim->access >= cs_aggressor->access)
1474 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1476 struct chanData *cData = channel->channel_info;
1477 struct userData *cs_victim;
1479 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1480 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1481 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1483 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1491 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1493 if(IsService(victim))
1495 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1499 if(protect_user(victim, user, channel->channel_info))
1501 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1508 static struct do_not_register *
1509 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1511 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1512 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1513 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1514 strcpy(dnr->reason, reason);
1516 dnr->expires = expires;
1517 if(dnr->chan_name[0] == '*')
1518 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1519 else if(strpbrk(dnr->chan_name, "*?"))
1520 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1522 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1526 static struct dnrList
1527 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1529 struct dnrList list;
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 dnrList_init(&list);
1535 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, handle);
1539 else if(list.used < max)
1540 dnrList_append(&list, dnr);
1543 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1545 if(dnr->expires && dnr->expires <= now)
1546 dict_remove(plain_dnrs, chan_name);
1547 else if(list.used < max)
1548 dnrList_append(&list, dnr);
1553 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1555 next = iter_next(it);
1556 if(!match_ircglob(chan_name, iter_key(it)))
1558 dnr = iter_data(it);
1559 if(dnr->expires && dnr->expires <= now)
1560 dict_remove(mask_dnrs, iter_key(it));
1562 dnrList_append(&list, dnr);
1569 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1571 struct userNode *user;
1572 char buf1[INTERVALLEN];
1573 char buf2[INTERVALLEN];
1580 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1585 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1586 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1590 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1593 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1598 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1600 struct dnrList list;
1603 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1604 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1605 dnr_print_func(list.list[ii], user);
1607 reply("CSMSG_MORE_DNRS", list.used - ii);
1612 struct do_not_register *
1613 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1615 struct dnrList list;
1616 struct do_not_register *dnr;
1618 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1619 dnr = list.used ? list.list[0] : NULL;
1624 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1626 struct do_not_register *dnr;
1627 dict_iterator_t it, next;
1628 unsigned int matches = 0;
1630 for(it = dict_first(dict); it; it = next)
1632 dnr = iter_data(it);
1633 next = iter_next(it);
1634 if(dnr->expires && dnr->expires <= now)
1636 dict_remove(dict, iter_key(it));
1639 dnr_print_func(dnr, user);
1646 static CHANSERV_FUNC(cmd_noregister)
1649 unsigned long expiry, duration;
1650 unsigned int matches;
1654 reply("CSMSG_DNR_SEARCH_RESULTS");
1655 matches = send_dnrs(user, handle_dnrs);
1656 matches += send_dnrs(user, plain_dnrs);
1657 matches += send_dnrs(user, mask_dnrs);
1659 reply("MSG_MATCH_COUNT", matches);
1661 reply("MSG_NO_MATCHES");
1667 if(!IsChannelName(target) && (*target != '*'))
1669 reply("CSMSG_NOT_DNR", target);
1677 reply("MSG_INVALID_DURATION", argv[2]);
1681 if(!strcmp(argv[2], "0"))
1683 else if((duration = ParseInterval(argv[2])))
1684 expiry = now + duration;
1687 reply("MSG_INVALID_DURATION", argv[2]);
1691 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1692 if((*target == '*') && !get_handle_info(target + 1))
1694 reply("MSG_HANDLE_UNKNOWN", target + 1);
1697 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1698 reply("CSMSG_NOREGISTER_CHANNEL", target);
1702 reply("CSMSG_DNR_SEARCH_RESULTS");
1704 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1706 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1708 reply("MSG_NO_MATCHES");
1712 static CHANSERV_FUNC(cmd_allowregister)
1714 const char *chan_name = argv[1];
1716 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1717 || dict_remove(plain_dnrs, chan_name)
1718 || dict_remove(mask_dnrs, chan_name))
1720 reply("CSMSG_DNR_REMOVED", chan_name);
1723 reply("CSMSG_NO_SUCH_DNR", chan_name);
1728 struct userNode *source;
1732 unsigned long min_set, max_set;
1733 unsigned long min_expires, max_expires;
1738 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1740 return !((dnr->set < search->min_set)
1741 || (dnr->set > search->max_set)
1742 || (dnr->expires < search->min_expires)
1743 || (search->max_expires
1744 && ((dnr->expires == 0)
1745 || (dnr->expires > search->max_expires)))
1746 || (search->chan_mask
1747 && !match_ircglob(dnr->chan_name, search->chan_mask))
1748 || (search->setter_mask
1749 && !match_ircglob(dnr->setter, search->setter_mask))
1750 || (search->reason_mask
1751 && !match_ircglob(dnr->reason, search->reason_mask)));
1754 static struct dnr_search *
1755 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1757 struct dnr_search *discrim;
1760 discrim = calloc(1, sizeof(*discrim));
1761 discrim->source = user;
1762 discrim->chan_mask = NULL;
1763 discrim->setter_mask = NULL;
1764 discrim->reason_mask = NULL;
1765 discrim->max_set = INT_MAX;
1766 discrim->limit = 50;
1768 for(ii=0; ii<argc; ++ii)
1772 reply("MSG_MISSING_PARAMS", argv[ii]);
1775 else if(0 == irccasecmp(argv[ii], "channel"))
1777 discrim->chan_mask = argv[++ii];
1779 else if(0 == irccasecmp(argv[ii], "setter"))
1781 discrim->setter_mask = argv[++ii];
1783 else if(0 == irccasecmp(argv[ii], "reason"))
1785 discrim->reason_mask = argv[++ii];
1787 else if(0 == irccasecmp(argv[ii], "limit"))
1789 discrim->limit = strtoul(argv[++ii], NULL, 0);
1791 else if(0 == irccasecmp(argv[ii], "set"))
1793 const char *cmp = argv[++ii];
1796 discrim->min_set = now - ParseInterval(cmp + 2);
1798 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1799 } else if(cmp[0] == '=') {
1800 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1801 } else if(cmp[0] == '>') {
1803 discrim->max_set = now - ParseInterval(cmp + 2);
1805 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1807 discrim->max_set = now - (ParseInterval(cmp) - 1);
1810 else if(0 == irccasecmp(argv[ii], "expires"))
1812 const char *cmp = argv[++ii];
1815 discrim->max_expires = now + ParseInterval(cmp + 2);
1817 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1818 } else if(cmp[0] == '=') {
1819 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1820 } else if(cmp[0] == '>') {
1822 discrim->min_expires = now + ParseInterval(cmp + 2);
1824 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1826 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1831 reply("MSG_INVALID_CRITERIA", argv[ii]);
1842 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1845 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1847 struct do_not_register *dnr;
1848 dict_iterator_t next;
1853 /* Initialize local variables. */
1856 if(discrim->chan_mask)
1858 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1859 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1863 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1865 /* Check against account-based DNRs. */
1866 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1867 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1870 else if(target_fixed)
1872 /* Check against channel-based DNRs. */
1873 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1874 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1879 /* Exhaustively search account DNRs. */
1880 for(it = dict_first(handle_dnrs); it; it = next)
1882 next = iter_next(it);
1883 dnr = iter_data(it);
1884 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1888 /* Do the same for channel DNRs. */
1889 for(it = dict_first(plain_dnrs); it; it = next)
1891 next = iter_next(it);
1892 dnr = iter_data(it);
1893 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1897 /* Do the same for wildcarded channel DNRs. */
1898 for(it = dict_first(mask_dnrs); it; it = next)
1900 next = iter_next(it);
1901 dnr = iter_data(it);
1902 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1910 dnr_remove_func(struct do_not_register *match, void *extra)
1912 struct userNode *user;
1915 chan_name = alloca(strlen(match->chan_name) + 1);
1916 strcpy(chan_name, match->chan_name);
1918 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1919 || dict_remove(plain_dnrs, chan_name)
1920 || dict_remove(mask_dnrs, chan_name))
1922 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1928 dnr_count_func(struct do_not_register *match, void *extra)
1930 return 0; (void)match; (void)extra;
1933 static MODCMD_FUNC(cmd_dnrsearch)
1935 struct dnr_search *discrim;
1936 dnr_search_func action;
1937 struct svccmd *subcmd;
1938 unsigned int matches;
1941 sprintf(buf, "dnrsearch %s", argv[1]);
1942 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1945 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1948 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1950 if(!irccasecmp(argv[1], "print"))
1951 action = dnr_print_func;
1952 else if(!irccasecmp(argv[1], "remove"))
1953 action = dnr_remove_func;
1954 else if(!irccasecmp(argv[1], "count"))
1955 action = dnr_count_func;
1958 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1962 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1966 if(action == dnr_print_func)
1967 reply("CSMSG_DNR_SEARCH_RESULTS");
1968 matches = dnr_search(discrim, action, user);
1970 reply("MSG_MATCH_COUNT", matches);
1972 reply("MSG_NO_MATCHES");
1978 chanserv_get_owned_count(struct handle_info *hi)
1980 struct userData *cList;
1983 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1984 if(cList->access == UL_OWNER)
1989 static CHANSERV_FUNC(cmd_register)
1991 struct handle_info *handle;
1992 struct chanData *cData;
1993 struct modeNode *mn;
1994 char reason[MAXLEN];
1996 unsigned int new_channel, force=0;
1997 struct do_not_register *dnr;
2001 if(channel->channel_info)
2003 reply("CSMSG_ALREADY_REGGED", channel->name);
2007 if(channel->bad_channel)
2009 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2014 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2016 reply("CSMSG_MUST_BE_OPPED", channel->name);
2021 chan_name = channel->name;
2025 if((argc < 2) || !IsChannelName(argv[1]))
2027 reply("MSG_NOT_CHANNEL_NAME");
2031 if(opserv_bad_channel(argv[1]))
2033 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2038 chan_name = argv[1];
2041 if(argc >= (new_channel+2))
2043 if(!IsHelping(user))
2045 reply("CSMSG_PROXY_FORBIDDEN");
2049 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2051 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2052 dnr = chanserv_is_dnr(chan_name, handle);
2056 handle = user->handle_info;
2057 dnr = chanserv_is_dnr(chan_name, handle);
2061 if(!IsHelping(user))
2062 reply("CSMSG_DNR_CHANNEL", chan_name);
2064 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2068 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2070 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2075 channel = AddChannel(argv[1], now, NULL, NULL);
2077 cData = register_channel(channel, user->handle_info->handle);
2078 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2079 cData->modes = chanserv_conf.default_modes;
2081 cData->modes.modes_set |= MODE_REGISTERED;
2082 if (IsOffChannel(cData))
2084 mod_chanmode_announce(chanserv, channel, &cData->modes);
2088 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2089 change->args[change->argc].mode = MODE_CHANOP;
2090 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2092 mod_chanmode_announce(chanserv, channel, change);
2093 mod_chanmode_free(change);
2096 /* Initialize the channel's max user record. */
2097 cData->max = channel->members.used;
2099 if(handle != user->handle_info)
2100 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2102 reply("CSMSG_REG_SUCCESS", channel->name);
2104 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2105 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2110 make_confirmation_string(struct userData *uData)
2112 static char strbuf[16];
2117 for(src = uData->handle->handle; *src; )
2118 accum = accum * 31 + toupper(*src++);
2120 for(src = uData->channel->channel->name; *src; )
2121 accum = accum * 31 + toupper(*src++);
2122 sprintf(strbuf, "%08x", accum);
2126 static CHANSERV_FUNC(cmd_unregister)
2129 char reason[MAXLEN];
2130 struct chanData *cData;
2131 struct userData *uData;
2133 cData = channel->channel_info;
2136 reply("CSMSG_NOT_REGISTERED", channel->name);
2140 uData = GetChannelUser(cData, user->handle_info);
2141 if(!uData || (uData->access < UL_OWNER))
2143 reply("CSMSG_NO_ACCESS");
2147 if(IsProtected(cData))
2149 reply("CSMSG_UNREG_NODELETE", channel->name);
2153 if(!IsHelping(user))
2155 const char *confirm_string;
2156 if(IsSuspended(cData))
2158 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2161 confirm_string = make_confirmation_string(uData);
2162 if((argc < 2) || strcmp(argv[1], confirm_string))
2164 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2169 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2170 name = strdup(channel->name);
2171 unregister_channel(cData, reason);
2172 reply("CSMSG_UNREG_SUCCESS", name);
2177 static CHANSERV_FUNC(cmd_move)
2179 struct mod_chanmode change;
2180 struct chanNode *target;
2181 struct modeNode *mn;
2182 struct userData *uData;
2183 char reason[MAXLEN];
2184 struct do_not_register *dnr;
2188 if(IsProtected(channel->channel_info))
2190 reply("CSMSG_MOVE_NODELETE", channel->name);
2194 if(!IsChannelName(argv[1]))
2196 reply("MSG_NOT_CHANNEL_NAME");
2200 if(opserv_bad_channel(argv[1]))
2202 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2206 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2208 for(uData = channel->channel_info->users; uData; uData = uData->next)
2210 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2212 if(!IsHelping(user))
2213 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2215 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2221 mod_chanmode_init(&change);
2222 if(!(target = GetChannel(argv[1])))
2224 target = AddChannel(argv[1], now, NULL, NULL);
2225 if(!IsSuspended(channel->channel_info))
2226 AddChannelUser(chanserv, target);
2228 else if(target->channel_info)
2230 reply("CSMSG_ALREADY_REGGED", target->name);
2233 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2234 && !IsHelping(user))
2236 reply("CSMSG_MUST_BE_OPPED", target->name);
2239 else if(!IsSuspended(channel->channel_info))
2242 change.args[0].mode = MODE_CHANOP;
2243 change.args[0].u.member = AddChannelUser(chanserv, target);
2244 mod_chanmode_announce(chanserv, target, &change);
2249 /* Clear MODE_REGISTERED from old channel, add it to new. */
2251 change.modes_clear = MODE_REGISTERED;
2252 mod_chanmode_announce(chanserv, channel, &change);
2253 change.modes_clear = 0;
2254 change.modes_set = MODE_REGISTERED;
2255 mod_chanmode_announce(chanserv, target, &change);
2258 /* Move the channel_info to the target channel; it
2259 shouldn't be necessary to clear timeq callbacks
2260 for the old channel. */
2261 target->channel_info = channel->channel_info;
2262 target->channel_info->channel = target;
2263 channel->channel_info = NULL;
2265 reply("CSMSG_MOVE_SUCCESS", target->name);
2267 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2268 if(!IsSuspended(target->channel_info))
2270 char reason2[MAXLEN];
2271 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2272 DelChannelUser(chanserv, channel, reason2, 0);
2274 UnlockChannel(channel);
2275 LockChannel(target);
2276 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2281 merge_users(struct chanData *source, struct chanData *target)
2283 struct userData *suData, *tuData, *next;
2289 /* Insert the source's users into the scratch area. */
2290 for(suData = source->users; suData; suData = suData->next)
2291 dict_insert(merge, suData->handle->handle, suData);
2293 /* Iterate through the target's users, looking for
2294 users common to both channels. The lower access is
2295 removed from either the scratch area or target user
2297 for(tuData = target->users; tuData; tuData = next)
2299 struct userData *choice;
2301 next = tuData->next;
2303 /* If a source user exists with the same handle as a target
2304 channel's user, resolve the conflict by removing one. */
2305 suData = dict_find(merge, tuData->handle->handle, NULL);
2309 /* Pick the data we want to keep. */
2310 /* If the access is the same, use the later seen time. */
2311 if(suData->access == tuData->access)
2312 choice = (suData->seen > tuData->seen) ? suData : tuData;
2313 else /* Otherwise, keep the higher access level. */
2314 choice = (suData->access > tuData->access) ? suData : tuData;
2316 /* Remove the user that wasn't picked. */
2317 if(choice == tuData)
2319 dict_remove(merge, suData->handle->handle);
2320 del_channel_user(suData, 0);
2323 del_channel_user(tuData, 0);
2326 /* Move the remaining users to the target channel. */
2327 for(it = dict_first(merge); it; it = iter_next(it))
2329 suData = iter_data(it);
2331 /* Insert the user into the target channel's linked list. */
2332 suData->prev = NULL;
2333 suData->next = target->users;
2334 suData->channel = target;
2337 target->users->prev = suData;
2338 target->users = suData;
2340 /* Update the user counts for the target channel; the
2341 source counts are left alone. */
2342 target->userCount++;
2345 /* Possible to assert (source->users == NULL) here. */
2346 source->users = NULL;
2351 merge_bans(struct chanData *source, struct chanData *target)
2353 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2355 /* Hold on to the original head of the target ban list
2356 to avoid comparing source bans with source bans. */
2357 tFront = target->bans;
2359 /* Perform a totally expensive O(n*m) merge, ick. */
2360 for(sbData = source->bans; sbData; sbData = sNext)
2362 /* Flag to track whether the ban's been moved
2363 to the destination yet. */
2366 /* Possible to assert (sbData->prev == NULL) here. */
2367 sNext = sbData->next;
2369 for(tbData = tFront; tbData; tbData = tNext)
2371 tNext = tbData->next;
2373 /* Perform two comparisons between each source
2374 and target ban, conflicts are resolved by
2375 keeping the broader ban and copying the later
2376 expiration and triggered time. */
2377 if(match_ircglobs(tbData->mask, sbData->mask))
2379 /* There is a broader ban in the target channel that
2380 overrides one in the source channel; remove the
2381 source ban and break. */
2382 if(sbData->expires > tbData->expires)
2383 tbData->expires = sbData->expires;
2384 if(sbData->triggered > tbData->triggered)
2385 tbData->triggered = sbData->triggered;
2386 del_channel_ban(sbData);
2389 else if(match_ircglobs(sbData->mask, tbData->mask))
2391 /* There is a broader ban in the source channel that
2392 overrides one in the target channel; remove the
2393 target ban, fall through and move the source over. */
2394 if(tbData->expires > sbData->expires)
2395 sbData->expires = tbData->expires;
2396 if(tbData->triggered > sbData->triggered)
2397 sbData->triggered = tbData->triggered;
2398 if(tbData == tFront)
2400 del_channel_ban(tbData);
2403 /* Source bans can override multiple target bans, so
2404 we allow a source to run through this loop multiple
2405 times, but we can only move it once. */
2410 /* Remove the source ban from the source ban list. */
2412 sbData->next->prev = sbData->prev;
2414 /* Modify the source ban's associated channel. */
2415 sbData->channel = target;
2417 /* Insert the ban into the target channel's linked list. */
2418 sbData->prev = NULL;
2419 sbData->next = target->bans;
2422 target->bans->prev = sbData;
2423 target->bans = sbData;
2425 /* Update the user counts for the target channel. */
2430 /* Possible to assert (source->bans == NULL) here. */
2431 source->bans = NULL;
2435 merge_data(struct chanData *source, struct chanData *target)
2437 /* Use more recent visited and owner-transfer time; use older
2438 * registered time. Bitwise or may_opchan. Use higher max.
2439 * Do not touch last_refresh, ban count or user counts.
2441 if(source->visited > target->visited)
2442 target->visited = source->visited;
2443 if(source->registered < target->registered)
2444 target->registered = source->registered;
2445 if(source->ownerTransfer > target->ownerTransfer)
2446 target->ownerTransfer = source->ownerTransfer;
2447 if(source->may_opchan)
2448 target->may_opchan = 1;
2449 if(source->max > target->max)
2450 target->max = source->max;
2454 merge_channel(struct chanData *source, struct chanData *target)
2456 merge_users(source, target);
2457 merge_bans(source, target);
2458 merge_data(source, target);
2461 static CHANSERV_FUNC(cmd_merge)
2463 struct userData *target_user;
2464 struct chanNode *target;
2465 char reason[MAXLEN];
2469 /* Make sure the target channel exists and is registered to the user
2470 performing the command. */
2471 if(!(target = GetChannel(argv[1])))
2473 reply("MSG_INVALID_CHANNEL");
2477 if(!target->channel_info)
2479 reply("CSMSG_NOT_REGISTERED", target->name);
2483 if(IsProtected(channel->channel_info))
2485 reply("CSMSG_MERGE_NODELETE");
2489 if(IsSuspended(target->channel_info))
2491 reply("CSMSG_MERGE_SUSPENDED");
2495 if(channel == target)
2497 reply("CSMSG_MERGE_SELF");
2501 target_user = GetChannelUser(target->channel_info, user->handle_info);
2502 if(!target_user || (target_user->access < UL_OWNER))
2504 reply("CSMSG_MERGE_NOT_OWNER");
2508 /* Merge the channel structures and associated data. */
2509 merge_channel(channel->channel_info, target->channel_info);
2510 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2511 unregister_channel(channel->channel_info, reason);
2512 reply("CSMSG_MERGE_SUCCESS", target->name);
2516 static CHANSERV_FUNC(cmd_opchan)
2518 struct mod_chanmode change;
2519 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2521 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2524 channel->channel_info->may_opchan = 0;
2525 mod_chanmode_init(&change);
2527 change.args[0].mode = MODE_CHANOP;
2528 change.args[0].u.member = GetUserMode(channel, chanserv);
2529 mod_chanmode_announce(chanserv, channel, &change);
2530 reply("CSMSG_OPCHAN_DONE", channel->name);
2534 static CHANSERV_FUNC(cmd_adduser)
2536 struct userData *actee;
2537 struct userData *actor, *real_actor;
2538 struct handle_info *handle;
2539 unsigned short access, override = 0;
2543 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2545 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2549 access = user_level_from_name(argv[2], UL_OWNER);
2552 reply("CSMSG_INVALID_ACCESS", argv[2]);
2556 actor = GetChannelUser(channel->channel_info, user->handle_info);
2557 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2559 if(actor->access <= access)
2561 reply("CSMSG_NO_BUMP_ACCESS");
2565 /* Trying to add someone with equal/more access? */
2566 if (!real_actor || real_actor->access <= access)
2567 override = CMD_LOG_OVERRIDE;
2569 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2572 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2574 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2578 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2579 scan_user_presence(actee, NULL);
2580 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2581 return 1 | override;
2584 static CHANSERV_FUNC(cmd_clvl)
2586 struct handle_info *handle;
2587 struct userData *victim;
2588 struct userData *actor, *real_actor;
2589 unsigned short new_access, override = 0;
2590 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2594 actor = GetChannelUser(channel->channel_info, user->handle_info);
2595 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2597 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2600 if(handle == user->handle_info && !privileged)
2602 reply("CSMSG_NO_SELF_CLVL");
2606 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2608 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2612 if(actor->access <= victim->access && !privileged)
2614 reply("MSG_USER_OUTRANKED", handle->handle);
2618 new_access = user_level_from_name(argv[2], UL_OWNER);
2622 reply("CSMSG_INVALID_ACCESS", argv[2]);
2626 if(new_access >= actor->access && !privileged)
2628 reply("CSMSG_NO_BUMP_ACCESS");
2632 /* Trying to clvl a equal/higher user? */
2633 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2634 override = CMD_LOG_OVERRIDE;
2635 /* Trying to clvl someone to equal/higher access? */
2636 if(!real_actor || new_access >= real_actor->access)
2637 override = CMD_LOG_OVERRIDE;
2638 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2639 * If they lower their own access it's not a big problem.
2642 victim->access = new_access;
2643 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2644 return 1 | override;
2647 static CHANSERV_FUNC(cmd_deluser)
2649 struct handle_info *handle;
2650 struct userData *victim;
2651 struct userData *actor, *real_actor;
2652 unsigned short access, override = 0;
2657 actor = GetChannelUser(channel->channel_info, user->handle_info);
2658 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2660 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2663 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2665 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2671 access = user_level_from_name(argv[1], UL_OWNER);
2674 reply("CSMSG_INVALID_ACCESS", argv[1]);
2677 if(access != victim->access)
2679 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2685 access = victim->access;
2688 if((actor->access <= victim->access) && !IsHelping(user))
2690 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2694 /* If people delete themselves it is an override, but they
2695 * could've used deleteme so we don't log it as an override
2697 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2698 override = CMD_LOG_OVERRIDE;
2700 chan_name = strdup(channel->name);
2701 del_channel_user(victim, 1);
2702 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2704 return 1 | override;
2708 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2710 struct userData *actor, *real_actor, *uData, *next;
2711 unsigned int override = 0;
2713 actor = GetChannelUser(channel->channel_info, user->handle_info);
2714 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2716 if(min_access > max_access)
2718 reply("CSMSG_BAD_RANGE", min_access, max_access);
2722 if((actor->access <= max_access) && !IsHelping(user))
2724 reply("CSMSG_NO_ACCESS");
2728 if(!real_actor || real_actor->access <= max_access)
2729 override = CMD_LOG_OVERRIDE;
2731 for(uData = channel->channel_info->users; uData; uData = next)
2735 if((uData->access >= min_access)
2736 && (uData->access <= max_access)
2737 && match_ircglob(uData->handle->handle, mask))
2738 del_channel_user(uData, 1);
2741 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2742 return 1 | override;
2745 static CHANSERV_FUNC(cmd_mdelowner)
2747 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2750 static CHANSERV_FUNC(cmd_mdelcoowner)
2752 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2755 static CHANSERV_FUNC(cmd_mdelmaster)
2757 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2760 static CHANSERV_FUNC(cmd_mdelop)
2762 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2765 static CHANSERV_FUNC(cmd_mdelpeon)
2767 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2771 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2773 struct banData *bData, *next;
2774 char interval[INTERVALLEN];
2776 unsigned long limit;
2779 limit = now - duration;
2780 for(bData = channel->channel_info->bans; bData; bData = next)
2784 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2787 del_channel_ban(bData);
2791 intervalString(interval, duration, user->handle_info);
2792 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2797 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2799 struct userData *actor, *uData, *next;
2800 char interval[INTERVALLEN];
2802 unsigned long limit;
2804 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2805 if(min_access > max_access)
2807 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2811 if(!actor || actor->access <= max_access)
2813 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2818 limit = now - duration;
2819 for(uData = channel->channel_info->users; uData; uData = next)
2823 if((uData->seen > limit)
2825 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2828 if(((uData->access >= min_access) && (uData->access <= max_access))
2829 || (!max_access && (uData->access < actor->access)))
2831 del_channel_user(uData, 1);
2839 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2841 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2845 static CHANSERV_FUNC(cmd_trim)
2847 unsigned long duration;
2848 unsigned short min_level, max_level;
2853 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2854 duration = ParseInterval(argv[2]);
2857 reply("CSMSG_CANNOT_TRIM");
2861 if(!irccasecmp(argv[1], "bans"))
2863 cmd_trim_bans(user, channel, duration);
2866 else if(!irccasecmp(argv[1], "users"))
2868 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2871 else if(parse_level_range(&min_level, &max_level, argv[1]))
2873 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2876 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2878 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2883 reply("CSMSG_INVALID_TRIM", argv[1]);
2888 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2889 to the user. cmd_all takes advantage of this. */
2890 static CHANSERV_FUNC(cmd_up)
2892 struct mod_chanmode change;
2893 struct userData *uData;
2896 mod_chanmode_init(&change);
2898 change.args[0].u.member = GetUserMode(channel, user);
2899 if(!change.args[0].u.member)
2902 reply("MSG_CHANNEL_ABSENT", channel->name);
2906 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2910 reply("CSMSG_GODMODE_UP", argv[0]);
2913 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2915 change.args[0].mode = MODE_CHANOP;
2916 errmsg = "CSMSG_ALREADY_OPPED";
2918 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2920 change.args[0].mode = MODE_VOICE;
2921 errmsg = "CSMSG_ALREADY_VOICED";
2926 reply("CSMSG_NO_ACCESS");
2929 change.args[0].mode &= ~change.args[0].u.member->modes;
2930 if(!change.args[0].mode)
2933 reply(errmsg, channel->name);
2936 modcmd_chanmode_announce(&change);
2940 static CHANSERV_FUNC(cmd_down)
2942 struct mod_chanmode change;
2944 mod_chanmode_init(&change);
2946 change.args[0].u.member = GetUserMode(channel, user);
2947 if(!change.args[0].u.member)
2950 reply("MSG_CHANNEL_ABSENT", channel->name);
2954 if(!change.args[0].u.member->modes)
2957 reply("CSMSG_ALREADY_DOWN", channel->name);
2961 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2962 modcmd_chanmode_announce(&change);
2966 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2968 struct userData *cList;
2970 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2972 if(IsSuspended(cList->channel)
2973 || IsUserSuspended(cList)
2974 || !GetUserMode(cList->channel->channel, user))
2977 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2983 static CHANSERV_FUNC(cmd_upall)
2985 return cmd_all(CSFUNC_ARGS, cmd_up);
2988 static CHANSERV_FUNC(cmd_downall)
2990 return cmd_all(CSFUNC_ARGS, cmd_down);
2993 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2994 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2997 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
2999 unsigned int ii, valid;
3000 struct userNode *victim;
3001 struct mod_chanmode *change;
3003 change = mod_chanmode_alloc(argc - 1);
3005 for(ii=valid=0; ++ii < argc; )
3007 if(!(victim = GetUserH(argv[ii])))
3009 change->args[valid].mode = mode;
3010 change->args[valid].u.member = GetUserMode(channel, victim);
3011 if(!change->args[valid].u.member)
3013 if(validate && !validate(user, channel, victim))
3018 change->argc = valid;
3019 if(valid < (argc-1))
3020 reply("CSMSG_PROCESS_FAILED");
3023 modcmd_chanmode_announce(change);
3024 reply(action, channel->name);
3026 mod_chanmode_free(change);
3030 static CHANSERV_FUNC(cmd_op)
3032 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3035 static CHANSERV_FUNC(cmd_deop)
3037 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3040 static CHANSERV_FUNC(cmd_voice)
3042 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3045 static CHANSERV_FUNC(cmd_devoice)
3047 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3051 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3057 for(ii=0; ii<channel->members.used; ii++)
3059 struct modeNode *mn = channel->members.list[ii];
3061 if(IsService(mn->user))
3064 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3067 if(protect_user(mn->user, user, channel->channel_info))
3071 victims[(*victimCount)++] = mn;
3077 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3079 struct userNode *victim;
3080 struct modeNode **victims;
3081 unsigned int offset, n, victimCount, duration = 0;
3082 char *reason = "Bye.", *ban, *name;
3083 char interval[INTERVALLEN];
3085 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3086 REQUIRE_PARAMS(offset);
3089 reason = unsplit_string(argv + offset, argc - offset, NULL);
3090 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3092 /* Truncate the reason to a length of TOPICLEN, as
3093 the ircd does; however, leave room for an ellipsis
3094 and the kicker's nick. */
3095 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3099 if((victim = GetUserH(argv[1])))
3101 victims = alloca(sizeof(victims[0]));
3102 victims[0] = GetUserMode(channel, victim);
3103 /* XXX: The comparison with ACTION_KICK is just because all
3104 * other actions can work on users outside the channel, and we
3105 * want to allow those (e.g. unbans) in that case. If we add
3106 * some other ejection action for in-channel users, change
3108 victimCount = victims[0] ? 1 : 0;
3110 if(IsService(victim))
3112 reply("MSG_SERVICE_IMMUNE", victim->nick);
3116 if((action == ACTION_KICK) && !victimCount)
3118 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3122 if(protect_user(victim, user, channel->channel_info))
3124 reply("CSMSG_USER_PROTECTED", victim->nick);
3128 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3129 name = victim->nick;
3131 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3133 struct handle_info *hi;
3134 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3135 const char *accountname = argv[1] + 1;
3137 if(!(hi = get_handle_info(accountname)))
3139 reply("MSG_HANDLE_UNKNOWN", accountname);
3143 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3144 victims = alloca(sizeof(victims[0]) * channel->members.used);
3146 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3148 reply("CSMSG_MASK_PROTECTED", banmask);
3152 if((action == ACTION_KICK) && (victimCount == 0))
3154 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3158 name = ban = strdup(banmask);
3162 if(!is_ircmask(argv[1]))
3164 reply("MSG_NICK_UNKNOWN", argv[1]);
3168 victims = alloca(sizeof(victims[0]) * channel->members.used);
3170 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3172 reply("CSMSG_MASK_PROTECTED", argv[1]);
3176 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3178 reply("CSMSG_LAME_MASK", argv[1]);
3182 if((action == ACTION_KICK) && (victimCount == 0))
3184 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3188 name = ban = strdup(argv[1]);
3191 /* Truncate the ban in place if necessary; we must ensure
3192 that 'ban' is a valid ban mask before sanitizing it. */
3193 sanitize_ircmask(ban);
3195 if(action & ACTION_ADD_BAN)
3197 struct banData *bData, *next;
3199 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3201 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3206 if(action & ACTION_ADD_TIMED_BAN)
3208 duration = ParseInterval(argv[2]);
3212 reply("CSMSG_DURATION_TOO_LOW");
3216 else if(duration > (86400 * 365 * 2))
3218 reply("CSMSG_DURATION_TOO_HIGH");
3224 for(bData = channel->channel_info->bans; bData; bData = next)
3226 if(match_ircglobs(bData->mask, ban))
3228 int exact = !irccasecmp(bData->mask, ban);
3230 /* The ban is redundant; there is already a ban
3231 with the same effect in place. */
3235 free(bData->reason);
3236 bData->reason = strdup(reason);
3237 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3239 reply("CSMSG_REASON_CHANGE", ban);
3243 if(exact && bData->expires)
3247 /* If the ban matches an existing one exactly,
3248 extend the expiration time if the provided
3249 duration is longer. */
3250 if(duration && (now + duration > bData->expires))
3252 bData->expires = now + duration;
3263 /* Delete the expiration timeq entry and
3264 requeue if necessary. */
3265 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3268 timeq_add(bData->expires, expire_ban, bData);
3272 /* automated kickban */
3275 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3277 reply("CSMSG_BAN_ADDED", name, channel->name);
3283 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3290 if(match_ircglobs(ban, bData->mask))
3292 /* The ban we are adding makes previously existing
3293 bans redundant; silently remove them. */
3294 del_channel_ban(bData);
3298 bData = add_channel_ban(channel->channel_info, ban, (user->handle_info ? user->handle_info->handle : user->nick), now, (victimCount ? now : 0), (duration ? now + duration : 0), reason);
3300 name = ban = strdup(bData->mask);
3304 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3306 extern const char *hidden_host_suffix;
3307 const char *old_name = chanserv_conf.old_ban_names->list[n];
3309 unsigned int l1, l2;
3312 l2 = strlen(old_name);
3315 if(irccasecmp(ban + l1 - l2, old_name))
3317 new_mask = malloc(MAXLEN);
3318 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3320 name = ban = new_mask;
3325 if(action & ACTION_BAN)
3327 unsigned int exists;
3328 struct mod_chanmode *change;
3330 if(channel->banlist.used >= MAXBANS)
3333 reply("CSMSG_BANLIST_FULL", channel->name);
3338 exists = ChannelBanExists(channel, ban);
3339 change = mod_chanmode_alloc(victimCount + 1);
3340 for(n = 0; n < victimCount; ++n)
3342 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3343 change->args[n].u.member = victims[n];
3347 change->args[n].mode = MODE_BAN;
3348 change->args[n++].u.hostmask = ban;
3352 modcmd_chanmode_announce(change);
3354 mod_chanmode_announce(chanserv, channel, change);
3355 mod_chanmode_free(change);
3357 if(exists && (action == ACTION_BAN))
3360 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3366 if(action & ACTION_KICK)
3368 char kick_reason[MAXLEN];
3369 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3371 for(n = 0; n < victimCount; n++)
3372 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3377 /* No response, since it was automated. */
3379 else if(action & ACTION_ADD_BAN)
3382 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3384 reply("CSMSG_BAN_ADDED", name, channel->name);
3386 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3387 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3388 else if(action & ACTION_BAN)
3389 reply("CSMSG_BAN_DONE", name, channel->name);
3390 else if(action & ACTION_KICK && victimCount)
3391 reply("CSMSG_KICK_DONE", name, channel->name);
3397 static CHANSERV_FUNC(cmd_kickban)
3399 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3402 static CHANSERV_FUNC(cmd_kick)
3404 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3407 static CHANSERV_FUNC(cmd_ban)
3409 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3412 static CHANSERV_FUNC(cmd_addban)
3414 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3417 static CHANSERV_FUNC(cmd_addtimedban)
3419 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3422 static struct mod_chanmode *
3423 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3425 struct mod_chanmode *change;
3426 unsigned char *match;
3427 unsigned int ii, count;
3429 match = alloca(bans->used);
3432 for(ii = count = 0; ii < bans->used; ++ii)
3434 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3435 MATCH_USENICK | MATCH_VISIBLE);
3442 for(ii = count = 0; ii < bans->used; ++ii)
3444 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3451 change = mod_chanmode_alloc(count);
3452 for(ii = count = 0; ii < bans->used; ++ii)
3456 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3457 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3459 assert(count == change->argc);
3464 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3466 struct userNode *actee;
3472 /* may want to allow a comma delimited list of users... */
3473 if(!(actee = GetUserH(argv[1])))
3475 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3477 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3478 const char *accountname = argv[1] + 1;
3480 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3481 mask = strdup(banmask);
3483 else if(!is_ircmask(argv[1]))
3485 reply("MSG_NICK_UNKNOWN", argv[1]);
3490 mask = strdup(argv[1]);
3494 /* We don't sanitize the mask here because ircu
3496 if(action & ACTION_UNBAN)
3498 struct mod_chanmode *change;
3499 change = find_matching_bans(&channel->banlist, actee, mask);
3504 modcmd_chanmode_announce(change);
3505 for(ii = 0; ii < change->argc; ++ii)
3506 free((char*)change->args[ii].u.hostmask);
3507 mod_chanmode_free(change);
3512 if(action & ACTION_DEL_BAN)
3514 struct banData *ban, *next;
3516 ban = channel->channel_info->bans;
3520 for( ; ban && !user_matches_glob(actee, ban->mask,
3521 MATCH_USENICK | MATCH_VISIBLE);
3524 for( ; ban && !match_ircglobs(mask, ban->mask);
3529 del_channel_ban(ban);
3536 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3538 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3544 static CHANSERV_FUNC(cmd_unban)
3546 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3549 static CHANSERV_FUNC(cmd_delban)
3551 /* it doesn't necessarily have to remove the channel ban - may want
3552 to make that an option. */
3553 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3556 static CHANSERV_FUNC(cmd_unbanme)
3558 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3559 long flags = ACTION_UNBAN;
3561 /* remove permanent bans if the user has the proper access. */
3562 if(uData->access >= UL_MASTER)
3563 flags |= ACTION_DEL_BAN;
3565 argv[1] = user->nick;
3566 return unban_user(user, channel, 2, argv, cmd, flags);
3569 static CHANSERV_FUNC(cmd_unbanall)
3571 struct mod_chanmode *change;
3574 if(!channel->banlist.used)
3576 reply("CSMSG_NO_BANS", channel->name);
3580 change = mod_chanmode_alloc(channel->banlist.used);
3581 for(ii=0; ii<channel->banlist.used; ii++)
3583 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3584 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3586 modcmd_chanmode_announce(change);
3587 for(ii = 0; ii < change->argc; ++ii)
3588 free((char*)change->args[ii].u.hostmask);
3589 mod_chanmode_free(change);
3590 reply("CSMSG_BANS_REMOVED", channel->name);
3594 static CHANSERV_FUNC(cmd_open)
3596 struct mod_chanmode *change;
3599 change = find_matching_bans(&channel->banlist, user, NULL);
3601 change = mod_chanmode_alloc(0);
3602 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3603 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3604 && channel->channel_info->modes.modes_set)
3605 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3606 modcmd_chanmode_announce(change);
3607 reply("CSMSG_CHANNEL_OPENED", channel->name);
3608 for(ii = 0; ii < change->argc; ++ii)
3609 free((char*)change->args[ii].u.hostmask);
3610 mod_chanmode_free(change);
3614 static CHANSERV_FUNC(cmd_myaccess)
3616 static struct string_buffer sbuf;
3617 struct handle_info *target_handle;
3618 struct userData *uData;
3621 target_handle = user->handle_info;
3622 else if(!IsHelping(user))
3624 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3627 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3630 if(!target_handle->channels)
3632 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3636 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3637 for(uData = target_handle->channels; uData; uData = uData->u_next)
3639 struct chanData *cData = uData->channel;
3641 if(uData->access > UL_OWNER)
3643 if(IsProtected(cData)
3644 && (target_handle != user->handle_info)
3645 && !GetTrueChannelAccess(cData, user->handle_info))
3648 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3649 if(uData->flags != USER_AUTO_OP)
3650 string_buffer_append(&sbuf, ',');
3651 if(IsUserSuspended(uData))
3652 string_buffer_append(&sbuf, 's');
3653 if(IsUserAutoOp(uData))
3655 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3656 string_buffer_append(&sbuf, 'o');
3657 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3658 string_buffer_append(&sbuf, 'v');
3660 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3661 string_buffer_append(&sbuf, 'i');
3663 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3665 string_buffer_append_string(&sbuf, ")]");
3666 string_buffer_append(&sbuf, '\0');
3667 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3673 static CHANSERV_FUNC(cmd_access)
3675 struct userNode *target;
3676 struct handle_info *target_handle;
3677 struct userData *uData;
3679 char prefix[MAXLEN];
3684 target_handle = target->handle_info;
3686 else if((target = GetUserH(argv[1])))
3688 target_handle = target->handle_info;
3690 else if(argv[1][0] == '*')
3692 if(!(target_handle = get_handle_info(argv[1]+1)))
3694 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3700 reply("MSG_NICK_UNKNOWN", argv[1]);
3704 assert(target || target_handle);
3706 if(target == chanserv)
3708 reply("CSMSG_IS_CHANSERV");
3716 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3721 reply("MSG_USER_AUTHENTICATE", target->nick);
3724 reply("MSG_AUTHENTICATE");
3730 const char *epithet = NULL, *type = NULL;
3733 epithet = chanserv_conf.irc_operator_epithet;
3734 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3736 else if(IsNetworkHelper(target))
3738 epithet = chanserv_conf.network_helper_epithet;
3739 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3741 else if(IsSupportHelper(target))
3743 epithet = chanserv_conf.support_helper_epithet;
3744 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3748 if(target_handle->epithet)
3749 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3751 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3753 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3757 sprintf(prefix, "%s", target_handle->handle);
3760 if(!channel->channel_info)
3762 reply("CSMSG_NOT_REGISTERED", channel->name);
3766 helping = HANDLE_FLAGGED(target_handle, HELPING)
3767 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3768 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3770 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3771 /* To prevent possible information leaks, only show infolines
3772 * if the requestor is in the channel or it's their own
3774 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3776 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3778 /* Likewise, only say it's suspended if the user has active
3779 * access in that channel or it's their own entry. */
3780 if(IsUserSuspended(uData)
3781 && (GetChannelUser(channel->channel_info, user->handle_info)
3782 || (user->handle_info == uData->handle)))
3784 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3789 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3796 zoot_list(struct listData *list)
3798 struct userData *uData;
3799 unsigned int start, curr, highest, lowest;
3800 struct helpfile_table tmp_table;
3801 const char **temp, *msg;
3803 if(list->table.length == 1)
3806 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3808 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3809 msg = user_find_message(list->user, "MSG_NONE");
3810 send_message_type(4, list->user, list->bot, " %s", msg);
3812 tmp_table.width = list->table.width;
3813 tmp_table.flags = list->table.flags;
3814 list->table.contents[0][0] = " ";
3815 highest = list->highest;
3816 if(list->lowest != 0)
3817 lowest = list->lowest;
3818 else if(highest < 100)
3821 lowest = highest - 100;
3822 for(start = curr = 1; curr < list->table.length; )
3824 uData = list->users[curr-1];
3825 list->table.contents[curr++][0] = " ";
3826 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3829 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3831 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3832 temp = list->table.contents[--start];
3833 list->table.contents[start] = list->table.contents[0];
3834 tmp_table.contents = list->table.contents + start;
3835 tmp_table.length = curr - start;
3836 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3837 list->table.contents[start] = temp;
3839 highest = lowest - 1;
3840 lowest = (highest < 100) ? 0 : (highest - 99);
3846 def_list(struct listData *list)
3850 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3852 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3853 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3854 if(list->table.length == 1)
3856 msg = user_find_message(list->user, "MSG_NONE");
3857 send_message_type(4, list->user, list->bot, " %s", msg);
3862 userData_access_comp(const void *arg_a, const void *arg_b)
3864 const struct userData *a = *(struct userData**)arg_a;
3865 const struct userData *b = *(struct userData**)arg_b;
3867 if(a->access != b->access)
3868 res = b->access - a->access;
3870 res = irccasecmp(a->handle->handle, b->handle->handle);
3875 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3877 void (*send_list)(struct listData *);
3878 struct userData *uData;
3879 struct listData lData;
3880 unsigned int matches;
3884 lData.bot = cmd->parent->bot;
3885 lData.channel = channel;
3886 lData.lowest = lowest;
3887 lData.highest = highest;
3888 lData.search = (argc > 1) ? argv[1] : NULL;
3889 send_list = def_list;
3890 (void)zoot_list; /* since it doesn't show user levels */
3892 if(user->handle_info)
3894 switch(user->handle_info->userlist_style)
3896 case HI_STYLE_DEF: send_list = def_list; break;
3897 case HI_STYLE_ZOOT: send_list = def_list; break;
3901 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3903 for(uData = channel->channel_info->users; uData; uData = uData->next)
3905 if((uData->access < lowest)
3906 || (uData->access > highest)
3907 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3909 lData.users[matches++] = uData;
3911 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3913 lData.table.length = matches+1;
3914 lData.table.width = 4;
3915 lData.table.flags = TABLE_NO_FREE;
3916 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3917 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3918 lData.table.contents[0] = ary;
3921 ary[2] = "Last Seen";
3923 for(matches = 1; matches < lData.table.length; ++matches)
3925 struct userData *uData = lData.users[matches-1];
3926 char seen[INTERVALLEN];
3928 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3929 lData.table.contents[matches] = ary;
3930 ary[0] = strtab(uData->access);
3931 ary[1] = uData->handle->handle;
3934 else if(!uData->seen)
3937 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3938 ary[2] = strdup(ary[2]);
3939 if(IsUserSuspended(uData))
3940 ary[3] = "Suspended";
3941 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3942 ary[3] = "Vacation";
3947 for(matches = 1; matches < lData.table.length; ++matches)
3949 free((char*)lData.table.contents[matches][2]);
3950 free(lData.table.contents[matches]);
3952 free(lData.table.contents[0]);
3953 free(lData.table.contents);
3957 static CHANSERV_FUNC(cmd_users)
3959 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3962 static CHANSERV_FUNC(cmd_wlist)
3964 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3967 static CHANSERV_FUNC(cmd_clist)
3969 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3972 static CHANSERV_FUNC(cmd_mlist)
3974 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3977 static CHANSERV_FUNC(cmd_olist)
3979 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3982 static CHANSERV_FUNC(cmd_plist)
3984 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3987 static CHANSERV_FUNC(cmd_bans)
3989 struct userNode *search_u = NULL;
3990 struct helpfile_table tbl;
3991 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3992 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3993 const char *msg_never, *triggered, *expires;
3994 struct banData *ban, **bans;
3998 else if(strchr(search = argv[1], '!'))
4001 search_wilds = search[strcspn(search, "?*")];
4003 else if(!(search_u = GetUserH(search)))
4004 reply("MSG_NICK_UNKNOWN", search);
4006 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4008 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4012 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4017 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4020 bans[matches++] = ban;
4025 tbl.length = matches + 1;
4026 tbl.width = 4 + timed;
4028 tbl.flags = TABLE_NO_FREE;
4029 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4030 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4031 tbl.contents[0][0] = "Mask";
4032 tbl.contents[0][1] = "Set By";
4033 tbl.contents[0][2] = "Triggered";
4036 tbl.contents[0][3] = "Expires";
4037 tbl.contents[0][4] = "Reason";
4040 tbl.contents[0][3] = "Reason";
4043 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4045 free(tbl.contents[0]);
4050 msg_never = user_find_message(user, "MSG_NEVER");
4051 for(ii = 0; ii < matches; )
4057 else if(ban->expires)
4058 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4060 expires = msg_never;
4063 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4065 triggered = msg_never;
4067 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4068 tbl.contents[ii][0] = ban->mask;
4069 tbl.contents[ii][1] = ban->owner;
4070 tbl.contents[ii][2] = strdup(triggered);
4073 tbl.contents[ii][3] = strdup(expires);
4074 tbl.contents[ii][4] = ban->reason;
4077 tbl.contents[ii][3] = ban->reason;
4079 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4080 reply("MSG_MATCH_COUNT", matches);
4081 for(ii = 1; ii < tbl.length; ++ii)
4083 free((char*)tbl.contents[ii][2]);
4085 free((char*)tbl.contents[ii][3]);
4086 free(tbl.contents[ii]);
4088 free(tbl.contents[0]);
4094 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4096 struct chanData *cData = channel->channel_info;
4097 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4099 if(cData->topic_mask)
4100 return !match_ircglob(new_topic, cData->topic_mask);
4101 else if(cData->topic)
4102 return irccasecmp(new_topic, cData->topic);
4107 static CHANSERV_FUNC(cmd_topic)
4109 struct chanData *cData;
4112 cData = channel->channel_info;
4117 SetChannelTopic(channel, chanserv, cData->topic, 1);
4118 reply("CSMSG_TOPIC_SET", cData->topic);
4122 reply("CSMSG_NO_TOPIC", channel->name);
4126 topic = unsplit_string(argv + 1, argc - 1, NULL);
4127 /* If they say "!topic *", use an empty topic. */
4128 if((topic[0] == '*') && (topic[1] == 0))
4130 if(bad_topic(channel, user, topic))
4132 char *topic_mask = cData->topic_mask;
4135 char new_topic[TOPICLEN+1], tchar;
4136 int pos=0, starpos=-1, dpos=0, len;
4138 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4145 len = strlen(topic);
4146 if((dpos + len) > TOPICLEN)
4147 len = TOPICLEN + 1 - dpos;
4148 memcpy(new_topic+dpos, topic, len);
4152 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4153 default: new_topic[dpos++] = tchar; break;
4156 if((dpos > TOPICLEN) || tchar)
4159 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4160 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4163 new_topic[dpos] = 0;
4164 SetChannelTopic(channel, chanserv, new_topic, 1);
4166 reply("CSMSG_TOPIC_LOCKED", channel->name);
4171 SetChannelTopic(channel, chanserv, topic, 1);
4173 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4175 /* Grab the topic and save it as the default topic. */
4177 cData->topic = strdup(channel->topic);
4183 static CHANSERV_FUNC(cmd_mode)
4185 struct userData *uData;
4186 struct mod_chanmode *change;
4191 change = &channel->channel_info->modes;
4192 if(change->modes_set || change->modes_clear) {
4193 modcmd_chanmode_announce(change);
4194 reply("CSMSG_DEFAULTED_MODES", channel->name);
4196 reply("CSMSG_NO_MODES", channel->name);
4200 uData = GetChannelUser(channel->channel_info, user->handle_info);
4202 base_oplevel = MAXOPLEVEL;
4203 else if (uData->access >= UL_OWNER)
4206 base_oplevel = 1 + UL_OWNER - uData->access;
4207 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4210 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4214 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4215 && mode_lock_violated(&channel->channel_info->modes, change))
4218 mod_chanmode_format(&channel->channel_info->modes, modes);
4219 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4223 modcmd_chanmode_announce(change);
4224 mod_chanmode_free(change);
4225 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4229 static CHANSERV_FUNC(cmd_invite)
4231 struct userData *uData;
4232 struct userNode *invite;
4234 uData = GetChannelUser(channel->channel_info, user->handle_info);
4238 if(!(invite = GetUserH(argv[1])))
4240 reply("MSG_NICK_UNKNOWN", argv[1]);
4247 if(GetUserMode(channel, invite))
4249 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4257 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4258 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4261 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4263 irc_invite(chanserv, invite, channel);
4265 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4270 static CHANSERV_FUNC(cmd_inviteme)
4272 if(GetUserMode(channel, user))
4274 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4277 if(channel->channel_info
4278 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4280 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4283 irc_invite(cmd->parent->bot, user, channel);
4288 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4291 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4293 /* We display things based on two dimensions:
4294 * - Issue time: present or absent
4295 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4296 * (in order of precedence, so something both expired and revoked
4297 * only counts as revoked)
4299 combo = (suspended->issued ? 4 : 0)
4300 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4302 case 0: /* no issue time, indefinite expiration */
4303 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4305 case 1: /* no issue time, expires in future */
4306 intervalString(buf1, suspended->expires-now, user->handle_info);
4307 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4309 case 2: /* no issue time, expired */
4310 intervalString(buf1, now-suspended->expires, user->handle_info);
4311 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4313 case 3: /* no issue time, revoked */
4314 intervalString(buf1, now-suspended->revoked, user->handle_info);
4315 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4317 case 4: /* issue time set, indefinite expiration */
4318 intervalString(buf1, now-suspended->issued, user->handle_info);
4319 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4321 case 5: /* issue time set, expires in future */
4322 intervalString(buf1, now-suspended->issued, user->handle_info);
4323 intervalString(buf2, suspended->expires-now, user->handle_info);
4324 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4326 case 6: /* issue time set, expired */
4327 intervalString(buf1, now-suspended->issued, user->handle_info);
4328 intervalString(buf2, now-suspended->expires, user->handle_info);
4329 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4331 case 7: /* issue time set, revoked */
4332 intervalString(buf1, now-suspended->issued, user->handle_info);
4333 intervalString(buf2, now-suspended->revoked, user->handle_info);
4334 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4337 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4342 static CHANSERV_FUNC(cmd_info)
4344 char modes[MAXLEN], buffer[INTERVALLEN];
4345 struct userData *uData, *owner;
4346 struct chanData *cData;
4347 struct do_not_register *dnr;
4352 cData = channel->channel_info;
4353 reply("CSMSG_CHANNEL_INFO", channel->name);
4355 uData = GetChannelUser(cData, user->handle_info);
4356 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4358 mod_chanmode_format(&cData->modes, modes);
4359 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4360 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4363 for(it = dict_first(cData->notes); it; it = iter_next(it))
4367 note = iter_data(it);
4368 if(!note_type_visible_to_user(cData, note->type, user))
4371 padding = PADLEN - 1 - strlen(iter_key(it));
4372 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4375 reply("CSMSG_CHANNEL_MAX", cData->max);
4376 for(owner = cData->users; owner; owner = owner->next)
4377 if(owner->access == UL_OWNER)
4378 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4379 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4380 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4381 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4383 privileged = IsStaff(user);
4385 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4386 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4387 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4389 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4390 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4392 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4394 struct suspended *suspended;
4395 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4396 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4397 show_suspension_info(cmd, user, suspended);
4399 else if(IsSuspended(cData))
4401 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4402 show_suspension_info(cmd, user, cData->suspended);
4407 static CHANSERV_FUNC(cmd_netinfo)
4409 extern unsigned long boot_time;
4410 extern unsigned long burst_length;
4411 char interval[INTERVALLEN];
4413 reply("CSMSG_NETWORK_INFO");
4414 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4415 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4416 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4417 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4418 reply("CSMSG_NETWORK_BANS", banCount);
4419 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4420 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4421 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4426 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4428 struct helpfile_table table;
4430 struct userNode *user;
4435 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4436 table.contents = alloca(list->used*sizeof(*table.contents));
4437 for(nn=0; nn<list->used; nn++)
4439 user = list->list[nn];
4440 if(user->modes & skip_flags)
4444 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4447 nick = alloca(strlen(user->nick)+3);
4448 sprintf(nick, "(%s)", user->nick);
4452 table.contents[table.length][0] = nick;
4455 table_send(chanserv, to->nick, 0, NULL, table);
4458 static CHANSERV_FUNC(cmd_ircops)
4460 reply("CSMSG_STAFF_OPERS");
4461 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4465 static CHANSERV_FUNC(cmd_helpers)
4467 reply("CSMSG_STAFF_HELPERS");
4468 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4472 static CHANSERV_FUNC(cmd_staff)
4474 reply("CSMSG_NETWORK_STAFF");
4475 cmd_ircops(CSFUNC_ARGS);
4476 cmd_helpers(CSFUNC_ARGS);
4480 static CHANSERV_FUNC(cmd_peek)
4482 struct modeNode *mn;
4483 char modes[MODELEN];
4485 struct helpfile_table table;
4487 irc_make_chanmode(channel, modes);
4489 reply("CSMSG_PEEK_INFO", channel->name);
4490 reply("CSMSG_PEEK_TOPIC", channel->topic);
4491 reply("CSMSG_PEEK_MODES", modes);
4492 reply("CSMSG_PEEK_USERS", channel->members.used);
4496 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4497 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4498 for(n = 0; n < channel->members.used; n++)
4500 mn = channel->members.list[n];
4501 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4503 table.contents[table.length] = alloca(sizeof(**table.contents));
4504 table.contents[table.length][0] = mn->user->nick;
4509 reply("CSMSG_PEEK_OPS");
4510 table_send(chanserv, user->nick, 0, NULL, table);
4513 reply("CSMSG_PEEK_NO_OPS");
4517 static MODCMD_FUNC(cmd_wipeinfo)
4519 struct handle_info *victim;
4520 struct userData *ud, *actor, *real_actor;
4521 unsigned int override = 0;
4524 actor = GetChannelUser(channel->channel_info, user->handle_info);
4525 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4526 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4528 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4530 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4533 if((ud->access >= actor->access) && (ud != actor))
4535 reply("MSG_USER_OUTRANKED", victim->handle);
4538 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4539 override = CMD_LOG_OVERRIDE;
4543 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4544 return 1 | override;
4547 static CHANSERV_FUNC(cmd_resync)
4549 struct mod_chanmode *changes;
4550 struct chanData *cData = channel->channel_info;
4551 unsigned int ii, used;
4553 changes = mod_chanmode_alloc(channel->members.used * 2);
4554 for(ii = used = 0; ii < channel->members.used; ++ii)
4556 struct modeNode *mn = channel->members.list[ii];
4557 struct userData *uData;
4559 if(IsService(mn->user))
4562 uData = GetChannelAccess(cData, mn->user->handle_info);
4563 if(!cData->lvlOpts[lvlGiveOps]
4564 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4566 if(!(mn->modes & MODE_CHANOP))
4568 changes->args[used].mode = MODE_CHANOP;
4569 changes->args[used++].u.member = mn;
4572 else if(!cData->lvlOpts[lvlGiveVoice]
4573 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4575 if(mn->modes & MODE_CHANOP)
4577 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4578 changes->args[used++].u.member = mn;
4580 if(!(mn->modes & MODE_VOICE))
4582 changes->args[used].mode = MODE_VOICE;
4583 changes->args[used++].u.member = mn;
4590 changes->args[used].mode = MODE_REMOVE | mn->modes;
4591 changes->args[used++].u.member = mn;
4595 changes->argc = used;
4596 modcmd_chanmode_announce(changes);
4597 mod_chanmode_free(changes);
4598 reply("CSMSG_RESYNCED_USERS", channel->name);
4602 static CHANSERV_FUNC(cmd_seen)
4604 struct userData *uData;
4605 struct handle_info *handle;
4606 char seen[INTERVALLEN];
4610 if(!irccasecmp(argv[1], chanserv->nick))
4612 reply("CSMSG_IS_CHANSERV");
4616 if(!(handle = get_handle_info(argv[1])))
4618 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4622 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4624 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4629 reply("CSMSG_USER_PRESENT", handle->handle);
4630 else if(uData->seen)
4631 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4633 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4635 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4636 reply("CSMSG_USER_VACATION", handle->handle);
4641 static MODCMD_FUNC(cmd_names)
4643 struct userNode *targ;
4644 struct userData *targData;
4645 unsigned int ii, pos;
4648 for(ii=pos=0; ii<channel->members.used; ++ii)
4650 targ = channel->members.list[ii]->user;
4651 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4654 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4657 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4661 if(IsUserSuspended(targData))
4663 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4666 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4667 reply("CSMSG_END_NAMES", channel->name);
4672 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4674 switch(ntype->visible_type)
4676 case NOTE_VIS_ALL: return 1;
4677 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4678 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4683 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4685 struct userData *uData;
4687 switch(ntype->set_access_type)
4689 case NOTE_SET_CHANNEL_ACCESS:
4690 if(!user->handle_info)
4692 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4694 return uData->access >= ntype->set_access.min_ulevel;
4695 case NOTE_SET_CHANNEL_SETTER:
4696 return check_user_level(channel, user, lvlSetters, 1, 0);
4697 case NOTE_SET_PRIVILEGED: default:
4698 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4702 static CHANSERV_FUNC(cmd_note)
4704 struct chanData *cData;
4706 struct note_type *ntype;
4708 cData = channel->channel_info;
4711 reply("CSMSG_NOT_REGISTERED", channel->name);
4715 /* If no arguments, show all visible notes for the channel. */
4721 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4723 note = iter_data(it);
4724 if(!note_type_visible_to_user(cData, note->type, user))
4727 reply("CSMSG_NOTELIST_HEADER", channel->name);
4728 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4731 reply("CSMSG_NOTELIST_END", channel->name);
4733 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4735 /* If one argument, show the named note. */
4738 if((note = dict_find(cData->notes, argv[1], NULL))
4739 && note_type_visible_to_user(cData, note->type, user))
4741 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4743 else if((ntype = dict_find(note_types, argv[1], NULL))
4744 && note_type_visible_to_user(NULL, ntype, user))
4746 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4751 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4755 /* Assume they're trying to set a note. */
4759 ntype = dict_find(note_types, argv[1], NULL);
4762 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4765 else if(note_type_settable_by_user(channel, ntype, user))
4767 note_text = unsplit_string(argv+2, argc-2, NULL);
4768 if((note = dict_find(cData->notes, argv[1], NULL)))
4769 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4770 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4771 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4773 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4775 /* The note is viewable to staff only, so return 0
4776 to keep the invocation from getting logged (or
4777 regular users can see it in !events). */
4783 reply("CSMSG_NO_ACCESS");
4790 static CHANSERV_FUNC(cmd_delnote)
4795 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4796 || !note_type_settable_by_user(channel, note->type, user))
4798 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4801 dict_remove(channel->channel_info->notes, note->type->name);
4802 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4806 static CHANSERV_FUNC(cmd_events)
4808 struct logSearch discrim;
4809 struct logReport report;
4810 unsigned int matches, limit;
4812 limit = (argc > 1) ? atoi(argv[1]) : 10;
4813 if(limit < 1 || limit > 200)
4816 memset(&discrim, 0, sizeof(discrim));
4817 discrim.masks.bot = chanserv;
4818 discrim.masks.channel_name = channel->name;
4820 discrim.masks.command = argv[2];
4821 discrim.limit = limit;
4822 discrim.max_time = INT_MAX;
4823 discrim.severities = 1 << LOG_COMMAND;
4824 report.reporter = chanserv;
4826 reply("CSMSG_EVENT_SEARCH_RESULTS");
4827 matches = log_entry_search(&discrim, log_report_entry, &report);
4829 reply("MSG_MATCH_COUNT", matches);
4831 reply("MSG_NO_MATCHES");
4835 static CHANSERV_FUNC(cmd_say)
4841 msg = unsplit_string(argv + 1, argc - 1, NULL);
4842 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4844 else if(*argv[1] == '*' && argv[1][1] != '\0')
4846 struct handle_info *hi;
4847 struct userNode *authed;
4850 msg = unsplit_string(argv + 2, argc - 2, NULL);
4852 if (!(hi = get_handle_info(argv[1] + 1)))
4854 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4858 for (authed = hi->users; authed; authed = authed->next_authed)
4859 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4861 else if(GetUserH(argv[1]))
4864 msg = unsplit_string(argv + 2, argc - 2, NULL);
4865 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4869 reply("MSG_NOT_TARGET_NAME");
4875 static CHANSERV_FUNC(cmd_emote)
4881 /* CTCP is so annoying. */
4882 msg = unsplit_string(argv + 1, argc - 1, NULL);
4883 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4885 else if(*argv[1] == '*' && argv[1][1] != '\0')
4887 struct handle_info *hi;
4888 struct userNode *authed;
4891 msg = unsplit_string(argv + 2, argc - 2, NULL);
4893 if (!(hi = get_handle_info(argv[1] + 1)))
4895 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4899 for (authed = hi->users; authed; authed = authed->next_authed)
4900 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4902 else if(GetUserH(argv[1]))
4904 msg = unsplit_string(argv + 2, argc - 2, NULL);
4905 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4909 reply("MSG_NOT_TARGET_NAME");
4915 struct channelList *
4916 chanserv_support_channels(void)
4918 return &chanserv_conf.support_channels;
4921 static CHANSERV_FUNC(cmd_expire)
4923 int channel_count = registered_channels;
4924 expire_channels(NULL);
4925 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4930 chanserv_expire_suspension(void *data)
4932 struct suspended *suspended = data;
4933 struct chanNode *channel;
4935 if(!suspended->expires || (now < suspended->expires))
4936 suspended->revoked = now;
4937 channel = suspended->cData->channel;
4938 suspended->cData->channel = channel;
4939 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4940 if(!IsOffChannel(suspended->cData))
4942 struct mod_chanmode change;
4943 mod_chanmode_init(&change);
4945 change.args[0].mode = MODE_CHANOP;
4946 change.args[0].u.member = AddChannelUser(chanserv, channel);
4947 mod_chanmode_announce(chanserv, channel, &change);
4951 static CHANSERV_FUNC(cmd_csuspend)
4953 struct suspended *suspended;
4954 char reason[MAXLEN];
4955 unsigned long expiry, duration;
4956 struct userData *uData;
4960 if(IsProtected(channel->channel_info))
4962 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4966 if(argv[1][0] == '!')
4968 else if(IsSuspended(channel->channel_info))
4970 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4971 show_suspension_info(cmd, user, channel->channel_info->suspended);
4975 if(!strcmp(argv[1], "0"))
4977 else if((duration = ParseInterval(argv[1])))
4978 expiry = now + duration;
4981 reply("MSG_INVALID_DURATION", argv[1]);
4985 unsplit_string(argv + 2, argc - 2, reason);
4987 suspended = calloc(1, sizeof(*suspended));
4988 suspended->revoked = 0;
4989 suspended->issued = now;
4990 suspended->suspender = strdup(user->handle_info->handle);
4991 suspended->expires = expiry;
4992 suspended->reason = strdup(reason);
4993 suspended->cData = channel->channel_info;
4994 suspended->previous = suspended->cData->suspended;
4995 suspended->cData->suspended = suspended;
4997 if(suspended->expires)
4998 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5000 if(IsSuspended(channel->channel_info))
5002 suspended->previous->revoked = now;
5003 if(suspended->previous->expires)
5004 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5005 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5006 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5010 /* Mark all users in channel as absent. */
5011 for(uData = channel->channel_info->users; uData; uData = uData->next)
5020 /* Mark the channel as suspended, then part. */
5021 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5022 DelChannelUser(chanserv, channel, suspended->reason, 0);
5023 reply("CSMSG_SUSPENDED", channel->name);
5024 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5025 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5030 static CHANSERV_FUNC(cmd_cunsuspend)
5032 struct suspended *suspended;
5033 char message[MAXLEN];
5035 if(!IsSuspended(channel->channel_info))
5037 reply("CSMSG_NOT_SUSPENDED", channel->name);
5041 suspended = channel->channel_info->suspended;
5043 /* Expire the suspension and join ChanServ to the channel. */
5044 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5045 chanserv_expire_suspension(suspended);
5046 reply("CSMSG_UNSUSPENDED", channel->name);
5047 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5048 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5052 typedef struct chanservSearch
5057 unsigned long unvisited;
5058 unsigned long registered;
5060 unsigned long flags;
5064 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5067 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5072 search = malloc(sizeof(struct chanservSearch));
5073 memset(search, 0, sizeof(*search));
5076 for(i = 0; i < argc; i++)
5078 /* Assume all criteria require arguments. */
5081 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5085 if(!irccasecmp(argv[i], "name"))
5086 search->name = argv[++i];
5087 else if(!irccasecmp(argv[i], "registrar"))
5088 search->registrar = argv[++i];
5089 else if(!irccasecmp(argv[i], "unvisited"))
5090 search->unvisited = ParseInterval(argv[++i]);
5091 else if(!irccasecmp(argv[i], "registered"))
5092 search->registered = ParseInterval(argv[++i]);
5093 else if(!irccasecmp(argv[i], "flags"))
5096 if(!irccasecmp(argv[i], "nodelete"))
5097 search->flags |= CHANNEL_NODELETE;
5098 else if(!irccasecmp(argv[i], "suspended"))
5099 search->flags |= CHANNEL_SUSPENDED;
5100 else if(!irccasecmp(argv[i], "unreviewed"))
5101 search->flags |= CHANNEL_UNREVIEWED;
5104 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5108 else if(!irccasecmp(argv[i], "limit"))
5109 search->limit = strtoul(argv[++i], NULL, 10);
5112 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5117 if(search->name && !strcmp(search->name, "*"))
5119 if(search->registrar && !strcmp(search->registrar, "*"))
5120 search->registrar = 0;
5129 chanserv_channel_match(struct chanData *channel, search_t search)
5131 const char *name = channel->channel->name;
5132 if((search->name && !match_ircglob(name, search->name)) ||
5133 (search->registrar && !channel->registrar) ||
5134 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5135 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5136 (search->registered && (now - channel->registered) > search->registered) ||
5137 (search->flags && ((search->flags & channel->flags) != search->flags)))
5144 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5146 struct chanData *channel;
5147 unsigned int matches = 0;
5149 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5151 if(!chanserv_channel_match(channel, search))
5161 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5166 search_print(struct chanData *channel, void *data)
5168 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5171 static CHANSERV_FUNC(cmd_search)
5174 unsigned int matches;
5175 channel_search_func action;
5179 if(!irccasecmp(argv[1], "count"))
5180 action = search_count;
5181 else if(!irccasecmp(argv[1], "print"))
5182 action = search_print;
5185 reply("CSMSG_ACTION_INVALID", argv[1]);
5189 search = chanserv_search_create(user, argc - 2, argv + 2);
5193 if(action == search_count)
5194 search->limit = INT_MAX;
5196 if(action == search_print)
5197 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5199 matches = chanserv_channel_search(search, action, user);
5202 reply("MSG_MATCH_COUNT", matches);
5204 reply("MSG_NO_MATCHES");
5210 static CHANSERV_FUNC(cmd_unvisited)
5212 struct chanData *cData;
5213 unsigned long interval = chanserv_conf.channel_expire_delay;
5214 char buffer[INTERVALLEN];
5215 unsigned int limit = 25, matches = 0;
5219 interval = ParseInterval(argv[1]);
5221 limit = atoi(argv[2]);
5224 intervalString(buffer, interval, user->handle_info);
5225 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5227 for(cData = channelList; cData && matches < limit; cData = cData->next)
5229 if((now - cData->visited) < interval)
5232 intervalString(buffer, now - cData->visited, user->handle_info);
5233 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5240 static MODCMD_FUNC(chan_opt_defaulttopic)
5246 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5248 reply("CSMSG_TOPIC_LOCKED", channel->name);
5252 topic = unsplit_string(argv+1, argc-1, NULL);
5254 free(channel->channel_info->topic);
5255 if(topic[0] == '*' && topic[1] == 0)
5257 topic = channel->channel_info->topic = NULL;
5261 topic = channel->channel_info->topic = strdup(topic);
5262 if(channel->channel_info->topic_mask
5263 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5264 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5266 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5269 if(channel->channel_info->topic)
5270 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5272 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5276 static MODCMD_FUNC(chan_opt_topicmask)
5280 struct chanData *cData = channel->channel_info;
5283 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5285 reply("CSMSG_TOPIC_LOCKED", channel->name);
5289 mask = unsplit_string(argv+1, argc-1, NULL);
5291 if(cData->topic_mask)
5292 free(cData->topic_mask);
5293 if(mask[0] == '*' && mask[1] == 0)
5295 cData->topic_mask = 0;
5299 cData->topic_mask = strdup(mask);
5301 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5302 else if(!match_ircglob(cData->topic, cData->topic_mask))
5303 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5307 if(channel->channel_info->topic_mask)
5308 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5310 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5314 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5318 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5322 if(greeting[0] == '*' && greeting[1] == 0)
5326 unsigned int length = strlen(greeting);
5327 if(length > chanserv_conf.greeting_length)
5329 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5332 *data = strdup(greeting);
5341 reply(name, user_find_message(user, "MSG_NONE"));
5345 static MODCMD_FUNC(chan_opt_greeting)
5347 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5350 static MODCMD_FUNC(chan_opt_usergreeting)
5352 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5355 static MODCMD_FUNC(chan_opt_modes)
5357 struct mod_chanmode *new_modes;
5358 char modes[MODELEN];
5362 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5364 reply("CSMSG_NO_ACCESS");
5367 if(argv[1][0] == '*' && argv[1][1] == 0)
5369 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5371 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5373 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5376 else if(new_modes->argc > 1)
5378 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5379 mod_chanmode_free(new_modes);
5384 channel->channel_info->modes = *new_modes;
5385 modcmd_chanmode_announce(new_modes);
5386 mod_chanmode_free(new_modes);
5390 mod_chanmode_format(&channel->channel_info->modes, modes);
5392 reply("CSMSG_SET_MODES", modes);
5394 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5398 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5400 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5402 struct chanData *cData = channel->channel_info;
5407 /* Set flag according to value. */
5408 if(enabled_string(argv[1]))
5410 cData->flags |= mask;
5413 else if(disabled_string(argv[1]))
5415 cData->flags &= ~mask;
5420 reply("MSG_INVALID_BINARY", argv[1]);
5426 /* Find current option value. */
5427 value = (cData->flags & mask) ? 1 : 0;
5431 reply(name, user_find_message(user, "MSG_ON"));
5433 reply(name, user_find_message(user, "MSG_OFF"));
5437 static MODCMD_FUNC(chan_opt_nodelete)
5439 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5441 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5445 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5448 static MODCMD_FUNC(chan_opt_dynlimit)
5450 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5453 static MODCMD_FUNC(chan_opt_offchannel)
5455 struct chanData *cData = channel->channel_info;
5460 /* Set flag according to value. */
5461 if(enabled_string(argv[1]))
5463 if(!IsOffChannel(cData))
5464 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5465 cData->flags |= CHANNEL_OFFCHANNEL;
5468 else if(disabled_string(argv[1]))
5470 if(IsOffChannel(cData))
5472 struct mod_chanmode change;
5473 mod_chanmode_init(&change);
5475 change.args[0].mode = MODE_CHANOP;
5476 change.args[0].u.member = AddChannelUser(chanserv, channel);
5477 mod_chanmode_announce(chanserv, channel, &change);
5479 cData->flags &= ~CHANNEL_OFFCHANNEL;
5484 reply("MSG_INVALID_BINARY", argv[1]);
5490 /* Find current option value. */
5491 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5495 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5497 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5501 static MODCMD_FUNC(chan_opt_unreviewed)
5503 struct chanData *cData = channel->channel_info;
5504 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5510 /* The two directions can have different ACLs. */
5511 if(enabled_string(argv[1]))
5513 else if(disabled_string(argv[1]))
5517 reply("MSG_INVALID_BINARY", argv[1]);
5521 if (new_value != value)
5523 struct svccmd *subcmd;
5524 char subcmd_name[32];
5526 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5527 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5530 reply("MSG_COMMAND_DISABLED", subcmd_name);
5533 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5537 cData->flags |= CHANNEL_UNREVIEWED;
5540 free(cData->registrar);
5541 cData->registrar = strdup(user->handle_info->handle);
5542 cData->flags &= ~CHANNEL_UNREVIEWED;
5549 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5551 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5555 static MODCMD_FUNC(chan_opt_defaults)
5557 struct userData *uData;
5558 struct chanData *cData;
5559 const char *confirm;
5560 enum levelOption lvlOpt;
5561 enum charOption chOpt;
5563 cData = channel->channel_info;
5564 uData = GetChannelUser(cData, user->handle_info);
5565 if(!uData || (uData->access < UL_OWNER))
5567 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5570 confirm = make_confirmation_string(uData);
5571 if((argc < 2) || strcmp(argv[1], confirm))
5573 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5576 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5577 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5578 cData->modes = chanserv_conf.default_modes;
5579 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5580 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5581 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5582 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5583 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5588 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5590 struct chanData *cData = channel->channel_info;
5591 struct userData *uData;
5592 unsigned short value;
5596 if(!check_user_level(channel, user, option, 1, 1))
5598 reply("CSMSG_CANNOT_SET");
5601 value = user_level_from_name(argv[1], UL_OWNER+1);
5602 if(!value && strcmp(argv[1], "0"))
5604 reply("CSMSG_INVALID_ACCESS", argv[1]);
5607 uData = GetChannelUser(cData, user->handle_info);
5608 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5610 reply("CSMSG_BAD_SETLEVEL");
5616 if(value > cData->lvlOpts[lvlGiveOps])
5618 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5623 if(value < cData->lvlOpts[lvlGiveVoice])
5625 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5630 /* This test only applies to owners, since non-owners
5631 * trying to set an option to above their level get caught
5632 * by the CSMSG_BAD_SETLEVEL test above.
5634 if(value > uData->access)
5636 reply("CSMSG_BAD_SETTERS");
5643 cData->lvlOpts[option] = value;
5645 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5649 static MODCMD_FUNC(chan_opt_enfops)
5651 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5654 static MODCMD_FUNC(chan_opt_giveops)
5656 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5659 static MODCMD_FUNC(chan_opt_enfmodes)
5661 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5664 static MODCMD_FUNC(chan_opt_enftopic)
5666 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5669 static MODCMD_FUNC(chan_opt_pubcmd)
5671 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5674 static MODCMD_FUNC(chan_opt_setters)
5676 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5679 static MODCMD_FUNC(chan_opt_ctcpusers)
5681 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5684 static MODCMD_FUNC(chan_opt_userinfo)
5686 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5689 static MODCMD_FUNC(chan_opt_givevoice)
5691 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5694 static MODCMD_FUNC(chan_opt_topicsnarf)
5696 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5699 static MODCMD_FUNC(chan_opt_inviteme)
5701 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5705 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5707 struct chanData *cData = channel->channel_info;
5708 int count = charOptions[option].count, index;
5712 index = atoi(argv[1]);
5714 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5716 reply("CSMSG_INVALID_NUMERIC", index);
5717 /* Show possible values. */
5718 for(index = 0; index < count; index++)
5719 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5723 cData->chOpts[option] = charOptions[option].values[index].value;
5727 /* Find current option value. */
5730 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5734 /* Somehow, the option value is corrupt; reset it to the default. */
5735 cData->chOpts[option] = charOptions[option].default_value;
5740 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5744 static MODCMD_FUNC(chan_opt_protect)
5746 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5749 static MODCMD_FUNC(chan_opt_toys)
5751 return channel_multiple_option(chToys, CSFUNC_ARGS);
5754 static MODCMD_FUNC(chan_opt_ctcpreaction)
5756 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5759 static MODCMD_FUNC(chan_opt_topicrefresh)
5761 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5764 static struct svccmd_list set_shows_list;
5767 handle_svccmd_unbind(struct svccmd *target) {
5769 for(ii=0; ii<set_shows_list.used; ++ii)
5770 if(target == set_shows_list.list[ii])
5771 set_shows_list.used = 0;
5774 static CHANSERV_FUNC(cmd_set)
5776 struct svccmd *subcmd;
5780 /* Check if we need to (re-)initialize set_shows_list. */
5781 if(!set_shows_list.used)
5783 if(!set_shows_list.size)
5785 set_shows_list.size = chanserv_conf.set_shows->used;
5786 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5788 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5790 const char *name = chanserv_conf.set_shows->list[ii];
5791 sprintf(buf, "%s %s", argv[0], name);
5792 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5795 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5798 svccmd_list_append(&set_shows_list, subcmd);
5804 reply("CSMSG_CHANNEL_OPTIONS");
5805 for(ii = 0; ii < set_shows_list.used; ii++)
5807 subcmd = set_shows_list.list[ii];
5808 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5813 sprintf(buf, "%s %s", argv[0], argv[1]);
5814 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5817 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5820 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5822 reply("CSMSG_NO_ACCESS");
5828 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5832 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5834 struct userData *uData;
5836 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5839 reply("CSMSG_NOT_USER", channel->name);
5845 /* Just show current option value. */
5847 else if(enabled_string(argv[1]))
5849 uData->flags |= mask;
5851 else if(disabled_string(argv[1]))
5853 uData->flags &= ~mask;
5857 reply("MSG_INVALID_BINARY", argv[1]);
5861 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5865 static MODCMD_FUNC(user_opt_noautoop)
5867 struct userData *uData;
5869 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5872 reply("CSMSG_NOT_USER", channel->name);
5875 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5876 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5878 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5881 static MODCMD_FUNC(user_opt_autoinvite)
5883 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5886 static MODCMD_FUNC(user_opt_info)
5888 struct userData *uData;
5891 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5895 /* If they got past the command restrictions (which require access)
5896 * but fail this test, we have some fool with security override on.
5898 reply("CSMSG_NOT_USER", channel->name);
5905 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5906 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5908 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5911 bp = strcspn(infoline, "\001");
5914 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5919 if(infoline[0] == '*' && infoline[1] == 0)
5922 uData->info = strdup(infoline);
5925 reply("CSMSG_USET_INFO", uData->info);
5927 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5931 struct svccmd_list uset_shows_list;
5933 static CHANSERV_FUNC(cmd_uset)
5935 struct svccmd *subcmd;
5939 /* Check if we need to (re-)initialize uset_shows_list. */
5940 if(!uset_shows_list.used)
5944 "NoAutoOp", "AutoInvite", "Info"
5947 if(!uset_shows_list.size)
5949 uset_shows_list.size = ArrayLength(options);
5950 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5952 for(ii = 0; ii < ArrayLength(options); ii++)
5954 const char *name = options[ii];
5955 sprintf(buf, "%s %s", argv[0], name);
5956 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5959 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5962 svccmd_list_append(&uset_shows_list, subcmd);
5968 /* Do this so options are presented in a consistent order. */
5969 reply("CSMSG_USER_OPTIONS");
5970 for(ii = 0; ii < uset_shows_list.used; ii++)
5971 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5975 sprintf(buf, "%s %s", argv[0], argv[1]);
5976 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5979 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5983 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5986 static CHANSERV_FUNC(cmd_giveownership)
5988 struct handle_info *new_owner_hi;
5989 struct userData *new_owner;
5990 struct userData *curr_user;
5991 struct userData *invoker;
5992 struct chanData *cData = channel->channel_info;
5993 struct do_not_register *dnr;
5994 const char *confirm;
5996 unsigned short co_access;
5997 char reason[MAXLEN];
6000 curr_user = GetChannelAccess(cData, user->handle_info);
6001 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6002 if(!curr_user || (curr_user->access != UL_OWNER))
6004 struct userData *owner = NULL;
6005 for(curr_user = channel->channel_info->users;
6007 curr_user = curr_user->next)
6009 if(curr_user->access != UL_OWNER)
6013 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6020 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6022 char delay[INTERVALLEN];
6023 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6024 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6027 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6029 if(new_owner_hi == user->handle_info)
6031 reply("CSMSG_NO_TRANSFER_SELF");
6034 new_owner = GetChannelAccess(cData, new_owner_hi);
6039 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6043 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6047 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6049 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6052 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6053 if(!IsHelping(user))
6054 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6056 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6059 invoker = GetChannelUser(cData, user->handle_info);
6060 if(invoker->access <= UL_OWNER)
6062 confirm = make_confirmation_string(curr_user);
6063 if((argc < 3) || strcmp(argv[2], confirm))
6065 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6069 if(new_owner->access >= UL_COOWNER)
6070 co_access = new_owner->access;
6072 co_access = UL_COOWNER;
6073 new_owner->access = UL_OWNER;
6075 curr_user->access = co_access;
6076 cData->ownerTransfer = now;
6077 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6078 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6079 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6083 static CHANSERV_FUNC(cmd_suspend)
6085 struct handle_info *hi;
6086 struct userData *self, *real_self, *target;
6087 unsigned int override = 0;
6090 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6091 self = GetChannelUser(channel->channel_info, user->handle_info);
6092 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6093 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6095 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6098 if(target->access >= self->access)
6100 reply("MSG_USER_OUTRANKED", hi->handle);
6103 if(target->flags & USER_SUSPENDED)
6105 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6110 target->present = 0;
6113 if(!real_self || target->access >= real_self->access)
6114 override = CMD_LOG_OVERRIDE;
6115 target->flags |= USER_SUSPENDED;
6116 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6117 return 1 | override;
6120 static CHANSERV_FUNC(cmd_unsuspend)
6122 struct handle_info *hi;
6123 struct userData *self, *real_self, *target;
6124 unsigned int override = 0;
6127 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6128 self = GetChannelUser(channel->channel_info, user->handle_info);
6129 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6130 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6132 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6135 if(target->access >= self->access)
6137 reply("MSG_USER_OUTRANKED", hi->handle);
6140 if(!(target->flags & USER_SUSPENDED))
6142 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6145 if(!real_self || target->access >= real_self->access)
6146 override = CMD_LOG_OVERRIDE;
6147 target->flags &= ~USER_SUSPENDED;
6148 scan_user_presence(target, NULL);
6149 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6150 return 1 | override;
6153 static MODCMD_FUNC(cmd_deleteme)
6155 struct handle_info *hi;
6156 struct userData *target;
6157 const char *confirm_string;
6158 unsigned short access;
6161 hi = user->handle_info;
6162 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6164 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6167 if(target->access == UL_OWNER)
6169 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6172 confirm_string = make_confirmation_string(target);
6173 if((argc < 2) || strcmp(argv[1], confirm_string))
6175 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6178 access = target->access;
6179 channel_name = strdup(channel->name);
6180 del_channel_user(target, 1);
6181 reply("CSMSG_DELETED_YOU", access, channel_name);
6187 chanserv_refresh_topics(UNUSED_ARG(void *data))
6189 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6190 struct chanData *cData;
6193 for(cData = channelList; cData; cData = cData->next)
6195 if(IsSuspended(cData))
6197 opt = cData->chOpts[chTopicRefresh];
6200 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6203 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6204 cData->last_refresh = refresh_num;
6206 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6209 static CHANSERV_FUNC(cmd_unf)
6213 char response[MAXLEN];
6214 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6215 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6216 irc_privmsg(cmd->parent->bot, channel->name, response);
6219 reply("CSMSG_UNF_RESPONSE");
6223 static CHANSERV_FUNC(cmd_ping)
6227 char response[MAXLEN];
6228 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6229 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6230 irc_privmsg(cmd->parent->bot, channel->name, response);
6233 reply("CSMSG_PING_RESPONSE");
6237 static CHANSERV_FUNC(cmd_wut)
6241 char response[MAXLEN];
6242 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6243 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6244 irc_privmsg(cmd->parent->bot, channel->name, response);
6247 reply("CSMSG_WUT_RESPONSE");
6251 static CHANSERV_FUNC(cmd_8ball)
6253 unsigned int i, j, accum;
6258 for(i=1; i<argc; i++)
6259 for(j=0; argv[i][j]; j++)
6260 accum = (accum << 5) - accum + toupper(argv[i][j]);
6261 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6264 char response[MAXLEN];
6265 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6266 irc_privmsg(cmd->parent->bot, channel->name, response);
6269 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6273 static CHANSERV_FUNC(cmd_d)
6275 unsigned long sides, count, modifier, ii, total;
6276 char response[MAXLEN], *sep;
6280 if((count = strtoul(argv[1], &sep, 10)) < 1)
6290 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6291 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6295 else if((sep[0] == '-') && isdigit(sep[1]))
6296 modifier = strtoul(sep, NULL, 10);
6297 else if((sep[0] == '+') && isdigit(sep[1]))
6298 modifier = strtoul(sep+1, NULL, 10);
6305 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6310 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6313 for(total = ii = 0; ii < count; ++ii)
6314 total += (rand() % sides) + 1;
6317 if((count > 1) || modifier)
6319 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6320 sprintf(response, fmt, total, count, sides, modifier);
6324 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6325 sprintf(response, fmt, total, sides);
6328 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6330 send_message_type(4, user, cmd->parent->bot, "%s", response);
6334 static CHANSERV_FUNC(cmd_huggle)
6336 /* CTCP must be via PRIVMSG, never notice */
6338 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6340 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6345 chanserv_adjust_limit(void *data)
6347 struct mod_chanmode change;
6348 struct chanData *cData = data;
6349 struct chanNode *channel = cData->channel;
6352 if(IsSuspended(cData))
6355 cData->limitAdjusted = now;
6356 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6357 if(cData->modes.modes_set & MODE_LIMIT)
6359 if(limit > cData->modes.new_limit)
6360 limit = cData->modes.new_limit;
6361 else if(limit == cData->modes.new_limit)
6365 mod_chanmode_init(&change);
6366 change.modes_set = MODE_LIMIT;
6367 change.new_limit = limit;
6368 mod_chanmode_announce(chanserv, channel, &change);
6372 handle_new_channel(struct chanNode *channel)
6374 struct chanData *cData;
6376 if(!(cData = channel->channel_info))
6379 if(cData->modes.modes_set || cData->modes.modes_clear)
6380 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6382 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6383 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6386 /* Welcome to my worst nightmare. Warning: Read (or modify)
6387 the code below at your own risk. */
6389 handle_join(struct modeNode *mNode)
6391 struct mod_chanmode change;
6392 struct userNode *user = mNode->user;
6393 struct chanNode *channel = mNode->channel;
6394 struct chanData *cData;
6395 struct userData *uData = NULL;
6396 struct banData *bData;
6397 struct handle_info *handle;
6398 unsigned int modes = 0, info = 0;
6401 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6404 cData = channel->channel_info;
6405 if(channel->members.used > cData->max)
6406 cData->max = channel->members.used;
6408 /* Check for bans. If they're joining through a ban, one of two
6410 * 1: Join during a netburst, by riding the break. Kick them
6411 * unless they have ops or voice in the channel.
6412 * 2: They're allowed to join through the ban (an invite in
6413 * ircu2.10, or a +e on Hybrid, or something).
6414 * If they're not joining through a ban, and the banlist is not
6415 * full, see if they're on the banlist for the channel. If so,
6418 if(user->uplink->burst && !mNode->modes)
6421 for(ii = 0; ii < channel->banlist.used; ii++)
6423 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6425 /* Riding a netburst. Naughty. */
6426 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6432 mod_chanmode_init(&change);
6434 if(channel->banlist.used < MAXBANS)
6436 /* Not joining through a ban. */
6437 for(bData = cData->bans;
6438 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6439 bData = bData->next);
6443 char kick_reason[MAXLEN];
6444 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6446 bData->triggered = now;
6447 if(bData != cData->bans)
6449 /* Shuffle the ban to the head of the list. */
6451 bData->next->prev = bData->prev;
6453 bData->prev->next = bData->next;
6456 bData->next = cData->bans;
6459 cData->bans->prev = bData;
6460 cData->bans = bData;
6463 change.args[0].mode = MODE_BAN;
6464 change.args[0].u.hostmask = bData->mask;
6465 mod_chanmode_announce(chanserv, channel, &change);
6466 KickChannelUser(user, channel, chanserv, kick_reason);
6471 /* ChanServ will not modify the limits in join-flooded channels.
6472 It will also skip DynLimit processing when the user (or srvx)
6473 is bursting in, because there are likely more incoming. */
6474 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6475 && !user->uplink->burst
6476 && !channel->join_flooded
6477 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6479 /* The user count has begun "bumping" into the channel limit,
6480 so set a timer to raise the limit a bit. Any previous
6481 timers are removed so three incoming users within the delay
6482 results in one limit change, not three. */
6484 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6485 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6488 if(channel->join_flooded)
6490 /* don't automatically give ops or voice during a join flood */
6492 else if(cData->lvlOpts[lvlGiveOps] == 0)
6493 modes |= MODE_CHANOP;
6494 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6495 modes |= MODE_VOICE;
6497 greeting = cData->greeting;
6498 if(user->handle_info)
6500 handle = user->handle_info;
6502 if(IsHelper(user) && !IsHelping(user))
6505 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6507 if(channel == chanserv_conf.support_channels.list[ii])
6509 HANDLE_SET_FLAG(user->handle_info, HELPING);
6515 uData = GetTrueChannelAccess(cData, handle);
6516 if(uData && !IsUserSuspended(uData))
6518 /* Ops and above were handled by the above case. */
6519 if(IsUserAutoOp(uData))
6521 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6522 modes |= MODE_CHANOP;
6523 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6524 modes |= MODE_VOICE;
6526 if(uData->access >= UL_PRESENT)
6527 cData->visited = now;
6528 if(cData->user_greeting)
6529 greeting = cData->user_greeting;
6531 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6532 && ((now - uData->seen) >= chanserv_conf.info_delay)
6540 /* If user joining normally (not during burst), apply op or voice,
6541 * and send greeting/userinfo as appropriate.
6543 if(!user->uplink->burst)
6547 if(modes & MODE_CHANOP)
6548 modes &= ~MODE_VOICE;
6549 change.args[0].mode = modes;
6550 change.args[0].u.member = mNode;
6551 mod_chanmode_announce(chanserv, channel, &change);
6554 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6556 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6562 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6564 struct mod_chanmode change;
6565 struct userData *channel;
6566 unsigned int ii, jj;
6568 if(!user->handle_info)
6571 mod_chanmode_init(&change);
6573 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6575 struct chanNode *cn;
6576 struct modeNode *mn;
6577 if(IsUserSuspended(channel)
6578 || IsSuspended(channel->channel)
6579 || !(cn = channel->channel->channel))
6582 mn = GetUserMode(cn, user);
6585 if(!IsUserSuspended(channel)
6586 && IsUserAutoInvite(channel)
6587 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6589 && !user->uplink->burst)
6590 irc_invite(chanserv, user, cn);
6594 if(channel->access >= UL_PRESENT)
6595 channel->channel->visited = now;
6597 if(IsUserAutoOp(channel))
6599 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6600 change.args[0].mode = MODE_CHANOP;
6601 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6602 change.args[0].mode = MODE_VOICE;
6604 change.args[0].mode = 0;
6605 change.args[0].u.member = mn;
6606 if(change.args[0].mode)
6607 mod_chanmode_announce(chanserv, cn, &change);
6610 channel->seen = now;
6611 channel->present = 1;
6614 for(ii = 0; ii < user->channels.used; ++ii)
6616 struct chanNode *channel = user->channels.list[ii]->channel;
6617 struct banData *ban;
6619 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6620 || !channel->channel_info
6621 || IsSuspended(channel->channel_info))
6623 for(jj = 0; jj < channel->banlist.used; ++jj)
6624 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6626 if(jj < channel->banlist.used)
6628 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6630 char kick_reason[MAXLEN];
6631 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6633 change.args[0].mode = MODE_BAN;
6634 change.args[0].u.hostmask = ban->mask;
6635 mod_chanmode_announce(chanserv, channel, &change);
6636 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6637 KickChannelUser(user, channel, chanserv, kick_reason);
6638 ban->triggered = now;
6643 if(IsSupportHelper(user))
6645 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6647 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6649 HANDLE_SET_FLAG(user->handle_info, HELPING);
6657 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6659 struct chanData *cData;
6660 struct userData *uData;
6662 cData = mn->channel->channel_info;
6663 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6666 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6668 /* Allow for a bit of padding so that the limit doesn't
6669 track the user count exactly, which could get annoying. */
6670 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6672 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6673 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6677 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6679 scan_user_presence(uData, mn->user);
6681 if (uData->access >= UL_PRESENT)
6682 cData->visited = now;
6685 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6687 unsigned int ii, jj;
6688 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6690 for(jj = 0; jj < mn->user->channels.used; ++jj)
6691 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6693 if(jj < mn->user->channels.used)
6696 if(ii == chanserv_conf.support_channels.used)
6697 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6702 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6704 struct userData *uData;
6706 if(!channel->channel_info || !kicker || IsService(kicker)
6707 || (kicker == victim) || IsSuspended(channel->channel_info)
6708 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6711 if(protect_user(victim, kicker, channel->channel_info))
6713 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6714 KickChannelUser(kicker, channel, chanserv, reason);
6717 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6722 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6724 struct chanData *cData;
6726 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6729 cData = channel->channel_info;
6730 if(bad_topic(channel, user, channel->topic))
6732 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6733 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6734 SetChannelTopic(channel, chanserv, old_topic, 1);
6735 else if(cData->topic)
6736 SetChannelTopic(channel, chanserv, cData->topic, 1);
6739 /* With topicsnarf, grab the topic and save it as the default topic. */
6740 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6743 cData->topic = strdup(channel->topic);
6749 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6751 struct mod_chanmode *bounce = NULL;
6752 unsigned int bnc, ii;
6755 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6758 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6759 && mode_lock_violated(&channel->channel_info->modes, change))
6761 char correct[MAXLEN];
6762 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6763 mod_chanmode_format(&channel->channel_info->modes, correct);
6764 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6766 for(ii = bnc = 0; ii < change->argc; ++ii)
6768 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6770 const struct userNode *victim = change->args[ii].u.member->user;
6771 if(!protect_user(victim, user, channel->channel_info))
6774 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6777 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6778 bounce->args[bnc].u.member = GetUserMode(channel, user);
6779 if(bounce->args[bnc].u.member)
6783 bounce->args[bnc].mode = MODE_CHANOP;
6784 bounce->args[bnc].u.member = change->args[ii].u.member;
6786 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6788 else if(change->args[ii].mode & MODE_CHANOP)
6790 const struct userNode *victim = change->args[ii].u.member->user;
6791 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6794 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6795 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6796 bounce->args[bnc].u.member = change->args[ii].u.member;
6799 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6801 const char *ban = change->args[ii].u.hostmask;
6802 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6805 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6806 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6807 bounce->args[bnc].u.hostmask = strdup(ban);
6809 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6814 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6815 mod_chanmode_announce(chanserv, channel, bounce);
6816 for(ii = 0; ii < change->argc; ++ii)
6817 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6818 free((char*)bounce->args[ii].u.hostmask);
6819 mod_chanmode_free(bounce);
6824 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6826 struct chanNode *channel;
6827 struct banData *bData;
6828 struct mod_chanmode change;
6829 unsigned int ii, jj;
6830 char kick_reason[MAXLEN];
6832 mod_chanmode_init(&change);
6834 change.args[0].mode = MODE_BAN;
6835 for(ii = 0; ii < user->channels.used; ++ii)
6837 channel = user->channels.list[ii]->channel;
6838 /* Need not check for bans if they're opped or voiced. */
6839 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6841 /* Need not check for bans unless channel registration is active. */
6842 if(!channel->channel_info || IsSuspended(channel->channel_info))
6844 /* Look for a matching ban already on the channel. */
6845 for(jj = 0; jj < channel->banlist.used; ++jj)
6846 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6848 /* Need not act if we found one. */
6849 if(jj < channel->banlist.used)
6851 /* Look for a matching ban in this channel. */
6852 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6854 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6856 change.args[0].u.hostmask = bData->mask;
6857 mod_chanmode_announce(chanserv, channel, &change);
6858 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6859 KickChannelUser(user, channel, chanserv, kick_reason);
6860 bData->triggered = now;
6861 break; /* we don't need to check any more bans in the channel */
6866 static void handle_rename(struct handle_info *handle, const char *old_handle)
6868 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6872 dict_remove2(handle_dnrs, old_handle, 1);
6873 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6874 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6879 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6881 struct userNode *h_user;
6883 if(handle->channels)
6885 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6886 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6888 while(handle->channels)
6889 del_channel_user(handle->channels, 1);
6894 handle_server_link(UNUSED_ARG(struct server *server))
6896 struct chanData *cData;
6898 for(cData = channelList; cData; cData = cData->next)
6900 if(!IsSuspended(cData))
6901 cData->may_opchan = 1;
6902 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6903 && !cData->channel->join_flooded
6904 && ((cData->channel->limit - cData->channel->members.used)
6905 < chanserv_conf.adjust_threshold))
6907 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6908 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6914 chanserv_conf_read(void)
6918 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6919 struct mod_chanmode *change;
6920 struct string_list *strlist;
6921 struct chanNode *chan;
6924 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6926 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6929 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6930 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6931 chanserv_conf.support_channels.used = 0;
6932 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6934 for(ii = 0; ii < strlist->used; ++ii)
6936 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6939 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6941 channelList_append(&chanserv_conf.support_channels, chan);
6944 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6947 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6950 chan = AddChannel(str, now, str2, NULL);
6952 channelList_append(&chanserv_conf.support_channels, chan);
6954 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6955 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6956 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6957 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6958 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6959 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6960 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6961 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6962 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6963 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6964 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6965 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6966 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6967 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6968 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6969 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6970 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6971 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6972 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6973 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6974 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6975 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6976 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6977 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6978 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6980 NickChange(chanserv, str, 0);
6981 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6982 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6983 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6984 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6985 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6986 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6987 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6988 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6989 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6990 chanserv_conf.max_owned = str ? atoi(str) : 5;
6991 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6992 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6993 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6994 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6995 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6996 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6997 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7000 safestrncpy(mode_line, str, sizeof(mode_line));
7001 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7002 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7003 && (change->argc < 2))
7005 chanserv_conf.default_modes = *change;
7006 mod_chanmode_free(change);
7008 free_string_list(chanserv_conf.set_shows);
7009 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7011 strlist = string_list_copy(strlist);
7014 static const char *list[] = {
7015 /* free form text */
7016 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7017 /* options based on user level */
7018 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7019 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7020 /* multiple choice options */
7021 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7022 /* binary options */
7023 "DynLimit", "NoDelete",
7028 strlist = alloc_string_list(ArrayLength(list)-1);
7029 for(ii=0; list[ii]; ii++)
7030 string_list_append(strlist, strdup(list[ii]));
7032 chanserv_conf.set_shows = strlist;
7033 /* We don't look things up now, in case the list refers to options
7034 * defined by modules initialized after this point. Just mark the
7035 * function list as invalid, so it will be initialized.
7037 set_shows_list.used = 0;
7038 free_string_list(chanserv_conf.eightball);
7039 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7042 strlist = string_list_copy(strlist);
7046 strlist = alloc_string_list(4);
7047 string_list_append(strlist, strdup("Yes."));
7048 string_list_append(strlist, strdup("No."));
7049 string_list_append(strlist, strdup("Maybe so."));
7051 chanserv_conf.eightball = strlist;
7052 free_string_list(chanserv_conf.old_ban_names);
7053 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7055 strlist = string_list_copy(strlist);
7057 strlist = alloc_string_list(2);
7058 chanserv_conf.old_ban_names = strlist;
7059 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7060 off_channel = str ? atoi(str) : 0;
7064 chanserv_note_type_read(const char *key, struct record_data *rd)
7067 struct note_type *ntype;
7070 if(!(obj = GET_RECORD_OBJECT(rd)))
7072 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7075 if(!(ntype = chanserv_create_note_type(key)))
7077 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7081 /* Figure out set access */
7082 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7084 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7085 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7087 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7089 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7090 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7092 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7094 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7098 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7099 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7100 ntype->set_access.min_opserv = 0;
7103 /* Figure out visibility */
7104 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7105 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7106 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7107 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7108 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7109 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7110 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7111 ntype->visible_type = NOTE_VIS_ALL;
7113 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7115 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7116 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7120 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7122 struct handle_info *handle;
7123 struct userData *uData;
7124 char *seen, *inf, *flags;
7125 unsigned long last_seen;
7126 unsigned short access;
7128 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7130 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7134 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7135 if(access > UL_OWNER)
7137 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7141 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7142 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7143 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7144 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7145 handle = get_handle_info(key);
7148 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7152 uData = add_channel_user(chan, handle, access, last_seen, inf);
7153 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7157 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7159 struct banData *bData;
7160 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7161 unsigned long set_time, triggered_time, expires_time;
7163 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7165 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7169 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7170 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7171 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7172 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7173 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7174 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7175 if (!reason || !owner)
7178 set_time = set ? strtoul(set, NULL, 0) : now;
7179 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7181 expires_time = strtoul(s_expires, NULL, 0);
7183 expires_time = set_time + atoi(s_duration);
7187 if(!reason || (expires_time && (expires_time < now)))
7190 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7193 static struct suspended *
7194 chanserv_read_suspended(dict_t obj)
7196 struct suspended *suspended = calloc(1, sizeof(*suspended));
7200 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7201 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7202 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7203 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7204 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7205 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7206 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7207 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7208 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7209 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7214 chanserv_channel_read(const char *key, struct record_data *hir)
7216 struct suspended *suspended;
7217 struct mod_chanmode *modes;
7218 struct chanNode *cNode;
7219 struct chanData *cData;
7220 struct dict *channel, *obj;
7221 char *str, *argv[10];
7225 channel = hir->d.object;
7227 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7230 cNode = AddChannel(key, now, NULL, NULL);
7233 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7236 cData = register_channel(cNode, str);
7239 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7243 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7245 enum levelOption lvlOpt;
7246 enum charOption chOpt;
7248 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7249 cData->flags = atoi(str);
7251 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7253 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7255 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7256 else if(levelOptions[lvlOpt].old_flag)
7258 if(cData->flags & levelOptions[lvlOpt].old_flag)
7259 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7261 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7265 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7267 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7269 cData->chOpts[chOpt] = str[0];
7272 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7274 enum levelOption lvlOpt;
7275 enum charOption chOpt;
7278 cData->flags = base64toint(str, 5);
7279 count = strlen(str += 5);
7280 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7283 if(levelOptions[lvlOpt].old_flag)
7285 if(cData->flags & levelOptions[lvlOpt].old_flag)
7286 lvl = levelOptions[lvlOpt].flag_value;
7288 lvl = levelOptions[lvlOpt].default_value;
7290 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7292 case 'c': lvl = UL_COOWNER; break;
7293 case 'm': lvl = UL_MASTER; break;
7294 case 'n': lvl = UL_OWNER+1; break;
7295 case 'o': lvl = UL_OP; break;
7296 case 'p': lvl = UL_PEON; break;
7297 case 'w': lvl = UL_OWNER; break;
7298 default: lvl = 0; break;
7300 cData->lvlOpts[lvlOpt] = lvl;
7302 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7303 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7306 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7308 suspended = chanserv_read_suspended(obj);
7309 cData->suspended = suspended;
7310 suspended->cData = cData;
7311 /* We could use suspended->expires and suspended->revoked to
7312 * set the CHANNEL_SUSPENDED flag, but we don't. */
7314 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7316 suspended = calloc(1, sizeof(*suspended));
7317 suspended->issued = 0;
7318 suspended->revoked = 0;
7319 suspended->suspender = strdup(str);
7320 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7321 suspended->expires = str ? atoi(str) : 0;
7322 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7323 suspended->reason = strdup(str ? str : "No reason");
7324 suspended->previous = NULL;
7325 cData->suspended = suspended;
7326 suspended->cData = cData;
7330 cData->flags &= ~CHANNEL_SUSPENDED;
7331 suspended = NULL; /* to squelch a warning */
7334 if(IsSuspended(cData)) {
7335 if(suspended->expires > now)
7336 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7337 else if(suspended->expires)
7338 cData->flags &= ~CHANNEL_SUSPENDED;
7341 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7342 struct mod_chanmode change;
7343 mod_chanmode_init(&change);
7345 change.args[0].mode = MODE_CHANOP;
7346 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7347 mod_chanmode_announce(chanserv, cNode, &change);
7350 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7351 cData->registered = str ? strtoul(str, NULL, 0) : now;
7352 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7353 cData->visited = str ? strtoul(str, NULL, 0) : now;
7354 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7355 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7356 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7357 cData->max = str ? atoi(str) : 0;
7358 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7359 cData->greeting = str ? strdup(str) : NULL;
7360 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7361 cData->user_greeting = str ? strdup(str) : NULL;
7362 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7363 cData->topic_mask = str ? strdup(str) : NULL;
7364 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7365 cData->topic = str ? strdup(str) : NULL;
7367 if(!IsSuspended(cData)
7368 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7369 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7370 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7371 cData->modes = *modes;
7373 cData->modes.modes_set |= MODE_REGISTERED;
7374 if(cData->modes.argc > 1)
7375 cData->modes.argc = 1;
7376 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7377 mod_chanmode_free(modes);
7380 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7381 for(it = dict_first(obj); it; it = iter_next(it))
7382 user_read_helper(iter_key(it), iter_data(it), cData);
7384 if(!cData->users && !IsProtected(cData))
7386 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7387 unregister_channel(cData, "has empty user list.");
7391 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7392 for(it = dict_first(obj); it; it = iter_next(it))
7393 ban_read_helper(iter_key(it), iter_data(it), cData);
7395 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7396 for(it = dict_first(obj); it; it = iter_next(it))
7398 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7399 struct record_data *rd = iter_data(it);
7400 const char *note, *setter;
7402 if(rd->type != RECDB_OBJECT)
7404 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7408 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7410 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7412 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7416 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7417 if(!setter) setter = "<unknown>";
7418 chanserv_add_channel_note(cData, ntype, setter, note);
7426 chanserv_dnr_read(const char *key, struct record_data *hir)
7428 const char *setter, *reason, *str;
7429 struct do_not_register *dnr;
7430 unsigned long expiry;
7432 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7435 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7438 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7441 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7444 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7445 expiry = str ? strtoul(str, NULL, 0) : 0;
7446 if(expiry && expiry <= now)
7448 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7451 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7453 dnr->set = atoi(str);
7459 chanserv_saxdb_read(struct dict *database)
7461 struct dict *section;
7464 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7465 for(it = dict_first(section); it; it = iter_next(it))
7466 chanserv_note_type_read(iter_key(it), iter_data(it));
7468 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7469 for(it = dict_first(section); it; it = iter_next(it))
7470 chanserv_channel_read(iter_key(it), iter_data(it));
7472 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7473 for(it = dict_first(section); it; it = iter_next(it))
7474 chanserv_dnr_read(iter_key(it), iter_data(it));
7480 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7482 int high_present = 0;
7483 saxdb_start_record(ctx, KEY_USERS, 1);
7484 for(; uData; uData = uData->next)
7486 if((uData->access >= UL_PRESENT) && uData->present)
7488 saxdb_start_record(ctx, uData->handle->handle, 0);
7489 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7490 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7492 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7494 saxdb_write_string(ctx, KEY_INFO, uData->info);
7495 saxdb_end_record(ctx);
7497 saxdb_end_record(ctx);
7498 return high_present;
7502 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7506 saxdb_start_record(ctx, KEY_BANS, 1);
7507 for(; bData; bData = bData->next)
7509 saxdb_start_record(ctx, bData->mask, 0);
7510 saxdb_write_int(ctx, KEY_SET, bData->set);
7511 if(bData->triggered)
7512 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7514 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7516 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7518 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7519 saxdb_end_record(ctx);
7521 saxdb_end_record(ctx);
7525 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7527 saxdb_start_record(ctx, name, 0);
7528 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7529 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7531 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7533 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7535 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7537 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7538 saxdb_end_record(ctx);
7542 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7546 enum levelOption lvlOpt;
7547 enum charOption chOpt;
7549 saxdb_start_record(ctx, channel->channel->name, 1);
7551 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7552 saxdb_write_int(ctx, KEY_MAX, channel->max);
7554 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7555 if(channel->registrar)
7556 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7557 if(channel->greeting)
7558 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7559 if(channel->user_greeting)
7560 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7561 if(channel->topic_mask)
7562 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7563 if(channel->suspended)
7564 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7566 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7567 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7568 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7569 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7570 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7572 buf[0] = channel->chOpts[chOpt];
7574 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7576 saxdb_end_record(ctx);
7578 if(channel->modes.modes_set || channel->modes.modes_clear)
7580 mod_chanmode_format(&channel->modes, buf);
7581 saxdb_write_string(ctx, KEY_MODES, buf);
7584 high_present = chanserv_write_users(ctx, channel->users);
7585 chanserv_write_bans(ctx, channel->bans);
7587 if(dict_size(channel->notes))
7591 saxdb_start_record(ctx, KEY_NOTES, 1);
7592 for(it = dict_first(channel->notes); it; it = iter_next(it))
7594 struct note *note = iter_data(it);
7595 saxdb_start_record(ctx, iter_key(it), 0);
7596 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7597 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7598 saxdb_end_record(ctx);
7600 saxdb_end_record(ctx);
7603 if(channel->ownerTransfer)
7604 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7605 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7606 saxdb_end_record(ctx);
7610 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7614 saxdb_start_record(ctx, ntype->name, 0);
7615 switch(ntype->set_access_type)
7617 case NOTE_SET_CHANNEL_ACCESS:
7618 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7620 case NOTE_SET_CHANNEL_SETTER:
7621 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7623 case NOTE_SET_PRIVILEGED: default:
7624 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7627 switch(ntype->visible_type)
7629 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7630 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7631 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7633 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7634 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7635 saxdb_end_record(ctx);
7639 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7641 struct do_not_register *dnr;
7642 dict_iterator_t it, next;
7644 for(it = dict_first(dnrs); it; it = next)
7646 next = iter_next(it);
7647 dnr = iter_data(it);
7648 if(dnr->expires && dnr->expires <= now)
7650 dict_remove(dnrs, iter_key(it));
7653 saxdb_start_record(ctx, dnr->chan_name, 0);
7655 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7657 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7658 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7659 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7660 saxdb_end_record(ctx);
7665 chanserv_saxdb_write(struct saxdb_context *ctx)
7668 struct chanData *channel;
7671 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7672 for(it = dict_first(note_types); it; it = iter_next(it))
7673 chanserv_write_note_type(ctx, iter_data(it));
7674 saxdb_end_record(ctx);
7677 saxdb_start_record(ctx, KEY_DNR, 1);
7678 write_dnrs_helper(ctx, handle_dnrs);
7679 write_dnrs_helper(ctx, plain_dnrs);
7680 write_dnrs_helper(ctx, mask_dnrs);
7681 saxdb_end_record(ctx);
7684 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7685 for(channel = channelList; channel; channel = channel->next)
7686 chanserv_write_channel(ctx, channel);
7687 saxdb_end_record(ctx);
7693 chanserv_db_cleanup(void) {
7695 unreg_part_func(handle_part);
7697 unregister_channel(channelList, "terminating.");
7698 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7699 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7700 free(chanserv_conf.support_channels.list);
7701 dict_delete(handle_dnrs);
7702 dict_delete(plain_dnrs);
7703 dict_delete(mask_dnrs);
7704 dict_delete(note_types);
7705 free_string_list(chanserv_conf.eightball);
7706 free_string_list(chanserv_conf.old_ban_names);
7707 free_string_list(chanserv_conf.set_shows);
7708 free(set_shows_list.list);
7709 free(uset_shows_list.list);
7712 struct userData *helper = helperList;
7713 helperList = helperList->next;
7718 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7719 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7720 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7723 init_chanserv(const char *nick)
7725 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7726 conf_register_reload(chanserv_conf_read);
7730 reg_server_link_func(handle_server_link);
7731 reg_new_channel_func(handle_new_channel);
7732 reg_join_func(handle_join);
7733 reg_part_func(handle_part);
7734 reg_kick_func(handle_kick);
7735 reg_topic_func(handle_topic);
7736 reg_mode_change_func(handle_mode);
7737 reg_nick_change_func(handle_nick_change);
7738 reg_auth_func(handle_auth);
7741 reg_handle_rename_func(handle_rename);
7742 reg_unreg_func(handle_unreg);
7744 handle_dnrs = dict_new();
7745 dict_set_free_data(handle_dnrs, free);
7746 plain_dnrs = dict_new();
7747 dict_set_free_data(plain_dnrs, free);
7748 mask_dnrs = dict_new();
7749 dict_set_free_data(mask_dnrs, free);
7751 reg_svccmd_unbind_func(handle_svccmd_unbind);
7752 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7753 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7754 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7755 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7756 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7757 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7758 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7759 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7760 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7761 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7762 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7763 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7764 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7766 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7767 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7769 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7770 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7771 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7772 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7773 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7775 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7776 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7777 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7778 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7779 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7781 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7782 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7783 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7784 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7786 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7787 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7788 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7789 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7790 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7791 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7792 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7793 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7795 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7796 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7797 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7798 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7799 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7800 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7801 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7802 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7803 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7804 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7805 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7806 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7807 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7808 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7810 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7811 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7812 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7813 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7814 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7816 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7817 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7819 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7820 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7821 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7822 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7823 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7824 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7825 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7826 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7827 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7828 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7829 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7831 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7832 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7834 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7835 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7836 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7837 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7839 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7840 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7841 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7842 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7843 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7845 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7846 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7847 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7848 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7849 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7850 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7852 /* Channel options */
7853 DEFINE_CHANNEL_OPTION(defaulttopic);
7854 DEFINE_CHANNEL_OPTION(topicmask);
7855 DEFINE_CHANNEL_OPTION(greeting);
7856 DEFINE_CHANNEL_OPTION(usergreeting);
7857 DEFINE_CHANNEL_OPTION(modes);
7858 DEFINE_CHANNEL_OPTION(enfops);
7859 DEFINE_CHANNEL_OPTION(giveops);
7860 DEFINE_CHANNEL_OPTION(protect);
7861 DEFINE_CHANNEL_OPTION(enfmodes);
7862 DEFINE_CHANNEL_OPTION(enftopic);
7863 DEFINE_CHANNEL_OPTION(pubcmd);
7864 DEFINE_CHANNEL_OPTION(givevoice);
7865 DEFINE_CHANNEL_OPTION(userinfo);
7866 DEFINE_CHANNEL_OPTION(dynlimit);
7867 DEFINE_CHANNEL_OPTION(topicsnarf);
7868 DEFINE_CHANNEL_OPTION(nodelete);
7869 DEFINE_CHANNEL_OPTION(toys);
7870 DEFINE_CHANNEL_OPTION(setters);
7871 DEFINE_CHANNEL_OPTION(topicrefresh);
7872 DEFINE_CHANNEL_OPTION(ctcpusers);
7873 DEFINE_CHANNEL_OPTION(ctcpreaction);
7874 DEFINE_CHANNEL_OPTION(inviteme);
7875 DEFINE_CHANNEL_OPTION(unreviewed);
7876 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7877 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7879 DEFINE_CHANNEL_OPTION(offchannel);
7880 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7882 /* Alias set topic to set defaulttopic for compatibility. */
7883 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7886 DEFINE_USER_OPTION(noautoop);
7887 DEFINE_USER_OPTION(autoinvite);
7888 DEFINE_USER_OPTION(info);
7890 /* Alias uset autovoice to uset autoop. */
7891 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7893 note_types = dict_new();
7894 dict_set_free_data(note_types, chanserv_deref_note_type);
7897 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7898 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7899 service_register(chanserv)->trigger = '!';
7900 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7902 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7904 if(chanserv_conf.channel_expire_frequency)
7905 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7907 if(chanserv_conf.dnr_expire_frequency)
7908 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7910 if(chanserv_conf.refresh_period)
7912 unsigned long next_refresh;
7913 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7914 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7917 reg_exit_func(chanserv_db_cleanup);
7918 message_register_table(msgtab);