1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 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_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_MAX_SWITCH_LOAD "max_switch_load"
46 #define KEY_SWITCH_TIMEOUT "switch_timeout"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
104 #define KEY_LEVEL "level"
105 #define KEY_INFO "info"
106 #define KEY_SEEN "seen"
109 #define KEY_OWNER "owner"
110 #define KEY_REASON "reason"
111 #define KEY_SET "set"
112 #define KEY_DURATION "duration"
113 #define KEY_EXPIRES "expires"
114 #define KEY_TRIGGERED "triggered"
116 #define CHANNEL_DEFAULT_FLAGS (0)
117 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
119 /* Administrative messages */
120 static const struct message_entry msgtab[] = {
121 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
123 /* Channel registration */
124 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
125 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
126 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
127 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
128 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
129 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
131 /* Do-not-register channels */
132 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
133 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
134 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
135 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
136 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
137 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
138 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
139 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
140 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
141 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
142 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
144 /* Channel unregistration */
145 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
146 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
147 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
148 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
151 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
152 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
154 /* Channel merging */
155 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
156 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
157 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
158 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
159 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
161 /* Handle unregistration */
162 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
165 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
166 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
167 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
168 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
169 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
170 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
171 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
172 { "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." },
173 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
174 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
175 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
176 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
177 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
179 /* Removing yourself from a channel. */
180 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
181 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
182 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
184 /* User management */
185 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
186 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
187 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
188 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
189 { "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." },
190 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
191 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
192 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
194 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
195 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
196 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
197 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
198 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
201 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
202 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
203 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
204 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
205 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
206 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
207 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
208 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
209 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
210 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
211 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
212 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
213 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
214 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
215 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
216 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
218 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
220 /* Channel management */
221 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
222 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
223 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
225 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
226 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
227 { "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" },
228 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
229 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
230 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
231 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
233 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
234 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
235 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
236 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
237 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
238 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
239 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
240 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
241 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
242 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
243 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
244 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
245 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
246 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
247 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
248 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
249 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
250 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
251 { "CSMSG_SET_MODES", "$bModes $b %s" },
252 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
253 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
254 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
255 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
256 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
257 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
258 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
259 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
260 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
261 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
262 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
263 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
264 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
265 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
266 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
267 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
268 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
269 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
270 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
271 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
272 { "CSMSG_USET_INFO", "$bInfo $b %s" },
274 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
275 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
276 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
277 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
278 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
279 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
280 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
281 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
282 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
283 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
284 { "CSMSG_PROTECT_NONE", "No users will be protected." },
285 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
286 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
287 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
288 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
289 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
290 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
291 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
292 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
293 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
294 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
295 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
296 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
298 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
299 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
300 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
301 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
302 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
303 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
305 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
306 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
307 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
309 /* Channel userlist */
310 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
311 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
312 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
313 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
315 /* Channel note list */
316 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
317 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
318 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
319 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
320 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
321 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
322 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
323 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
324 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
325 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
326 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
327 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
328 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
329 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
330 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
332 /* Channel [un]suspension */
333 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
334 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
335 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
336 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
337 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
338 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
339 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
341 /* Access information */
342 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
343 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
344 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
345 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
346 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
347 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
348 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
349 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
350 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
351 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
352 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
354 /* Seen information */
355 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
356 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
357 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
358 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
360 /* Names information */
361 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
362 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
364 /* Channel information */
365 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
366 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
367 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
368 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
369 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
370 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
371 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
372 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
373 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
374 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
375 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
376 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
377 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
378 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
379 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
380 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
381 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
384 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
385 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
387 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
388 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
389 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
390 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
391 { "CSMSG_PEEK_OPS", "$bOps:$b" },
392 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
394 /* Network information */
395 { "CSMSG_NETWORK_INFO", "Network Information:" },
396 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
397 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
398 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
399 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
400 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
401 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
402 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
405 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
406 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
407 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
409 /* Channel searches */
410 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
411 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
412 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
413 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
415 /* Channel configuration */
416 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
417 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
418 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
421 { "CSMSG_USER_OPTIONS", "User Options:" },
422 { "CSMSG_USER_PROTECTED", "That user is protected." },
425 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
426 { "CSMSG_PING_RESPONSE", "Pong!" },
427 { "CSMSG_WUT_RESPONSE", "wut" },
428 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
429 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
430 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
431 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
432 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
433 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
434 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
437 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
441 /* eject_user and unban_user flags */
442 #define ACTION_KICK 0x0001
443 #define ACTION_BAN 0x0002
444 #define ACTION_ADD_BAN 0x0004
445 #define ACTION_ADD_TIMED_BAN 0x0008
446 #define ACTION_UNBAN 0x0010
447 #define ACTION_DEL_BAN 0x0020
449 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
450 #define MODELEN 40 + KEYLEN
454 #define CSFUNC_ARGS user, channel, argc, argv, cmd
456 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
457 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
458 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
459 reply("MSG_MISSING_PARAMS", argv[0]); \
463 DECLARE_LIST(dnrList, struct do_not_register *);
464 DEFINE_LIST(dnrList, struct do_not_register *);
466 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
468 struct userNode *chanserv;
470 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
471 static struct log_type *CS_LOG;
475 struct channelList support_channels;
476 struct mod_chanmode default_modes;
478 unsigned long db_backup_frequency;
479 unsigned long channel_expire_frequency;
482 unsigned int adjust_delay;
483 long channel_expire_delay;
484 unsigned int nodelete_level;
486 unsigned int adjust_threshold;
487 int join_flood_threshold;
489 unsigned int greeting_length;
490 unsigned int refresh_period;
492 unsigned int max_owned;
493 unsigned int max_chan_users;
494 unsigned int max_chan_bans;
496 struct string_list *set_shows;
497 struct string_list *eightball;
498 struct string_list *old_ban_names;
500 const char *ctcp_short_ban_duration;
501 const char *ctcp_long_ban_duration;
503 const char *irc_operator_epithet;
504 const char *network_helper_epithet;
505 const char *support_helper_epithet;
510 struct userNode *user;
511 struct userNode *bot;
512 struct chanNode *channel;
514 unsigned short lowest;
515 unsigned short highest;
516 struct userData **users;
517 struct helpfile_table table;
520 enum note_access_type
522 NOTE_SET_CHANNEL_ACCESS,
523 NOTE_SET_CHANNEL_SETTER,
527 enum note_visible_type
530 NOTE_VIS_CHANNEL_USERS,
536 enum note_access_type set_access_type;
538 unsigned int min_opserv;
539 unsigned short min_ulevel;
541 enum note_visible_type visible_type;
542 unsigned int max_length;
549 struct note_type *type;
550 char setter[NICKSERV_HANDLE_LEN+1];
554 static unsigned int registered_channels;
555 static unsigned int banCount;
557 static const struct {
560 unsigned short level;
563 { "peon", "Peon", UL_PEON, '+' },
564 { "op", "Op", UL_OP, '@' },
565 { "master", "Master", UL_MASTER, '%' },
566 { "coowner", "Coowner", UL_COOWNER, '*' },
567 { "owner", "Owner", UL_OWNER, '!' },
568 { "helper", "BUG:", UL_HELPER, 'X' }
571 static const struct {
574 unsigned short default_value;
575 unsigned int old_idx;
576 unsigned int old_flag;
577 unsigned short flag_value;
579 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
580 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
581 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
582 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
583 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
584 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
585 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
586 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
587 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
588 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
589 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
592 struct charOptionValues {
595 } protectValues[] = {
596 { 'a', "CSMSG_PROTECT_ALL" },
597 { 'e', "CSMSG_PROTECT_EQUAL" },
598 { 'l', "CSMSG_PROTECT_LOWER" },
599 { 'n', "CSMSG_PROTECT_NONE" }
601 { 'd', "CSMSG_TOYS_DISABLED" },
602 { 'n', "CSMSG_TOYS_PRIVATE" },
603 { 'p', "CSMSG_TOYS_PUBLIC" }
604 }, topicRefreshValues[] = {
605 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
606 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
607 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
608 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
609 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
610 }, ctcpReactionValues[] = {
611 { 'k', "CSMSG_CTCPREACTION_KICK" },
612 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
613 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
614 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
617 static const struct {
621 unsigned int old_idx;
623 struct charOptionValues *values;
625 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
626 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
627 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
628 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
631 struct userData *helperList;
632 struct chanData *channelList;
633 static struct module *chanserv_module;
634 static unsigned int userCount;
636 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
637 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
638 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
641 user_level_from_name(const char *name, unsigned short clamp_level)
643 unsigned int level = 0, ii;
646 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
647 if(!irccasecmp(name, accessLevels[ii].name))
648 level = accessLevels[ii].level;
649 if(level > clamp_level)
655 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
658 *minl = strtoul(arg, &sep, 10);
666 *maxl = strtoul(sep+1, &sep, 10);
674 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
676 struct userData *uData, **head;
678 if(!channel || !handle)
681 if(override && HANDLE_FLAGGED(handle, HELPING)
682 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
684 for(uData = helperList;
685 uData && uData->handle != handle;
686 uData = uData->next);
690 uData = calloc(1, sizeof(struct userData));
691 uData->handle = handle;
693 uData->access = UL_HELPER;
699 uData->next = helperList;
701 helperList->prev = uData;
709 for(uData = channel->users; uData; uData = uData->next)
710 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
713 head = &(channel->users);
716 if(uData && (uData != *head))
718 /* Shuffle the user to the head of whatever list he was in. */
720 uData->next->prev = uData->prev;
722 uData->prev->next = uData->next;
728 (**head).prev = uData;
735 /* Returns non-zero if user has at least the minimum access.
736 * exempt_owner is set when handling !set, so the owner can set things
739 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
741 struct userData *uData;
742 struct chanData *cData = channel->channel_info;
743 unsigned short minimum = cData->lvlOpts[opt];
746 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
749 if(minimum <= uData->access)
751 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
756 /* Scan for other users authenticated to the same handle
757 still in the channel. If so, keep them listed as present.
759 user is optional, if not null, it skips checking that userNode
760 (for the handle_part function) */
762 scan_user_presence(struct userData *uData, struct userNode *user)
766 if(IsSuspended(uData->channel)
767 || IsUserSuspended(uData)
768 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
780 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
782 unsigned int eflags, argc;
784 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
786 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
787 if(!channel->channel_info
788 || IsSuspended(channel->channel_info)
790 || !ircncasecmp(text, "ACTION ", 7))
792 /* Figure out the minimum level needed to CTCP the channel */
793 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
795 /* We need to enforce against them; do so. */
798 argv[1] = user->nick;
800 if(GetUserMode(channel, user))
801 eflags |= ACTION_KICK;
802 switch(channel->channel_info->chOpts[chCTCPReaction]) {
803 default: case 'k': /* just do the kick */ break;
805 eflags |= ACTION_BAN;
808 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
809 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
812 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
813 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
816 argv[argc++] = bad_ctcp_reason;
817 eject_user(chanserv, channel, argc, argv, NULL, eflags);
821 chanserv_create_note_type(const char *name)
823 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
824 strcpy(ntype->name, name);
826 dict_insert(note_types, ntype->name, ntype);
831 chanserv_deref_note_type(void *data)
833 struct note_type *ntype = data;
835 if(--ntype->refs > 0)
841 chanserv_flush_note_type(struct note_type *ntype)
843 struct chanData *cData;
844 for(cData = channelList; cData; cData = cData->next)
845 dict_remove(cData->notes, ntype->name);
849 chanserv_truncate_notes(struct note_type *ntype)
851 struct chanData *cData;
853 unsigned int size = sizeof(*note) + ntype->max_length;
855 for(cData = channelList; cData; cData = cData->next) {
856 note = dict_find(cData->notes, ntype->name, NULL);
859 if(strlen(note->note) <= ntype->max_length)
861 dict_remove2(cData->notes, ntype->name, 1);
862 note = realloc(note, size);
863 note->note[ntype->max_length] = 0;
864 dict_insert(cData->notes, ntype->name, note);
868 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
871 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
874 unsigned int len = strlen(text);
876 if(len > type->max_length) len = type->max_length;
877 note = calloc(1, sizeof(*note) + len);
879 strncpy(note->setter, setter, sizeof(note->setter)-1);
880 memcpy(note->note, text, len);
882 dict_insert(channel->notes, type->name, note);
888 chanserv_free_note(void *data)
890 struct note *note = data;
892 chanserv_deref_note_type(note->type);
893 assert(note->type->refs > 0); /* must use delnote to remove the type */
897 static MODCMD_FUNC(cmd_createnote) {
898 struct note_type *ntype;
899 unsigned int arg = 1, existed = 0, max_length;
901 if((ntype = dict_find(note_types, argv[1], NULL)))
904 ntype = chanserv_create_note_type(argv[arg]);
905 if(!irccasecmp(argv[++arg], "privileged"))
908 ntype->set_access_type = NOTE_SET_PRIVILEGED;
909 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
911 else if(!irccasecmp(argv[arg], "channel"))
913 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
916 reply("CSMSG_INVALID_ACCESS", argv[arg]);
919 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
920 ntype->set_access.min_ulevel = ulvl;
922 else if(!irccasecmp(argv[arg], "setter"))
924 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
928 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
932 if(!irccasecmp(argv[++arg], "privileged"))
933 ntype->visible_type = NOTE_VIS_PRIVILEGED;
934 else if(!irccasecmp(argv[arg], "channel_users"))
935 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
936 else if(!irccasecmp(argv[arg], "all"))
937 ntype->visible_type = NOTE_VIS_ALL;
939 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
943 if((arg+1) >= argc) {
944 reply("MSG_MISSING_PARAMS", argv[0]);
947 max_length = strtoul(argv[++arg], NULL, 0);
948 if(max_length < 20 || max_length > 450)
950 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
953 if(existed && (max_length < ntype->max_length))
955 ntype->max_length = max_length;
956 chanserv_truncate_notes(ntype);
958 ntype->max_length = max_length;
961 reply("CSMSG_NOTE_MODIFIED", ntype->name);
963 reply("CSMSG_NOTE_CREATED", ntype->name);
968 dict_remove(note_types, ntype->name);
972 static MODCMD_FUNC(cmd_removenote) {
973 struct note_type *ntype;
976 ntype = dict_find(note_types, argv[1], NULL);
977 force = (argc > 2) && !irccasecmp(argv[2], "force");
980 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
987 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
990 chanserv_flush_note_type(ntype);
992 dict_remove(note_types, argv[1]);
993 reply("CSMSG_NOTE_DELETED", argv[1]);
998 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1002 if(orig->modes_set & change->modes_clear)
1004 if(orig->modes_clear & change->modes_set)
1006 if((orig->modes_set & MODE_KEY)
1007 && strcmp(orig->new_key, change->new_key))
1009 if((orig->modes_set & MODE_LIMIT)
1010 && (orig->new_limit != change->new_limit))
1015 static char max_length_text[MAXLEN+1][16];
1017 static struct helpfile_expansion
1018 chanserv_expand_variable(const char *variable)
1020 struct helpfile_expansion exp;
1022 if(!irccasecmp(variable, "notes"))
1025 exp.type = HF_TABLE;
1026 exp.value.table.length = 1;
1027 exp.value.table.width = 3;
1028 exp.value.table.flags = 0;
1029 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1030 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1031 exp.value.table.contents[0][0] = "Note Type";
1032 exp.value.table.contents[0][1] = "Visibility";
1033 exp.value.table.contents[0][2] = "Max Length";
1034 for(it=dict_first(note_types); it; it=iter_next(it))
1036 struct note_type *ntype = iter_data(it);
1039 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1040 row = exp.value.table.length++;
1041 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1042 exp.value.table.contents[row][0] = ntype->name;
1043 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1044 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1046 if(!max_length_text[ntype->max_length][0])
1047 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1048 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1053 exp.type = HF_STRING;
1054 exp.value.str = NULL;
1058 static struct chanData*
1059 register_channel(struct chanNode *cNode, char *registrar)
1061 struct chanData *channel;
1062 enum levelOption lvlOpt;
1063 enum charOption chOpt;
1065 channel = calloc(1, sizeof(struct chanData));
1067 channel->notes = dict_new();
1068 dict_set_free_data(channel->notes, chanserv_free_note);
1070 channel->registrar = strdup(registrar);
1071 channel->registered = now;
1072 channel->visited = now;
1073 channel->limitAdjusted = now;
1074 channel->flags = CHANNEL_DEFAULT_FLAGS;
1075 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1076 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1077 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1078 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1080 channel->prev = NULL;
1081 channel->next = channelList;
1084 channelList->prev = channel;
1085 channelList = channel;
1086 registered_channels++;
1088 channel->channel = cNode;
1090 cNode->channel_info = channel;
1095 static struct userData*
1096 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1098 struct userData *ud;
1100 if(access > UL_OWNER)
1103 ud = calloc(1, sizeof(*ud));
1104 ud->channel = channel;
1105 ud->handle = handle;
1107 ud->access = access;
1108 ud->info = info ? strdup(info) : NULL;
1111 ud->next = channel->users;
1113 channel->users->prev = ud;
1114 channel->users = ud;
1116 channel->userCount++;
1120 ud->u_next = ud->handle->channels;
1122 ud->u_next->u_prev = ud;
1123 ud->handle->channels = ud;
1128 static void unregister_channel(struct chanData *channel, const char *reason);
1131 del_channel_user(struct userData *user, int do_gc)
1133 struct chanData *channel = user->channel;
1135 channel->userCount--;
1139 user->prev->next = user->next;
1141 channel->users = user->next;
1143 user->next->prev = user->prev;
1146 user->u_prev->u_next = user->u_next;
1148 user->handle->channels = user->u_next;
1150 user->u_next->u_prev = user->u_prev;
1154 if(do_gc && !channel->users && !IsProtected(channel))
1155 unregister_channel(channel, "lost all users.");
1158 static void expire_ban(void *data);
1160 static struct banData*
1161 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1164 unsigned int ii, l1, l2;
1169 bd = malloc(sizeof(struct banData));
1171 bd->channel = channel;
1173 bd->triggered = triggered;
1174 bd->expires = expires;
1176 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1178 extern const char *hidden_host_suffix;
1179 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1183 l2 = strlen(old_name);
1186 if(irccasecmp(mask + l1 - l2, old_name))
1188 new_mask = alloca(MAXLEN);
1189 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1192 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1194 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1195 bd->reason = reason ? strdup(reason) : NULL;
1198 timeq_add(expires, expire_ban, bd);
1201 bd->next = channel->bans;
1203 channel->bans->prev = bd;
1205 channel->banCount++;
1212 del_channel_ban(struct banData *ban)
1214 ban->channel->banCount--;
1218 ban->prev->next = ban->next;
1220 ban->channel->bans = ban->next;
1223 ban->next->prev = ban->prev;
1226 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1235 expire_ban(void *data)
1237 struct banData *bd = data;
1238 if(!IsSuspended(bd->channel))
1240 struct banList bans;
1241 struct mod_chanmode change;
1243 bans = bd->channel->channel->banlist;
1244 mod_chanmode_init(&change);
1245 for(ii=0; ii<bans.used; ii++)
1247 if(!strcmp(bans.list[ii]->ban, bd->mask))
1250 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1251 change.args[0].hostmask = bd->mask;
1252 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1258 del_channel_ban(bd);
1261 static void chanserv_expire_suspension(void *data);
1264 unregister_channel(struct chanData *channel, const char *reason)
1266 char msgbuf[MAXLEN];
1268 /* After channel unregistration, the following must be cleaned
1270 - Channel information.
1273 - Channel suspension data.
1274 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1280 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1282 while(channel->users)
1283 del_channel_user(channel->users, 0);
1285 while(channel->bans)
1286 del_channel_ban(channel->bans);
1288 if(channel->topic) free(channel->topic);
1289 if(channel->registrar) free(channel->registrar);
1290 if(channel->greeting) free(channel->greeting);
1291 if(channel->user_greeting) free(channel->user_greeting);
1292 if(channel->topic_mask) free(channel->topic_mask);
1294 if(channel->prev) channel->prev->next = channel->next;
1295 else channelList = channel->next;
1297 if(channel->next) channel->next->prev = channel->prev;
1299 if(channel->suspended)
1301 struct chanNode *cNode = channel->channel;
1302 struct suspended *suspended, *next_suspended;
1304 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1306 next_suspended = suspended->previous;
1307 free(suspended->suspender);
1308 free(suspended->reason);
1309 if(suspended->expires)
1310 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1315 cNode->channel_info = NULL;
1317 channel->channel->channel_info = NULL;
1320 dict_delete(channel->notes);
1321 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1322 if(!IsSuspended(channel))
1323 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1324 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1325 UnlockChannel(channel->channel);
1327 registered_channels--;
1331 expire_channels(UNUSED_ARG(void *data))
1333 struct chanData *channel, *next;
1334 struct userData *user;
1335 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1337 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1338 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1340 for(channel = channelList; channel; channel = next)
1342 next = channel->next;
1344 /* See if the channel can be expired. */
1345 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1346 || IsProtected(channel))
1349 /* Make sure there are no high-ranking users still in the channel. */
1350 for(user=channel->users; user; user=user->next)
1351 if(user->present && (user->access >= UL_PRESENT))
1356 /* Unregister the channel */
1357 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1358 unregister_channel(channel, "registration expired.");
1361 if(chanserv_conf.channel_expire_frequency)
1362 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1366 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1368 char protect = channel->chOpts[chProtect];
1369 struct userData *cs_victim, *cs_aggressor;
1371 /* Don't protect if no one is to be protected, someone is attacking
1372 himself, or if the aggressor is an IRC Operator. */
1373 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1376 /* Don't protect if the victim isn't authenticated (because they
1377 can't be a channel user), unless we are to protect non-users
1379 cs_victim = GetChannelAccess(channel, victim->handle_info);
1380 if(protect != 'a' && !cs_victim)
1383 /* Protect if the aggressor isn't a user because at this point,
1384 the aggressor can only be less than or equal to the victim. */
1385 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1389 /* If the aggressor was a user, then the victim can't be helped. */
1396 if(cs_victim->access > cs_aggressor->access)
1401 if(cs_victim->access >= cs_aggressor->access)
1410 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1412 struct chanData *cData = channel->channel_info;
1413 struct userData *cs_victim;
1415 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1416 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1417 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1419 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1427 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1429 if(IsService(victim))
1431 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1435 if(protect_user(victim, user, channel->channel_info))
1437 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1444 static struct do_not_register *
1445 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1447 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1448 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1449 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1450 strcpy(dnr->reason, reason);
1452 if(dnr->chan_name[0] == '*')
1453 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1454 else if(strpbrk(dnr->chan_name, "*?"))
1455 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1457 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1461 static struct dnrList
1462 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1464 struct dnrList list;
1466 struct do_not_register *dnr;
1468 dnrList_init(&list);
1469 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1470 dnrList_append(&list, dnr);
1471 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1472 dnrList_append(&list, dnr);
1474 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1475 if(match_ircglob(chan_name, iter_key(it)))
1476 dnrList_append(&list, iter_data(it));
1481 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1483 struct dnrList list;
1484 struct do_not_register *dnr;
1486 char buf[INTERVALLEN];
1488 list = chanserv_find_dnrs(chan_name, handle);
1489 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1491 dnr = list.list[ii];
1494 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1495 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1498 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1501 reply("CSMSG_MORE_DNRS", list.used - ii);
1506 struct do_not_register *
1507 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1509 struct do_not_register *dnr;
1512 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1516 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1518 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1519 if(match_ircglob(chan_name, iter_key(it)))
1520 return iter_data(it);
1525 static CHANSERV_FUNC(cmd_noregister)
1528 struct do_not_register *dnr;
1529 char buf[INTERVALLEN];
1530 unsigned int matches;
1536 reply("CSMSG_DNR_SEARCH_RESULTS");
1538 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1540 dnr = iter_data(it);
1542 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1544 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1547 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1549 dnr = iter_data(it);
1551 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1553 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1556 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1558 dnr = iter_data(it);
1560 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1562 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 reply("MSG_MATCH_COUNT", matches);
1569 reply("MSG_NO_MATCHES");
1575 if(!IsChannelName(target) && (*target != '*'))
1577 reply("CSMSG_NOT_DNR", target);
1583 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1584 if((*target == '*') && !get_handle_info(target + 1))
1586 reply("MSG_HANDLE_UNKNOWN", target + 1);
1589 chanserv_add_dnr(target, user->handle_info->handle, reason);
1590 reply("CSMSG_NOREGISTER_CHANNEL", target);
1594 reply("CSMSG_DNR_SEARCH_RESULTS");
1596 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1598 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1600 reply("MSG_NO_MATCHES");
1604 static CHANSERV_FUNC(cmd_allowregister)
1606 const char *chan_name = argv[1];
1608 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1610 dict_remove(handle_dnrs, chan_name+1);
1611 reply("CSMSG_DNR_REMOVED", chan_name);
1613 else if(dict_find(plain_dnrs, chan_name, NULL))
1615 dict_remove(plain_dnrs, chan_name);
1616 reply("CSMSG_DNR_REMOVED", chan_name);
1618 else if(dict_find(mask_dnrs, chan_name, NULL))
1620 dict_remove(mask_dnrs, chan_name);
1621 reply("CSMSG_DNR_REMOVED", chan_name);
1625 reply("CSMSG_NO_SUCH_DNR", chan_name);
1632 chanserv_get_owned_count(struct handle_info *hi)
1634 struct userData *cList;
1637 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1638 if(cList->access == UL_OWNER)
1643 static CHANSERV_FUNC(cmd_register)
1645 struct mod_chanmode *change;
1646 struct handle_info *handle;
1647 struct chanData *cData;
1648 struct modeNode *mn;
1649 char reason[MAXLEN];
1651 unsigned int new_channel, force=0;
1652 struct do_not_register *dnr;
1656 if(channel->channel_info)
1658 reply("CSMSG_ALREADY_REGGED", channel->name);
1662 if(channel->bad_channel)
1664 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1668 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1670 reply("CSMSG_MUST_BE_OPPED", channel->name);
1675 chan_name = channel->name;
1679 if((argc < 2) || !IsChannelName(argv[1]))
1681 reply("MSG_NOT_CHANNEL_NAME");
1685 if(opserv_bad_channel(argv[1]))
1687 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1692 chan_name = argv[1];
1695 if(argc >= (new_channel+2))
1697 if(!IsHelping(user))
1699 reply("CSMSG_PROXY_FORBIDDEN");
1703 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1705 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1706 dnr = chanserv_is_dnr(chan_name, handle);
1710 handle = user->handle_info;
1711 dnr = chanserv_is_dnr(chan_name, handle);
1715 if(!IsHelping(user))
1716 reply("CSMSG_DNR_CHANNEL", chan_name);
1718 chanserv_show_dnrs(user, cmd, chan_name, handle);
1722 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1724 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1729 channel = AddChannel(argv[1], now, NULL, NULL);
1731 cData = register_channel(channel, user->handle_info->handle);
1732 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1733 cData->modes = chanserv_conf.default_modes;
1734 change = mod_chanmode_dup(&cData->modes, 1);
1735 change->args[change->argc].mode = MODE_CHANOP;
1736 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1738 mod_chanmode_announce(chanserv, channel, change);
1739 mod_chanmode_free(change);
1741 /* Initialize the channel's max user record. */
1742 cData->max = channel->members.used;
1744 if(handle != user->handle_info)
1745 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1747 reply("CSMSG_REG_SUCCESS", channel->name);
1749 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1750 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1755 make_confirmation_string(struct userData *uData)
1757 static char strbuf[16];
1762 for(src = uData->handle->handle; *src; )
1763 accum = accum * 31 + toupper(*src++);
1765 for(src = uData->channel->channel->name; *src; )
1766 accum = accum * 31 + toupper(*src++);
1767 sprintf(strbuf, "%08x", accum);
1771 static CHANSERV_FUNC(cmd_unregister)
1774 char reason[MAXLEN];
1775 struct chanData *cData;
1776 struct userData *uData;
1778 cData = channel->channel_info;
1781 reply("CSMSG_NOT_REGISTERED", channel->name);
1785 uData = GetChannelUser(cData, user->handle_info);
1786 if(!uData || (uData->access < UL_OWNER))
1788 reply("CSMSG_NO_ACCESS");
1792 if(IsProtected(cData))
1794 reply("CSMSG_UNREG_NODELETE", channel->name);
1798 if(!IsHelping(user))
1800 const char *confirm_string;
1801 if(IsSuspended(cData))
1803 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1806 confirm_string = make_confirmation_string(uData);
1807 if((argc < 2) || strcmp(argv[1], confirm_string))
1809 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1814 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1815 name = strdup(channel->name);
1816 unregister_channel(cData, reason);
1817 reply("CSMSG_UNREG_SUCCESS", name);
1822 static CHANSERV_FUNC(cmd_move)
1824 struct chanNode *target;
1825 struct modeNode *mn;
1826 struct userData *uData;
1827 char reason[MAXLEN];
1828 struct do_not_register *dnr;
1832 if(IsProtected(channel->channel_info))
1834 reply("CSMSG_MOVE_NODELETE", channel->name);
1838 if(!IsChannelName(argv[1]))
1840 reply("MSG_NOT_CHANNEL_NAME");
1844 if(opserv_bad_channel(argv[1]))
1846 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1850 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1852 for(uData = channel->channel_info->users; uData; uData = uData->next)
1854 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1856 if(!IsHelping(user))
1857 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1859 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1865 if(!(target = GetChannel(argv[1])))
1867 target = AddChannel(argv[1], now, NULL, NULL);
1868 if(!IsSuspended(channel->channel_info))
1869 AddChannelUser(chanserv, target);
1871 else if(target->channel_info)
1873 reply("CSMSG_ALREADY_REGGED", target->name);
1876 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1877 && !IsHelping(user))
1879 reply("CSMSG_MUST_BE_OPPED", target->name);
1882 else if(!IsSuspended(channel->channel_info))
1884 struct mod_chanmode change;
1885 mod_chanmode_init(&change);
1887 change.args[0].mode = MODE_CHANOP;
1888 change.args[0].member = AddChannelUser(chanserv, target);
1889 mod_chanmode_announce(chanserv, target, &change);
1892 /* Move the channel_info to the target channel; it
1893 shouldn't be necessary to clear timeq callbacks
1894 for the old channel. */
1895 target->channel_info = channel->channel_info;
1896 target->channel_info->channel = target;
1897 channel->channel_info = NULL;
1899 reply("CSMSG_MOVE_SUCCESS", target->name);
1901 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1902 if(!IsSuspended(target->channel_info))
1904 char reason2[MAXLEN];
1905 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1906 DelChannelUser(chanserv, channel, reason2, 0);
1908 UnlockChannel(channel);
1909 LockChannel(target);
1910 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1915 merge_users(struct chanData *source, struct chanData *target)
1917 struct userData *suData, *tuData, *next;
1923 /* Insert the source's users into the scratch area. */
1924 for(suData = source->users; suData; suData = suData->next)
1925 dict_insert(merge, suData->handle->handle, suData);
1927 /* Iterate through the target's users, looking for
1928 users common to both channels. The lower access is
1929 removed from either the scratch area or target user
1931 for(tuData = target->users; tuData; tuData = next)
1933 struct userData *choice;
1935 next = tuData->next;
1937 /* If a source user exists with the same handle as a target
1938 channel's user, resolve the conflict by removing one. */
1939 suData = dict_find(merge, tuData->handle->handle, NULL);
1943 /* Pick the data we want to keep. */
1944 /* If the access is the same, use the later seen time. */
1945 if(suData->access == tuData->access)
1946 choice = (suData->seen > tuData->seen) ? suData : tuData;
1947 else /* Otherwise, keep the higher access level. */
1948 choice = (suData->access > tuData->access) ? suData : tuData;
1950 /* Remove the user that wasn't picked. */
1951 if(choice == tuData)
1953 dict_remove(merge, suData->handle->handle);
1954 del_channel_user(suData, 0);
1957 del_channel_user(tuData, 0);
1960 /* Move the remaining users to the target channel. */
1961 for(it = dict_first(merge); it; it = iter_next(it))
1963 suData = iter_data(it);
1965 /* Insert the user into the target channel's linked list. */
1966 suData->prev = NULL;
1967 suData->next = target->users;
1968 suData->channel = target;
1971 target->users->prev = suData;
1972 target->users = suData;
1974 /* Update the user counts for the target channel; the
1975 source counts are left alone. */
1976 target->userCount++;
1979 /* Possible to assert (source->users == NULL) here. */
1980 source->users = NULL;
1985 merge_bans(struct chanData *source, struct chanData *target)
1987 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1989 /* Hold on to the original head of the target ban list
1990 to avoid comparing source bans with source bans. */
1991 tFront = target->bans;
1993 /* Perform a totally expensive O(n*m) merge, ick. */
1994 for(sbData = source->bans; sbData; sbData = sNext)
1996 /* Flag to track whether the ban's been moved
1997 to the destination yet. */
2000 /* Possible to assert (sbData->prev == NULL) here. */
2001 sNext = sbData->next;
2003 for(tbData = tFront; tbData; tbData = tNext)
2005 tNext = tbData->next;
2007 /* Perform two comparisons between each source
2008 and target ban, conflicts are resolved by
2009 keeping the broader ban and copying the later
2010 expiration and triggered time. */
2011 if(match_ircglobs(tbData->mask, sbData->mask))
2013 /* There is a broader ban in the target channel that
2014 overrides one in the source channel; remove the
2015 source ban and break. */
2016 if(sbData->expires > tbData->expires)
2017 tbData->expires = sbData->expires;
2018 if(sbData->triggered > tbData->triggered)
2019 tbData->triggered = sbData->triggered;
2020 del_channel_ban(sbData);
2023 else if(match_ircglobs(sbData->mask, tbData->mask))
2025 /* There is a broader ban in the source channel that
2026 overrides one in the target channel; remove the
2027 target ban, fall through and move the source over. */
2028 if(tbData->expires > sbData->expires)
2029 sbData->expires = tbData->expires;
2030 if(tbData->triggered > sbData->triggered)
2031 sbData->triggered = tbData->triggered;
2032 if(tbData == tFront)
2034 del_channel_ban(tbData);
2037 /* Source bans can override multiple target bans, so
2038 we allow a source to run through this loop multiple
2039 times, but we can only move it once. */
2044 /* Remove the source ban from the source ban list. */
2046 sbData->next->prev = sbData->prev;
2048 /* Modify the source ban's associated channel. */
2049 sbData->channel = target;
2051 /* Insert the ban into the target channel's linked list. */
2052 sbData->prev = NULL;
2053 sbData->next = target->bans;
2056 target->bans->prev = sbData;
2057 target->bans = sbData;
2059 /* Update the user counts for the target channel. */
2064 /* Possible to assert (source->bans == NULL) here. */
2065 source->bans = NULL;
2069 merge_data(struct chanData *source, struct chanData *target)
2071 if(source->visited > target->visited)
2072 target->visited = source->visited;
2076 merge_channel(struct chanData *source, struct chanData *target)
2078 merge_users(source, target);
2079 merge_bans(source, target);
2080 merge_data(source, target);
2083 static CHANSERV_FUNC(cmd_merge)
2085 struct userData *target_user;
2086 struct chanNode *target;
2087 char reason[MAXLEN];
2091 /* Make sure the target channel exists and is registered to the user
2092 performing the command. */
2093 if(!(target = GetChannel(argv[1])))
2095 reply("MSG_INVALID_CHANNEL");
2099 if(!target->channel_info)
2101 reply("CSMSG_NOT_REGISTERED", target->name);
2105 if(IsProtected(channel->channel_info))
2107 reply("CSMSG_MERGE_NODELETE");
2111 if(IsSuspended(target->channel_info))
2113 reply("CSMSG_MERGE_SUSPENDED");
2117 if(channel == target)
2119 reply("CSMSG_MERGE_SELF");
2123 target_user = GetChannelUser(target->channel_info, user->handle_info);
2124 if(!target_user || (target_user->access < UL_OWNER))
2126 reply("CSMSG_MERGE_NOT_OWNER");
2130 /* Merge the channel structures and associated data. */
2131 merge_channel(channel->channel_info, target->channel_info);
2132 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2133 unregister_channel(channel->channel_info, reason);
2134 reply("CSMSG_MERGE_SUCCESS", target->name);
2138 static CHANSERV_FUNC(cmd_opchan)
2140 struct mod_chanmode change;
2141 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2143 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2146 channel->channel_info->may_opchan = 0;
2147 mod_chanmode_init(&change);
2149 change.args[0].mode = MODE_CHANOP;
2150 change.args[0].member = GetUserMode(channel, chanserv);
2151 mod_chanmode_announce(chanserv, channel, &change);
2152 reply("CSMSG_OPCHAN_DONE", channel->name);
2156 static CHANSERV_FUNC(cmd_adduser)
2158 struct userData *actee;
2159 struct userData *actor;
2160 struct handle_info *handle;
2161 unsigned short access;
2165 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2167 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2171 access = user_level_from_name(argv[2], UL_OWNER);
2174 reply("CSMSG_INVALID_ACCESS", argv[2]);
2178 actor = GetChannelUser(channel->channel_info, user->handle_info);
2179 if(actor->access <= access)
2181 reply("CSMSG_NO_BUMP_ACCESS");
2185 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2188 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2190 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2194 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2195 scan_user_presence(actee, NULL);
2196 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2200 static CHANSERV_FUNC(cmd_clvl)
2202 struct handle_info *handle;
2203 struct userData *victim;
2204 struct userData *actor;
2205 unsigned short new_access;
2206 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2210 actor = GetChannelUser(channel->channel_info, user->handle_info);
2212 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2215 if(handle == user->handle_info && !privileged)
2217 reply("CSMSG_NO_SELF_CLVL");
2221 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2223 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2227 if(actor->access <= victim->access && !privileged)
2229 reply("MSG_USER_OUTRANKED", handle->handle);
2233 new_access = user_level_from_name(argv[2], UL_OWNER);
2237 reply("CSMSG_INVALID_ACCESS", argv[2]);
2241 if(new_access >= actor->access && !privileged)
2243 reply("CSMSG_NO_BUMP_ACCESS");
2247 victim->access = new_access;
2248 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2252 static CHANSERV_FUNC(cmd_deluser)
2254 struct handle_info *handle;
2255 struct userData *victim;
2256 struct userData *actor;
2257 unsigned short access;
2262 actor = GetChannelUser(channel->channel_info, user->handle_info);
2264 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2267 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2269 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2275 access = user_level_from_name(argv[1], UL_OWNER);
2278 reply("CSMSG_INVALID_ACCESS", argv[1]);
2281 if(access != victim->access)
2283 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2289 access = victim->access;
2292 if((actor->access <= victim->access) && !IsHelping(user))
2294 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2298 chan_name = strdup(channel->name);
2299 del_channel_user(victim, 1);
2300 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2306 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2308 struct userData *actor, *uData, *next;
2310 actor = GetChannelUser(channel->channel_info, user->handle_info);
2312 if(min_access > max_access)
2314 reply("CSMSG_BAD_RANGE", min_access, max_access);
2318 if((actor->access <= max_access) && !IsHelping(user))
2320 reply("CSMSG_NO_ACCESS");
2324 for(uData = channel->channel_info->users; uData; uData = next)
2328 if((uData->access >= min_access)
2329 && (uData->access <= max_access)
2330 && match_ircglob(uData->handle->handle, mask))
2331 del_channel_user(uData, 1);
2334 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2338 static CHANSERV_FUNC(cmd_mdelowner)
2340 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2343 static CHANSERV_FUNC(cmd_mdelcoowner)
2345 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2348 static CHANSERV_FUNC(cmd_mdelmaster)
2350 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2353 static CHANSERV_FUNC(cmd_mdelop)
2355 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2358 static CHANSERV_FUNC(cmd_mdelpeon)
2360 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2364 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2366 struct banData *bData, *next;
2367 char interval[INTERVALLEN];
2372 limit = now - duration;
2373 for(bData = channel->channel_info->bans; bData; bData = next)
2377 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2380 del_channel_ban(bData);
2384 intervalString(interval, duration, user->handle_info);
2385 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2390 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2392 struct userData *actor, *uData, *next;
2393 char interval[INTERVALLEN];
2397 actor = GetChannelUser(channel->channel_info, user->handle_info);
2398 if(min_access > max_access)
2400 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2404 if((actor->access <= max_access) && !IsHelping(user))
2406 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2411 limit = now - duration;
2412 for(uData = channel->channel_info->users; uData; uData = next)
2416 if((uData->seen > limit) || uData->present)
2419 if(((uData->access >= min_access) && (uData->access <= max_access))
2420 || (!max_access && (uData->access < actor->access)))
2422 del_channel_user(uData, 1);
2430 max_access = UL_OWNER;
2432 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2436 static CHANSERV_FUNC(cmd_trim)
2438 unsigned long duration;
2439 unsigned short min_level, max_level;
2443 duration = ParseInterval(argv[2]);
2446 reply("CSMSG_CANNOT_TRIM");
2450 if(!irccasecmp(argv[1], "bans"))
2452 cmd_trim_bans(user, channel, duration);
2455 else if(!irccasecmp(argv[1], "users"))
2457 cmd_trim_users(user, channel, 0, 0, duration);
2460 else if(parse_level_range(&min_level, &max_level, argv[1]))
2462 cmd_trim_users(user, channel, min_level, max_level, duration);
2465 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2467 cmd_trim_users(user, channel, min_level, min_level, duration);
2472 reply("CSMSG_INVALID_TRIM", argv[1]);
2477 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2478 to the user. cmd_all takes advantage of this. */
2479 static CHANSERV_FUNC(cmd_up)
2481 struct mod_chanmode change;
2482 struct userData *uData;
2485 mod_chanmode_init(&change);
2487 change.args[0].member = GetUserMode(channel, user);
2488 if(!change.args[0].member)
2491 reply("MSG_CHANNEL_ABSENT", channel->name);
2495 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2499 reply("CSMSG_GODMODE_UP", argv[0]);
2502 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2504 change.args[0].mode = MODE_CHANOP;
2505 errmsg = "CSMSG_ALREADY_OPPED";
2509 change.args[0].mode = MODE_VOICE;
2510 errmsg = "CSMSG_ALREADY_VOICED";
2512 change.args[0].mode &= ~change.args[0].member->modes;
2513 if(!change.args[0].mode)
2516 reply(errmsg, channel->name);
2519 modcmd_chanmode_announce(&change);
2523 static CHANSERV_FUNC(cmd_down)
2525 struct mod_chanmode change;
2527 mod_chanmode_init(&change);
2529 change.args[0].member = GetUserMode(channel, user);
2530 if(!change.args[0].member)
2533 reply("MSG_CHANNEL_ABSENT", channel->name);
2537 if(!change.args[0].member->modes)
2540 reply("CSMSG_ALREADY_DOWN", channel->name);
2544 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2545 modcmd_chanmode_announce(&change);
2549 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)
2551 struct userData *cList;
2553 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2555 if(IsSuspended(cList->channel)
2556 || IsUserSuspended(cList)
2557 || !GetUserMode(cList->channel->channel, user))
2560 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2566 static CHANSERV_FUNC(cmd_upall)
2568 return cmd_all(CSFUNC_ARGS, cmd_up);
2571 static CHANSERV_FUNC(cmd_downall)
2573 return cmd_all(CSFUNC_ARGS, cmd_down);
2576 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2577 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2580 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)
2582 unsigned int ii, valid;
2583 struct userNode *victim;
2584 struct mod_chanmode *change;
2586 change = mod_chanmode_alloc(argc - 1);
2588 for(ii=valid=0; ++ii < argc; )
2590 if(!(victim = GetUserH(argv[ii])))
2592 change->args[valid].mode = mode;
2593 change->args[valid].member = GetUserMode(channel, victim);
2594 if(!change->args[valid].member)
2596 if(validate && !validate(user, channel, victim))
2601 change->argc = valid;
2602 if(valid < (argc-1))
2603 reply("CSMSG_PROCESS_FAILED");
2606 modcmd_chanmode_announce(change);
2607 reply(action, channel->name);
2609 mod_chanmode_free(change);
2613 static CHANSERV_FUNC(cmd_op)
2615 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2618 static CHANSERV_FUNC(cmd_deop)
2620 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2623 static CHANSERV_FUNC(cmd_voice)
2625 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2628 static CHANSERV_FUNC(cmd_devoice)
2630 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2634 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2640 for(ii=0; ii<channel->members.used; ii++)
2642 struct modeNode *mn = channel->members.list[ii];
2644 if(IsService(mn->user))
2647 if(!user_matches_glob(mn->user, ban, 1))
2650 if(protect_user(mn->user, user, channel->channel_info))
2654 victims[(*victimCount)++] = mn;
2660 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2662 struct userNode *victim;
2663 struct modeNode **victims;
2664 unsigned int offset, n, victimCount, duration = 0;
2665 char *reason = "Bye.", *ban, *name;
2666 char interval[INTERVALLEN];
2668 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2669 REQUIRE_PARAMS(offset);
2672 reason = unsplit_string(argv + offset, argc - offset, NULL);
2673 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2675 /* Truncate the reason to a length of TOPICLEN, as
2676 the ircd does; however, leave room for an ellipsis
2677 and the kicker's nick. */
2678 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2682 if((victim = GetUserH(argv[1])))
2684 victims = alloca(sizeof(victims[0]));
2685 victims[0] = GetUserMode(channel, victim);
2686 /* XXX: The comparison with ACTION_KICK is just because all
2687 * other actions can work on users outside the channel, and we
2688 * want to allow those (e.g. unbans) in that case. If we add
2689 * some other ejection action for in-channel users, change
2691 victimCount = victims[0] ? 1 : 0;
2693 if(IsService(victim))
2695 reply("MSG_SERVICE_IMMUNE", victim->nick);
2699 if((action == ACTION_KICK) && !victimCount)
2701 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2705 if(protect_user(victim, user, channel->channel_info))
2707 reply("CSMSG_USER_PROTECTED", victim->nick);
2711 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2712 name = victim->nick;
2716 if(!is_ircmask(argv[1]))
2718 reply("MSG_NICK_UNKNOWN", argv[1]);
2722 victims = alloca(sizeof(victims[0]) * channel->members.used);
2724 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2726 reply("CSMSG_MASK_PROTECTED", argv[1]);
2730 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2732 reply("CSMSG_LAME_MASK", argv[1]);
2736 if((action == ACTION_KICK) && (victimCount == 0))
2738 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2742 name = ban = strdup(argv[1]);
2745 /* Truncate the ban in place if necessary; we must ensure
2746 that 'ban' is a valid ban mask before sanitizing it. */
2747 sanitize_ircmask(ban);
2749 if(action & ACTION_ADD_BAN)
2751 struct banData *bData, *next;
2753 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2755 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2760 if(action & ACTION_ADD_TIMED_BAN)
2762 duration = ParseInterval(argv[2]);
2766 reply("CSMSG_DURATION_TOO_LOW");
2770 else if(duration > (86400 * 365 * 2))
2772 reply("CSMSG_DURATION_TOO_HIGH");
2778 for(bData = channel->channel_info->bans; bData; bData = next)
2780 if(match_ircglobs(bData->mask, ban))
2782 int exact = !irccasecmp(bData->mask, ban);
2784 /* The ban is redundant; there is already a ban
2785 with the same effect in place. */
2789 free(bData->reason);
2790 bData->reason = strdup(reason);
2791 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2793 reply("CSMSG_REASON_CHANGE", ban);
2797 if(exact && bData->expires)
2801 /* If the ban matches an existing one exactly,
2802 extend the expiration time if the provided
2803 duration is longer. */
2804 if(duration && ((time_t)(now + duration) > bData->expires))
2806 bData->expires = now + duration;
2817 /* Delete the expiration timeq entry and
2818 requeue if necessary. */
2819 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2822 timeq_add(bData->expires, expire_ban, bData);
2826 /* automated kickban */
2829 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2831 reply("CSMSG_BAN_ADDED", name, channel->name);
2837 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2844 if(match_ircglobs(ban, bData->mask))
2846 /* The ban we are adding makes previously existing
2847 bans redundant; silently remove them. */
2848 del_channel_ban(bData);
2852 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);
2854 name = ban = strdup(bData->mask);
2858 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2860 extern const char *hidden_host_suffix;
2861 const char *old_name = chanserv_conf.old_ban_names->list[n];
2863 unsigned int l1, l2;
2866 l2 = strlen(old_name);
2869 if(irccasecmp(ban + l1 - l2, old_name))
2871 new_mask = malloc(MAXLEN);
2872 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2874 name = ban = new_mask;
2879 if(action & ACTION_BAN)
2881 unsigned int exists;
2882 struct mod_chanmode *change;
2884 if(channel->banlist.used >= MAXBANS)
2887 reply("CSMSG_BANLIST_FULL", channel->name);
2892 exists = ChannelBanExists(channel, ban);
2893 change = mod_chanmode_alloc(victimCount + 1);
2894 for(n = 0; n < victimCount; ++n)
2896 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2897 change->args[n].member = victims[n];
2901 change->args[n].mode = MODE_BAN;
2902 change->args[n++].hostmask = ban;
2906 modcmd_chanmode_announce(change);
2908 mod_chanmode_announce(chanserv, channel, change);
2909 mod_chanmode_free(change);
2911 if(exists && (action == ACTION_BAN))
2914 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2920 if(action & ACTION_KICK)
2922 char kick_reason[MAXLEN];
2923 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2925 for(n = 0; n < victimCount; n++)
2926 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2931 /* No response, since it was automated. */
2933 else if(action & ACTION_ADD_BAN)
2936 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2938 reply("CSMSG_BAN_ADDED", name, channel->name);
2940 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2941 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2942 else if(action & ACTION_BAN)
2943 reply("CSMSG_BAN_DONE", name, channel->name);
2944 else if(action & ACTION_KICK && victimCount)
2945 reply("CSMSG_KICK_DONE", name, channel->name);
2951 static CHANSERV_FUNC(cmd_kickban)
2953 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2956 static CHANSERV_FUNC(cmd_kick)
2958 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2961 static CHANSERV_FUNC(cmd_ban)
2963 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2966 static CHANSERV_FUNC(cmd_addban)
2968 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2971 static CHANSERV_FUNC(cmd_addtimedban)
2973 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2976 static struct mod_chanmode *
2977 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2979 struct mod_chanmode *change;
2980 unsigned char *match;
2981 unsigned int ii, count;
2983 match = alloca(bans->used);
2986 for(ii = count = 0; ii < bans->used; ++ii)
2988 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2995 for(ii = count = 0; ii < bans->used; ++ii)
2997 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3004 change = mod_chanmode_alloc(count);
3005 for(ii = count = 0; ii < bans->used; ++ii)
3009 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3010 change->args[count++].hostmask = bans->list[ii]->ban;
3016 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3018 struct userNode *actee;
3024 /* may want to allow a comma delimited list of users... */
3025 if(!(actee = GetUserH(argv[1])))
3027 if(!is_ircmask(argv[1]))
3029 reply("MSG_NICK_UNKNOWN", argv[1]);
3033 mask = strdup(argv[1]);
3036 /* We don't sanitize the mask here because ircu
3038 if(action & ACTION_UNBAN)
3040 struct mod_chanmode *change;
3041 change = find_matching_bans(&channel->banlist, actee, mask);
3044 modcmd_chanmode_announce(change);
3045 mod_chanmode_free(change);
3050 if(action & ACTION_DEL_BAN)
3052 struct banData *ban, *next;
3054 ban = channel->channel_info->bans;
3058 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3061 for( ; ban && !match_ircglobs(mask, ban->mask);
3066 del_channel_ban(ban);
3073 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3075 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3081 static CHANSERV_FUNC(cmd_unban)
3083 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3086 static CHANSERV_FUNC(cmd_delban)
3088 /* it doesn't necessarily have to remove the channel ban - may want
3089 to make that an option. */
3090 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3093 static CHANSERV_FUNC(cmd_unbanme)
3095 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3096 long flags = ACTION_UNBAN;
3098 /* remove permanent bans if the user has the proper access. */
3099 if(uData->access >= UL_MASTER)
3100 flags |= ACTION_DEL_BAN;
3102 argv[1] = user->nick;
3103 return unban_user(user, channel, 2, argv, cmd, flags);
3106 static CHANSERV_FUNC(cmd_unbanall)
3108 struct mod_chanmode *change;
3111 if(!channel->banlist.used)
3113 reply("CSMSG_NO_BANS", channel->name);
3117 change = mod_chanmode_alloc(channel->banlist.used);
3118 for(ii=0; ii<channel->banlist.used; ii++)
3120 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3121 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3123 modcmd_chanmode_announce(change);
3124 mod_chanmode_free(change);
3125 reply("CSMSG_BANS_REMOVED", channel->name);
3129 static CHANSERV_FUNC(cmd_open)
3131 struct mod_chanmode *change;
3133 change = find_matching_bans(&channel->banlist, user, NULL);
3135 change = mod_chanmode_alloc(0);
3136 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3137 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3138 && channel->channel_info->modes.modes_set)
3139 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3140 modcmd_chanmode_announce(change);
3141 reply("CSMSG_CHANNEL_OPENED", channel->name);
3142 mod_chanmode_free(change);
3146 static CHANSERV_FUNC(cmd_myaccess)
3148 struct handle_info *target_handle;
3149 struct userData *uData;
3150 const char *chanName;
3153 target_handle = user->handle_info;
3154 else if(!IsHelping(user))
3156 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3159 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3162 if(!target_handle->channels)
3164 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3168 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3169 for(uData = target_handle->channels; uData; uData = uData->u_next)
3171 struct chanData *cData = uData->channel;
3173 if(uData->access > UL_OWNER)
3175 if(IsProtected(cData)
3176 && (target_handle != user->handle_info)
3177 && !GetTrueChannelAccess(cData, user->handle_info))
3179 chanName = cData->channel->name;
3181 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3183 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3189 static CHANSERV_FUNC(cmd_access)
3191 struct userNode *target;
3192 struct handle_info *target_handle;
3193 struct userData *uData;
3195 char prefix[MAXLEN];
3200 target_handle = target->handle_info;
3202 else if((target = GetUserH(argv[1])))
3204 target_handle = target->handle_info;
3206 else if(argv[1][0] == '*')
3208 if(!(target_handle = get_handle_info(argv[1]+1)))
3210 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3216 reply("MSG_NICK_UNKNOWN", argv[1]);
3220 assert(target || target_handle);
3222 if(target == chanserv)
3224 reply("CSMSG_IS_CHANSERV");
3232 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3237 reply("MSG_USER_AUTHENTICATE", target->nick);
3240 reply("MSG_AUTHENTICATE");
3246 const char *epithet = NULL, *type = NULL;
3249 epithet = chanserv_conf.irc_operator_epithet;
3252 else if(IsNetworkHelper(target))
3254 epithet = chanserv_conf.network_helper_epithet;
3255 type = "network helper";
3257 else if(IsSupportHelper(target))
3259 epithet = chanserv_conf.support_helper_epithet;
3260 type = "support helper";
3264 if(target_handle->epithet)
3265 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3267 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3269 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3273 sprintf(prefix, "%s", target_handle->handle);
3276 if(!channel->channel_info)
3278 reply("CSMSG_NOT_REGISTERED", channel->name);
3282 helping = HANDLE_FLAGGED(target_handle, HELPING)
3283 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3284 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3286 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3287 /* To prevent possible information leaks, only show infolines
3288 * if the requestor is in the channel or it's their own
3290 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3292 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3294 /* Likewise, only say it's suspended if the user has active
3295 * access in that channel or it's their own entry. */
3296 if(IsUserSuspended(uData)
3297 && (GetChannelUser(channel->channel_info, user->handle_info)
3298 || (user->handle_info == uData->handle)))
3300 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3305 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3312 zoot_list(struct listData *list)
3314 struct userData *uData;
3315 unsigned int start, curr, highest, lowest;
3316 struct helpfile_table tmp_table;
3317 const char **temp, *msg;
3319 if(list->table.length == 1)
3322 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3324 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3325 msg = user_find_message(list->user, "MSG_NONE");
3326 send_message_type(4, list->user, list->bot, " %s", msg);
3328 tmp_table.width = list->table.width;
3329 tmp_table.flags = list->table.flags;
3330 list->table.contents[0][0] = " ";
3331 highest = list->highest;
3332 if(list->lowest != 0)
3333 lowest = list->lowest;
3334 else if(highest < 100)
3337 lowest = highest - 100;
3338 for(start = curr = 1; curr < list->table.length; )
3340 uData = list->users[curr-1];
3341 list->table.contents[curr++][0] = " ";
3342 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3345 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3347 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3348 temp = list->table.contents[--start];
3349 list->table.contents[start] = list->table.contents[0];
3350 tmp_table.contents = list->table.contents + start;
3351 tmp_table.length = curr - start;
3352 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3353 list->table.contents[start] = temp;
3355 highest = lowest - 1;
3356 lowest = (highest < 100) ? 0 : (highest - 99);
3362 def_list(struct listData *list)
3366 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3368 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3369 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3370 if(list->table.length == 1)
3372 msg = user_find_message(list->user, "MSG_NONE");
3373 send_message_type(4, list->user, list->bot, " %s", msg);
3378 userData_access_comp(const void *arg_a, const void *arg_b)
3380 const struct userData *a = *(struct userData**)arg_a;
3381 const struct userData *b = *(struct userData**)arg_b;
3383 if(a->access != b->access)
3384 res = b->access - a->access;
3386 res = irccasecmp(a->handle->handle, b->handle->handle);
3391 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3393 void (*send_list)(struct listData *);
3394 struct userData *uData;
3395 struct listData lData;
3396 unsigned int matches;
3400 lData.bot = cmd->parent->bot;
3401 lData.channel = channel;
3402 lData.lowest = lowest;
3403 lData.highest = highest;
3404 lData.search = (argc > 1) ? argv[1] : NULL;
3405 send_list = zoot_list;
3407 if(user->handle_info)
3409 switch(user->handle_info->userlist_style)
3411 case HI_STYLE_DEF: send_list = def_list; break;
3412 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3416 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3418 for(uData = channel->channel_info->users; uData; uData = uData->next)
3420 if((uData->access < lowest)
3421 || (uData->access > highest)
3422 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3424 lData.users[matches++] = uData;
3426 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3428 lData.table.length = matches+1;
3429 lData.table.width = 4;
3430 lData.table.flags = TABLE_NO_FREE;
3431 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3432 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3433 lData.table.contents[0] = ary;
3436 ary[2] = "Last Seen";
3438 for(matches = 1; matches < lData.table.length; ++matches)
3440 struct userData *uData = lData.users[matches-1];
3441 char seen[INTERVALLEN];
3443 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3444 lData.table.contents[matches] = ary;
3445 ary[0] = strtab(uData->access);
3446 ary[1] = uData->handle->handle;
3449 else if(!uData->seen)
3452 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3453 ary[2] = strdup(ary[2]);
3454 if(IsUserSuspended(uData))
3455 ary[3] = "Suspended";
3456 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3457 ary[3] = "Vacation";
3462 for(matches = 1; matches < lData.table.length; ++matches)
3464 free((char*)lData.table.contents[matches][2]);
3465 free(lData.table.contents[matches]);
3467 free(lData.table.contents[0]);
3468 free(lData.table.contents);
3472 static CHANSERV_FUNC(cmd_users)
3474 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3477 static CHANSERV_FUNC(cmd_wlist)
3479 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3482 static CHANSERV_FUNC(cmd_clist)
3484 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3487 static CHANSERV_FUNC(cmd_mlist)
3489 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3492 static CHANSERV_FUNC(cmd_olist)
3494 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3497 static CHANSERV_FUNC(cmd_plist)
3499 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3502 static CHANSERV_FUNC(cmd_bans)
3504 struct helpfile_table tbl;
3505 unsigned int matches = 0, timed = 0, ii;
3506 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3507 const char *msg_never, *triggered, *expires;
3508 struct banData *ban, **bans;
3515 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3517 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3519 if(search && !match_ircglobs(search, ban->mask))
3521 bans[matches++] = ban;
3526 tbl.length = matches + 1;
3527 tbl.width = 4 + timed;
3529 tbl.flags = TABLE_NO_FREE;
3530 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3531 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3532 tbl.contents[0][0] = "Mask";
3533 tbl.contents[0][1] = "Set By";
3534 tbl.contents[0][2] = "Triggered";
3537 tbl.contents[0][3] = "Expires";
3538 tbl.contents[0][4] = "Reason";
3541 tbl.contents[0][3] = "Reason";
3544 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3546 free(tbl.contents[0]);
3551 msg_never = user_find_message(user, "MSG_NEVER");
3552 for(ii = 0; ii < matches; )
3558 else if(ban->expires)
3559 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3561 expires = msg_never;
3564 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3566 triggered = msg_never;
3568 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3569 tbl.contents[ii][0] = ban->mask;
3570 tbl.contents[ii][1] = ban->owner;
3571 tbl.contents[ii][2] = strdup(triggered);
3574 tbl.contents[ii][3] = strdup(expires);
3575 tbl.contents[ii][4] = ban->reason;
3578 tbl.contents[ii][3] = ban->reason;
3580 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3581 reply("MSG_MATCH_COUNT", matches);
3582 for(ii = 1; ii < tbl.length; ++ii)
3584 free((char*)tbl.contents[ii][2]);
3586 free((char*)tbl.contents[ii][3]);
3587 free(tbl.contents[ii]);
3589 free(tbl.contents[0]);
3595 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3597 struct chanData *cData = channel->channel_info;
3598 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3600 if(cData->topic_mask)
3601 return !match_ircglob(new_topic, cData->topic_mask);
3602 else if(cData->topic)
3603 return irccasecmp(new_topic, cData->topic);
3608 static CHANSERV_FUNC(cmd_topic)
3610 struct chanData *cData;
3613 cData = channel->channel_info;
3618 SetChannelTopic(channel, chanserv, cData->topic, 1);
3619 reply("CSMSG_TOPIC_SET", cData->topic);
3623 reply("CSMSG_NO_TOPIC", channel->name);
3627 topic = unsplit_string(argv + 1, argc - 1, NULL);
3628 /* If they say "!topic *", use an empty topic. */
3629 if((topic[0] == '*') && (topic[1] == 0))
3631 if(bad_topic(channel, user, topic))
3633 char *topic_mask = cData->topic_mask;
3636 char new_topic[TOPICLEN+1], tchar;
3637 int pos=0, starpos=-1, dpos=0, len;
3639 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3646 len = strlen(topic);
3647 if((dpos + len) > TOPICLEN)
3648 len = TOPICLEN + 1 - dpos;
3649 memcpy(new_topic+dpos, topic, len);
3653 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3654 default: new_topic[dpos++] = tchar; break;
3657 if((dpos > TOPICLEN) || tchar)
3660 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3661 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3664 new_topic[dpos] = 0;
3665 SetChannelTopic(channel, chanserv, new_topic, 1);
3667 reply("CSMSG_TOPIC_LOCKED", channel->name);
3672 SetChannelTopic(channel, chanserv, topic, 1);
3674 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3676 /* Grab the topic and save it as the default topic. */
3678 cData->topic = strdup(channel->topic);
3684 static CHANSERV_FUNC(cmd_mode)
3686 struct mod_chanmode *change;
3690 change = &channel->channel_info->modes;
3691 if(change->modes_set || change->modes_clear) {
3692 modcmd_chanmode_announce(change);
3693 reply("CSMSG_DEFAULTED_MODES", channel->name);
3695 reply("CSMSG_NO_MODES", channel->name);
3699 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3702 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3706 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3707 && mode_lock_violated(&channel->channel_info->modes, change))
3710 mod_chanmode_format(&channel->channel_info->modes, modes);
3711 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3715 modcmd_chanmode_announce(change);
3716 mod_chanmode_free(change);
3717 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3721 static CHANSERV_FUNC(cmd_invite)
3723 struct userData *uData;
3724 struct userNode *invite;
3726 uData = GetChannelUser(channel->channel_info, user->handle_info);
3730 if(!(invite = GetUserH(argv[1])))
3732 reply("MSG_NICK_UNKNOWN", argv[1]);
3739 if(GetUserMode(channel, invite))
3741 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3749 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3750 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3753 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3755 irc_invite(chanserv, invite, channel);
3757 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3762 static CHANSERV_FUNC(cmd_inviteme)
3764 if(GetUserMode(channel, user))
3766 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3769 if(channel->channel_info
3770 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3772 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3775 irc_invite(cmd->parent->bot, user, channel);
3780 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3783 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3785 /* We display things based on two dimensions:
3786 * - Issue time: present or absent
3787 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3788 * (in order of precedence, so something both expired and revoked
3789 * only counts as revoked)
3791 combo = (suspended->issued ? 4 : 0)
3792 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3794 case 0: /* no issue time, indefinite expiration */
3795 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3797 case 1: /* no issue time, expires in future */
3798 intervalString(buf1, suspended->expires-now, user->handle_info);
3799 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3801 case 2: /* no issue time, expired */
3802 intervalString(buf1, now-suspended->expires, user->handle_info);
3803 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3805 case 3: /* no issue time, revoked */
3806 intervalString(buf1, now-suspended->revoked, user->handle_info);
3807 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3809 case 4: /* issue time set, indefinite expiration */
3810 intervalString(buf1, now-suspended->issued, user->handle_info);
3811 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3813 case 5: /* issue time set, expires in future */
3814 intervalString(buf1, now-suspended->issued, user->handle_info);
3815 intervalString(buf2, suspended->expires-now, user->handle_info);
3816 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3818 case 6: /* issue time set, expired */
3819 intervalString(buf1, now-suspended->issued, user->handle_info);
3820 intervalString(buf2, now-suspended->expires, user->handle_info);
3821 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3823 case 7: /* issue time set, revoked */
3824 intervalString(buf1, now-suspended->issued, user->handle_info);
3825 intervalString(buf2, now-suspended->revoked, user->handle_info);
3826 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3829 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3834 static CHANSERV_FUNC(cmd_info)
3836 char modes[MAXLEN], buffer[INTERVALLEN];
3837 struct userData *uData, *owner;
3838 struct chanData *cData;
3839 struct do_not_register *dnr;
3844 cData = channel->channel_info;
3845 reply("CSMSG_CHANNEL_INFO", channel->name);
3847 uData = GetChannelUser(cData, user->handle_info);
3848 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3850 mod_chanmode_format(&cData->modes, modes);
3851 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3852 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3855 for(it = dict_first(cData->notes); it; it = iter_next(it))
3859 note = iter_data(it);
3860 if(!note_type_visible_to_user(cData, note->type, user))
3863 padding = PADLEN - 1 - strlen(iter_key(it));
3864 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3867 reply("CSMSG_CHANNEL_MAX", cData->max);
3868 for(owner = cData->users; owner; owner = owner->next)
3869 if(owner->access == UL_OWNER)
3870 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3871 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3872 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3873 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3874 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3876 privileged = IsStaff(user);
3877 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3878 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3880 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3881 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3883 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3885 struct suspended *suspended;
3886 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3887 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3888 show_suspension_info(cmd, user, suspended);
3890 else if(IsSuspended(cData))
3892 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3893 show_suspension_info(cmd, user, cData->suspended);
3898 static CHANSERV_FUNC(cmd_netinfo)
3900 extern time_t boot_time;
3901 extern unsigned long burst_length;
3902 char interval[INTERVALLEN];
3904 reply("CSMSG_NETWORK_INFO");
3905 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3906 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3907 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3908 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3909 reply("CSMSG_NETWORK_BANS", banCount);
3910 reply("CSMSG_CHANNEL_USERS", userCount);
3911 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3912 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3917 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3919 struct helpfile_table table;
3921 struct userNode *user;
3926 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3927 table.contents = alloca(list->used*sizeof(*table.contents));
3928 for(nn=0; nn<list->used; nn++)
3930 user = list->list[nn];
3931 if(user->modes & skip_flags)
3935 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3938 nick = alloca(strlen(user->nick)+3);
3939 sprintf(nick, "(%s)", user->nick);
3943 table.contents[table.length][0] = nick;
3946 table_send(chanserv, to->nick, 0, NULL, table);
3949 static CHANSERV_FUNC(cmd_ircops)
3951 reply("CSMSG_STAFF_OPERS");
3952 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3956 static CHANSERV_FUNC(cmd_helpers)
3958 reply("CSMSG_STAFF_HELPERS");
3959 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3963 static CHANSERV_FUNC(cmd_staff)
3965 reply("CSMSG_NETWORK_STAFF");
3966 cmd_ircops(CSFUNC_ARGS);
3967 cmd_helpers(CSFUNC_ARGS);
3971 static CHANSERV_FUNC(cmd_peek)
3973 struct modeNode *mn;
3974 char modes[MODELEN];
3976 struct helpfile_table table;
3978 irc_make_chanmode(channel, modes);
3980 reply("CSMSG_PEEK_INFO", channel->name);
3981 reply("CSMSG_PEEK_TOPIC", channel->topic);
3982 reply("CSMSG_PEEK_MODES", modes);
3983 reply("CSMSG_PEEK_USERS", channel->members.used);
3987 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3988 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3989 for(n = 0; n < channel->members.used; n++)
3991 mn = channel->members.list[n];
3992 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3994 table.contents[table.length] = alloca(sizeof(**table.contents));
3995 table.contents[table.length][0] = mn->user->nick;
4000 reply("CSMSG_PEEK_OPS");
4001 table_send(chanserv, user->nick, 0, NULL, table);
4004 reply("CSMSG_PEEK_NO_OPS");
4008 static MODCMD_FUNC(cmd_wipeinfo)
4010 struct handle_info *victim;
4011 struct userData *ud, *actor;
4014 actor = GetChannelUser(channel->channel_info, user->handle_info);
4015 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4017 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4019 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4022 if((ud->access >= actor->access) && (ud != actor))
4024 reply("MSG_USER_OUTRANKED", victim->handle);
4030 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4034 static CHANSERV_FUNC(cmd_resync)
4036 struct mod_chanmode *changes;
4037 struct chanData *cData = channel->channel_info;
4038 unsigned int ii, used;
4040 changes = mod_chanmode_alloc(channel->members.used * 2);
4041 for(ii = used = 0; ii < channel->members.used; ++ii)
4043 struct modeNode *mn = channel->members.list[ii];
4044 struct userData *uData;
4046 if(IsService(mn->user))
4049 uData = GetChannelAccess(cData, mn->user->handle_info);
4050 if(!cData->lvlOpts[lvlGiveOps]
4051 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4053 if(!(mn->modes & MODE_CHANOP))
4055 changes->args[used].mode = MODE_CHANOP;
4056 changes->args[used++].member = mn;
4059 else if(!cData->lvlOpts[lvlGiveVoice]
4060 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4062 if(mn->modes & MODE_CHANOP)
4064 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4065 changes->args[used++].member = mn;
4067 if(!(mn->modes & MODE_VOICE))
4069 changes->args[used].mode = MODE_VOICE;
4070 changes->args[used++].member = mn;
4077 changes->args[used].mode = MODE_REMOVE | mn->modes;
4078 changes->args[used++].member = mn;
4082 changes->argc = used;
4083 modcmd_chanmode_announce(changes);
4084 mod_chanmode_free(changes);
4085 reply("CSMSG_RESYNCED_USERS", channel->name);
4089 static CHANSERV_FUNC(cmd_seen)
4091 struct userData *uData;
4092 struct handle_info *handle;
4093 char seen[INTERVALLEN];
4097 if(!irccasecmp(argv[1], chanserv->nick))
4099 reply("CSMSG_IS_CHANSERV");
4103 if(!(handle = get_handle_info(argv[1])))
4105 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4109 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4111 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4116 reply("CSMSG_USER_PRESENT", handle->handle);
4117 else if(uData->seen)
4118 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4120 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4122 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4123 reply("CSMSG_USER_VACATION", handle->handle);
4128 static MODCMD_FUNC(cmd_names)
4130 struct userNode *targ;
4131 struct userData *targData;
4132 unsigned int ii, pos;
4135 for(ii=pos=0; ii<channel->members.used; ++ii)
4137 targ = channel->members.list[ii]->user;
4138 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4141 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4144 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4148 if(IsUserSuspended(targData))
4150 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4153 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4154 reply("CSMSG_END_NAMES", channel->name);
4159 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4161 switch(ntype->visible_type)
4163 case NOTE_VIS_ALL: return 1;
4164 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4165 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4170 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4172 struct userData *uData;
4174 switch(ntype->set_access_type)
4176 case NOTE_SET_CHANNEL_ACCESS:
4177 if(!user->handle_info)
4179 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4181 return uData->access >= ntype->set_access.min_ulevel;
4182 case NOTE_SET_CHANNEL_SETTER:
4183 return check_user_level(channel, user, lvlSetters, 1, 0);
4184 case NOTE_SET_PRIVILEGED: default:
4185 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4189 static CHANSERV_FUNC(cmd_note)
4191 struct chanData *cData;
4193 struct note_type *ntype;
4195 cData = channel->channel_info;
4198 reply("CSMSG_NOT_REGISTERED", channel->name);
4202 /* If no arguments, show all visible notes for the channel. */
4208 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4210 note = iter_data(it);
4211 if(!note_type_visible_to_user(cData, note->type, user))
4214 reply("CSMSG_NOTELIST_HEADER", channel->name);
4215 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4218 reply("CSMSG_NOTELIST_END", channel->name);
4220 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4222 /* If one argument, show the named note. */
4225 if((note = dict_find(cData->notes, argv[1], NULL))
4226 && note_type_visible_to_user(cData, note->type, user))
4228 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4230 else if((ntype = dict_find(note_types, argv[1], NULL))
4231 && note_type_visible_to_user(NULL, ntype, user))
4233 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4238 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4242 /* Assume they're trying to set a note. */
4246 ntype = dict_find(note_types, argv[1], NULL);
4249 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4252 else if(note_type_settable_by_user(channel, ntype, user))
4254 note_text = unsplit_string(argv+2, argc-2, NULL);
4255 if((note = dict_find(cData->notes, argv[1], NULL)))
4256 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4257 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4258 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4260 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4262 /* The note is viewable to staff only, so return 0
4263 to keep the invocation from getting logged (or
4264 regular users can see it in !events). */
4270 reply("CSMSG_NO_ACCESS");
4277 static CHANSERV_FUNC(cmd_delnote)
4282 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4283 || !note_type_settable_by_user(channel, note->type, user))
4285 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4288 dict_remove(channel->channel_info->notes, note->type->name);
4289 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4293 static CHANSERV_FUNC(cmd_events)
4295 struct logSearch discrim;
4296 struct logReport report;
4297 unsigned int matches, limit;
4299 limit = (argc > 1) ? atoi(argv[1]) : 10;
4300 if(limit < 1 || limit > 200) limit = 10;
4302 memset(&discrim, 0, sizeof(discrim));
4303 discrim.masks.bot = chanserv;
4304 discrim.masks.channel_name = channel->name;
4305 if(argc > 2) discrim.masks.command = argv[2];
4306 discrim.limit = limit;
4307 discrim.max_time = INT_MAX;
4308 discrim.severities = 1 << LOG_COMMAND;
4309 report.reporter = chanserv;
4311 reply("CSMSG_EVENT_SEARCH_RESULTS");
4312 matches = log_entry_search(&discrim, log_report_entry, &report);
4314 reply("MSG_MATCH_COUNT", matches);
4316 reply("MSG_NO_MATCHES");
4320 static CHANSERV_FUNC(cmd_say)
4326 msg = unsplit_string(argv + 1, argc - 1, NULL);
4327 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4329 else if(GetUserH(argv[1]))
4332 msg = unsplit_string(argv + 2, argc - 2, NULL);
4333 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4337 reply("MSG_NOT_TARGET_NAME");
4343 static CHANSERV_FUNC(cmd_emote)
4349 /* CTCP is so annoying. */
4350 msg = unsplit_string(argv + 1, argc - 1, NULL);
4351 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4353 else if(GetUserH(argv[1]))
4355 msg = unsplit_string(argv + 2, argc - 2, NULL);
4356 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4360 reply("MSG_NOT_TARGET_NAME");
4366 struct channelList *
4367 chanserv_support_channels(void)
4369 return &chanserv_conf.support_channels;
4372 static CHANSERV_FUNC(cmd_expire)
4374 int channel_count = registered_channels;
4375 expire_channels(NULL);
4376 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4381 chanserv_expire_suspension(void *data)
4383 struct suspended *suspended = data;
4384 struct chanNode *channel;
4385 struct mod_chanmode change;
4387 if(!suspended->expires || (now < suspended->expires))
4388 suspended->revoked = now;
4389 channel = suspended->cData->channel;
4390 suspended->cData->channel = channel;
4391 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4392 mod_chanmode_init(&change);
4394 change.args[0].mode = MODE_CHANOP;
4395 change.args[0].member = AddChannelUser(chanserv, channel);
4396 mod_chanmode_announce(chanserv, channel, &change);
4399 static CHANSERV_FUNC(cmd_csuspend)
4401 struct suspended *suspended;
4402 char reason[MAXLEN];
4403 time_t expiry, duration;
4404 struct userData *uData;
4408 if(IsProtected(channel->channel_info))
4410 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4414 if(argv[1][0] == '!')
4416 else if(IsSuspended(channel->channel_info))
4418 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4419 show_suspension_info(cmd, user, channel->channel_info->suspended);
4423 if(!strcmp(argv[1], "0"))
4425 else if((duration = ParseInterval(argv[1])))
4426 expiry = now + duration;
4429 reply("MSG_INVALID_DURATION", argv[1]);
4433 unsplit_string(argv + 2, argc - 2, reason);
4435 suspended = calloc(1, sizeof(*suspended));
4436 suspended->revoked = 0;
4437 suspended->issued = now;
4438 suspended->suspender = strdup(user->handle_info->handle);
4439 suspended->expires = expiry;
4440 suspended->reason = strdup(reason);
4441 suspended->cData = channel->channel_info;
4442 suspended->previous = suspended->cData->suspended;
4443 suspended->cData->suspended = suspended;
4445 if(suspended->expires)
4446 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4448 if(IsSuspended(channel->channel_info))
4450 suspended->previous->revoked = now;
4451 if(suspended->previous->expires)
4452 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4453 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4454 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4458 /* Mark all users in channel as absent. */
4459 for(uData = channel->channel_info->users; uData; uData = uData->next)
4468 /* Mark the channel as suspended, then part. */
4469 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4470 DelChannelUser(chanserv, channel, suspended->reason, 0);
4471 reply("CSMSG_SUSPENDED", channel->name);
4472 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4473 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4478 static CHANSERV_FUNC(cmd_cunsuspend)
4480 struct suspended *suspended;
4481 char message[MAXLEN];
4483 if(!IsSuspended(channel->channel_info))
4485 reply("CSMSG_NOT_SUSPENDED", channel->name);
4489 suspended = channel->channel_info->suspended;
4491 /* Expire the suspension and join ChanServ to the channel. */
4492 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4493 chanserv_expire_suspension(suspended);
4494 reply("CSMSG_UNSUSPENDED", channel->name);
4495 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4496 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4500 typedef struct chanservSearch
4508 unsigned long flags;
4512 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4515 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4520 search = malloc(sizeof(struct chanservSearch));
4521 memset(search, 0, sizeof(*search));
4524 for(i = 0; i < argc; i++)
4526 /* Assume all criteria require arguments. */
4529 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4533 if(!irccasecmp(argv[i], "name"))
4534 search->name = argv[++i];
4535 else if(!irccasecmp(argv[i], "registrar"))
4536 search->registrar = argv[++i];
4537 else if(!irccasecmp(argv[i], "unvisited"))
4538 search->unvisited = ParseInterval(argv[++i]);
4539 else if(!irccasecmp(argv[i], "registered"))
4540 search->registered = ParseInterval(argv[++i]);
4541 else if(!irccasecmp(argv[i], "flags"))
4544 if(!irccasecmp(argv[i], "nodelete"))
4545 search->flags |= CHANNEL_NODELETE;
4546 else if(!irccasecmp(argv[i], "suspended"))
4547 search->flags |= CHANNEL_SUSPENDED;
4550 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4554 else if(!irccasecmp(argv[i], "limit"))
4555 search->limit = strtoul(argv[++i], NULL, 10);
4558 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4563 if(search->name && !strcmp(search->name, "*"))
4565 if(search->registrar && !strcmp(search->registrar, "*"))
4566 search->registrar = 0;
4575 chanserv_channel_match(struct chanData *channel, search_t search)
4577 const char *name = channel->channel->name;
4578 if((search->name && !match_ircglob(name, search->name)) ||
4579 (search->registrar && !channel->registrar) ||
4580 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4581 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4582 (search->registered && (now - channel->registered) > search->registered) ||
4583 (search->flags && ((search->flags & channel->flags) != search->flags)))
4590 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4592 struct chanData *channel;
4593 unsigned int matches = 0;
4595 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4597 if(!chanserv_channel_match(channel, search))
4607 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4612 search_print(struct chanData *channel, void *data)
4614 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4617 static CHANSERV_FUNC(cmd_search)
4620 unsigned int matches;
4621 channel_search_func action;
4625 if(!irccasecmp(argv[1], "count"))
4626 action = search_count;
4627 else if(!irccasecmp(argv[1], "print"))
4628 action = search_print;
4631 reply("CSMSG_ACTION_INVALID", argv[1]);
4635 search = chanserv_search_create(user, argc - 2, argv + 2);
4639 if(action == search_count)
4640 search->limit = INT_MAX;
4642 if(action == search_print)
4643 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4645 matches = chanserv_channel_search(search, action, user);
4648 reply("MSG_MATCH_COUNT", matches);
4650 reply("MSG_NO_MATCHES");
4656 static CHANSERV_FUNC(cmd_unvisited)
4658 struct chanData *cData;
4659 time_t interval = chanserv_conf.channel_expire_delay;
4660 char buffer[INTERVALLEN];
4661 unsigned int limit = 25, matches = 0;
4665 interval = ParseInterval(argv[1]);
4667 limit = atoi(argv[2]);
4670 intervalString(buffer, interval, user->handle_info);
4671 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4673 for(cData = channelList; cData && matches < limit; cData = cData->next)
4675 if((now - cData->visited) < interval)
4678 intervalString(buffer, now - cData->visited, user->handle_info);
4679 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4686 static MODCMD_FUNC(chan_opt_defaulttopic)
4692 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4694 reply("CSMSG_TOPIC_LOCKED", channel->name);
4698 topic = unsplit_string(argv+1, argc-1, NULL);
4700 free(channel->channel_info->topic);
4701 if(topic[0] == '*' && topic[1] == 0)
4703 topic = channel->channel_info->topic = NULL;
4707 topic = channel->channel_info->topic = strdup(topic);
4708 if(channel->channel_info->topic_mask
4709 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4710 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4712 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4715 if(channel->channel_info->topic)
4716 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4718 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4722 static MODCMD_FUNC(chan_opt_topicmask)
4726 struct chanData *cData = channel->channel_info;
4729 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4731 reply("CSMSG_TOPIC_LOCKED", channel->name);
4735 mask = unsplit_string(argv+1, argc-1, NULL);
4737 if(cData->topic_mask)
4738 free(cData->topic_mask);
4739 if(mask[0] == '*' && mask[1] == 0)
4741 cData->topic_mask = 0;
4745 cData->topic_mask = strdup(mask);
4747 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4748 else if(!match_ircglob(cData->topic, cData->topic_mask))
4749 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4753 if(channel->channel_info->topic_mask)
4754 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4756 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4760 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4764 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4768 if(greeting[0] == '*' && greeting[1] == 0)
4772 unsigned int length = strlen(greeting);
4773 if(length > chanserv_conf.greeting_length)
4775 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4778 *data = strdup(greeting);
4787 reply(name, user_find_message(user, "MSG_NONE"));
4791 static MODCMD_FUNC(chan_opt_greeting)
4793 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4796 static MODCMD_FUNC(chan_opt_usergreeting)
4798 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4801 static MODCMD_FUNC(chan_opt_modes)
4803 struct mod_chanmode *new_modes;
4804 char modes[MODELEN];
4808 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4810 reply("CSMSG_NO_ACCESS");
4813 if(argv[1][0] == '*' && argv[1][1] == 0)
4815 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4817 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4819 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4822 else if(new_modes->argc > 1)
4824 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4825 mod_chanmode_free(new_modes);
4830 channel->channel_info->modes = *new_modes;
4831 modcmd_chanmode_announce(new_modes);
4832 mod_chanmode_free(new_modes);
4836 mod_chanmode_format(&channel->channel_info->modes, modes);
4838 reply("CSMSG_SET_MODES", modes);
4840 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4844 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4846 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4848 struct chanData *cData = channel->channel_info;
4853 /* Set flag according to value. */
4854 if(enabled_string(argv[1]))
4856 cData->flags |= mask;
4859 else if(disabled_string(argv[1]))
4861 cData->flags &= ~mask;
4866 reply("MSG_INVALID_BINARY", argv[1]);
4872 /* Find current option value. */
4873 value = (cData->flags & mask) ? 1 : 0;
4877 reply(name, user_find_message(user, "MSG_ON"));
4879 reply(name, user_find_message(user, "MSG_OFF"));
4883 static MODCMD_FUNC(chan_opt_nodelete)
4885 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4887 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4891 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4894 static MODCMD_FUNC(chan_opt_dynlimit)
4896 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4899 static MODCMD_FUNC(chan_opt_defaults)
4901 struct userData *uData;
4902 struct chanData *cData;
4903 const char *confirm;
4904 enum levelOption lvlOpt;
4905 enum charOption chOpt;
4907 cData = channel->channel_info;
4908 uData = GetChannelUser(cData, user->handle_info);
4909 if(!uData || (uData->access < UL_OWNER))
4911 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4914 confirm = make_confirmation_string(uData);
4915 if((argc < 2) || strcmp(argv[1], confirm))
4917 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4920 cData->flags = CHANNEL_DEFAULT_FLAGS;
4921 cData->modes = chanserv_conf.default_modes;
4922 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4923 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4924 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4925 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4926 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4931 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4933 struct chanData *cData = channel->channel_info;
4934 struct userData *uData;
4935 unsigned short value;
4939 if(!check_user_level(channel, user, option, 1, 1))
4941 reply("CSMSG_CANNOT_SET");
4944 value = user_level_from_name(argv[1], UL_OWNER+1);
4945 if(!value && strcmp(argv[1], "0"))
4947 reply("CSMSG_INVALID_ACCESS", argv[1]);
4950 uData = GetChannelUser(cData, user->handle_info);
4951 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4953 reply("CSMSG_BAD_SETLEVEL");
4959 if(value > cData->lvlOpts[lvlGiveOps])
4961 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4966 if(value < cData->lvlOpts[lvlGiveVoice])
4968 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4973 /* This test only applies to owners, since non-owners
4974 * trying to set an option to above their level get caught
4975 * by the CSMSG_BAD_SETLEVEL test above.
4977 if(value > uData->access)
4979 reply("CSMSG_BAD_SETTERS");
4986 cData->lvlOpts[option] = value;
4988 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4992 static MODCMD_FUNC(chan_opt_enfops)
4994 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4997 static MODCMD_FUNC(chan_opt_giveops)
4999 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5002 static MODCMD_FUNC(chan_opt_enfmodes)
5004 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5007 static MODCMD_FUNC(chan_opt_enftopic)
5009 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5012 static MODCMD_FUNC(chan_opt_pubcmd)
5014 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5017 static MODCMD_FUNC(chan_opt_setters)
5019 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5022 static MODCMD_FUNC(chan_opt_ctcpusers)
5024 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5027 static MODCMD_FUNC(chan_opt_userinfo)
5029 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5032 static MODCMD_FUNC(chan_opt_givevoice)
5034 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5037 static MODCMD_FUNC(chan_opt_topicsnarf)
5039 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5042 static MODCMD_FUNC(chan_opt_inviteme)
5044 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5048 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5050 struct chanData *cData = channel->channel_info;
5051 int count = charOptions[option].count, index;
5055 index = atoi(argv[1]);
5057 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5059 reply("CSMSG_INVALID_NUMERIC", index);
5060 /* Show possible values. */
5061 for(index = 0; index < count; index++)
5062 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5066 cData->chOpts[option] = charOptions[option].values[index].value;
5070 /* Find current option value. */
5073 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5077 /* Somehow, the option value is corrupt; reset it to the default. */
5078 cData->chOpts[option] = charOptions[option].default_value;
5083 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5087 static MODCMD_FUNC(chan_opt_protect)
5089 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5092 static MODCMD_FUNC(chan_opt_toys)
5094 return channel_multiple_option(chToys, CSFUNC_ARGS);
5097 static MODCMD_FUNC(chan_opt_ctcpreaction)
5099 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5102 static MODCMD_FUNC(chan_opt_topicrefresh)
5104 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5107 static struct svccmd_list set_shows_list;
5110 handle_svccmd_unbind(struct svccmd *target) {
5112 for(ii=0; ii<set_shows_list.used; ++ii)
5113 if(target == set_shows_list.list[ii])
5114 set_shows_list.used = 0;
5117 static CHANSERV_FUNC(cmd_set)
5119 struct svccmd *subcmd;
5123 /* Check if we need to (re-)initialize set_shows_list. */
5124 if(!set_shows_list.used)
5126 if(!set_shows_list.size)
5128 set_shows_list.size = chanserv_conf.set_shows->used;
5129 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5131 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5133 const char *name = chanserv_conf.set_shows->list[ii];
5134 sprintf(buf, "%s %s", argv[0], name);
5135 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5138 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5141 svccmd_list_append(&set_shows_list, subcmd);
5147 reply("CSMSG_CHANNEL_OPTIONS");
5148 for(ii = 0; ii < set_shows_list.used; ii++)
5150 subcmd = set_shows_list.list[ii];
5151 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5156 sprintf(buf, "%s %s", argv[0], argv[1]);
5157 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5160 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5163 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5165 reply("CSMSG_NO_ACCESS");
5169 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5173 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5175 struct userData *uData;
5177 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5180 reply("CSMSG_NOT_USER", channel->name);
5186 /* Just show current option value. */
5188 else if(enabled_string(argv[1]))
5190 uData->flags |= mask;
5192 else if(disabled_string(argv[1]))
5194 uData->flags &= ~mask;
5198 reply("MSG_INVALID_BINARY", argv[1]);
5202 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5206 static MODCMD_FUNC(user_opt_noautoop)
5208 struct userData *uData;
5210 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5213 reply("CSMSG_NOT_USER", channel->name);
5216 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5217 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5219 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5222 static MODCMD_FUNC(user_opt_autoinvite)
5224 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5227 static MODCMD_FUNC(user_opt_info)
5229 struct userData *uData;
5232 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5236 /* If they got past the command restrictions (which require access)
5237 * but fail this test, we have some fool with security override on.
5239 reply("CSMSG_NOT_USER", channel->name);
5245 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5248 if(infoline[0] == '*' && infoline[1] == 0)
5251 uData->info = strdup(infoline);
5254 reply("CSMSG_USET_INFO", uData->info);
5256 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5260 struct svccmd_list uset_shows_list;
5262 static CHANSERV_FUNC(cmd_uset)
5264 struct svccmd *subcmd;
5268 /* Check if we need to (re-)initialize uset_shows_list. */
5269 if(!uset_shows_list.used)
5273 "NoAutoOp", "AutoInvite", "Info"
5276 if(!uset_shows_list.size)
5278 uset_shows_list.size = ArrayLength(options);
5279 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5281 for(ii = 0; ii < ArrayLength(options); ii++)
5283 const char *name = options[ii];
5284 sprintf(buf, "%s %s", argv[0], name);
5285 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5288 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5291 svccmd_list_append(&uset_shows_list, subcmd);
5297 /* Do this so options are presented in a consistent order. */
5298 reply("CSMSG_USER_OPTIONS");
5299 for(ii = 0; ii < uset_shows_list.used; ii++)
5300 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5304 sprintf(buf, "%s %s", argv[0], argv[1]);
5305 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5308 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5312 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5315 static CHANSERV_FUNC(cmd_giveownership)
5317 struct handle_info *new_owner_hi;
5318 struct userData *new_owner, *curr_user;
5319 struct chanData *cData = channel->channel_info;
5320 struct do_not_register *dnr;
5322 unsigned short co_access;
5323 char reason[MAXLEN];
5326 curr_user = GetChannelAccess(cData, user->handle_info);
5327 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5328 if(!curr_user || (curr_user->access != UL_OWNER))
5330 struct userData *owner = NULL;
5331 for(curr_user = channel->channel_info->users;
5333 curr_user = curr_user->next)
5335 if(curr_user->access != UL_OWNER)
5339 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5346 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5348 if(new_owner_hi == user->handle_info)
5350 reply("CSMSG_NO_TRANSFER_SELF");
5353 new_owner = GetChannelAccess(cData, new_owner_hi);
5356 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5359 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5361 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5364 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5365 if(!IsHelping(user))
5366 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5368 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5371 if(new_owner->access >= UL_COOWNER)
5372 co_access = new_owner->access;
5374 co_access = UL_COOWNER;
5375 new_owner->access = UL_OWNER;
5377 curr_user->access = co_access;
5378 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5379 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5380 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5384 static CHANSERV_FUNC(cmd_suspend)
5386 struct handle_info *hi;
5387 struct userData *self, *target;
5390 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5391 self = GetChannelUser(channel->channel_info, user->handle_info);
5392 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5394 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5397 if(target->access >= self->access)
5399 reply("MSG_USER_OUTRANKED", hi->handle);
5402 if(target->flags & USER_SUSPENDED)
5404 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5409 target->present = 0;
5412 target->flags |= USER_SUSPENDED;
5413 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5417 static CHANSERV_FUNC(cmd_unsuspend)
5419 struct handle_info *hi;
5420 struct userData *self, *target;
5423 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5424 self = GetChannelUser(channel->channel_info, user->handle_info);
5425 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5427 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5430 if(target->access >= self->access)
5432 reply("MSG_USER_OUTRANKED", hi->handle);
5435 if(!(target->flags & USER_SUSPENDED))
5437 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5440 target->flags &= ~USER_SUSPENDED;
5441 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5445 static MODCMD_FUNC(cmd_deleteme)
5447 struct handle_info *hi;
5448 struct userData *target;
5449 const char *confirm_string;
5450 unsigned short access;
5453 hi = user->handle_info;
5454 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5456 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5459 if(target->access == UL_OWNER)
5461 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5464 confirm_string = make_confirmation_string(target);
5465 if((argc < 2) || strcmp(argv[1], confirm_string))
5467 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5470 access = target->access;
5471 channel_name = strdup(channel->name);
5472 del_channel_user(target, 1);
5473 reply("CSMSG_DELETED_YOU", access, channel_name);
5479 chanserv_refresh_topics(UNUSED_ARG(void *data))
5481 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5482 struct chanData *cData;
5485 for(cData = channelList; cData; cData = cData->next)
5487 if(IsSuspended(cData))
5489 opt = cData->chOpts[chTopicRefresh];
5492 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5495 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5496 cData->last_refresh = refresh_num;
5498 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5501 static CHANSERV_FUNC(cmd_unf)
5505 char response[MAXLEN];
5506 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5507 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5508 irc_privmsg(cmd->parent->bot, channel->name, response);
5511 reply("CSMSG_UNF_RESPONSE");
5515 static CHANSERV_FUNC(cmd_ping)
5519 char response[MAXLEN];
5520 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5521 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5522 irc_privmsg(cmd->parent->bot, channel->name, response);
5525 reply("CSMSG_PING_RESPONSE");
5529 static CHANSERV_FUNC(cmd_wut)
5533 char response[MAXLEN];
5534 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5535 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5536 irc_privmsg(cmd->parent->bot, channel->name, response);
5539 reply("CSMSG_WUT_RESPONSE");
5543 static CHANSERV_FUNC(cmd_8ball)
5545 unsigned int i, j, accum;
5550 for(i=1; i<argc; i++)
5551 for(j=0; argv[i][j]; j++)
5552 accum = (accum << 5) - accum + toupper(argv[i][j]);
5553 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5556 char response[MAXLEN];
5557 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5558 irc_privmsg(cmd->parent->bot, channel->name, response);
5561 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5565 static CHANSERV_FUNC(cmd_d)
5567 unsigned long sides, count, modifier, ii, total;
5568 char response[MAXLEN], *sep;
5572 if((count = strtoul(argv[1], &sep, 10)) < 1)
5582 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5583 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5587 else if((sep[0] == '-') && isdigit(sep[1]))
5588 modifier = strtoul(sep, NULL, 10);
5589 else if((sep[0] == '+') && isdigit(sep[1]))
5590 modifier = strtoul(sep+1, NULL, 10);
5597 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5602 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5605 for(total = ii = 0; ii < count; ++ii)
5606 total += (rand() % sides) + 1;
5609 if((count > 1) || modifier)
5611 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5612 sprintf(response, fmt, total, count, sides, modifier);
5616 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5617 sprintf(response, fmt, total, sides);
5620 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5622 send_message_type(4, user, cmd->parent->bot, "%s", response);
5626 static CHANSERV_FUNC(cmd_huggle)
5628 /* CTCP must be via PRIVMSG, never notice */
5630 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5632 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5637 chanserv_adjust_limit(void *data)
5639 struct mod_chanmode change;
5640 struct chanData *cData = data;
5641 struct chanNode *channel = cData->channel;
5644 if(IsSuspended(cData))
5647 cData->limitAdjusted = now;
5648 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5649 if(cData->modes.modes_set & MODE_LIMIT)
5651 if(limit > cData->modes.new_limit)
5652 limit = cData->modes.new_limit;
5653 else if(limit == cData->modes.new_limit)
5657 mod_chanmode_init(&change);
5658 change.modes_set = MODE_LIMIT;
5659 change.new_limit = limit;
5660 mod_chanmode_announce(chanserv, channel, &change);
5664 handle_new_channel(struct chanNode *channel)
5666 struct chanData *cData;
5668 if(!(cData = channel->channel_info))
5671 if(cData->modes.modes_set || cData->modes.modes_clear)
5672 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5674 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5675 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5678 /* Welcome to my worst nightmare. Warning: Read (or modify)
5679 the code below at your own risk. */
5681 handle_join(struct modeNode *mNode)
5683 struct mod_chanmode change;
5684 struct userNode *user = mNode->user;
5685 struct chanNode *channel = mNode->channel;
5686 struct chanData *cData;
5687 struct userData *uData = NULL;
5688 struct banData *bData;
5689 struct handle_info *handle;
5690 unsigned int modes = 0, info = 0;
5693 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5696 cData = channel->channel_info;
5697 if(channel->members.used > cData->max)
5698 cData->max = channel->members.used;
5700 /* Check for bans. If they're joining through a ban, one of two
5702 * 1: Join during a netburst, by riding the break. Kick them
5703 * unless they have ops or voice in the channel.
5704 * 2: They're allowed to join through the ban (an invite in
5705 * ircu2.10, or a +e on Hybrid, or something).
5706 * If they're not joining through a ban, and the banlist is not
5707 * full, see if they're on the banlist for the channel. If so,
5710 if(user->uplink->burst && !mNode->modes)
5713 for(ii = 0; ii < channel->banlist.used; ii++)
5715 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5717 /* Riding a netburst. Naughty. */
5718 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5724 mod_chanmode_init(&change);
5726 if(channel->banlist.used < MAXBANS)
5728 /* Not joining through a ban. */
5729 for(bData = cData->bans;
5730 bData && !user_matches_glob(user, bData->mask, 1);
5731 bData = bData->next);
5735 char kick_reason[MAXLEN];
5736 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5738 bData->triggered = now;
5739 if(bData != cData->bans)
5741 /* Shuffle the ban to the head of the list. */
5743 bData->next->prev = bData->prev;
5745 bData->prev->next = bData->next;
5748 bData->next = cData->bans;
5751 cData->bans->prev = bData;
5752 cData->bans = bData;
5755 change.args[0].mode = MODE_BAN;
5756 change.args[0].hostmask = bData->mask;
5757 mod_chanmode_announce(chanserv, channel, &change);
5758 KickChannelUser(user, channel, chanserv, kick_reason);
5763 /* ChanServ will not modify the limits in join-flooded channels.
5764 It will also skip DynLimit processing when the user (or srvx)
5765 is bursting in, because there are likely more incoming. */
5766 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5767 && !user->uplink->burst
5768 && !channel->join_flooded
5769 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5771 /* The user count has begun "bumping" into the channel limit,
5772 so set a timer to raise the limit a bit. Any previous
5773 timers are removed so three incoming users within the delay
5774 results in one limit change, not three. */
5776 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5777 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5780 if(channel->join_flooded)
5782 /* don't automatically give ops or voice during a join flood */
5784 else if(cData->lvlOpts[lvlGiveOps] == 0)
5785 modes |= MODE_CHANOP;
5786 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5787 modes |= MODE_VOICE;
5789 greeting = cData->greeting;
5790 if(user->handle_info)
5792 handle = user->handle_info;
5794 if(IsHelper(user) && !IsHelping(user))
5797 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5799 if(channel == chanserv_conf.support_channels.list[ii])
5801 HANDLE_SET_FLAG(user->handle_info, HELPING);
5807 uData = GetTrueChannelAccess(cData, handle);
5808 if(uData && !IsUserSuspended(uData))
5810 /* Ops and above were handled by the above case. */
5811 if(IsUserAutoOp(uData))
5813 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5814 modes |= MODE_CHANOP;
5815 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5816 modes |= MODE_VOICE;
5818 if(uData->access >= UL_PRESENT)
5819 cData->visited = now;
5820 if(cData->user_greeting)
5821 greeting = cData->user_greeting;
5823 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5824 && ((now - uData->seen) >= chanserv_conf.info_delay)
5831 if(!user->uplink->burst)
5835 if(modes & MODE_CHANOP)
5836 modes &= ~MODE_VOICE;
5837 change.args[0].mode = modes;
5838 change.args[0].member = mNode;
5839 mod_chanmode_announce(chanserv, channel, &change);
5841 if(greeting && !user->uplink->burst)
5842 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5844 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5850 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5852 struct mod_chanmode change;
5853 struct userData *channel;
5854 unsigned int ii, jj;
5856 if(!user->handle_info)
5859 mod_chanmode_init(&change);
5861 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5863 struct chanNode *cn;
5864 struct modeNode *mn;
5865 if(IsUserSuspended(channel)
5866 || IsSuspended(channel->channel)
5867 || !(cn = channel->channel->channel))
5870 mn = GetUserMode(cn, user);
5873 if(!IsUserSuspended(channel)
5874 && IsUserAutoInvite(channel)
5875 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5876 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5878 irc_invite(chanserv, user, cn);
5882 if(channel->access >= UL_PRESENT)
5883 channel->channel->visited = now;
5885 if(IsUserAutoOp(channel))
5887 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5888 change.args[0].mode = MODE_CHANOP;
5889 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5890 change.args[0].mode = MODE_VOICE;
5892 change.args[0].mode = 0;
5893 change.args[0].member = mn;
5894 if(change.args[0].mode)
5895 mod_chanmode_announce(chanserv, cn, &change);
5898 channel->seen = now;
5899 channel->present = 1;
5902 for(ii = 0; ii < user->channels.used; ++ii)
5904 struct chanNode *channel = user->channels.list[ii]->channel;
5905 struct banData *ban;
5907 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5908 || !channel->channel_info)
5910 for(jj = 0; jj < channel->banlist.used; ++jj)
5911 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5913 if(jj < channel->banlist.used)
5915 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5917 char kick_reason[MAXLEN];
5918 if(!user_matches_glob(user, ban->mask, 1))
5920 change.args[0].mode = MODE_BAN;
5921 change.args[0].hostmask = ban->mask;
5922 mod_chanmode_announce(chanserv, channel, &change);
5923 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5924 KickChannelUser(user, channel, chanserv, kick_reason);
5925 ban->triggered = now;
5930 if(IsSupportHelper(user))
5932 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5934 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5936 HANDLE_SET_FLAG(user->handle_info, HELPING);
5944 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5946 struct chanData *cData;
5947 struct userData *uData;
5949 cData = channel->channel_info;
5950 if(!cData || IsSuspended(cData) || IsLocal(user))
5953 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5955 /* Allow for a bit of padding so that the limit doesn't
5956 track the user count exactly, which could get annoying. */
5957 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5959 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5960 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5964 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5966 scan_user_presence(uData, user);
5970 if(IsHelping(user) && IsSupportHelper(user))
5972 unsigned int ii, jj;
5973 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5975 for(jj = 0; jj < user->channels.used; ++jj)
5976 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5978 if(jj < user->channels.used)
5981 if(ii == chanserv_conf.support_channels.used)
5982 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5987 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5989 struct userData *uData;
5991 if(!channel->channel_info || !kicker || IsService(kicker)
5992 || (kicker == victim) || IsSuspended(channel->channel_info)
5993 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5996 if(protect_user(victim, kicker, channel->channel_info))
5998 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
5999 KickChannelUser(kicker, channel, chanserv, reason);
6002 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6007 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6009 struct chanData *cData;
6011 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6014 cData = channel->channel_info;
6015 if(bad_topic(channel, user, channel->topic))
6017 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6018 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6019 SetChannelTopic(channel, chanserv, old_topic, 1);
6020 else if(cData->topic)
6021 SetChannelTopic(channel, chanserv, cData->topic, 1);
6024 /* With topicsnarf, grab the topic and save it as the default topic. */
6025 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6028 cData->topic = strdup(channel->topic);
6034 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6036 struct mod_chanmode *bounce = NULL;
6037 unsigned int bnc, ii;
6040 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6043 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6044 && mode_lock_violated(&channel->channel_info->modes, change))
6046 char correct[MAXLEN];
6047 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6048 mod_chanmode_format(&channel->channel_info->modes, correct);
6049 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6051 for(ii = bnc = 0; ii < change->argc; ++ii)
6053 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6055 const struct userNode *victim = change->args[ii].member->user;
6056 if(!protect_user(victim, user, channel->channel_info))
6059 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6062 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6063 bounce->args[bnc].member = GetUserMode(channel, user);
6064 if(bounce->args[bnc].member)
6068 bounce->args[bnc].mode = MODE_CHANOP;
6069 bounce->args[bnc].member = change->args[ii].member;
6071 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6073 else if(change->args[ii].mode & MODE_CHANOP)
6075 const struct userNode *victim = change->args[ii].member->user;
6076 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6079 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6080 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6081 bounce->args[bnc].member = change->args[ii].member;
6084 else if(change->args[ii].mode & MODE_BAN)
6086 const char *ban = change->args[ii].hostmask;
6087 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6090 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6091 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6092 bounce->args[bnc].hostmask = ban;
6094 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6099 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6100 mod_chanmode_announce(chanserv, channel, bounce);
6101 mod_chanmode_free(bounce);
6106 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6108 struct chanNode *channel;
6109 struct banData *bData;
6110 struct mod_chanmode change;
6111 unsigned int ii, jj;
6112 char kick_reason[MAXLEN];
6114 mod_chanmode_init(&change);
6116 change.args[0].mode = MODE_BAN;
6117 for(ii = 0; ii < user->channels.used; ++ii)
6119 channel = user->channels.list[ii]->channel;
6120 /* Need not check for bans if they're opped or voiced. */
6121 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6123 /* Need not check for bans unless channel registration is active. */
6124 if(!channel->channel_info || IsSuspended(channel->channel_info))
6126 /* Look for a matching ban already on the channel. */
6127 for(jj = 0; jj < channel->banlist.used; ++jj)
6128 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6130 /* Need not act if we found one. */
6131 if(jj < channel->banlist.used)
6133 /* Look for a matching ban in this channel. */
6134 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6136 if(!user_matches_glob(user, bData->mask, 1))
6138 change.args[0].hostmask = bData->mask;
6139 mod_chanmode_announce(chanserv, channel, &change);
6140 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6141 KickChannelUser(user, channel, chanserv, kick_reason);
6142 bData->triggered = now;
6143 break; /* we don't need to check any more bans in the channel */
6148 static void handle_rename(struct handle_info *handle, const char *old_handle)
6150 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6154 dict_remove2(handle_dnrs, old_handle, 1);
6155 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6156 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6161 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6163 struct userNode *h_user;
6165 if(handle->channels)
6167 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6168 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6170 while(handle->channels)
6171 del_channel_user(handle->channels, 1);
6176 handle_server_link(UNUSED_ARG(struct server *server))
6178 struct chanData *cData;
6180 for(cData = channelList; cData; cData = cData->next)
6182 if(!IsSuspended(cData))
6183 cData->may_opchan = 1;
6184 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6185 && !cData->channel->join_flooded
6186 && ((cData->channel->limit - cData->channel->members.used)
6187 < chanserv_conf.adjust_threshold))
6189 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6190 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6196 chanserv_conf_read(void)
6200 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6201 struct mod_chanmode *change;
6202 struct string_list *strlist;
6203 struct chanNode *chan;
6206 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6208 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6211 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6212 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6213 chanserv_conf.support_channels.used = 0;
6214 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6216 for(ii = 0; ii < strlist->used; ++ii)
6218 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6221 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6223 channelList_append(&chanserv_conf.support_channels, chan);
6226 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6229 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6232 chan = AddChannel(str, now, str2, NULL);
6234 channelList_append(&chanserv_conf.support_channels, chan);
6236 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6237 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6238 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6239 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6240 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6241 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6242 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6243 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6244 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6245 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6246 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6247 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6248 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6249 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6250 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6251 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6252 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6253 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6254 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6255 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6256 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6258 NickChange(chanserv, str, 0);
6259 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6260 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6261 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6262 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6263 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6264 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6265 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6266 chanserv_conf.max_owned = str ? atoi(str) : 5;
6267 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6268 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6269 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6270 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6271 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6272 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6273 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6276 safestrncpy(mode_line, str, sizeof(mode_line));
6277 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6278 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6280 chanserv_conf.default_modes = *change;
6281 mod_chanmode_free(change);
6283 free_string_list(chanserv_conf.set_shows);
6284 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6286 strlist = string_list_copy(strlist);
6289 static const char *list[] = {
6290 /* free form text */
6291 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6292 /* options based on user level */
6293 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6294 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6295 /* multiple choice options */
6296 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6297 /* binary options */
6298 "DynLimit", "NoDelete",
6303 strlist = alloc_string_list(ArrayLength(list)-1);
6304 for(ii=0; list[ii]; ii++)
6305 string_list_append(strlist, strdup(list[ii]));
6307 chanserv_conf.set_shows = strlist;
6308 /* We don't look things up now, in case the list refers to options
6309 * defined by modules initialized after this point. Just mark the
6310 * function list as invalid, so it will be initialized.
6312 set_shows_list.used = 0;
6313 free_string_list(chanserv_conf.eightball);
6314 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6317 strlist = string_list_copy(strlist);
6321 strlist = alloc_string_list(4);
6322 string_list_append(strlist, strdup("Yes."));
6323 string_list_append(strlist, strdup("No."));
6324 string_list_append(strlist, strdup("Maybe so."));
6326 chanserv_conf.eightball = strlist;
6327 free_string_list(chanserv_conf.old_ban_names);
6328 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6330 strlist = string_list_copy(strlist);
6332 strlist = alloc_string_list(2);
6333 chanserv_conf.old_ban_names = strlist;
6337 chanserv_note_type_read(const char *key, struct record_data *rd)
6340 struct note_type *ntype;
6343 if(!(obj = GET_RECORD_OBJECT(rd)))
6345 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6348 if(!(ntype = chanserv_create_note_type(key)))
6350 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6354 /* Figure out set access */
6355 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6357 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6358 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6360 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6362 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6363 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6365 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6367 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6371 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6372 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6373 ntype->set_access.min_opserv = 0;
6376 /* Figure out visibility */
6377 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6378 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6379 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6380 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6381 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6382 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6383 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6384 ntype->visible_type = NOTE_VIS_ALL;
6386 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6388 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6389 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6393 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6395 struct handle_info *handle;
6396 struct userData *uData;
6397 char *seen, *inf, *flags;
6399 unsigned short access;
6401 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6403 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6407 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6408 if(access > UL_OWNER)
6410 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6414 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6415 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6416 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6417 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6418 handle = get_handle_info(key);
6421 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6425 uData = add_channel_user(chan, handle, access, last_seen, inf);
6426 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6430 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6432 struct banData *bData;
6433 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6434 time_t set_time, triggered_time, expires_time;
6436 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6438 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6442 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6443 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6444 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6445 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6446 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6447 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6449 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6450 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6452 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6454 expires_time = set_time + atoi(s_duration);
6458 if(expires_time && (expires_time < now))
6461 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6464 static struct suspended *
6465 chanserv_read_suspended(dict_t obj)
6467 struct suspended *suspended = calloc(1, sizeof(*suspended));
6471 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6472 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6473 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6474 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6475 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6476 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6477 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6478 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6479 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6480 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6485 chanserv_channel_read(const char *key, struct record_data *hir)
6487 struct suspended *suspended;
6488 struct mod_chanmode *modes;
6489 struct chanNode *cNode;
6490 struct chanData *cData;
6491 struct dict *channel, *obj;
6492 char *str, *argv[10];
6496 channel = hir->d.object;
6498 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6501 cNode = AddChannel(key, now, NULL, NULL);
6504 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6507 cData = register_channel(cNode, str);
6510 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6514 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6516 enum levelOption lvlOpt;
6517 enum charOption chOpt;
6519 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6520 cData->flags = atoi(str);
6522 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6524 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6526 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6527 else if(levelOptions[lvlOpt].old_flag)
6529 if(cData->flags & levelOptions[lvlOpt].old_flag)
6530 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6532 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6536 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6538 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6540 cData->chOpts[chOpt] = str[0];
6543 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6545 enum levelOption lvlOpt;
6546 enum charOption chOpt;
6549 cData->flags = base64toint(str, 5);
6550 count = strlen(str += 5);
6551 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6554 if(levelOptions[lvlOpt].old_flag)
6556 if(cData->flags & levelOptions[lvlOpt].old_flag)
6557 lvl = levelOptions[lvlOpt].flag_value;
6559 lvl = levelOptions[lvlOpt].default_value;
6561 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6563 case 'c': lvl = UL_COOWNER; break;
6564 case 'm': lvl = UL_MASTER; break;
6565 case 'n': lvl = UL_OWNER+1; break;
6566 case 'o': lvl = UL_OP; break;
6567 case 'p': lvl = UL_PEON; break;
6568 case 'w': lvl = UL_OWNER; break;
6569 default: lvl = 0; break;
6571 cData->lvlOpts[lvlOpt] = lvl;
6573 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6574 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6577 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6579 suspended = chanserv_read_suspended(obj);
6580 cData->suspended = suspended;
6581 suspended->cData = cData;
6582 /* We could use suspended->expires and suspended->revoked to
6583 * set the CHANNEL_SUSPENDED flag, but we don't. */
6585 else if(cData->flags & CHANNEL_SUSPENDED)
6587 suspended = calloc(1, sizeof(*suspended));
6588 suspended->issued = 0;
6589 suspended->revoked = 0;
6590 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6591 suspended->expires = str ? atoi(str) : 0;
6592 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6593 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6594 suspended->reason = strdup(str ? str : "No reason");
6595 suspended->previous = NULL;
6596 cData->suspended = suspended;
6597 suspended->cData = cData;
6602 if((cData->flags & CHANNEL_SUSPENDED)
6603 && suspended->expires
6604 && (suspended->expires <= now))
6606 cData->flags &= ~CHANNEL_SUSPENDED;
6609 if(!(cData->flags & CHANNEL_SUSPENDED))
6611 struct mod_chanmode change;
6612 mod_chanmode_init(&change);
6614 change.args[0].mode = MODE_CHANOP;
6615 change.args[0].member = AddChannelUser(chanserv, cNode);
6616 mod_chanmode_announce(chanserv, cNode, &change);
6618 else if(suspended->expires > now)
6620 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6623 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6624 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6625 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6626 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6627 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6628 cData->max = str ? atoi(str) : 0;
6629 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6630 cData->greeting = str ? strdup(str) : NULL;
6631 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6632 cData->user_greeting = str ? strdup(str) : NULL;
6633 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6634 cData->topic_mask = str ? strdup(str) : NULL;
6635 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6636 cData->topic = str ? strdup(str) : NULL;
6638 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6639 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6640 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6641 cData->modes = *modes;
6642 if(cData->modes.argc > 1)
6643 cData->modes.argc = 1;
6644 if(!IsSuspended(cData))
6645 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6646 mod_chanmode_free(modes);
6649 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6650 for(it = dict_first(obj); it; it = iter_next(it))
6651 user_read_helper(iter_key(it), iter_data(it), cData);
6653 if(!cData->users && !IsProtected(cData))
6655 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6656 unregister_channel(cData, "has empty user list.");
6660 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6661 for(it = dict_first(obj); it; it = iter_next(it))
6662 ban_read_helper(iter_key(it), iter_data(it), cData);
6664 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6665 for(it = dict_first(obj); it; it = iter_next(it))
6667 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6668 struct record_data *rd = iter_data(it);
6669 const char *note, *setter;
6671 if(rd->type != RECDB_OBJECT)
6673 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6677 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6679 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6681 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6685 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6686 if(!setter) setter = "<unknown>";
6687 chanserv_add_channel_note(cData, ntype, setter, note);
6695 chanserv_dnr_read(const char *key, struct record_data *hir)
6697 const char *setter, *reason, *str;
6698 struct do_not_register *dnr;
6700 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6703 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6706 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6709 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6712 dnr = chanserv_add_dnr(key, setter, reason);
6715 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6717 dnr->set = atoi(str);
6723 chanserv_saxdb_read(struct dict *database)
6725 struct dict *section;
6728 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6729 for(it = dict_first(section); it; it = iter_next(it))
6730 chanserv_note_type_read(iter_key(it), iter_data(it));
6732 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6733 for(it = dict_first(section); it; it = iter_next(it))
6734 chanserv_channel_read(iter_key(it), iter_data(it));
6736 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6737 for(it = dict_first(section); it; it = iter_next(it))
6738 chanserv_dnr_read(iter_key(it), iter_data(it));
6744 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6746 int high_present = 0;
6747 saxdb_start_record(ctx, KEY_USERS, 1);
6748 for(; uData; uData = uData->next)
6750 if((uData->access >= UL_PRESENT) && uData->present)
6752 saxdb_start_record(ctx, uData->handle->handle, 0);
6753 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6754 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6756 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6758 saxdb_write_string(ctx, KEY_INFO, uData->info);
6759 saxdb_end_record(ctx);
6761 saxdb_end_record(ctx);
6762 return high_present;
6766 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6770 saxdb_start_record(ctx, KEY_BANS, 1);
6771 for(; bData; bData = bData->next)
6773 saxdb_start_record(ctx, bData->mask, 0);
6774 saxdb_write_int(ctx, KEY_SET, bData->set);
6775 if(bData->triggered)
6776 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6778 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6780 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6782 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6783 saxdb_end_record(ctx);
6785 saxdb_end_record(ctx);
6789 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6791 saxdb_start_record(ctx, name, 0);
6792 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6793 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6795 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6797 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6799 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6801 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6802 saxdb_end_record(ctx);
6806 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6810 enum levelOption lvlOpt;
6811 enum charOption chOpt;
6813 saxdb_start_record(ctx, channel->channel->name, 1);
6815 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6816 saxdb_write_int(ctx, KEY_MAX, channel->max);
6818 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6819 if(channel->registrar)
6820 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6821 if(channel->greeting)
6822 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6823 if(channel->user_greeting)
6824 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6825 if(channel->topic_mask)
6826 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6827 if(channel->suspended)
6828 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6830 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6831 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6832 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6833 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6834 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6836 buf[0] = channel->chOpts[chOpt];
6838 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6840 saxdb_end_record(ctx);
6842 if(channel->modes.modes_set || channel->modes.modes_clear)
6844 mod_chanmode_format(&channel->modes, buf);
6845 saxdb_write_string(ctx, KEY_MODES, buf);
6848 high_present = chanserv_write_users(ctx, channel->users);
6849 chanserv_write_bans(ctx, channel->bans);
6851 if(dict_size(channel->notes))
6855 saxdb_start_record(ctx, KEY_NOTES, 1);
6856 for(it = dict_first(channel->notes); it; it = iter_next(it))
6858 struct note *note = iter_data(it);
6859 saxdb_start_record(ctx, iter_key(it), 0);
6860 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6861 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6862 saxdb_end_record(ctx);
6864 saxdb_end_record(ctx);
6867 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6868 saxdb_end_record(ctx);
6872 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6876 saxdb_start_record(ctx, ntype->name, 0);
6877 switch(ntype->set_access_type)
6879 case NOTE_SET_CHANNEL_ACCESS:
6880 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6882 case NOTE_SET_CHANNEL_SETTER:
6883 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6885 case NOTE_SET_PRIVILEGED: default:
6886 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6889 switch(ntype->visible_type)
6891 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6892 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6893 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6895 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6896 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6897 saxdb_end_record(ctx);
6901 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6903 struct do_not_register *dnr;
6906 for(it = dict_first(dnrs); it; it = iter_next(it))
6908 dnr = iter_data(it);
6909 saxdb_start_record(ctx, dnr->chan_name, 0);
6911 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6912 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6913 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6914 saxdb_end_record(ctx);
6919 chanserv_saxdb_write(struct saxdb_context *ctx)
6922 struct chanData *channel;
6925 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6926 for(it = dict_first(note_types); it; it = iter_next(it))
6927 chanserv_write_note_type(ctx, iter_data(it));
6928 saxdb_end_record(ctx);
6931 saxdb_start_record(ctx, KEY_DNR, 1);
6932 write_dnrs_helper(ctx, handle_dnrs);
6933 write_dnrs_helper(ctx, plain_dnrs);
6934 write_dnrs_helper(ctx, mask_dnrs);
6935 saxdb_end_record(ctx);
6938 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6939 for(channel = channelList; channel; channel = channel->next)
6940 chanserv_write_channel(ctx, channel);
6941 saxdb_end_record(ctx);
6947 chanserv_db_cleanup(void) {
6949 unreg_part_func(handle_part);
6951 unregister_channel(channelList, "terminating.");
6952 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6953 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6954 free(chanserv_conf.support_channels.list);
6955 dict_delete(handle_dnrs);
6956 dict_delete(plain_dnrs);
6957 dict_delete(mask_dnrs);
6958 dict_delete(note_types);
6959 free_string_list(chanserv_conf.eightball);
6960 free_string_list(chanserv_conf.old_ban_names);
6961 free_string_list(chanserv_conf.set_shows);
6962 free(set_shows_list.list);
6963 free(uset_shows_list.list);
6966 struct userData *helper = helperList;
6967 helperList = helperList->next;
6972 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6973 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6974 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6977 init_chanserv(const char *nick)
6979 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6980 conf_register_reload(chanserv_conf_read);
6982 reg_server_link_func(handle_server_link);
6984 reg_new_channel_func(handle_new_channel);
6985 reg_join_func(handle_join);
6986 reg_part_func(handle_part);
6987 reg_kick_func(handle_kick);
6988 reg_topic_func(handle_topic);
6989 reg_mode_change_func(handle_mode);
6990 reg_nick_change_func(handle_nick_change);
6992 reg_auth_func(handle_auth);
6993 reg_handle_rename_func(handle_rename);
6994 reg_unreg_func(handle_unreg);
6996 handle_dnrs = dict_new();
6997 dict_set_free_data(handle_dnrs, free);
6998 plain_dnrs = dict_new();
6999 dict_set_free_data(plain_dnrs, free);
7000 mask_dnrs = dict_new();
7001 dict_set_free_data(mask_dnrs, free);
7003 reg_svccmd_unbind_func(handle_svccmd_unbind);
7004 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7005 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7006 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7007 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7008 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7009 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7010 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7011 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7012 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7014 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7015 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7017 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7018 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7019 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7020 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7021 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7023 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7024 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7025 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7026 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7027 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7029 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7030 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7031 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7032 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7034 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7035 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7036 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7037 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7038 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7039 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7040 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7041 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7043 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7044 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7045 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7046 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7047 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7048 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7049 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7050 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7051 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7052 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7053 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7054 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7055 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7056 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7058 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7059 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7060 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7061 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7062 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7064 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7065 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7067 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7068 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7069 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7070 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7071 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7072 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7073 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7074 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7075 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7076 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7077 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7079 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7080 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7082 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7083 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7084 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7085 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7087 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7088 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7089 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7090 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7091 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7093 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7094 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7095 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7096 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7097 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7098 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7100 /* Channel options */
7101 DEFINE_CHANNEL_OPTION(defaulttopic);
7102 DEFINE_CHANNEL_OPTION(topicmask);
7103 DEFINE_CHANNEL_OPTION(greeting);
7104 DEFINE_CHANNEL_OPTION(usergreeting);
7105 DEFINE_CHANNEL_OPTION(modes);
7106 DEFINE_CHANNEL_OPTION(enfops);
7107 DEFINE_CHANNEL_OPTION(giveops);
7108 DEFINE_CHANNEL_OPTION(protect);
7109 DEFINE_CHANNEL_OPTION(enfmodes);
7110 DEFINE_CHANNEL_OPTION(enftopic);
7111 DEFINE_CHANNEL_OPTION(pubcmd);
7112 DEFINE_CHANNEL_OPTION(givevoice);
7113 DEFINE_CHANNEL_OPTION(userinfo);
7114 DEFINE_CHANNEL_OPTION(dynlimit);
7115 DEFINE_CHANNEL_OPTION(topicsnarf);
7116 DEFINE_CHANNEL_OPTION(nodelete);
7117 DEFINE_CHANNEL_OPTION(toys);
7118 DEFINE_CHANNEL_OPTION(setters);
7119 DEFINE_CHANNEL_OPTION(topicrefresh);
7120 DEFINE_CHANNEL_OPTION(ctcpusers);
7121 DEFINE_CHANNEL_OPTION(ctcpreaction);
7122 DEFINE_CHANNEL_OPTION(inviteme);
7123 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7125 /* Alias set topic to set defaulttopic for compatibility. */
7126 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7129 DEFINE_USER_OPTION(noautoop);
7130 DEFINE_USER_OPTION(autoinvite);
7131 DEFINE_USER_OPTION(info);
7133 /* Alias uset autovoice to uset autoop. */
7134 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7136 note_types = dict_new();
7137 dict_set_free_data(note_types, chanserv_deref_note_type);
7140 chanserv = AddService(nick, "Channel Services");
7141 service_register(chanserv, '!');
7142 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7144 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7146 if(chanserv_conf.channel_expire_frequency)
7147 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7149 if(chanserv_conf.refresh_period)
7151 time_t next_refresh;
7152 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7153 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7156 reg_exit_func(chanserv_db_cleanup);
7157 message_register_table(msgtab);