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_CHANUSERS", "$bTotal User Count: $b%i" },
400 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
401 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
402 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
403 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
406 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
407 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
408 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
410 /* Channel searches */
411 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
412 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
413 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
414 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
416 /* Channel configuration */
417 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
418 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
419 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
422 { "CSMSG_USER_OPTIONS", "User Options:" },
423 { "CSMSG_USER_PROTECTED", "That user is protected." },
426 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
427 { "CSMSG_PING_RESPONSE", "Pong!" },
428 { "CSMSG_WUT_RESPONSE", "wut" },
429 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
430 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
431 { "CSMSG_BAD_DICE_COUNT", "%d is too many dice. Please use at most %d." },
432 { "CSMSG_DICE_ROLL", "The total is $b%d$b from rolling %dd%d+%d." },
433 { "CSMSG_DIE_ROLL", "A $b%d$b shows on the %d-sided die." },
434 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
435 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
438 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
442 /* eject_user and unban_user flags */
443 #define ACTION_KICK 0x0001
444 #define ACTION_BAN 0x0002
445 #define ACTION_ADD_BAN 0x0004
446 #define ACTION_ADD_TIMED_BAN 0x0008
447 #define ACTION_UNBAN 0x0010
448 #define ACTION_DEL_BAN 0x0020
450 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
451 #define MODELEN 40 + KEYLEN
455 #define CSFUNC_ARGS user, channel, argc, argv, cmd
457 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
458 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
459 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
460 reply("MSG_MISSING_PARAMS", argv[0]); \
464 DECLARE_LIST(dnrList, struct do_not_register *);
465 DEFINE_LIST(dnrList, struct do_not_register *);
467 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
469 struct userNode *chanserv;
471 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
472 static struct log_type *CS_LOG;
476 struct channelList support_channels;
477 struct mod_chanmode default_modes;
479 unsigned long db_backup_frequency;
480 unsigned long channel_expire_frequency;
483 unsigned int adjust_delay;
484 long channel_expire_delay;
485 unsigned int nodelete_level;
487 unsigned int adjust_threshold;
488 int join_flood_threshold;
490 unsigned int greeting_length;
491 unsigned int refresh_period;
493 unsigned int max_owned;
494 unsigned int max_chan_users;
495 unsigned int max_chan_bans;
497 struct string_list *set_shows;
498 struct string_list *eightball;
499 struct string_list *old_ban_names;
501 const char *ctcp_short_ban_duration;
502 const char *ctcp_long_ban_duration;
504 const char *irc_operator_epithet;
505 const char *network_helper_epithet;
506 const char *support_helper_epithet;
511 struct userNode *user;
512 struct userNode *bot;
513 struct chanNode *channel;
515 unsigned short lowest;
516 unsigned short highest;
517 struct userData **users;
518 struct helpfile_table table;
521 enum note_access_type
523 NOTE_SET_CHANNEL_ACCESS,
524 NOTE_SET_CHANNEL_SETTER,
528 enum note_visible_type
531 NOTE_VIS_CHANNEL_USERS,
537 enum note_access_type set_access_type;
539 unsigned int min_opserv;
540 unsigned short min_ulevel;
542 enum note_visible_type visible_type;
543 unsigned int max_length;
550 struct note_type *type;
551 char setter[NICKSERV_HANDLE_LEN+1];
555 static unsigned int registered_channels;
556 static unsigned int banCount;
558 static const struct {
561 unsigned short level;
564 { "peon", "Peon", UL_PEON, '+' },
565 { "op", "Op", UL_OP, '@' },
566 { "master", "Master", UL_MASTER, '%' },
567 { "coowner", "Coowner", UL_COOWNER, '*' },
568 { "owner", "Owner", UL_OWNER, '!' },
569 { "helper", "BUG:", UL_HELPER, 'X' }
572 static const struct {
575 unsigned short default_value;
576 unsigned int old_idx;
577 unsigned int old_flag;
578 unsigned short flag_value;
580 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
581 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
582 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
583 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
584 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
585 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
586 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
587 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
588 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
589 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
590 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
593 struct charOptionValues {
596 } protectValues[] = {
597 { 'a', "CSMSG_PROTECT_ALL" },
598 { 'e', "CSMSG_PROTECT_EQUAL" },
599 { 'l', "CSMSG_PROTECT_LOWER" },
600 { 'n', "CSMSG_PROTECT_NONE" }
602 { 'd', "CSMSG_TOYS_DISABLED" },
603 { 'n', "CSMSG_TOYS_PRIVATE" },
604 { 'p', "CSMSG_TOYS_PUBLIC" }
605 }, topicRefreshValues[] = {
606 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
607 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
608 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
609 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
610 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
611 }, ctcpReactionValues[] = {
612 { 'k', "CSMSG_CTCPREACTION_KICK" },
613 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
614 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
615 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
618 static const struct {
622 unsigned int old_idx;
624 struct charOptionValues *values;
626 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
627 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
628 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
629 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
632 struct userData *helperList;
633 struct chanData *channelList;
634 static struct module *chanserv_module;
635 static unsigned int userCount;
637 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
638 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
639 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
642 user_level_from_name(const char *name, unsigned short clamp_level)
644 unsigned int level = 0, ii;
647 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
648 if(!irccasecmp(name, accessLevels[ii].name))
649 level = accessLevels[ii].level;
650 if(level > clamp_level)
656 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
659 *minl = strtoul(arg, &sep, 10);
667 *maxl = strtoul(sep+1, &sep, 10);
675 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
677 struct userData *uData, **head;
679 if(!channel || !handle)
682 if(override && HANDLE_FLAGGED(handle, HELPING)
683 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
685 for(uData = helperList;
686 uData && uData->handle != handle;
687 uData = uData->next);
691 uData = calloc(1, sizeof(struct userData));
692 uData->handle = handle;
694 uData->access = UL_HELPER;
700 uData->next = helperList;
702 helperList->prev = uData;
710 for(uData = channel->users; uData; uData = uData->next)
711 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
714 head = &(channel->users);
717 if(uData && (uData != *head))
719 /* Shuffle the user to the head of whatever list he was in. */
721 uData->next->prev = uData->prev;
723 uData->prev->next = uData->next;
729 (**head).prev = uData;
736 /* Returns non-zero if user has at least the minimum access.
737 * exempt_owner is set when handling !set, so the owner can set things
740 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
742 struct userData *uData;
743 struct chanData *cData = channel->channel_info;
744 unsigned short minimum = cData->lvlOpts[opt];
747 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
750 if(minimum <= uData->access)
752 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
757 /* Scan for other users authenticated to the same handle
758 still in the channel. If so, keep them listed as present.
760 user is optional, if not null, it skips checking that userNode
761 (for the handle_part function) */
763 scan_user_presence(struct userData *uData, struct userNode *user)
767 if(IsSuspended(uData->channel)
768 || IsUserSuspended(uData)
769 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
781 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
783 unsigned int eflags, argc;
785 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
787 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
788 if(!channel->channel_info
789 || IsSuspended(channel->channel_info)
791 || !ircncasecmp(text, "ACTION ", 7))
793 /* Figure out the minimum level needed to CTCP the channel */
794 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
796 /* We need to enforce against them; do so. */
799 argv[1] = user->nick;
801 if(GetUserMode(channel, user))
802 eflags |= ACTION_KICK;
803 switch(channel->channel_info->chOpts[chCTCPReaction]) {
804 default: case 'k': /* just do the kick */ break;
806 eflags |= ACTION_BAN;
809 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
810 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
813 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
814 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
817 argv[argc++] = bad_ctcp_reason;
818 eject_user(chanserv, channel, argc, argv, NULL, eflags);
822 chanserv_create_note_type(const char *name)
824 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
825 strcpy(ntype->name, name);
827 dict_insert(note_types, ntype->name, ntype);
832 chanserv_deref_note_type(void *data)
834 struct note_type *ntype = data;
836 if(--ntype->refs > 0)
842 chanserv_flush_note_type(struct note_type *ntype)
844 struct chanData *cData;
845 for(cData = channelList; cData; cData = cData->next)
846 dict_remove(cData->notes, ntype->name);
850 chanserv_truncate_notes(struct note_type *ntype)
852 struct chanData *cData;
854 unsigned int size = sizeof(*note) + ntype->max_length;
856 for(cData = channelList; cData; cData = cData->next) {
857 note = dict_find(cData->notes, ntype->name, NULL);
860 if(strlen(note->note) <= ntype->max_length)
862 dict_remove2(cData->notes, ntype->name, 1);
863 note = realloc(note, size);
864 note->note[ntype->max_length] = 0;
865 dict_insert(cData->notes, ntype->name, note);
869 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
872 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
875 unsigned int len = strlen(text);
877 if(len > type->max_length) len = type->max_length;
878 note = calloc(1, sizeof(*note) + len);
880 strncpy(note->setter, setter, sizeof(note->setter)-1);
881 memcpy(note->note, text, len);
883 dict_insert(channel->notes, type->name, note);
889 chanserv_free_note(void *data)
891 struct note *note = data;
893 chanserv_deref_note_type(note->type);
894 assert(note->type->refs > 0); /* must use delnote to remove the type */
898 static MODCMD_FUNC(cmd_createnote) {
899 struct note_type *ntype;
900 unsigned int arg = 1, existed = 0, max_length;
902 if((ntype = dict_find(note_types, argv[1], NULL)))
905 ntype = chanserv_create_note_type(argv[arg]);
906 if(!irccasecmp(argv[++arg], "privileged"))
909 ntype->set_access_type = NOTE_SET_PRIVILEGED;
910 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
912 else if(!irccasecmp(argv[arg], "channel"))
914 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
917 reply("CSMSG_INVALID_ACCESS", argv[arg]);
920 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
921 ntype->set_access.min_ulevel = ulvl;
923 else if(!irccasecmp(argv[arg], "setter"))
925 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
929 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
933 if(!irccasecmp(argv[++arg], "privileged"))
934 ntype->visible_type = NOTE_VIS_PRIVILEGED;
935 else if(!irccasecmp(argv[arg], "channel_users"))
936 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
937 else if(!irccasecmp(argv[arg], "all"))
938 ntype->visible_type = NOTE_VIS_ALL;
940 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
944 if((arg+1) >= argc) {
945 reply("MSG_MISSING_PARAMS", argv[0]);
948 max_length = strtoul(argv[++arg], NULL, 0);
949 if(max_length < 20 || max_length > 450)
951 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
954 if(existed && (max_length < ntype->max_length))
956 ntype->max_length = max_length;
957 chanserv_truncate_notes(ntype);
959 ntype->max_length = max_length;
962 reply("CSMSG_NOTE_MODIFIED", ntype->name);
964 reply("CSMSG_NOTE_CREATED", ntype->name);
969 dict_remove(note_types, ntype->name);
973 static MODCMD_FUNC(cmd_removenote) {
974 struct note_type *ntype;
977 ntype = dict_find(note_types, argv[1], NULL);
978 force = (argc > 2) && !irccasecmp(argv[2], "force");
981 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
988 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
991 chanserv_flush_note_type(ntype);
993 dict_remove(note_types, argv[1]);
994 reply("CSMSG_NOTE_DELETED", argv[1]);
999 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1003 if(orig->modes_set & change->modes_clear)
1005 if(orig->modes_clear & change->modes_set)
1007 if((orig->modes_set & MODE_KEY)
1008 && strcmp(orig->new_key, change->new_key))
1010 if((orig->modes_set & MODE_LIMIT)
1011 && (orig->new_limit != change->new_limit))
1016 static char max_length_text[MAXLEN+1][16];
1018 static struct helpfile_expansion
1019 chanserv_expand_variable(const char *variable)
1021 struct helpfile_expansion exp;
1023 if(!irccasecmp(variable, "notes"))
1026 exp.type = HF_TABLE;
1027 exp.value.table.length = 1;
1028 exp.value.table.width = 3;
1029 exp.value.table.flags = 0;
1030 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1031 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1032 exp.value.table.contents[0][0] = "Note Type";
1033 exp.value.table.contents[0][1] = "Visibility";
1034 exp.value.table.contents[0][2] = "Max Length";
1035 for(it=dict_first(note_types); it; it=iter_next(it))
1037 struct note_type *ntype = iter_data(it);
1040 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1041 row = exp.value.table.length++;
1042 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1043 exp.value.table.contents[row][0] = ntype->name;
1044 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1045 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1047 if(!max_length_text[ntype->max_length][0])
1048 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1049 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1054 exp.type = HF_STRING;
1055 exp.value.str = NULL;
1059 static struct chanData*
1060 register_channel(struct chanNode *cNode, char *registrar)
1062 struct chanData *channel;
1063 enum levelOption lvlOpt;
1064 enum charOption chOpt;
1066 channel = calloc(1, sizeof(struct chanData));
1068 channel->notes = dict_new();
1069 dict_set_free_data(channel->notes, chanserv_free_note);
1071 channel->registrar = strdup(registrar);
1072 channel->registered = now;
1073 channel->visited = now;
1074 channel->limitAdjusted = now;
1075 channel->flags = CHANNEL_DEFAULT_FLAGS;
1076 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1077 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1078 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1079 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1081 channel->prev = NULL;
1082 channel->next = channelList;
1085 channelList->prev = channel;
1086 channelList = channel;
1087 registered_channels++;
1089 channel->channel = cNode;
1091 cNode->channel_info = channel;
1096 static struct userData*
1097 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1099 struct userData *ud;
1101 if(access > UL_OWNER)
1104 ud = calloc(1, sizeof(*ud));
1105 ud->channel = channel;
1106 ud->handle = handle;
1108 ud->access = access;
1109 ud->info = info ? strdup(info) : NULL;
1112 ud->next = channel->users;
1114 channel->users->prev = ud;
1115 channel->users = ud;
1117 channel->userCount++;
1121 ud->u_next = ud->handle->channels;
1123 ud->u_next->u_prev = ud;
1124 ud->handle->channels = ud;
1129 static void unregister_channel(struct chanData *channel, const char *reason);
1132 del_channel_user(struct userData *user, int do_gc)
1134 struct chanData *channel = user->channel;
1136 channel->userCount--;
1140 user->prev->next = user->next;
1142 channel->users = user->next;
1144 user->next->prev = user->prev;
1147 user->u_prev->u_next = user->u_next;
1149 user->handle->channels = user->u_next;
1151 user->u_next->u_prev = user->u_prev;
1155 if(do_gc && !channel->users && !IsProtected(channel))
1156 unregister_channel(channel, "lost all users.");
1159 static void expire_ban(void *data);
1161 static struct banData*
1162 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1165 unsigned int ii, l1, l2;
1170 bd = malloc(sizeof(struct banData));
1172 bd->channel = channel;
1174 bd->triggered = triggered;
1175 bd->expires = expires;
1177 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1179 extern const char *hidden_host_suffix;
1180 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1184 l2 = strlen(old_name);
1187 if(irccasecmp(mask + l1 - l2, old_name))
1189 new_mask = alloca(MAXLEN);
1190 sprintf(new_mask, "%.*s%s", l1-l2, mask, hidden_host_suffix);
1193 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1195 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1196 bd->reason = reason ? strdup(reason) : NULL;
1199 timeq_add(expires, expire_ban, bd);
1202 bd->next = channel->bans;
1204 channel->bans->prev = bd;
1206 channel->banCount++;
1213 del_channel_ban(struct banData *ban)
1215 ban->channel->banCount--;
1219 ban->prev->next = ban->next;
1221 ban->channel->bans = ban->next;
1224 ban->next->prev = ban->prev;
1227 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1236 expire_ban(void *data)
1238 struct banData *bd = data;
1239 if(!IsSuspended(bd->channel))
1241 struct banList bans;
1242 struct mod_chanmode change;
1244 bans = bd->channel->channel->banlist;
1245 mod_chanmode_init(&change);
1246 for(ii=0; ii<bans.used; ii++)
1248 if(!strcmp(bans.list[ii]->ban, bd->mask))
1251 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1252 change.args[0].hostmask = bd->mask;
1253 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1259 del_channel_ban(bd);
1262 static void chanserv_expire_suspension(void *data);
1265 unregister_channel(struct chanData *channel, const char *reason)
1267 char msgbuf[MAXLEN];
1269 /* After channel unregistration, the following must be cleaned
1271 - Channel information.
1274 - Channel suspension data.
1275 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1281 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1283 while(channel->users)
1284 del_channel_user(channel->users, 0);
1286 while(channel->bans)
1287 del_channel_ban(channel->bans);
1289 if(channel->topic) free(channel->topic);
1290 if(channel->registrar) free(channel->registrar);
1291 if(channel->greeting) free(channel->greeting);
1292 if(channel->user_greeting) free(channel->user_greeting);
1293 if(channel->topic_mask) free(channel->topic_mask);
1295 if(channel->prev) channel->prev->next = channel->next;
1296 else channelList = channel->next;
1298 if(channel->next) channel->next->prev = channel->prev;
1300 if(channel->suspended)
1302 struct chanNode *cNode = channel->channel;
1303 struct suspended *suspended, *next_suspended;
1305 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1307 next_suspended = suspended->previous;
1308 free(suspended->suspender);
1309 free(suspended->reason);
1310 if(suspended->expires)
1311 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1316 cNode->channel_info = NULL;
1318 channel->channel->channel_info = NULL;
1321 dict_delete(channel->notes);
1322 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1323 if(!IsSuspended(channel))
1324 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1325 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1326 UnlockChannel(channel->channel);
1328 registered_channels--;
1332 expire_channels(UNUSED_ARG(void *data))
1334 struct chanData *channel, *next;
1335 struct userData *user;
1336 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1338 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1339 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1341 for(channel = channelList; channel; channel = next)
1343 next = channel->next;
1345 /* See if the channel can be expired. */
1346 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1347 || IsProtected(channel))
1350 /* Make sure there are no high-ranking users still in the channel. */
1351 for(user=channel->users; user; user=user->next)
1352 if(user->present && (user->access >= UL_PRESENT))
1357 /* Unregister the channel */
1358 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1359 unregister_channel(channel, "registration expired.");
1362 if(chanserv_conf.channel_expire_frequency)
1363 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1367 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1369 char protect = channel->chOpts[chProtect];
1370 struct userData *cs_victim, *cs_aggressor;
1372 /* Don't protect if no one is to be protected, someone is attacking
1373 himself, or if the aggressor is an IRC Operator. */
1374 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1377 /* Don't protect if the victim isn't authenticated (because they
1378 can't be a channel user), unless we are to protect non-users
1380 cs_victim = GetChannelAccess(channel, victim->handle_info);
1381 if(protect != 'a' && !cs_victim)
1384 /* Protect if the aggressor isn't a user because at this point,
1385 the aggressor can only be less than or equal to the victim. */
1386 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1390 /* If the aggressor was a user, then the victim can't be helped. */
1397 if(cs_victim->access > cs_aggressor->access)
1402 if(cs_victim->access >= cs_aggressor->access)
1411 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1413 struct chanData *cData = channel->channel_info;
1414 struct userData *cs_victim;
1416 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1417 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1418 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1420 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1428 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1430 if(IsService(victim))
1432 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1436 if(protect_user(victim, user, channel->channel_info))
1438 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1445 static struct do_not_register *
1446 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1448 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1449 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1450 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1451 strcpy(dnr->reason, reason);
1453 if(dnr->chan_name[0] == '*')
1454 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1455 else if(strpbrk(dnr->chan_name, "*?"))
1456 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1458 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1462 static struct dnrList
1463 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1465 struct dnrList list;
1467 struct do_not_register *dnr;
1469 dnrList_init(&list);
1470 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1471 dnrList_append(&list, dnr);
1472 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1473 dnrList_append(&list, dnr);
1475 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1476 if(match_ircglob(chan_name, iter_key(it)))
1477 dnrList_append(&list, iter_data(it));
1482 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1484 struct dnrList list;
1485 struct do_not_register *dnr;
1487 char buf[INTERVALLEN];
1489 list = chanserv_find_dnrs(chan_name, handle);
1490 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1492 dnr = list.list[ii];
1495 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1496 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1499 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1502 reply("CSMSG_MORE_DNRS", list.used - ii);
1507 struct do_not_register *
1508 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1510 struct do_not_register *dnr;
1513 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1517 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1519 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1520 if(match_ircglob(chan_name, iter_key(it)))
1521 return iter_data(it);
1526 static CHANSERV_FUNC(cmd_noregister)
1529 struct do_not_register *dnr;
1530 char buf[INTERVALLEN];
1531 unsigned int matches;
1537 reply("CSMSG_DNR_SEARCH_RESULTS");
1539 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1541 dnr = iter_data(it);
1543 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1545 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1548 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1550 dnr = iter_data(it);
1552 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1554 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1557 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1559 dnr = iter_data(it);
1561 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1563 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1568 reply("MSG_MATCH_COUNT", matches);
1570 reply("MSG_NO_MATCHES");
1576 if(!IsChannelName(target) && (*target != '*'))
1578 reply("CSMSG_NOT_DNR", target);
1584 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1585 if((*target == '*') && !get_handle_info(target + 1))
1587 reply("MSG_HANDLE_UNKNOWN", target + 1);
1590 chanserv_add_dnr(target, user->handle_info->handle, reason);
1591 reply("CSMSG_NOREGISTER_CHANNEL", target);
1595 reply("CSMSG_DNR_SEARCH_RESULTS");
1597 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1599 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1601 reply("MSG_NO_MATCHES");
1605 static CHANSERV_FUNC(cmd_allowregister)
1607 const char *chan_name = argv[1];
1609 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1611 dict_remove(handle_dnrs, chan_name+1);
1612 reply("CSMSG_DNR_REMOVED", chan_name);
1614 else if(dict_find(plain_dnrs, chan_name, NULL))
1616 dict_remove(plain_dnrs, chan_name);
1617 reply("CSMSG_DNR_REMOVED", chan_name);
1619 else if(dict_find(mask_dnrs, chan_name, NULL))
1621 dict_remove(mask_dnrs, chan_name);
1622 reply("CSMSG_DNR_REMOVED", chan_name);
1626 reply("CSMSG_NO_SUCH_DNR", chan_name);
1633 chanserv_get_owned_count(struct handle_info *hi)
1635 struct userData *cList;
1638 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1639 if(cList->access == UL_OWNER)
1644 static CHANSERV_FUNC(cmd_register)
1646 struct mod_chanmode *change;
1647 struct handle_info *handle;
1648 struct chanData *cData;
1649 struct modeNode *mn;
1650 char reason[MAXLEN];
1652 unsigned int new_channel, force=0;
1653 struct do_not_register *dnr;
1657 if(channel->channel_info)
1659 reply("CSMSG_ALREADY_REGGED", channel->name);
1663 if(channel->bad_channel)
1665 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1669 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1671 reply("CSMSG_MUST_BE_OPPED", channel->name);
1676 chan_name = channel->name;
1680 if((argc < 2) || !IsChannelName(argv[1]))
1682 reply("MSG_NOT_CHANNEL_NAME");
1686 if(opserv_bad_channel(argv[1]))
1688 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1693 chan_name = argv[1];
1696 if(argc >= (new_channel+2))
1698 if(!IsHelping(user))
1700 reply("CSMSG_PROXY_FORBIDDEN");
1704 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1706 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1707 dnr = chanserv_is_dnr(chan_name, handle);
1711 handle = user->handle_info;
1712 dnr = chanserv_is_dnr(chan_name, handle);
1716 if(!IsHelping(user))
1717 reply("CSMSG_DNR_CHANNEL", chan_name);
1719 chanserv_show_dnrs(user, cmd, chan_name, handle);
1723 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1725 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1730 channel = AddChannel(argv[1], now, NULL, NULL);
1732 cData = register_channel(channel, user->handle_info->handle);
1733 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1734 cData->modes = chanserv_conf.default_modes;
1735 change = mod_chanmode_dup(&cData->modes, 1);
1736 change->args[change->argc].mode = MODE_CHANOP;
1737 change->args[change->argc].member = AddChannelUser(chanserv, channel);
1739 mod_chanmode_announce(chanserv, channel, change);
1740 mod_chanmode_free(change);
1742 /* Initialize the channel's max user record. */
1743 cData->max = channel->members.used;
1745 if(handle != user->handle_info)
1746 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1748 reply("CSMSG_REG_SUCCESS", channel->name);
1750 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1751 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1756 make_confirmation_string(struct userData *uData)
1758 static char strbuf[16];
1763 for(src = uData->handle->handle; *src; )
1764 accum = accum * 31 + toupper(*src++);
1766 for(src = uData->channel->channel->name; *src; )
1767 accum = accum * 31 + toupper(*src++);
1768 sprintf(strbuf, "%08x", accum);
1772 static CHANSERV_FUNC(cmd_unregister)
1775 char reason[MAXLEN];
1776 struct chanData *cData;
1777 struct userData *uData;
1779 cData = channel->channel_info;
1782 reply("CSMSG_NOT_REGISTERED", channel->name);
1786 uData = GetChannelUser(cData, user->handle_info);
1787 if(!uData || (uData->access < UL_OWNER))
1789 reply("CSMSG_NO_ACCESS");
1793 if(IsProtected(cData))
1795 reply("CSMSG_UNREG_NODELETE", channel->name);
1799 if(!IsHelping(user))
1801 const char *confirm_string;
1802 if(IsSuspended(cData))
1804 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1807 confirm_string = make_confirmation_string(uData);
1808 if((argc < 2) || strcmp(argv[1], confirm_string))
1810 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1815 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1816 name = strdup(channel->name);
1817 unregister_channel(cData, reason);
1818 reply("CSMSG_UNREG_SUCCESS", name);
1823 static CHANSERV_FUNC(cmd_move)
1825 struct chanNode *target;
1826 struct modeNode *mn;
1827 struct userData *uData;
1828 char reason[MAXLEN];
1829 struct do_not_register *dnr;
1833 if(IsProtected(channel->channel_info))
1835 reply("CSMSG_MOVE_NODELETE", channel->name);
1839 if(!IsChannelName(argv[1]))
1841 reply("MSG_NOT_CHANNEL_NAME");
1845 if(opserv_bad_channel(argv[1]))
1847 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1851 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1853 for(uData = channel->channel_info->users; uData; uData = uData->next)
1855 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1857 if(!IsHelping(user))
1858 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1860 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1866 if(!(target = GetChannel(argv[1])))
1868 target = AddChannel(argv[1], now, NULL, NULL);
1869 if(!IsSuspended(channel->channel_info))
1870 AddChannelUser(chanserv, target);
1872 else if(target->channel_info)
1874 reply("CSMSG_ALREADY_REGGED", target->name);
1877 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1878 && !IsHelping(user))
1880 reply("CSMSG_MUST_BE_OPPED", target->name);
1883 else if(!IsSuspended(channel->channel_info))
1885 struct mod_chanmode change;
1886 mod_chanmode_init(&change);
1888 change.args[0].mode = MODE_CHANOP;
1889 change.args[0].member = AddChannelUser(chanserv, target);
1890 mod_chanmode_announce(chanserv, target, &change);
1893 /* Move the channel_info to the target channel; it
1894 shouldn't be necessary to clear timeq callbacks
1895 for the old channel. */
1896 target->channel_info = channel->channel_info;
1897 target->channel_info->channel = target;
1898 channel->channel_info = NULL;
1900 reply("CSMSG_MOVE_SUCCESS", target->name);
1902 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1903 if(!IsSuspended(target->channel_info))
1905 char reason2[MAXLEN];
1906 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1907 DelChannelUser(chanserv, channel, reason2, 0);
1909 UnlockChannel(channel);
1910 LockChannel(target);
1911 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1916 merge_users(struct chanData *source, struct chanData *target)
1918 struct userData *suData, *tuData, *next;
1924 /* Insert the source's users into the scratch area. */
1925 for(suData = source->users; suData; suData = suData->next)
1926 dict_insert(merge, suData->handle->handle, suData);
1928 /* Iterate through the target's users, looking for
1929 users common to both channels. The lower access is
1930 removed from either the scratch area or target user
1932 for(tuData = target->users; tuData; tuData = next)
1934 struct userData *choice;
1936 next = tuData->next;
1938 /* If a source user exists with the same handle as a target
1939 channel's user, resolve the conflict by removing one. */
1940 suData = dict_find(merge, tuData->handle->handle, NULL);
1944 /* Pick the data we want to keep. */
1945 /* If the access is the same, use the later seen time. */
1946 if(suData->access == tuData->access)
1947 choice = (suData->seen > tuData->seen) ? suData : tuData;
1948 else /* Otherwise, keep the higher access level. */
1949 choice = (suData->access > tuData->access) ? suData : tuData;
1951 /* Remove the user that wasn't picked. */
1952 if(choice == tuData)
1954 dict_remove(merge, suData->handle->handle);
1955 del_channel_user(suData, 0);
1958 del_channel_user(tuData, 0);
1961 /* Move the remaining users to the target channel. */
1962 for(it = dict_first(merge); it; it = iter_next(it))
1964 suData = iter_data(it);
1966 /* Insert the user into the target channel's linked list. */
1967 suData->prev = NULL;
1968 suData->next = target->users;
1969 suData->channel = target;
1972 target->users->prev = suData;
1973 target->users = suData;
1975 /* Update the user counts for the target channel; the
1976 source counts are left alone. */
1977 target->userCount++;
1980 /* Possible to assert (source->users == NULL) here. */
1981 source->users = NULL;
1986 merge_bans(struct chanData *source, struct chanData *target)
1988 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
1990 /* Hold on to the original head of the target ban list
1991 to avoid comparing source bans with source bans. */
1992 tFront = target->bans;
1994 /* Perform a totally expensive O(n*m) merge, ick. */
1995 for(sbData = source->bans; sbData; sbData = sNext)
1997 /* Flag to track whether the ban's been moved
1998 to the destination yet. */
2001 /* Possible to assert (sbData->prev == NULL) here. */
2002 sNext = sbData->next;
2004 for(tbData = tFront; tbData; tbData = tNext)
2006 tNext = tbData->next;
2008 /* Perform two comparisons between each source
2009 and target ban, conflicts are resolved by
2010 keeping the broader ban and copying the later
2011 expiration and triggered time. */
2012 if(match_ircglobs(tbData->mask, sbData->mask))
2014 /* There is a broader ban in the target channel that
2015 overrides one in the source channel; remove the
2016 source ban and break. */
2017 if(sbData->expires > tbData->expires)
2018 tbData->expires = sbData->expires;
2019 if(sbData->triggered > tbData->triggered)
2020 tbData->triggered = sbData->triggered;
2021 del_channel_ban(sbData);
2024 else if(match_ircglobs(sbData->mask, tbData->mask))
2026 /* There is a broader ban in the source channel that
2027 overrides one in the target channel; remove the
2028 target ban, fall through and move the source over. */
2029 if(tbData->expires > sbData->expires)
2030 sbData->expires = tbData->expires;
2031 if(tbData->triggered > sbData->triggered)
2032 sbData->triggered = tbData->triggered;
2033 if(tbData == tFront)
2035 del_channel_ban(tbData);
2038 /* Source bans can override multiple target bans, so
2039 we allow a source to run through this loop multiple
2040 times, but we can only move it once. */
2045 /* Remove the source ban from the source ban list. */
2047 sbData->next->prev = sbData->prev;
2049 /* Modify the source ban's associated channel. */
2050 sbData->channel = target;
2052 /* Insert the ban into the target channel's linked list. */
2053 sbData->prev = NULL;
2054 sbData->next = target->bans;
2057 target->bans->prev = sbData;
2058 target->bans = sbData;
2060 /* Update the user counts for the target channel. */
2065 /* Possible to assert (source->bans == NULL) here. */
2066 source->bans = NULL;
2070 merge_data(struct chanData *source, struct chanData *target)
2072 if(source->visited > target->visited)
2073 target->visited = source->visited;
2077 merge_channel(struct chanData *source, struct chanData *target)
2079 merge_users(source, target);
2080 merge_bans(source, target);
2081 merge_data(source, target);
2084 static CHANSERV_FUNC(cmd_merge)
2086 struct userData *target_user;
2087 struct chanNode *target;
2088 char reason[MAXLEN];
2092 /* Make sure the target channel exists and is registered to the user
2093 performing the command. */
2094 if(!(target = GetChannel(argv[1])))
2096 reply("MSG_INVALID_CHANNEL");
2100 if(!target->channel_info)
2102 reply("CSMSG_NOT_REGISTERED", target->name);
2106 if(IsProtected(channel->channel_info))
2108 reply("CSMSG_MERGE_NODELETE");
2112 if(IsSuspended(target->channel_info))
2114 reply("CSMSG_MERGE_SUSPENDED");
2118 if(channel == target)
2120 reply("CSMSG_MERGE_SELF");
2124 target_user = GetChannelUser(target->channel_info, user->handle_info);
2125 if(!target_user || (target_user->access < UL_OWNER))
2127 reply("CSMSG_MERGE_NOT_OWNER");
2131 /* Merge the channel structures and associated data. */
2132 merge_channel(channel->channel_info, target->channel_info);
2133 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2134 unregister_channel(channel->channel_info, reason);
2135 reply("CSMSG_MERGE_SUCCESS", target->name);
2139 static CHANSERV_FUNC(cmd_opchan)
2141 struct mod_chanmode change;
2142 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2144 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2147 channel->channel_info->may_opchan = 0;
2148 mod_chanmode_init(&change);
2150 change.args[0].mode = MODE_CHANOP;
2151 change.args[0].member = GetUserMode(channel, chanserv);
2152 mod_chanmode_announce(chanserv, channel, &change);
2153 reply("CSMSG_OPCHAN_DONE", channel->name);
2157 static CHANSERV_FUNC(cmd_adduser)
2159 struct userData *actee;
2160 struct userData *actor;
2161 struct handle_info *handle;
2162 unsigned short access;
2166 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2168 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2172 access = user_level_from_name(argv[2], UL_OWNER);
2175 reply("CSMSG_INVALID_ACCESS", argv[2]);
2179 actor = GetChannelUser(channel->channel_info, user->handle_info);
2180 if(actor->access <= access)
2182 reply("CSMSG_NO_BUMP_ACCESS");
2186 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2189 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2191 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2195 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2196 scan_user_presence(actee, NULL);
2197 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2201 static CHANSERV_FUNC(cmd_clvl)
2203 struct handle_info *handle;
2204 struct userData *victim;
2205 struct userData *actor;
2206 unsigned short new_access;
2207 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2211 actor = GetChannelUser(channel->channel_info, user->handle_info);
2213 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2216 if(handle == user->handle_info && !privileged)
2218 reply("CSMSG_NO_SELF_CLVL");
2222 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2224 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2228 if(actor->access <= victim->access && !privileged)
2230 reply("MSG_USER_OUTRANKED", handle->handle);
2234 new_access = user_level_from_name(argv[2], UL_OWNER);
2238 reply("CSMSG_INVALID_ACCESS", argv[2]);
2242 if(new_access >= actor->access && !privileged)
2244 reply("CSMSG_NO_BUMP_ACCESS");
2248 victim->access = new_access;
2249 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2253 static CHANSERV_FUNC(cmd_deluser)
2255 struct handle_info *handle;
2256 struct userData *victim;
2257 struct userData *actor;
2258 unsigned short access;
2263 actor = GetChannelUser(channel->channel_info, user->handle_info);
2265 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2268 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2270 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2276 access = user_level_from_name(argv[1], UL_OWNER);
2279 reply("CSMSG_INVALID_ACCESS", argv[1]);
2282 if(access != victim->access)
2284 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2290 access = victim->access;
2293 if((actor->access <= victim->access) && !IsHelping(user))
2295 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2299 chan_name = strdup(channel->name);
2300 del_channel_user(victim, 1);
2301 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2307 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2309 struct userData *actor, *uData, *next;
2311 actor = GetChannelUser(channel->channel_info, user->handle_info);
2313 if(min_access > max_access)
2315 reply("CSMSG_BAD_RANGE", min_access, max_access);
2319 if((actor->access <= max_access) && !IsHelping(user))
2321 reply("CSMSG_NO_ACCESS");
2325 for(uData = channel->channel_info->users; uData; uData = next)
2329 if((uData->access >= min_access)
2330 && (uData->access <= max_access)
2331 && match_ircglob(uData->handle->handle, mask))
2332 del_channel_user(uData, 1);
2335 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2339 static CHANSERV_FUNC(cmd_mdelowner)
2341 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2344 static CHANSERV_FUNC(cmd_mdelcoowner)
2346 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2349 static CHANSERV_FUNC(cmd_mdelmaster)
2351 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2354 static CHANSERV_FUNC(cmd_mdelop)
2356 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2359 static CHANSERV_FUNC(cmd_mdelpeon)
2361 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2365 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2367 struct banData *bData, *next;
2368 char interval[INTERVALLEN];
2373 limit = now - duration;
2374 for(bData = channel->channel_info->bans; bData; bData = next)
2378 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2381 del_channel_ban(bData);
2385 intervalString(interval, duration, user->handle_info);
2386 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2391 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2393 struct userData *actor, *uData, *next;
2394 char interval[INTERVALLEN];
2398 actor = GetChannelUser(channel->channel_info, user->handle_info);
2399 if(min_access > max_access)
2401 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2405 if((actor->access <= max_access) && !IsHelping(user))
2407 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2412 limit = now - duration;
2413 for(uData = channel->channel_info->users; uData; uData = next)
2417 if((uData->seen > limit) || uData->present)
2420 if(((uData->access >= min_access) && (uData->access <= max_access))
2421 || (!max_access && (uData->access < actor->access)))
2423 del_channel_user(uData, 1);
2431 max_access = UL_OWNER;
2433 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2437 static CHANSERV_FUNC(cmd_trim)
2439 unsigned long duration;
2440 unsigned short min_level, max_level;
2444 duration = ParseInterval(argv[2]);
2447 reply("CSMSG_CANNOT_TRIM");
2451 if(!irccasecmp(argv[1], "bans"))
2453 cmd_trim_bans(user, channel, duration);
2456 else if(!irccasecmp(argv[1], "users"))
2458 cmd_trim_users(user, channel, 0, 0, duration);
2461 else if(parse_level_range(&min_level, &max_level, argv[1]))
2463 cmd_trim_users(user, channel, min_level, max_level, duration);
2466 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2468 cmd_trim_users(user, channel, min_level, min_level, duration);
2473 reply("CSMSG_INVALID_TRIM", argv[1]);
2478 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2479 to the user. cmd_all takes advantage of this. */
2480 static CHANSERV_FUNC(cmd_up)
2482 struct mod_chanmode change;
2483 struct userData *uData;
2486 mod_chanmode_init(&change);
2488 change.args[0].member = GetUserMode(channel, user);
2489 if(!change.args[0].member)
2492 reply("MSG_CHANNEL_ABSENT", channel->name);
2496 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2500 reply("CSMSG_GODMODE_UP", argv[0]);
2503 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2505 change.args[0].mode = MODE_CHANOP;
2506 errmsg = "CSMSG_ALREADY_OPPED";
2510 change.args[0].mode = MODE_VOICE;
2511 errmsg = "CSMSG_ALREADY_VOICED";
2513 change.args[0].mode &= ~change.args[0].member->modes;
2514 if(!change.args[0].mode)
2517 reply(errmsg, channel->name);
2520 modcmd_chanmode_announce(&change);
2524 static CHANSERV_FUNC(cmd_down)
2526 struct mod_chanmode change;
2528 mod_chanmode_init(&change);
2530 change.args[0].member = GetUserMode(channel, user);
2531 if(!change.args[0].member)
2534 reply("MSG_CHANNEL_ABSENT", channel->name);
2538 if(!change.args[0].member->modes)
2541 reply("CSMSG_ALREADY_DOWN", channel->name);
2545 change.args[0].mode = MODE_REMOVE | change.args[0].member->modes;
2546 modcmd_chanmode_announce(&change);
2550 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)
2552 struct userData *cList;
2554 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2556 if(IsSuspended(cList->channel)
2557 || IsUserSuspended(cList)
2558 || !GetUserMode(cList->channel->channel, user))
2561 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2567 static CHANSERV_FUNC(cmd_upall)
2569 return cmd_all(CSFUNC_ARGS, cmd_up);
2572 static CHANSERV_FUNC(cmd_downall)
2574 return cmd_all(CSFUNC_ARGS, cmd_down);
2577 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2578 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2581 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)
2583 unsigned int ii, valid;
2584 struct userNode *victim;
2585 struct mod_chanmode *change;
2587 change = mod_chanmode_alloc(argc - 1);
2589 for(ii=valid=0; ++ii < argc; )
2591 if(!(victim = GetUserH(argv[ii])))
2593 change->args[valid].mode = mode;
2594 change->args[valid].member = GetUserMode(channel, victim);
2595 if(!change->args[valid].member)
2597 if(validate && !validate(user, channel, victim))
2602 change->argc = valid;
2603 if(valid < (argc-1))
2604 reply("CSMSG_PROCESS_FAILED");
2607 modcmd_chanmode_announce(change);
2608 reply(action, channel->name);
2610 mod_chanmode_free(change);
2614 static CHANSERV_FUNC(cmd_op)
2616 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2619 static CHANSERV_FUNC(cmd_deop)
2621 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2624 static CHANSERV_FUNC(cmd_voice)
2626 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2629 static CHANSERV_FUNC(cmd_devoice)
2631 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2635 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2641 for(ii=0; ii<channel->members.used; ii++)
2643 struct modeNode *mn = channel->members.list[ii];
2645 if(IsService(mn->user))
2648 if(!user_matches_glob(mn->user, ban, 1))
2651 if(protect_user(mn->user, user, channel->channel_info))
2655 victims[(*victimCount)++] = mn;
2661 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2663 struct userNode *victim;
2664 struct modeNode **victims;
2665 unsigned int offset, n, victimCount, duration = 0;
2666 char *reason = "Bye.", *ban, *name;
2667 char interval[INTERVALLEN];
2669 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2670 REQUIRE_PARAMS(offset);
2673 reason = unsplit_string(argv + offset, argc - offset, NULL);
2674 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2676 /* Truncate the reason to a length of TOPICLEN, as
2677 the ircd does; however, leave room for an ellipsis
2678 and the kicker's nick. */
2679 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2683 if((victim = GetUserH(argv[1])))
2685 victims = alloca(sizeof(victims[0]));
2686 victims[0] = GetUserMode(channel, victim);
2687 /* XXX: The comparison with ACTION_KICK is just because all
2688 * other actions can work on users outside the channel, and we
2689 * want to allow those (e.g. unbans) in that case. If we add
2690 * some other ejection action for in-channel users, change
2692 victimCount = victims[0] ? 1 : 0;
2694 if(IsService(victim))
2696 reply("MSG_SERVICE_IMMUNE", victim->nick);
2700 if((action == ACTION_KICK) && !victimCount)
2702 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2706 if(protect_user(victim, user, channel->channel_info))
2708 reply("CSMSG_USER_PROTECTED", victim->nick);
2712 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2713 name = victim->nick;
2717 if(!is_ircmask(argv[1]))
2719 reply("MSG_NICK_UNKNOWN", argv[1]);
2723 victims = alloca(sizeof(victims[0]) * channel->members.used);
2725 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2727 reply("CSMSG_MASK_PROTECTED", argv[1]);
2731 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2733 reply("CSMSG_LAME_MASK", argv[1]);
2737 if((action == ACTION_KICK) && (victimCount == 0))
2739 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2743 name = ban = strdup(argv[1]);
2746 /* Truncate the ban in place if necessary; we must ensure
2747 that 'ban' is a valid ban mask before sanitizing it. */
2748 sanitize_ircmask(ban);
2750 if(action & ACTION_ADD_BAN)
2752 struct banData *bData, *next;
2754 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2756 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2761 if(action & ACTION_ADD_TIMED_BAN)
2763 duration = ParseInterval(argv[2]);
2767 reply("CSMSG_DURATION_TOO_LOW");
2771 else if(duration > (86400 * 365 * 2))
2773 reply("CSMSG_DURATION_TOO_HIGH");
2779 for(bData = channel->channel_info->bans; bData; bData = next)
2781 if(match_ircglobs(bData->mask, ban))
2783 int exact = !irccasecmp(bData->mask, ban);
2785 /* The ban is redundant; there is already a ban
2786 with the same effect in place. */
2790 free(bData->reason);
2791 bData->reason = strdup(reason);
2792 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2794 reply("CSMSG_REASON_CHANGE", ban);
2798 if(exact && bData->expires)
2802 /* If the ban matches an existing one exactly,
2803 extend the expiration time if the provided
2804 duration is longer. */
2805 if(duration && ((time_t)(now + duration) > bData->expires))
2807 bData->expires = now + duration;
2818 /* Delete the expiration timeq entry and
2819 requeue if necessary. */
2820 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2823 timeq_add(bData->expires, expire_ban, bData);
2827 /* automated kickban */
2830 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2832 reply("CSMSG_BAN_ADDED", name, channel->name);
2838 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2845 if(match_ircglobs(ban, bData->mask))
2847 /* The ban we are adding makes previously existing
2848 bans redundant; silently remove them. */
2849 del_channel_ban(bData);
2853 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);
2855 name = ban = strdup(bData->mask);
2859 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2861 extern const char *hidden_host_suffix;
2862 const char *old_name = chanserv_conf.old_ban_names->list[n];
2864 unsigned int l1, l2;
2867 l2 = strlen(old_name);
2870 if(irccasecmp(ban + l1 - l2, old_name))
2872 new_mask = malloc(MAXLEN);
2873 sprintf(new_mask, "%.*s%s", l1-l2, ban, hidden_host_suffix);
2875 name = ban = new_mask;
2880 if(action & ACTION_BAN)
2882 unsigned int exists;
2883 struct mod_chanmode *change;
2885 if(channel->banlist.used >= MAXBANS)
2888 reply("CSMSG_BANLIST_FULL", channel->name);
2893 exists = ChannelBanExists(channel, ban);
2894 change = mod_chanmode_alloc(victimCount + 1);
2895 for(n = 0; n < victimCount; ++n)
2897 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2898 change->args[n].member = victims[n];
2902 change->args[n].mode = MODE_BAN;
2903 change->args[n++].hostmask = ban;
2907 modcmd_chanmode_announce(change);
2909 mod_chanmode_announce(chanserv, channel, change);
2910 mod_chanmode_free(change);
2912 if(exists && (action == ACTION_BAN))
2915 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2921 if(action & ACTION_KICK)
2923 char kick_reason[MAXLEN];
2924 sprintf(kick_reason, "%s (%s)", reason, user->nick);
2926 for(n = 0; n < victimCount; n++)
2927 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2932 /* No response, since it was automated. */
2934 else if(action & ACTION_ADD_BAN)
2937 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2939 reply("CSMSG_BAN_ADDED", name, channel->name);
2941 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2942 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2943 else if(action & ACTION_BAN)
2944 reply("CSMSG_BAN_DONE", name, channel->name);
2945 else if(action & ACTION_KICK && victimCount)
2946 reply("CSMSG_KICK_DONE", name, channel->name);
2952 static CHANSERV_FUNC(cmd_kickban)
2954 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2957 static CHANSERV_FUNC(cmd_kick)
2959 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2962 static CHANSERV_FUNC(cmd_ban)
2964 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2967 static CHANSERV_FUNC(cmd_addban)
2969 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
2972 static CHANSERV_FUNC(cmd_addtimedban)
2974 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
2977 static struct mod_chanmode *
2978 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
2980 struct mod_chanmode *change;
2981 unsigned char *match;
2982 unsigned int ii, count;
2984 match = alloca(bans->used);
2987 for(ii = count = 0; ii < bans->used; ++ii)
2989 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
2996 for(ii = count = 0; ii < bans->used; ++ii)
2998 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3005 change = mod_chanmode_alloc(count);
3006 for(ii = count = 0; ii < bans->used; ++ii)
3010 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3011 change->args[count++].hostmask = bans->list[ii]->ban;
3017 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3019 struct userNode *actee;
3025 /* may want to allow a comma delimited list of users... */
3026 if(!(actee = GetUserH(argv[1])))
3028 if(!is_ircmask(argv[1]))
3030 reply("MSG_NICK_UNKNOWN", argv[1]);
3034 mask = strdup(argv[1]);
3037 /* We don't sanitize the mask here because ircu
3039 if(action & ACTION_UNBAN)
3041 struct mod_chanmode *change;
3042 change = find_matching_bans(&channel->banlist, actee, mask);
3045 modcmd_chanmode_announce(change);
3046 mod_chanmode_free(change);
3051 if(action & ACTION_DEL_BAN)
3053 struct banData *ban, *next;
3055 ban = channel->channel_info->bans;
3059 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3062 for( ; ban && !match_ircglobs(mask, ban->mask);
3067 del_channel_ban(ban);
3074 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3076 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3082 static CHANSERV_FUNC(cmd_unban)
3084 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3087 static CHANSERV_FUNC(cmd_delban)
3089 /* it doesn't necessarily have to remove the channel ban - may want
3090 to make that an option. */
3091 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3094 static CHANSERV_FUNC(cmd_unbanme)
3096 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3097 long flags = ACTION_UNBAN;
3099 /* remove permanent bans if the user has the proper access. */
3100 if(uData->access >= UL_MASTER)
3101 flags |= ACTION_DEL_BAN;
3103 argv[1] = user->nick;
3104 return unban_user(user, channel, 2, argv, cmd, flags);
3107 static CHANSERV_FUNC(cmd_unbanall)
3109 struct mod_chanmode *change;
3112 if(!channel->banlist.used)
3114 reply("CSMSG_NO_BANS", channel->name);
3118 change = mod_chanmode_alloc(channel->banlist.used);
3119 for(ii=0; ii<channel->banlist.used; ii++)
3121 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3122 change->args[ii].hostmask = channel->banlist.list[ii]->ban;
3124 modcmd_chanmode_announce(change);
3125 mod_chanmode_free(change);
3126 reply("CSMSG_BANS_REMOVED", channel->name);
3130 static CHANSERV_FUNC(cmd_open)
3132 struct mod_chanmode *change;
3134 change = find_matching_bans(&channel->banlist, user, NULL);
3136 change = mod_chanmode_alloc(0);
3137 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3138 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3139 && channel->channel_info->modes.modes_set)
3140 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3141 modcmd_chanmode_announce(change);
3142 reply("CSMSG_CHANNEL_OPENED", channel->name);
3143 mod_chanmode_free(change);
3147 static CHANSERV_FUNC(cmd_myaccess)
3149 struct handle_info *target_handle;
3150 struct userData *uData;
3151 const char *chanName;
3154 target_handle = user->handle_info;
3155 else if(!IsHelping(user))
3157 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3160 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3163 if(!target_handle->channels)
3165 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3169 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3170 for(uData = target_handle->channels; uData; uData = uData->u_next)
3172 struct chanData *cData = uData->channel;
3174 if(uData->access > UL_OWNER)
3176 if(IsProtected(cData)
3177 && (target_handle != user->handle_info)
3178 && !GetTrueChannelAccess(cData, user->handle_info))
3180 chanName = cData->channel->name;
3182 send_message_type(4, user, cmd->parent->bot, "[%s (%d)] %s", chanName, uData->access, uData->info);
3184 send_message_type(4, user, cmd->parent->bot, "[%s (%d)]", chanName, uData->access);
3190 static CHANSERV_FUNC(cmd_access)
3192 struct userNode *target;
3193 struct handle_info *target_handle;
3194 struct userData *uData;
3196 char prefix[MAXLEN];
3201 target_handle = target->handle_info;
3203 else if((target = GetUserH(argv[1])))
3205 target_handle = target->handle_info;
3207 else if(argv[1][0] == '*')
3209 if(!(target_handle = get_handle_info(argv[1]+1)))
3211 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3217 reply("MSG_NICK_UNKNOWN", argv[1]);
3221 assert(target || target_handle);
3223 if(target == chanserv)
3225 reply("CSMSG_IS_CHANSERV");
3233 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3238 reply("MSG_USER_AUTHENTICATE", target->nick);
3241 reply("MSG_AUTHENTICATE");
3247 const char *epithet = NULL, *type = NULL;
3250 epithet = chanserv_conf.irc_operator_epithet;
3253 else if(IsNetworkHelper(target))
3255 epithet = chanserv_conf.network_helper_epithet;
3256 type = "network helper";
3258 else if(IsSupportHelper(target))
3260 epithet = chanserv_conf.support_helper_epithet;
3261 type = "support helper";
3265 if(target_handle->epithet)
3266 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3268 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3270 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3274 sprintf(prefix, "%s", target_handle->handle);
3277 if(!channel->channel_info)
3279 reply("CSMSG_NOT_REGISTERED", channel->name);
3283 helping = HANDLE_FLAGGED(target_handle, HELPING)
3284 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3285 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3287 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3288 /* To prevent possible information leaks, only show infolines
3289 * if the requestor is in the channel or it's their own
3291 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3293 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3295 /* Likewise, only say it's suspended if the user has active
3296 * access in that channel or it's their own entry. */
3297 if(IsUserSuspended(uData)
3298 && (GetChannelUser(channel->channel_info, user->handle_info)
3299 || (user->handle_info == uData->handle)))
3301 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3306 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3313 zoot_list(struct listData *list)
3315 struct userData *uData;
3316 unsigned int start, curr, highest, lowest;
3317 struct helpfile_table tmp_table;
3318 const char **temp, *msg;
3320 if(list->table.length == 1)
3323 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3325 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3326 msg = user_find_message(list->user, "MSG_NONE");
3327 send_message_type(4, list->user, list->bot, " %s", msg);
3329 tmp_table.width = list->table.width;
3330 tmp_table.flags = list->table.flags;
3331 list->table.contents[0][0] = " ";
3332 highest = list->highest;
3333 if(list->lowest != 0)
3334 lowest = list->lowest;
3335 else if(highest < 100)
3338 lowest = highest - 100;
3339 for(start = curr = 1; curr < list->table.length; )
3341 uData = list->users[curr-1];
3342 list->table.contents[curr++][0] = " ";
3343 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3346 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3348 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3349 temp = list->table.contents[--start];
3350 list->table.contents[start] = list->table.contents[0];
3351 tmp_table.contents = list->table.contents + start;
3352 tmp_table.length = curr - start;
3353 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3354 list->table.contents[start] = temp;
3356 highest = lowest - 1;
3357 lowest = (highest < 100) ? 0 : (highest - 99);
3363 def_list(struct listData *list)
3367 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3369 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3370 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3371 if(list->table.length == 1)
3373 msg = user_find_message(list->user, "MSG_NONE");
3374 send_message_type(4, list->user, list->bot, " %s", msg);
3379 userData_access_comp(const void *arg_a, const void *arg_b)
3381 const struct userData *a = *(struct userData**)arg_a;
3382 const struct userData *b = *(struct userData**)arg_b;
3384 if(a->access != b->access)
3385 res = b->access - a->access;
3387 res = irccasecmp(a->handle->handle, b->handle->handle);
3392 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3394 void (*send_list)(struct listData *);
3395 struct userData *uData;
3396 struct listData lData;
3397 unsigned int matches;
3401 lData.bot = cmd->parent->bot;
3402 lData.channel = channel;
3403 lData.lowest = lowest;
3404 lData.highest = highest;
3405 lData.search = (argc > 1) ? argv[1] : NULL;
3406 send_list = zoot_list;
3408 if(user->handle_info)
3410 switch(user->handle_info->userlist_style)
3412 case HI_STYLE_DEF: send_list = def_list; break;
3413 case HI_STYLE_ZOOT: send_list = zoot_list; break;
3417 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3419 for(uData = channel->channel_info->users; uData; uData = uData->next)
3421 if((uData->access < lowest)
3422 || (uData->access > highest)
3423 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3425 lData.users[matches++] = uData;
3427 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3429 lData.table.length = matches+1;
3430 lData.table.width = 4;
3431 lData.table.flags = TABLE_NO_FREE;
3432 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3433 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3434 lData.table.contents[0] = ary;
3437 ary[2] = "Last Seen";
3439 for(matches = 1; matches < lData.table.length; ++matches)
3441 struct userData *uData = lData.users[matches-1];
3442 char seen[INTERVALLEN];
3444 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3445 lData.table.contents[matches] = ary;
3446 ary[0] = strtab(uData->access);
3447 ary[1] = uData->handle->handle;
3450 else if(!uData->seen)
3453 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3454 ary[2] = strdup(ary[2]);
3455 if(IsUserSuspended(uData))
3456 ary[3] = "Suspended";
3457 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3458 ary[3] = "Vacation";
3463 for(matches = 1; matches < lData.table.length; ++matches)
3465 free((char*)lData.table.contents[matches][2]);
3466 free(lData.table.contents[matches]);
3468 free(lData.table.contents[0]);
3469 free(lData.table.contents);
3473 static CHANSERV_FUNC(cmd_users)
3475 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3478 static CHANSERV_FUNC(cmd_wlist)
3480 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3483 static CHANSERV_FUNC(cmd_clist)
3485 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3488 static CHANSERV_FUNC(cmd_mlist)
3490 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3493 static CHANSERV_FUNC(cmd_olist)
3495 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3498 static CHANSERV_FUNC(cmd_plist)
3500 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3503 static CHANSERV_FUNC(cmd_bans)
3505 struct helpfile_table tbl;
3506 unsigned int matches = 0, timed = 0, ii;
3507 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3508 const char *msg_never, *triggered, *expires;
3509 struct banData *ban, **bans;
3516 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3518 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3520 if(search && !match_ircglobs(search, ban->mask))
3522 bans[matches++] = ban;
3527 tbl.length = matches + 1;
3528 tbl.width = 4 + timed;
3530 tbl.flags = TABLE_NO_FREE;
3531 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3532 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3533 tbl.contents[0][0] = "Mask";
3534 tbl.contents[0][1] = "Set By";
3535 tbl.contents[0][2] = "Triggered";
3538 tbl.contents[0][3] = "Expires";
3539 tbl.contents[0][4] = "Reason";
3542 tbl.contents[0][3] = "Reason";
3545 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3547 free(tbl.contents[0]);
3552 msg_never = user_find_message(user, "MSG_NEVER");
3553 for(ii = 0; ii < matches; )
3559 else if(ban->expires)
3560 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3562 expires = msg_never;
3565 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3567 triggered = msg_never;
3569 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3570 tbl.contents[ii][0] = ban->mask;
3571 tbl.contents[ii][1] = ban->owner;
3572 tbl.contents[ii][2] = strdup(triggered);
3575 tbl.contents[ii][3] = strdup(expires);
3576 tbl.contents[ii][4] = ban->reason;
3579 tbl.contents[ii][3] = ban->reason;
3581 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3582 reply("MSG_MATCH_COUNT", matches);
3583 for(ii = 1; ii < tbl.length; ++ii)
3585 free((char*)tbl.contents[ii][2]);
3587 free((char*)tbl.contents[ii][3]);
3588 free(tbl.contents[ii]);
3590 free(tbl.contents[0]);
3596 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3598 struct chanData *cData = channel->channel_info;
3599 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3601 if(cData->topic_mask)
3602 return !match_ircglob(new_topic, cData->topic_mask);
3603 else if(cData->topic)
3604 return irccasecmp(new_topic, cData->topic);
3609 static CHANSERV_FUNC(cmd_topic)
3611 struct chanData *cData;
3614 cData = channel->channel_info;
3619 SetChannelTopic(channel, chanserv, cData->topic, 1);
3620 reply("CSMSG_TOPIC_SET", cData->topic);
3624 reply("CSMSG_NO_TOPIC", channel->name);
3628 topic = unsplit_string(argv + 1, argc - 1, NULL);
3629 /* If they say "!topic *", use an empty topic. */
3630 if((topic[0] == '*') && (topic[1] == 0))
3632 if(bad_topic(channel, user, topic))
3634 char *topic_mask = cData->topic_mask;
3637 char new_topic[TOPICLEN+1], tchar;
3638 int pos=0, starpos=-1, dpos=0, len;
3640 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3647 len = strlen(topic);
3648 if((dpos + len) > TOPICLEN)
3649 len = TOPICLEN + 1 - dpos;
3650 memcpy(new_topic+dpos, topic, len);
3654 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3655 default: new_topic[dpos++] = tchar; break;
3658 if((dpos > TOPICLEN) || tchar)
3661 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3662 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3665 new_topic[dpos] = 0;
3666 SetChannelTopic(channel, chanserv, new_topic, 1);
3668 reply("CSMSG_TOPIC_LOCKED", channel->name);
3673 SetChannelTopic(channel, chanserv, topic, 1);
3675 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3677 /* Grab the topic and save it as the default topic. */
3679 cData->topic = strdup(channel->topic);
3685 static CHANSERV_FUNC(cmd_mode)
3687 struct mod_chanmode *change;
3691 change = &channel->channel_info->modes;
3692 if(change->modes_set || change->modes_clear) {
3693 modcmd_chanmode_announce(change);
3694 reply("CSMSG_DEFAULTED_MODES", channel->name);
3696 reply("CSMSG_NO_MODES", channel->name);
3700 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE);
3703 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3707 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3708 && mode_lock_violated(&channel->channel_info->modes, change))
3711 mod_chanmode_format(&channel->channel_info->modes, modes);
3712 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3716 modcmd_chanmode_announce(change);
3717 mod_chanmode_free(change);
3718 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3722 static CHANSERV_FUNC(cmd_invite)
3724 struct userData *uData;
3725 struct userNode *invite;
3727 uData = GetChannelUser(channel->channel_info, user->handle_info);
3731 if(!(invite = GetUserH(argv[1])))
3733 reply("MSG_NICK_UNKNOWN", argv[1]);
3740 if(GetUserMode(channel, invite))
3742 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3750 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3751 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3754 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3756 irc_invite(chanserv, invite, channel);
3758 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3763 static CHANSERV_FUNC(cmd_inviteme)
3765 if(GetUserMode(channel, user))
3767 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3770 if(channel->channel_info
3771 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3773 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3776 irc_invite(cmd->parent->bot, user, channel);
3781 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3784 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3786 /* We display things based on two dimensions:
3787 * - Issue time: present or absent
3788 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3789 * (in order of precedence, so something both expired and revoked
3790 * only counts as revoked)
3792 combo = (suspended->issued ? 4 : 0)
3793 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3795 case 0: /* no issue time, indefinite expiration */
3796 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3798 case 1: /* no issue time, expires in future */
3799 intervalString(buf1, suspended->expires-now, user->handle_info);
3800 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3802 case 2: /* no issue time, expired */
3803 intervalString(buf1, now-suspended->expires, user->handle_info);
3804 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3806 case 3: /* no issue time, revoked */
3807 intervalString(buf1, now-suspended->revoked, user->handle_info);
3808 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3810 case 4: /* issue time set, indefinite expiration */
3811 intervalString(buf1, now-suspended->issued, user->handle_info);
3812 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3814 case 5: /* issue time set, expires in future */
3815 intervalString(buf1, now-suspended->issued, user->handle_info);
3816 intervalString(buf2, suspended->expires-now, user->handle_info);
3817 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3819 case 6: /* issue time set, expired */
3820 intervalString(buf1, now-suspended->issued, user->handle_info);
3821 intervalString(buf2, now-suspended->expires, user->handle_info);
3822 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3824 case 7: /* issue time set, revoked */
3825 intervalString(buf1, now-suspended->issued, user->handle_info);
3826 intervalString(buf2, now-suspended->revoked, user->handle_info);
3827 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3830 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3835 static CHANSERV_FUNC(cmd_info)
3837 char modes[MAXLEN], buffer[INTERVALLEN];
3838 struct userData *uData, *owner;
3839 struct chanData *cData;
3840 struct do_not_register *dnr;
3845 cData = channel->channel_info;
3846 reply("CSMSG_CHANNEL_INFO", channel->name);
3848 uData = GetChannelUser(cData, user->handle_info);
3849 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3851 mod_chanmode_format(&cData->modes, modes);
3852 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3853 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3856 for(it = dict_first(cData->notes); it; it = iter_next(it))
3860 note = iter_data(it);
3861 if(!note_type_visible_to_user(cData, note->type, user))
3864 padding = PADLEN - 1 - strlen(iter_key(it));
3865 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3868 reply("CSMSG_CHANNEL_MAX", cData->max);
3869 for(owner = cData->users; owner; owner = owner->next)
3870 if(owner->access == UL_OWNER)
3871 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3872 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3873 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3874 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3875 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3877 privileged = IsStaff(user);
3878 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3879 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3881 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3882 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3884 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3886 struct suspended *suspended;
3887 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3888 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3889 show_suspension_info(cmd, user, suspended);
3891 else if(IsSuspended(cData))
3893 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3894 show_suspension_info(cmd, user, cData->suspended);
3899 static CHANSERV_FUNC(cmd_netinfo)
3901 extern time_t boot_time;
3902 extern unsigned long burst_length;
3903 char interval[INTERVALLEN];
3905 reply("CSMSG_NETWORK_INFO");
3906 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3907 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3908 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3909 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3910 reply("CSMSG_NETWORK_BANS", banCount);
3911 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3912 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3913 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3918 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3920 struct helpfile_table table;
3922 struct userNode *user;
3927 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3928 table.contents = alloca(list->used*sizeof(*table.contents));
3929 for(nn=0; nn<list->used; nn++)
3931 user = list->list[nn];
3932 if(user->modes & skip_flags)
3936 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3939 nick = alloca(strlen(user->nick)+3);
3940 sprintf(nick, "(%s)", user->nick);
3944 table.contents[table.length][0] = nick;
3947 table_send(chanserv, to->nick, 0, NULL, table);
3950 static CHANSERV_FUNC(cmd_ircops)
3952 reply("CSMSG_STAFF_OPERS");
3953 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
3957 static CHANSERV_FUNC(cmd_helpers)
3959 reply("CSMSG_STAFF_HELPERS");
3960 send_staff_list(user, &curr_helpers, FLAGS_OPER);
3964 static CHANSERV_FUNC(cmd_staff)
3966 reply("CSMSG_NETWORK_STAFF");
3967 cmd_ircops(CSFUNC_ARGS);
3968 cmd_helpers(CSFUNC_ARGS);
3972 static CHANSERV_FUNC(cmd_peek)
3974 struct modeNode *mn;
3975 char modes[MODELEN];
3977 struct helpfile_table table;
3979 irc_make_chanmode(channel, modes);
3981 reply("CSMSG_PEEK_INFO", channel->name);
3982 reply("CSMSG_PEEK_TOPIC", channel->topic);
3983 reply("CSMSG_PEEK_MODES", modes);
3984 reply("CSMSG_PEEK_USERS", channel->members.used);
3988 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3989 table.contents = alloca(channel->members.used*sizeof(*table.contents));
3990 for(n = 0; n < channel->members.used; n++)
3992 mn = channel->members.list[n];
3993 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
3995 table.contents[table.length] = alloca(sizeof(**table.contents));
3996 table.contents[table.length][0] = mn->user->nick;
4001 reply("CSMSG_PEEK_OPS");
4002 table_send(chanserv, user->nick, 0, NULL, table);
4005 reply("CSMSG_PEEK_NO_OPS");
4009 static MODCMD_FUNC(cmd_wipeinfo)
4011 struct handle_info *victim;
4012 struct userData *ud, *actor;
4015 actor = GetChannelUser(channel->channel_info, user->handle_info);
4016 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4018 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4020 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4023 if((ud->access >= actor->access) && (ud != actor))
4025 reply("MSG_USER_OUTRANKED", victim->handle);
4031 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4035 static CHANSERV_FUNC(cmd_resync)
4037 struct mod_chanmode *changes;
4038 struct chanData *cData = channel->channel_info;
4039 unsigned int ii, used;
4041 changes = mod_chanmode_alloc(channel->members.used * 2);
4042 for(ii = used = 0; ii < channel->members.used; ++ii)
4044 struct modeNode *mn = channel->members.list[ii];
4045 struct userData *uData;
4047 if(IsService(mn->user))
4050 uData = GetChannelAccess(cData, mn->user->handle_info);
4051 if(!cData->lvlOpts[lvlGiveOps]
4052 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4054 if(!(mn->modes & MODE_CHANOP))
4056 changes->args[used].mode = MODE_CHANOP;
4057 changes->args[used++].member = mn;
4060 else if(!cData->lvlOpts[lvlGiveVoice]
4061 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4063 if(mn->modes & MODE_CHANOP)
4065 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4066 changes->args[used++].member = mn;
4068 if(!(mn->modes & MODE_VOICE))
4070 changes->args[used].mode = MODE_VOICE;
4071 changes->args[used++].member = mn;
4078 changes->args[used].mode = MODE_REMOVE | mn->modes;
4079 changes->args[used++].member = mn;
4083 changes->argc = used;
4084 modcmd_chanmode_announce(changes);
4085 mod_chanmode_free(changes);
4086 reply("CSMSG_RESYNCED_USERS", channel->name);
4090 static CHANSERV_FUNC(cmd_seen)
4092 struct userData *uData;
4093 struct handle_info *handle;
4094 char seen[INTERVALLEN];
4098 if(!irccasecmp(argv[1], chanserv->nick))
4100 reply("CSMSG_IS_CHANSERV");
4104 if(!(handle = get_handle_info(argv[1])))
4106 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4110 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4112 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4117 reply("CSMSG_USER_PRESENT", handle->handle);
4118 else if(uData->seen)
4119 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4121 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4123 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4124 reply("CSMSG_USER_VACATION", handle->handle);
4129 static MODCMD_FUNC(cmd_names)
4131 struct userNode *targ;
4132 struct userData *targData;
4133 unsigned int ii, pos;
4136 for(ii=pos=0; ii<channel->members.used; ++ii)
4138 targ = channel->members.list[ii]->user;
4139 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4142 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 6 > sizeof(buf))
4145 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4149 if(IsUserSuspended(targData))
4151 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4154 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4155 reply("CSMSG_END_NAMES", channel->name);
4160 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4162 switch(ntype->visible_type)
4164 case NOTE_VIS_ALL: return 1;
4165 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4166 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4171 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4173 struct userData *uData;
4175 switch(ntype->set_access_type)
4177 case NOTE_SET_CHANNEL_ACCESS:
4178 if(!user->handle_info)
4180 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4182 return uData->access >= ntype->set_access.min_ulevel;
4183 case NOTE_SET_CHANNEL_SETTER:
4184 return check_user_level(channel, user, lvlSetters, 1, 0);
4185 case NOTE_SET_PRIVILEGED: default:
4186 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4190 static CHANSERV_FUNC(cmd_note)
4192 struct chanData *cData;
4194 struct note_type *ntype;
4196 cData = channel->channel_info;
4199 reply("CSMSG_NOT_REGISTERED", channel->name);
4203 /* If no arguments, show all visible notes for the channel. */
4209 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4211 note = iter_data(it);
4212 if(!note_type_visible_to_user(cData, note->type, user))
4215 reply("CSMSG_NOTELIST_HEADER", channel->name);
4216 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4219 reply("CSMSG_NOTELIST_END", channel->name);
4221 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4223 /* If one argument, show the named note. */
4226 if((note = dict_find(cData->notes, argv[1], NULL))
4227 && note_type_visible_to_user(cData, note->type, user))
4229 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4231 else if((ntype = dict_find(note_types, argv[1], NULL))
4232 && note_type_visible_to_user(NULL, ntype, user))
4234 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4239 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4243 /* Assume they're trying to set a note. */
4247 ntype = dict_find(note_types, argv[1], NULL);
4250 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4253 else if(note_type_settable_by_user(channel, ntype, user))
4255 note_text = unsplit_string(argv+2, argc-2, NULL);
4256 if((note = dict_find(cData->notes, argv[1], NULL)))
4257 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4258 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4259 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4261 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4263 /* The note is viewable to staff only, so return 0
4264 to keep the invocation from getting logged (or
4265 regular users can see it in !events). */
4271 reply("CSMSG_NO_ACCESS");
4278 static CHANSERV_FUNC(cmd_delnote)
4283 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4284 || !note_type_settable_by_user(channel, note->type, user))
4286 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4289 dict_remove(channel->channel_info->notes, note->type->name);
4290 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4294 static CHANSERV_FUNC(cmd_events)
4296 struct logSearch discrim;
4297 struct logReport report;
4298 unsigned int matches, limit;
4300 limit = (argc > 1) ? atoi(argv[1]) : 10;
4301 if(limit < 1 || limit > 200) limit = 10;
4303 memset(&discrim, 0, sizeof(discrim));
4304 discrim.masks.bot = chanserv;
4305 discrim.masks.channel_name = channel->name;
4306 if(argc > 2) discrim.masks.command = argv[2];
4307 discrim.limit = limit;
4308 discrim.max_time = INT_MAX;
4309 discrim.severities = 1 << LOG_COMMAND;
4310 report.reporter = chanserv;
4312 reply("CSMSG_EVENT_SEARCH_RESULTS");
4313 matches = log_entry_search(&discrim, log_report_entry, &report);
4315 reply("MSG_MATCH_COUNT", matches);
4317 reply("MSG_NO_MATCHES");
4321 static CHANSERV_FUNC(cmd_say)
4327 msg = unsplit_string(argv + 1, argc - 1, NULL);
4328 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4330 else if(GetUserH(argv[1]))
4333 msg = unsplit_string(argv + 2, argc - 2, NULL);
4334 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4338 reply("MSG_NOT_TARGET_NAME");
4344 static CHANSERV_FUNC(cmd_emote)
4350 /* CTCP is so annoying. */
4351 msg = unsplit_string(argv + 1, argc - 1, NULL);
4352 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4354 else if(GetUserH(argv[1]))
4356 msg = unsplit_string(argv + 2, argc - 2, NULL);
4357 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4361 reply("MSG_NOT_TARGET_NAME");
4367 struct channelList *
4368 chanserv_support_channels(void)
4370 return &chanserv_conf.support_channels;
4373 static CHANSERV_FUNC(cmd_expire)
4375 int channel_count = registered_channels;
4376 expire_channels(NULL);
4377 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4382 chanserv_expire_suspension(void *data)
4384 struct suspended *suspended = data;
4385 struct chanNode *channel;
4386 struct mod_chanmode change;
4388 if(!suspended->expires || (now < suspended->expires))
4389 suspended->revoked = now;
4390 channel = suspended->cData->channel;
4391 suspended->cData->channel = channel;
4392 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4393 mod_chanmode_init(&change);
4395 change.args[0].mode = MODE_CHANOP;
4396 change.args[0].member = AddChannelUser(chanserv, channel);
4397 mod_chanmode_announce(chanserv, channel, &change);
4400 static CHANSERV_FUNC(cmd_csuspend)
4402 struct suspended *suspended;
4403 char reason[MAXLEN];
4404 time_t expiry, duration;
4405 struct userData *uData;
4409 if(IsProtected(channel->channel_info))
4411 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4415 if(argv[1][0] == '!')
4417 else if(IsSuspended(channel->channel_info))
4419 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4420 show_suspension_info(cmd, user, channel->channel_info->suspended);
4424 if(!strcmp(argv[1], "0"))
4426 else if((duration = ParseInterval(argv[1])))
4427 expiry = now + duration;
4430 reply("MSG_INVALID_DURATION", argv[1]);
4434 unsplit_string(argv + 2, argc - 2, reason);
4436 suspended = calloc(1, sizeof(*suspended));
4437 suspended->revoked = 0;
4438 suspended->issued = now;
4439 suspended->suspender = strdup(user->handle_info->handle);
4440 suspended->expires = expiry;
4441 suspended->reason = strdup(reason);
4442 suspended->cData = channel->channel_info;
4443 suspended->previous = suspended->cData->suspended;
4444 suspended->cData->suspended = suspended;
4446 if(suspended->expires)
4447 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4449 if(IsSuspended(channel->channel_info))
4451 suspended->previous->revoked = now;
4452 if(suspended->previous->expires)
4453 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4454 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4455 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4459 /* Mark all users in channel as absent. */
4460 for(uData = channel->channel_info->users; uData; uData = uData->next)
4469 /* Mark the channel as suspended, then part. */
4470 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4471 DelChannelUser(chanserv, channel, suspended->reason, 0);
4472 reply("CSMSG_SUSPENDED", channel->name);
4473 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4474 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4479 static CHANSERV_FUNC(cmd_cunsuspend)
4481 struct suspended *suspended;
4482 char message[MAXLEN];
4484 if(!IsSuspended(channel->channel_info))
4486 reply("CSMSG_NOT_SUSPENDED", channel->name);
4490 suspended = channel->channel_info->suspended;
4492 /* Expire the suspension and join ChanServ to the channel. */
4493 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4494 chanserv_expire_suspension(suspended);
4495 reply("CSMSG_UNSUSPENDED", channel->name);
4496 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4497 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4501 typedef struct chanservSearch
4509 unsigned long flags;
4513 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4516 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4521 search = malloc(sizeof(struct chanservSearch));
4522 memset(search, 0, sizeof(*search));
4525 for(i = 0; i < argc; i++)
4527 /* Assume all criteria require arguments. */
4530 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4534 if(!irccasecmp(argv[i], "name"))
4535 search->name = argv[++i];
4536 else if(!irccasecmp(argv[i], "registrar"))
4537 search->registrar = argv[++i];
4538 else if(!irccasecmp(argv[i], "unvisited"))
4539 search->unvisited = ParseInterval(argv[++i]);
4540 else if(!irccasecmp(argv[i], "registered"))
4541 search->registered = ParseInterval(argv[++i]);
4542 else if(!irccasecmp(argv[i], "flags"))
4545 if(!irccasecmp(argv[i], "nodelete"))
4546 search->flags |= CHANNEL_NODELETE;
4547 else if(!irccasecmp(argv[i], "suspended"))
4548 search->flags |= CHANNEL_SUSPENDED;
4551 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4555 else if(!irccasecmp(argv[i], "limit"))
4556 search->limit = strtoul(argv[++i], NULL, 10);
4559 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4564 if(search->name && !strcmp(search->name, "*"))
4566 if(search->registrar && !strcmp(search->registrar, "*"))
4567 search->registrar = 0;
4576 chanserv_channel_match(struct chanData *channel, search_t search)
4578 const char *name = channel->channel->name;
4579 if((search->name && !match_ircglob(name, search->name)) ||
4580 (search->registrar && !channel->registrar) ||
4581 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4582 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4583 (search->registered && (now - channel->registered) > search->registered) ||
4584 (search->flags && ((search->flags & channel->flags) != search->flags)))
4591 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4593 struct chanData *channel;
4594 unsigned int matches = 0;
4596 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4598 if(!chanserv_channel_match(channel, search))
4608 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4613 search_print(struct chanData *channel, void *data)
4615 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4618 static CHANSERV_FUNC(cmd_search)
4621 unsigned int matches;
4622 channel_search_func action;
4626 if(!irccasecmp(argv[1], "count"))
4627 action = search_count;
4628 else if(!irccasecmp(argv[1], "print"))
4629 action = search_print;
4632 reply("CSMSG_ACTION_INVALID", argv[1]);
4636 search = chanserv_search_create(user, argc - 2, argv + 2);
4640 if(action == search_count)
4641 search->limit = INT_MAX;
4643 if(action == search_print)
4644 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4646 matches = chanserv_channel_search(search, action, user);
4649 reply("MSG_MATCH_COUNT", matches);
4651 reply("MSG_NO_MATCHES");
4657 static CHANSERV_FUNC(cmd_unvisited)
4659 struct chanData *cData;
4660 time_t interval = chanserv_conf.channel_expire_delay;
4661 char buffer[INTERVALLEN];
4662 unsigned int limit = 25, matches = 0;
4666 interval = ParseInterval(argv[1]);
4668 limit = atoi(argv[2]);
4671 intervalString(buffer, interval, user->handle_info);
4672 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4674 for(cData = channelList; cData && matches < limit; cData = cData->next)
4676 if((now - cData->visited) < interval)
4679 intervalString(buffer, now - cData->visited, user->handle_info);
4680 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4687 static MODCMD_FUNC(chan_opt_defaulttopic)
4693 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4695 reply("CSMSG_TOPIC_LOCKED", channel->name);
4699 topic = unsplit_string(argv+1, argc-1, NULL);
4701 free(channel->channel_info->topic);
4702 if(topic[0] == '*' && topic[1] == 0)
4704 topic = channel->channel_info->topic = NULL;
4708 topic = channel->channel_info->topic = strdup(topic);
4709 if(channel->channel_info->topic_mask
4710 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4711 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4713 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4716 if(channel->channel_info->topic)
4717 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4719 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4723 static MODCMD_FUNC(chan_opt_topicmask)
4727 struct chanData *cData = channel->channel_info;
4730 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4732 reply("CSMSG_TOPIC_LOCKED", channel->name);
4736 mask = unsplit_string(argv+1, argc-1, NULL);
4738 if(cData->topic_mask)
4739 free(cData->topic_mask);
4740 if(mask[0] == '*' && mask[1] == 0)
4742 cData->topic_mask = 0;
4746 cData->topic_mask = strdup(mask);
4748 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4749 else if(!match_ircglob(cData->topic, cData->topic_mask))
4750 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4754 if(channel->channel_info->topic_mask)
4755 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4757 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4761 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4765 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4769 if(greeting[0] == '*' && greeting[1] == 0)
4773 unsigned int length = strlen(greeting);
4774 if(length > chanserv_conf.greeting_length)
4776 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4779 *data = strdup(greeting);
4788 reply(name, user_find_message(user, "MSG_NONE"));
4792 static MODCMD_FUNC(chan_opt_greeting)
4794 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4797 static MODCMD_FUNC(chan_opt_usergreeting)
4799 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4802 static MODCMD_FUNC(chan_opt_modes)
4804 struct mod_chanmode *new_modes;
4805 char modes[MODELEN];
4809 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4811 reply("CSMSG_NO_ACCESS");
4814 if(argv[1][0] == '*' && argv[1][1] == 0)
4816 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4818 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4820 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4823 else if(new_modes->argc > 1)
4825 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4826 mod_chanmode_free(new_modes);
4831 channel->channel_info->modes = *new_modes;
4832 modcmd_chanmode_announce(new_modes);
4833 mod_chanmode_free(new_modes);
4837 mod_chanmode_format(&channel->channel_info->modes, modes);
4839 reply("CSMSG_SET_MODES", modes);
4841 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4845 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4847 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4849 struct chanData *cData = channel->channel_info;
4854 /* Set flag according to value. */
4855 if(enabled_string(argv[1]))
4857 cData->flags |= mask;
4860 else if(disabled_string(argv[1]))
4862 cData->flags &= ~mask;
4867 reply("MSG_INVALID_BINARY", argv[1]);
4873 /* Find current option value. */
4874 value = (cData->flags & mask) ? 1 : 0;
4878 reply(name, user_find_message(user, "MSG_ON"));
4880 reply(name, user_find_message(user, "MSG_OFF"));
4884 static MODCMD_FUNC(chan_opt_nodelete)
4886 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4888 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4892 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4895 static MODCMD_FUNC(chan_opt_dynlimit)
4897 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4900 static MODCMD_FUNC(chan_opt_defaults)
4902 struct userData *uData;
4903 struct chanData *cData;
4904 const char *confirm;
4905 enum levelOption lvlOpt;
4906 enum charOption chOpt;
4908 cData = channel->channel_info;
4909 uData = GetChannelUser(cData, user->handle_info);
4910 if(!uData || (uData->access < UL_OWNER))
4912 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4915 confirm = make_confirmation_string(uData);
4916 if((argc < 2) || strcmp(argv[1], confirm))
4918 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4921 cData->flags = CHANNEL_DEFAULT_FLAGS;
4922 cData->modes = chanserv_conf.default_modes;
4923 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4924 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4925 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4926 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4927 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4932 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4934 struct chanData *cData = channel->channel_info;
4935 struct userData *uData;
4936 unsigned short value;
4940 if(!check_user_level(channel, user, option, 1, 1))
4942 reply("CSMSG_CANNOT_SET");
4945 value = user_level_from_name(argv[1], UL_OWNER+1);
4946 if(!value && strcmp(argv[1], "0"))
4948 reply("CSMSG_INVALID_ACCESS", argv[1]);
4951 uData = GetChannelUser(cData, user->handle_info);
4952 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4954 reply("CSMSG_BAD_SETLEVEL");
4960 if(value > cData->lvlOpts[lvlGiveOps])
4962 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4967 if(value < cData->lvlOpts[lvlGiveVoice])
4969 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4974 /* This test only applies to owners, since non-owners
4975 * trying to set an option to above their level get caught
4976 * by the CSMSG_BAD_SETLEVEL test above.
4978 if(value > uData->access)
4980 reply("CSMSG_BAD_SETTERS");
4987 cData->lvlOpts[option] = value;
4989 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4993 static MODCMD_FUNC(chan_opt_enfops)
4995 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
4998 static MODCMD_FUNC(chan_opt_giveops)
5000 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5003 static MODCMD_FUNC(chan_opt_enfmodes)
5005 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5008 static MODCMD_FUNC(chan_opt_enftopic)
5010 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5013 static MODCMD_FUNC(chan_opt_pubcmd)
5015 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5018 static MODCMD_FUNC(chan_opt_setters)
5020 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5023 static MODCMD_FUNC(chan_opt_ctcpusers)
5025 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5028 static MODCMD_FUNC(chan_opt_userinfo)
5030 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5033 static MODCMD_FUNC(chan_opt_givevoice)
5035 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5038 static MODCMD_FUNC(chan_opt_topicsnarf)
5040 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5043 static MODCMD_FUNC(chan_opt_inviteme)
5045 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5049 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5051 struct chanData *cData = channel->channel_info;
5052 int count = charOptions[option].count, index;
5056 index = atoi(argv[1]);
5058 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5060 reply("CSMSG_INVALID_NUMERIC", index);
5061 /* Show possible values. */
5062 for(index = 0; index < count; index++)
5063 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5067 cData->chOpts[option] = charOptions[option].values[index].value;
5071 /* Find current option value. */
5074 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5078 /* Somehow, the option value is corrupt; reset it to the default. */
5079 cData->chOpts[option] = charOptions[option].default_value;
5084 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5088 static MODCMD_FUNC(chan_opt_protect)
5090 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5093 static MODCMD_FUNC(chan_opt_toys)
5095 return channel_multiple_option(chToys, CSFUNC_ARGS);
5098 static MODCMD_FUNC(chan_opt_ctcpreaction)
5100 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5103 static MODCMD_FUNC(chan_opt_topicrefresh)
5105 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5108 static struct svccmd_list set_shows_list;
5111 handle_svccmd_unbind(struct svccmd *target) {
5113 for(ii=0; ii<set_shows_list.used; ++ii)
5114 if(target == set_shows_list.list[ii])
5115 set_shows_list.used = 0;
5118 static CHANSERV_FUNC(cmd_set)
5120 struct svccmd *subcmd;
5124 /* Check if we need to (re-)initialize set_shows_list. */
5125 if(!set_shows_list.used)
5127 if(!set_shows_list.size)
5129 set_shows_list.size = chanserv_conf.set_shows->used;
5130 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5132 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5134 const char *name = chanserv_conf.set_shows->list[ii];
5135 sprintf(buf, "%s %s", argv[0], name);
5136 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5139 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5142 svccmd_list_append(&set_shows_list, subcmd);
5148 reply("CSMSG_CHANNEL_OPTIONS");
5149 for(ii = 0; ii < set_shows_list.used; ii++)
5151 subcmd = set_shows_list.list[ii];
5152 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5157 sprintf(buf, "%s %s", argv[0], argv[1]);
5158 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5161 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5164 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5166 reply("CSMSG_NO_ACCESS");
5170 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5174 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5176 struct userData *uData;
5178 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5181 reply("CSMSG_NOT_USER", channel->name);
5187 /* Just show current option value. */
5189 else if(enabled_string(argv[1]))
5191 uData->flags |= mask;
5193 else if(disabled_string(argv[1]))
5195 uData->flags &= ~mask;
5199 reply("MSG_INVALID_BINARY", argv[1]);
5203 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5207 static MODCMD_FUNC(user_opt_noautoop)
5209 struct userData *uData;
5211 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5214 reply("CSMSG_NOT_USER", channel->name);
5217 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5218 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5220 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5223 static MODCMD_FUNC(user_opt_autoinvite)
5225 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5228 static MODCMD_FUNC(user_opt_info)
5230 struct userData *uData;
5233 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5237 /* If they got past the command restrictions (which require access)
5238 * but fail this test, we have some fool with security override on.
5240 reply("CSMSG_NOT_USER", channel->name);
5246 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5249 if(infoline[0] == '*' && infoline[1] == 0)
5252 uData->info = strdup(infoline);
5255 reply("CSMSG_USET_INFO", uData->info);
5257 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5261 struct svccmd_list uset_shows_list;
5263 static CHANSERV_FUNC(cmd_uset)
5265 struct svccmd *subcmd;
5269 /* Check if we need to (re-)initialize uset_shows_list. */
5270 if(!uset_shows_list.used)
5274 "NoAutoOp", "AutoInvite", "Info"
5277 if(!uset_shows_list.size)
5279 uset_shows_list.size = ArrayLength(options);
5280 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5282 for(ii = 0; ii < ArrayLength(options); ii++)
5284 const char *name = options[ii];
5285 sprintf(buf, "%s %s", argv[0], name);
5286 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5289 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5292 svccmd_list_append(&uset_shows_list, subcmd);
5298 /* Do this so options are presented in a consistent order. */
5299 reply("CSMSG_USER_OPTIONS");
5300 for(ii = 0; ii < uset_shows_list.used; ii++)
5301 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5305 sprintf(buf, "%s %s", argv[0], argv[1]);
5306 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5309 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5313 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5316 static CHANSERV_FUNC(cmd_giveownership)
5318 struct handle_info *new_owner_hi;
5319 struct userData *new_owner, *curr_user;
5320 struct chanData *cData = channel->channel_info;
5321 struct do_not_register *dnr;
5323 unsigned short co_access;
5324 char reason[MAXLEN];
5327 curr_user = GetChannelAccess(cData, user->handle_info);
5328 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5329 if(!curr_user || (curr_user->access != UL_OWNER))
5331 struct userData *owner = NULL;
5332 for(curr_user = channel->channel_info->users;
5334 curr_user = curr_user->next)
5336 if(curr_user->access != UL_OWNER)
5340 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5347 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5349 if(new_owner_hi == user->handle_info)
5351 reply("CSMSG_NO_TRANSFER_SELF");
5354 new_owner = GetChannelAccess(cData, new_owner_hi);
5357 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5360 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5362 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5365 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5366 if(!IsHelping(user))
5367 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5369 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5372 if(new_owner->access >= UL_COOWNER)
5373 co_access = new_owner->access;
5375 co_access = UL_COOWNER;
5376 new_owner->access = UL_OWNER;
5378 curr_user->access = co_access;
5379 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5380 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5381 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5385 static CHANSERV_FUNC(cmd_suspend)
5387 struct handle_info *hi;
5388 struct userData *self, *target;
5391 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5392 self = GetChannelUser(channel->channel_info, user->handle_info);
5393 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5395 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5398 if(target->access >= self->access)
5400 reply("MSG_USER_OUTRANKED", hi->handle);
5403 if(target->flags & USER_SUSPENDED)
5405 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5410 target->present = 0;
5413 target->flags |= USER_SUSPENDED;
5414 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5418 static CHANSERV_FUNC(cmd_unsuspend)
5420 struct handle_info *hi;
5421 struct userData *self, *target;
5424 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5425 self = GetChannelUser(channel->channel_info, user->handle_info);
5426 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5428 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5431 if(target->access >= self->access)
5433 reply("MSG_USER_OUTRANKED", hi->handle);
5436 if(!(target->flags & USER_SUSPENDED))
5438 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5441 target->flags &= ~USER_SUSPENDED;
5442 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5446 static MODCMD_FUNC(cmd_deleteme)
5448 struct handle_info *hi;
5449 struct userData *target;
5450 const char *confirm_string;
5451 unsigned short access;
5454 hi = user->handle_info;
5455 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5457 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5460 if(target->access == UL_OWNER)
5462 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5465 confirm_string = make_confirmation_string(target);
5466 if((argc < 2) || strcmp(argv[1], confirm_string))
5468 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5471 access = target->access;
5472 channel_name = strdup(channel->name);
5473 del_channel_user(target, 1);
5474 reply("CSMSG_DELETED_YOU", access, channel_name);
5480 chanserv_refresh_topics(UNUSED_ARG(void *data))
5482 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5483 struct chanData *cData;
5486 for(cData = channelList; cData; cData = cData->next)
5488 if(IsSuspended(cData))
5490 opt = cData->chOpts[chTopicRefresh];
5493 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5496 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5497 cData->last_refresh = refresh_num;
5499 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5502 static CHANSERV_FUNC(cmd_unf)
5506 char response[MAXLEN];
5507 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5508 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5509 irc_privmsg(cmd->parent->bot, channel->name, response);
5512 reply("CSMSG_UNF_RESPONSE");
5516 static CHANSERV_FUNC(cmd_ping)
5520 char response[MAXLEN];
5521 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5522 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5523 irc_privmsg(cmd->parent->bot, channel->name, response);
5526 reply("CSMSG_PING_RESPONSE");
5530 static CHANSERV_FUNC(cmd_wut)
5534 char response[MAXLEN];
5535 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5536 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5537 irc_privmsg(cmd->parent->bot, channel->name, response);
5540 reply("CSMSG_WUT_RESPONSE");
5544 static CHANSERV_FUNC(cmd_8ball)
5546 unsigned int i, j, accum;
5551 for(i=1; i<argc; i++)
5552 for(j=0; argv[i][j]; j++)
5553 accum = (accum << 5) - accum + toupper(argv[i][j]);
5554 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5557 char response[MAXLEN];
5558 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5559 irc_privmsg(cmd->parent->bot, channel->name, response);
5562 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5566 static CHANSERV_FUNC(cmd_d)
5568 unsigned long sides, count, modifier, ii, total;
5569 char response[MAXLEN], *sep;
5573 if((count = strtoul(argv[1], &sep, 10)) < 1)
5583 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5584 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5588 else if((sep[0] == '-') && isdigit(sep[1]))
5589 modifier = strtoul(sep, NULL, 10);
5590 else if((sep[0] == '+') && isdigit(sep[1]))
5591 modifier = strtoul(sep+1, NULL, 10);
5598 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5603 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5606 for(total = ii = 0; ii < count; ++ii)
5607 total += (rand() % sides) + 1;
5610 if((count > 1) || modifier)
5612 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5613 sprintf(response, fmt, total, count, sides, modifier);
5617 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5618 sprintf(response, fmt, total, sides);
5621 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5623 send_message_type(4, user, cmd->parent->bot, "%s", response);
5627 static CHANSERV_FUNC(cmd_huggle)
5629 /* CTCP must be via PRIVMSG, never notice */
5631 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5633 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5638 chanserv_adjust_limit(void *data)
5640 struct mod_chanmode change;
5641 struct chanData *cData = data;
5642 struct chanNode *channel = cData->channel;
5645 if(IsSuspended(cData))
5648 cData->limitAdjusted = now;
5649 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5650 if(cData->modes.modes_set & MODE_LIMIT)
5652 if(limit > cData->modes.new_limit)
5653 limit = cData->modes.new_limit;
5654 else if(limit == cData->modes.new_limit)
5658 mod_chanmode_init(&change);
5659 change.modes_set = MODE_LIMIT;
5660 change.new_limit = limit;
5661 mod_chanmode_announce(chanserv, channel, &change);
5665 handle_new_channel(struct chanNode *channel)
5667 struct chanData *cData;
5669 if(!(cData = channel->channel_info))
5672 if(cData->modes.modes_set || cData->modes.modes_clear)
5673 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5675 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5676 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5679 /* Welcome to my worst nightmare. Warning: Read (or modify)
5680 the code below at your own risk. */
5682 handle_join(struct modeNode *mNode)
5684 struct mod_chanmode change;
5685 struct userNode *user = mNode->user;
5686 struct chanNode *channel = mNode->channel;
5687 struct chanData *cData;
5688 struct userData *uData = NULL;
5689 struct banData *bData;
5690 struct handle_info *handle;
5691 unsigned int modes = 0, info = 0;
5694 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5697 cData = channel->channel_info;
5698 if(channel->members.used > cData->max)
5699 cData->max = channel->members.used;
5701 /* Check for bans. If they're joining through a ban, one of two
5703 * 1: Join during a netburst, by riding the break. Kick them
5704 * unless they have ops or voice in the channel.
5705 * 2: They're allowed to join through the ban (an invite in
5706 * ircu2.10, or a +e on Hybrid, or something).
5707 * If they're not joining through a ban, and the banlist is not
5708 * full, see if they're on the banlist for the channel. If so,
5711 if(user->uplink->burst && !mNode->modes)
5714 for(ii = 0; ii < channel->banlist.used; ii++)
5716 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5718 /* Riding a netburst. Naughty. */
5719 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5725 mod_chanmode_init(&change);
5727 if(channel->banlist.used < MAXBANS)
5729 /* Not joining through a ban. */
5730 for(bData = cData->bans;
5731 bData && !user_matches_glob(user, bData->mask, 1);
5732 bData = bData->next);
5736 char kick_reason[MAXLEN];
5737 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5739 bData->triggered = now;
5740 if(bData != cData->bans)
5742 /* Shuffle the ban to the head of the list. */
5744 bData->next->prev = bData->prev;
5746 bData->prev->next = bData->next;
5749 bData->next = cData->bans;
5752 cData->bans->prev = bData;
5753 cData->bans = bData;
5756 change.args[0].mode = MODE_BAN;
5757 change.args[0].hostmask = bData->mask;
5758 mod_chanmode_announce(chanserv, channel, &change);
5759 KickChannelUser(user, channel, chanserv, kick_reason);
5764 /* ChanServ will not modify the limits in join-flooded channels.
5765 It will also skip DynLimit processing when the user (or srvx)
5766 is bursting in, because there are likely more incoming. */
5767 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5768 && !user->uplink->burst
5769 && !channel->join_flooded
5770 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5772 /* The user count has begun "bumping" into the channel limit,
5773 so set a timer to raise the limit a bit. Any previous
5774 timers are removed so three incoming users within the delay
5775 results in one limit change, not three. */
5777 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5778 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5781 if(channel->join_flooded)
5783 /* don't automatically give ops or voice during a join flood */
5785 else if(cData->lvlOpts[lvlGiveOps] == 0)
5786 modes |= MODE_CHANOP;
5787 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5788 modes |= MODE_VOICE;
5790 greeting = cData->greeting;
5791 if(user->handle_info)
5793 handle = user->handle_info;
5795 if(IsHelper(user) && !IsHelping(user))
5798 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5800 if(channel == chanserv_conf.support_channels.list[ii])
5802 HANDLE_SET_FLAG(user->handle_info, HELPING);
5808 uData = GetTrueChannelAccess(cData, handle);
5809 if(uData && !IsUserSuspended(uData))
5811 /* Ops and above were handled by the above case. */
5812 if(IsUserAutoOp(uData))
5814 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5815 modes |= MODE_CHANOP;
5816 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5817 modes |= MODE_VOICE;
5819 if(uData->access >= UL_PRESENT)
5820 cData->visited = now;
5821 if(cData->user_greeting)
5822 greeting = cData->user_greeting;
5824 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5825 && ((now - uData->seen) >= chanserv_conf.info_delay)
5832 if(!user->uplink->burst)
5836 if(modes & MODE_CHANOP)
5837 modes &= ~MODE_VOICE;
5838 change.args[0].mode = modes;
5839 change.args[0].member = mNode;
5840 mod_chanmode_announce(chanserv, channel, &change);
5842 if(greeting && !user->uplink->burst)
5843 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5845 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5851 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5853 struct mod_chanmode change;
5854 struct userData *channel;
5855 unsigned int ii, jj;
5857 if(!user->handle_info)
5860 mod_chanmode_init(&change);
5862 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5864 struct chanNode *cn;
5865 struct modeNode *mn;
5866 if(IsUserSuspended(channel)
5867 || IsSuspended(channel->channel)
5868 || !(cn = channel->channel->channel))
5871 mn = GetUserMode(cn, user);
5874 if(!IsUserSuspended(channel)
5875 && IsUserAutoInvite(channel)
5876 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5877 && (cn->modes & (MODE_KEY | MODE_INVITEONLY))
5879 irc_invite(chanserv, user, cn);
5883 if(channel->access >= UL_PRESENT)
5884 channel->channel->visited = now;
5886 if(IsUserAutoOp(channel))
5888 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5889 change.args[0].mode = MODE_CHANOP;
5890 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5891 change.args[0].mode = MODE_VOICE;
5893 change.args[0].mode = 0;
5894 change.args[0].member = mn;
5895 if(change.args[0].mode)
5896 mod_chanmode_announce(chanserv, cn, &change);
5899 channel->seen = now;
5900 channel->present = 1;
5903 for(ii = 0; ii < user->channels.used; ++ii)
5905 struct chanNode *channel = user->channels.list[ii]->channel;
5906 struct banData *ban;
5908 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5909 || !channel->channel_info)
5911 for(jj = 0; jj < channel->banlist.used; ++jj)
5912 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5914 if(jj < channel->banlist.used)
5916 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5918 char kick_reason[MAXLEN];
5919 if(!user_matches_glob(user, ban->mask, 1))
5921 change.args[0].mode = MODE_BAN;
5922 change.args[0].hostmask = ban->mask;
5923 mod_chanmode_announce(chanserv, channel, &change);
5924 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5925 KickChannelUser(user, channel, chanserv, kick_reason);
5926 ban->triggered = now;
5931 if(IsSupportHelper(user))
5933 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5935 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5937 HANDLE_SET_FLAG(user->handle_info, HELPING);
5945 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5947 struct chanData *cData;
5948 struct userData *uData;
5950 cData = channel->channel_info;
5951 if(!cData || IsSuspended(cData) || IsLocal(user))
5954 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5956 /* Allow for a bit of padding so that the limit doesn't
5957 track the user count exactly, which could get annoying. */
5958 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5960 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5961 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5965 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5967 scan_user_presence(uData, user);
5971 if(IsHelping(user) && IsSupportHelper(user))
5973 unsigned int ii, jj;
5974 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5976 for(jj = 0; jj < user->channels.used; ++jj)
5977 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5979 if(jj < user->channels.used)
5982 if(ii == chanserv_conf.support_channels.used)
5983 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5988 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5990 struct userData *uData;
5992 if(!channel->channel_info || !kicker || IsService(kicker)
5993 || (kicker == victim) || IsSuspended(channel->channel_info)
5994 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5997 if(protect_user(victim, kicker, channel->channel_info))
5999 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6000 KickChannelUser(kicker, channel, chanserv, reason);
6003 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6008 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6010 struct chanData *cData;
6012 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6015 cData = channel->channel_info;
6016 if(bad_topic(channel, user, channel->topic))
6018 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6019 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6020 SetChannelTopic(channel, chanserv, old_topic, 1);
6021 else if(cData->topic)
6022 SetChannelTopic(channel, chanserv, cData->topic, 1);
6025 /* With topicsnarf, grab the topic and save it as the default topic. */
6026 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6029 cData->topic = strdup(channel->topic);
6035 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6037 struct mod_chanmode *bounce = NULL;
6038 unsigned int bnc, ii;
6041 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6044 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6045 && mode_lock_violated(&channel->channel_info->modes, change))
6047 char correct[MAXLEN];
6048 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6049 mod_chanmode_format(&channel->channel_info->modes, correct);
6050 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6052 for(ii = bnc = 0; ii < change->argc; ++ii)
6054 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6056 const struct userNode *victim = change->args[ii].member->user;
6057 if(!protect_user(victim, user, channel->channel_info))
6060 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6063 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6064 bounce->args[bnc].member = GetUserMode(channel, user);
6065 if(bounce->args[bnc].member)
6069 bounce->args[bnc].mode = MODE_CHANOP;
6070 bounce->args[bnc].member = change->args[ii].member;
6072 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6074 else if(change->args[ii].mode & MODE_CHANOP)
6076 const struct userNode *victim = change->args[ii].member->user;
6077 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6080 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6081 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6082 bounce->args[bnc].member = change->args[ii].member;
6085 else if(change->args[ii].mode & MODE_BAN)
6087 const char *ban = change->args[ii].hostmask;
6088 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6091 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6092 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6093 bounce->args[bnc].hostmask = ban;
6095 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6100 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6101 mod_chanmode_announce(chanserv, channel, bounce);
6102 mod_chanmode_free(bounce);
6107 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6109 struct chanNode *channel;
6110 struct banData *bData;
6111 struct mod_chanmode change;
6112 unsigned int ii, jj;
6113 char kick_reason[MAXLEN];
6115 mod_chanmode_init(&change);
6117 change.args[0].mode = MODE_BAN;
6118 for(ii = 0; ii < user->channels.used; ++ii)
6120 channel = user->channels.list[ii]->channel;
6121 /* Need not check for bans if they're opped or voiced. */
6122 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6124 /* Need not check for bans unless channel registration is active. */
6125 if(!channel->channel_info || IsSuspended(channel->channel_info))
6127 /* Look for a matching ban already on the channel. */
6128 for(jj = 0; jj < channel->banlist.used; ++jj)
6129 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6131 /* Need not act if we found one. */
6132 if(jj < channel->banlist.used)
6134 /* Look for a matching ban in this channel. */
6135 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6137 if(!user_matches_glob(user, bData->mask, 1))
6139 change.args[0].hostmask = bData->mask;
6140 mod_chanmode_announce(chanserv, channel, &change);
6141 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6142 KickChannelUser(user, channel, chanserv, kick_reason);
6143 bData->triggered = now;
6144 break; /* we don't need to check any more bans in the channel */
6149 static void handle_rename(struct handle_info *handle, const char *old_handle)
6151 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6155 dict_remove2(handle_dnrs, old_handle, 1);
6156 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6157 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6162 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6164 struct userNode *h_user;
6166 if(handle->channels)
6168 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6169 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6171 while(handle->channels)
6172 del_channel_user(handle->channels, 1);
6177 handle_server_link(UNUSED_ARG(struct server *server))
6179 struct chanData *cData;
6181 for(cData = channelList; cData; cData = cData->next)
6183 if(!IsSuspended(cData))
6184 cData->may_opchan = 1;
6185 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6186 && !cData->channel->join_flooded
6187 && ((cData->channel->limit - cData->channel->members.used)
6188 < chanserv_conf.adjust_threshold))
6190 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6191 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6197 chanserv_conf_read(void)
6201 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6202 struct mod_chanmode *change;
6203 struct string_list *strlist;
6204 struct chanNode *chan;
6207 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6209 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6212 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6213 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6214 chanserv_conf.support_channels.used = 0;
6215 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6217 for(ii = 0; ii < strlist->used; ++ii)
6219 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6222 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6224 channelList_append(&chanserv_conf.support_channels, chan);
6227 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6230 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6233 chan = AddChannel(str, now, str2, NULL);
6235 channelList_append(&chanserv_conf.support_channels, chan);
6237 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6238 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6239 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6240 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6241 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6242 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6243 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6244 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6245 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6246 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6247 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6248 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6249 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6250 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6251 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6252 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6253 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6254 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6255 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6256 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6257 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6259 NickChange(chanserv, str, 0);
6260 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6261 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6262 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6263 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6264 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6265 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6266 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6267 chanserv_conf.max_owned = str ? atoi(str) : 5;
6268 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6269 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6270 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6271 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6272 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6273 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6274 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6277 safestrncpy(mode_line, str, sizeof(mode_line));
6278 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6279 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6281 chanserv_conf.default_modes = *change;
6282 mod_chanmode_free(change);
6284 free_string_list(chanserv_conf.set_shows);
6285 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6287 strlist = string_list_copy(strlist);
6290 static const char *list[] = {
6291 /* free form text */
6292 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6293 /* options based on user level */
6294 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6295 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6296 /* multiple choice options */
6297 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6298 /* binary options */
6299 "DynLimit", "NoDelete",
6304 strlist = alloc_string_list(ArrayLength(list)-1);
6305 for(ii=0; list[ii]; ii++)
6306 string_list_append(strlist, strdup(list[ii]));
6308 chanserv_conf.set_shows = strlist;
6309 /* We don't look things up now, in case the list refers to options
6310 * defined by modules initialized after this point. Just mark the
6311 * function list as invalid, so it will be initialized.
6313 set_shows_list.used = 0;
6314 free_string_list(chanserv_conf.eightball);
6315 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6318 strlist = string_list_copy(strlist);
6322 strlist = alloc_string_list(4);
6323 string_list_append(strlist, strdup("Yes."));
6324 string_list_append(strlist, strdup("No."));
6325 string_list_append(strlist, strdup("Maybe so."));
6327 chanserv_conf.eightball = strlist;
6328 free_string_list(chanserv_conf.old_ban_names);
6329 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6331 strlist = string_list_copy(strlist);
6333 strlist = alloc_string_list(2);
6334 chanserv_conf.old_ban_names = strlist;
6338 chanserv_note_type_read(const char *key, struct record_data *rd)
6341 struct note_type *ntype;
6344 if(!(obj = GET_RECORD_OBJECT(rd)))
6346 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6349 if(!(ntype = chanserv_create_note_type(key)))
6351 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6355 /* Figure out set access */
6356 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6358 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6359 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6361 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6363 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6364 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6366 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6368 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6372 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6373 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6374 ntype->set_access.min_opserv = 0;
6377 /* Figure out visibility */
6378 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6379 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6380 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6381 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6382 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6383 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6384 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6385 ntype->visible_type = NOTE_VIS_ALL;
6387 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6389 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6390 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6394 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6396 struct handle_info *handle;
6397 struct userData *uData;
6398 char *seen, *inf, *flags;
6400 unsigned short access;
6402 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6404 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6408 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6409 if(access > UL_OWNER)
6411 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6415 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6416 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6417 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6418 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6419 handle = get_handle_info(key);
6422 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6426 uData = add_channel_user(chan, handle, access, last_seen, inf);
6427 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6431 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6433 struct banData *bData;
6434 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6435 time_t set_time, triggered_time, expires_time;
6437 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6439 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6443 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6444 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6445 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6446 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6447 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6448 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6450 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6451 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6453 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6455 expires_time = set_time + atoi(s_duration);
6459 if(expires_time && (expires_time < now))
6462 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6465 static struct suspended *
6466 chanserv_read_suspended(dict_t obj)
6468 struct suspended *suspended = calloc(1, sizeof(*suspended));
6472 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6473 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6474 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6475 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6476 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6477 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6478 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6479 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6480 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6481 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6486 chanserv_channel_read(const char *key, struct record_data *hir)
6488 struct suspended *suspended;
6489 struct mod_chanmode *modes;
6490 struct chanNode *cNode;
6491 struct chanData *cData;
6492 struct dict *channel, *obj;
6493 char *str, *argv[10];
6497 channel = hir->d.object;
6499 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6502 cNode = AddChannel(key, now, NULL, NULL);
6505 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6508 cData = register_channel(cNode, str);
6511 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6515 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6517 enum levelOption lvlOpt;
6518 enum charOption chOpt;
6520 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6521 cData->flags = atoi(str);
6523 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6525 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6527 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6528 else if(levelOptions[lvlOpt].old_flag)
6530 if(cData->flags & levelOptions[lvlOpt].old_flag)
6531 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6533 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6537 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6539 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6541 cData->chOpts[chOpt] = str[0];
6544 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6546 enum levelOption lvlOpt;
6547 enum charOption chOpt;
6550 cData->flags = base64toint(str, 5);
6551 count = strlen(str += 5);
6552 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6555 if(levelOptions[lvlOpt].old_flag)
6557 if(cData->flags & levelOptions[lvlOpt].old_flag)
6558 lvl = levelOptions[lvlOpt].flag_value;
6560 lvl = levelOptions[lvlOpt].default_value;
6562 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6564 case 'c': lvl = UL_COOWNER; break;
6565 case 'm': lvl = UL_MASTER; break;
6566 case 'n': lvl = UL_OWNER+1; break;
6567 case 'o': lvl = UL_OP; break;
6568 case 'p': lvl = UL_PEON; break;
6569 case 'w': lvl = UL_OWNER; break;
6570 default: lvl = 0; break;
6572 cData->lvlOpts[lvlOpt] = lvl;
6574 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6575 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6578 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6580 suspended = chanserv_read_suspended(obj);
6581 cData->suspended = suspended;
6582 suspended->cData = cData;
6583 /* We could use suspended->expires and suspended->revoked to
6584 * set the CHANNEL_SUSPENDED flag, but we don't. */
6586 else if(cData->flags & CHANNEL_SUSPENDED)
6588 suspended = calloc(1, sizeof(*suspended));
6589 suspended->issued = 0;
6590 suspended->revoked = 0;
6591 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6592 suspended->expires = str ? atoi(str) : 0;
6593 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6594 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6595 suspended->reason = strdup(str ? str : "No reason");
6596 suspended->previous = NULL;
6597 cData->suspended = suspended;
6598 suspended->cData = cData;
6603 if((cData->flags & CHANNEL_SUSPENDED)
6604 && suspended->expires
6605 && (suspended->expires <= now))
6607 cData->flags &= ~CHANNEL_SUSPENDED;
6610 if(!(cData->flags & CHANNEL_SUSPENDED))
6612 struct mod_chanmode change;
6613 mod_chanmode_init(&change);
6615 change.args[0].mode = MODE_CHANOP;
6616 change.args[0].member = AddChannelUser(chanserv, cNode);
6617 mod_chanmode_announce(chanserv, cNode, &change);
6619 else if(suspended->expires > now)
6621 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6624 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6625 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6626 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6627 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6628 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6629 cData->max = str ? atoi(str) : 0;
6630 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6631 cData->greeting = str ? strdup(str) : NULL;
6632 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6633 cData->user_greeting = str ? strdup(str) : NULL;
6634 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6635 cData->topic_mask = str ? strdup(str) : NULL;
6636 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6637 cData->topic = str ? strdup(str) : NULL;
6639 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6640 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6641 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6642 cData->modes = *modes;
6643 if(cData->modes.argc > 1)
6644 cData->modes.argc = 1;
6645 if(!IsSuspended(cData))
6646 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6647 mod_chanmode_free(modes);
6650 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6651 for(it = dict_first(obj); it; it = iter_next(it))
6652 user_read_helper(iter_key(it), iter_data(it), cData);
6654 if(!cData->users && !IsProtected(cData))
6656 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6657 unregister_channel(cData, "has empty user list.");
6661 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6662 for(it = dict_first(obj); it; it = iter_next(it))
6663 ban_read_helper(iter_key(it), iter_data(it), cData);
6665 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6666 for(it = dict_first(obj); it; it = iter_next(it))
6668 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6669 struct record_data *rd = iter_data(it);
6670 const char *note, *setter;
6672 if(rd->type != RECDB_OBJECT)
6674 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6678 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6680 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6682 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6686 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6687 if(!setter) setter = "<unknown>";
6688 chanserv_add_channel_note(cData, ntype, setter, note);
6696 chanserv_dnr_read(const char *key, struct record_data *hir)
6698 const char *setter, *reason, *str;
6699 struct do_not_register *dnr;
6701 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6704 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6707 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6710 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6713 dnr = chanserv_add_dnr(key, setter, reason);
6716 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6718 dnr->set = atoi(str);
6724 chanserv_saxdb_read(struct dict *database)
6726 struct dict *section;
6729 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6730 for(it = dict_first(section); it; it = iter_next(it))
6731 chanserv_note_type_read(iter_key(it), iter_data(it));
6733 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6734 for(it = dict_first(section); it; it = iter_next(it))
6735 chanserv_channel_read(iter_key(it), iter_data(it));
6737 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6738 for(it = dict_first(section); it; it = iter_next(it))
6739 chanserv_dnr_read(iter_key(it), iter_data(it));
6745 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6747 int high_present = 0;
6748 saxdb_start_record(ctx, KEY_USERS, 1);
6749 for(; uData; uData = uData->next)
6751 if((uData->access >= UL_PRESENT) && uData->present)
6753 saxdb_start_record(ctx, uData->handle->handle, 0);
6754 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6755 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6757 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6759 saxdb_write_string(ctx, KEY_INFO, uData->info);
6760 saxdb_end_record(ctx);
6762 saxdb_end_record(ctx);
6763 return high_present;
6767 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6771 saxdb_start_record(ctx, KEY_BANS, 1);
6772 for(; bData; bData = bData->next)
6774 saxdb_start_record(ctx, bData->mask, 0);
6775 saxdb_write_int(ctx, KEY_SET, bData->set);
6776 if(bData->triggered)
6777 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6779 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6781 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6783 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6784 saxdb_end_record(ctx);
6786 saxdb_end_record(ctx);
6790 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6792 saxdb_start_record(ctx, name, 0);
6793 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6794 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6796 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6798 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6800 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6802 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6803 saxdb_end_record(ctx);
6807 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6811 enum levelOption lvlOpt;
6812 enum charOption chOpt;
6814 saxdb_start_record(ctx, channel->channel->name, 1);
6816 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6817 saxdb_write_int(ctx, KEY_MAX, channel->max);
6819 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6820 if(channel->registrar)
6821 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6822 if(channel->greeting)
6823 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6824 if(channel->user_greeting)
6825 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6826 if(channel->topic_mask)
6827 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6828 if(channel->suspended)
6829 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6831 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6832 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6833 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6834 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6835 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6837 buf[0] = channel->chOpts[chOpt];
6839 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6841 saxdb_end_record(ctx);
6843 if(channel->modes.modes_set || channel->modes.modes_clear)
6845 mod_chanmode_format(&channel->modes, buf);
6846 saxdb_write_string(ctx, KEY_MODES, buf);
6849 high_present = chanserv_write_users(ctx, channel->users);
6850 chanserv_write_bans(ctx, channel->bans);
6852 if(dict_size(channel->notes))
6856 saxdb_start_record(ctx, KEY_NOTES, 1);
6857 for(it = dict_first(channel->notes); it; it = iter_next(it))
6859 struct note *note = iter_data(it);
6860 saxdb_start_record(ctx, iter_key(it), 0);
6861 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6862 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6863 saxdb_end_record(ctx);
6865 saxdb_end_record(ctx);
6868 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6869 saxdb_end_record(ctx);
6873 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6877 saxdb_start_record(ctx, ntype->name, 0);
6878 switch(ntype->set_access_type)
6880 case NOTE_SET_CHANNEL_ACCESS:
6881 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6883 case NOTE_SET_CHANNEL_SETTER:
6884 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6886 case NOTE_SET_PRIVILEGED: default:
6887 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6890 switch(ntype->visible_type)
6892 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6893 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6894 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6896 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6897 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6898 saxdb_end_record(ctx);
6902 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6904 struct do_not_register *dnr;
6907 for(it = dict_first(dnrs); it; it = iter_next(it))
6909 dnr = iter_data(it);
6910 saxdb_start_record(ctx, dnr->chan_name, 0);
6912 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6913 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6914 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6915 saxdb_end_record(ctx);
6920 chanserv_saxdb_write(struct saxdb_context *ctx)
6923 struct chanData *channel;
6926 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6927 for(it = dict_first(note_types); it; it = iter_next(it))
6928 chanserv_write_note_type(ctx, iter_data(it));
6929 saxdb_end_record(ctx);
6932 saxdb_start_record(ctx, KEY_DNR, 1);
6933 write_dnrs_helper(ctx, handle_dnrs);
6934 write_dnrs_helper(ctx, plain_dnrs);
6935 write_dnrs_helper(ctx, mask_dnrs);
6936 saxdb_end_record(ctx);
6939 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6940 for(channel = channelList; channel; channel = channel->next)
6941 chanserv_write_channel(ctx, channel);
6942 saxdb_end_record(ctx);
6948 chanserv_db_cleanup(void) {
6950 unreg_part_func(handle_part);
6952 unregister_channel(channelList, "terminating.");
6953 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6954 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6955 free(chanserv_conf.support_channels.list);
6956 dict_delete(handle_dnrs);
6957 dict_delete(plain_dnrs);
6958 dict_delete(mask_dnrs);
6959 dict_delete(note_types);
6960 free_string_list(chanserv_conf.eightball);
6961 free_string_list(chanserv_conf.old_ban_names);
6962 free_string_list(chanserv_conf.set_shows);
6963 free(set_shows_list.list);
6964 free(uset_shows_list.list);
6967 struct userData *helper = helperList;
6968 helperList = helperList->next;
6973 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6974 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6975 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6978 init_chanserv(const char *nick)
6980 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6981 conf_register_reload(chanserv_conf_read);
6983 reg_server_link_func(handle_server_link);
6985 reg_new_channel_func(handle_new_channel);
6986 reg_join_func(handle_join);
6987 reg_part_func(handle_part);
6988 reg_kick_func(handle_kick);
6989 reg_topic_func(handle_topic);
6990 reg_mode_change_func(handle_mode);
6991 reg_nick_change_func(handle_nick_change);
6993 reg_auth_func(handle_auth);
6994 reg_handle_rename_func(handle_rename);
6995 reg_unreg_func(handle_unreg);
6997 handle_dnrs = dict_new();
6998 dict_set_free_data(handle_dnrs, free);
6999 plain_dnrs = dict_new();
7000 dict_set_free_data(plain_dnrs, free);
7001 mask_dnrs = dict_new();
7002 dict_set_free_data(mask_dnrs, free);
7004 reg_svccmd_unbind_func(handle_svccmd_unbind);
7005 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7006 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7007 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7008 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7009 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7010 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7011 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7012 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7013 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7015 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7016 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7018 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7019 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7020 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7021 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7022 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7024 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7025 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7026 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7027 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7028 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7030 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7031 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7032 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7033 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7035 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7036 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7037 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7038 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7039 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7040 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7041 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7042 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7044 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7045 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7046 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7047 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7048 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7049 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7050 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7051 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7052 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7053 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7054 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7055 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7056 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7057 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7059 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7060 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7061 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7062 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7063 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7065 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7066 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7068 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7069 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7070 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7071 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7072 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7073 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7074 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7075 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7076 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7077 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7078 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7080 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7081 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7083 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7084 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7085 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7086 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7088 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7089 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7090 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7091 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7092 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7094 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7095 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7096 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7097 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7098 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7099 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7101 /* Channel options */
7102 DEFINE_CHANNEL_OPTION(defaulttopic);
7103 DEFINE_CHANNEL_OPTION(topicmask);
7104 DEFINE_CHANNEL_OPTION(greeting);
7105 DEFINE_CHANNEL_OPTION(usergreeting);
7106 DEFINE_CHANNEL_OPTION(modes);
7107 DEFINE_CHANNEL_OPTION(enfops);
7108 DEFINE_CHANNEL_OPTION(giveops);
7109 DEFINE_CHANNEL_OPTION(protect);
7110 DEFINE_CHANNEL_OPTION(enfmodes);
7111 DEFINE_CHANNEL_OPTION(enftopic);
7112 DEFINE_CHANNEL_OPTION(pubcmd);
7113 DEFINE_CHANNEL_OPTION(givevoice);
7114 DEFINE_CHANNEL_OPTION(userinfo);
7115 DEFINE_CHANNEL_OPTION(dynlimit);
7116 DEFINE_CHANNEL_OPTION(topicsnarf);
7117 DEFINE_CHANNEL_OPTION(nodelete);
7118 DEFINE_CHANNEL_OPTION(toys);
7119 DEFINE_CHANNEL_OPTION(setters);
7120 DEFINE_CHANNEL_OPTION(topicrefresh);
7121 DEFINE_CHANNEL_OPTION(ctcpusers);
7122 DEFINE_CHANNEL_OPTION(ctcpreaction);
7123 DEFINE_CHANNEL_OPTION(inviteme);
7124 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7126 /* Alias set topic to set defaulttopic for compatibility. */
7127 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7130 DEFINE_USER_OPTION(noautoop);
7131 DEFINE_USER_OPTION(autoinvite);
7132 DEFINE_USER_OPTION(info);
7134 /* Alias uset autovoice to uset autoop. */
7135 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7137 note_types = dict_new();
7138 dict_set_free_data(note_types, chanserv_deref_note_type);
7141 chanserv = AddService(nick, "Channel Services");
7142 service_register(chanserv, '!');
7143 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7145 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7147 if(chanserv_conf.channel_expire_frequency)
7148 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7150 if(chanserv_conf.refresh_period)
7152 time_t next_refresh;
7153 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7154 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7157 reg_exit_func(chanserv_db_cleanup);
7158 message_register_table(msgtab);