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", "%lu is too many dice. Please use at most %lu." },
432 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
433 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-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;
646 level = strtoul(name, NULL, 10);
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) + 8 > 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)
4304 memset(&discrim, 0, sizeof(discrim));
4305 discrim.masks.bot = chanserv;
4306 discrim.masks.channel_name = channel->name;
4308 discrim.masks.command = argv[2];
4309 discrim.limit = limit;
4310 discrim.max_time = INT_MAX;
4311 discrim.severities = 1 << LOG_COMMAND;
4312 report.reporter = chanserv;
4314 reply("CSMSG_EVENT_SEARCH_RESULTS");
4315 matches = log_entry_search(&discrim, log_report_entry, &report);
4317 reply("MSG_MATCH_COUNT", matches);
4319 reply("MSG_NO_MATCHES");
4323 static CHANSERV_FUNC(cmd_say)
4329 msg = unsplit_string(argv + 1, argc - 1, NULL);
4330 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4332 else if(GetUserH(argv[1]))
4335 msg = unsplit_string(argv + 2, argc - 2, NULL);
4336 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4340 reply("MSG_NOT_TARGET_NAME");
4346 static CHANSERV_FUNC(cmd_emote)
4352 /* CTCP is so annoying. */
4353 msg = unsplit_string(argv + 1, argc - 1, NULL);
4354 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4356 else if(GetUserH(argv[1]))
4358 msg = unsplit_string(argv + 2, argc - 2, NULL);
4359 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4363 reply("MSG_NOT_TARGET_NAME");
4369 struct channelList *
4370 chanserv_support_channels(void)
4372 return &chanserv_conf.support_channels;
4375 static CHANSERV_FUNC(cmd_expire)
4377 int channel_count = registered_channels;
4378 expire_channels(NULL);
4379 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4384 chanserv_expire_suspension(void *data)
4386 struct suspended *suspended = data;
4387 struct chanNode *channel;
4388 struct mod_chanmode change;
4390 if(!suspended->expires || (now < suspended->expires))
4391 suspended->revoked = now;
4392 channel = suspended->cData->channel;
4393 suspended->cData->channel = channel;
4394 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4395 mod_chanmode_init(&change);
4397 change.args[0].mode = MODE_CHANOP;
4398 change.args[0].member = AddChannelUser(chanserv, channel);
4399 mod_chanmode_announce(chanserv, channel, &change);
4402 static CHANSERV_FUNC(cmd_csuspend)
4404 struct suspended *suspended;
4405 char reason[MAXLEN];
4406 time_t expiry, duration;
4407 struct userData *uData;
4411 if(IsProtected(channel->channel_info))
4413 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4417 if(argv[1][0] == '!')
4419 else if(IsSuspended(channel->channel_info))
4421 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4422 show_suspension_info(cmd, user, channel->channel_info->suspended);
4426 if(!strcmp(argv[1], "0"))
4428 else if((duration = ParseInterval(argv[1])))
4429 expiry = now + duration;
4432 reply("MSG_INVALID_DURATION", argv[1]);
4436 unsplit_string(argv + 2, argc - 2, reason);
4438 suspended = calloc(1, sizeof(*suspended));
4439 suspended->revoked = 0;
4440 suspended->issued = now;
4441 suspended->suspender = strdup(user->handle_info->handle);
4442 suspended->expires = expiry;
4443 suspended->reason = strdup(reason);
4444 suspended->cData = channel->channel_info;
4445 suspended->previous = suspended->cData->suspended;
4446 suspended->cData->suspended = suspended;
4448 if(suspended->expires)
4449 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4451 if(IsSuspended(channel->channel_info))
4453 suspended->previous->revoked = now;
4454 if(suspended->previous->expires)
4455 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4456 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4457 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4461 /* Mark all users in channel as absent. */
4462 for(uData = channel->channel_info->users; uData; uData = uData->next)
4471 /* Mark the channel as suspended, then part. */
4472 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4473 DelChannelUser(chanserv, channel, suspended->reason, 0);
4474 reply("CSMSG_SUSPENDED", channel->name);
4475 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4476 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4481 static CHANSERV_FUNC(cmd_cunsuspend)
4483 struct suspended *suspended;
4484 char message[MAXLEN];
4486 if(!IsSuspended(channel->channel_info))
4488 reply("CSMSG_NOT_SUSPENDED", channel->name);
4492 suspended = channel->channel_info->suspended;
4494 /* Expire the suspension and join ChanServ to the channel. */
4495 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4496 chanserv_expire_suspension(suspended);
4497 reply("CSMSG_UNSUSPENDED", channel->name);
4498 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4499 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4503 typedef struct chanservSearch
4511 unsigned long flags;
4515 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4518 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4523 search = malloc(sizeof(struct chanservSearch));
4524 memset(search, 0, sizeof(*search));
4527 for(i = 0; i < argc; i++)
4529 /* Assume all criteria require arguments. */
4532 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4536 if(!irccasecmp(argv[i], "name"))
4537 search->name = argv[++i];
4538 else if(!irccasecmp(argv[i], "registrar"))
4539 search->registrar = argv[++i];
4540 else if(!irccasecmp(argv[i], "unvisited"))
4541 search->unvisited = ParseInterval(argv[++i]);
4542 else if(!irccasecmp(argv[i], "registered"))
4543 search->registered = ParseInterval(argv[++i]);
4544 else if(!irccasecmp(argv[i], "flags"))
4547 if(!irccasecmp(argv[i], "nodelete"))
4548 search->flags |= CHANNEL_NODELETE;
4549 else if(!irccasecmp(argv[i], "suspended"))
4550 search->flags |= CHANNEL_SUSPENDED;
4553 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4557 else if(!irccasecmp(argv[i], "limit"))
4558 search->limit = strtoul(argv[++i], NULL, 10);
4561 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4566 if(search->name && !strcmp(search->name, "*"))
4568 if(search->registrar && !strcmp(search->registrar, "*"))
4569 search->registrar = 0;
4578 chanserv_channel_match(struct chanData *channel, search_t search)
4580 const char *name = channel->channel->name;
4581 if((search->name && !match_ircglob(name, search->name)) ||
4582 (search->registrar && !channel->registrar) ||
4583 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4584 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4585 (search->registered && (now - channel->registered) > search->registered) ||
4586 (search->flags && ((search->flags & channel->flags) != search->flags)))
4593 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4595 struct chanData *channel;
4596 unsigned int matches = 0;
4598 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4600 if(!chanserv_channel_match(channel, search))
4610 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4615 search_print(struct chanData *channel, void *data)
4617 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4620 static CHANSERV_FUNC(cmd_search)
4623 unsigned int matches;
4624 channel_search_func action;
4628 if(!irccasecmp(argv[1], "count"))
4629 action = search_count;
4630 else if(!irccasecmp(argv[1], "print"))
4631 action = search_print;
4634 reply("CSMSG_ACTION_INVALID", argv[1]);
4638 search = chanserv_search_create(user, argc - 2, argv + 2);
4642 if(action == search_count)
4643 search->limit = INT_MAX;
4645 if(action == search_print)
4646 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4648 matches = chanserv_channel_search(search, action, user);
4651 reply("MSG_MATCH_COUNT", matches);
4653 reply("MSG_NO_MATCHES");
4659 static CHANSERV_FUNC(cmd_unvisited)
4661 struct chanData *cData;
4662 time_t interval = chanserv_conf.channel_expire_delay;
4663 char buffer[INTERVALLEN];
4664 unsigned int limit = 25, matches = 0;
4668 interval = ParseInterval(argv[1]);
4670 limit = atoi(argv[2]);
4673 intervalString(buffer, interval, user->handle_info);
4674 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4676 for(cData = channelList; cData && matches < limit; cData = cData->next)
4678 if((now - cData->visited) < interval)
4681 intervalString(buffer, now - cData->visited, user->handle_info);
4682 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4689 static MODCMD_FUNC(chan_opt_defaulttopic)
4695 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4697 reply("CSMSG_TOPIC_LOCKED", channel->name);
4701 topic = unsplit_string(argv+1, argc-1, NULL);
4703 free(channel->channel_info->topic);
4704 if(topic[0] == '*' && topic[1] == 0)
4706 topic = channel->channel_info->topic = NULL;
4710 topic = channel->channel_info->topic = strdup(topic);
4711 if(channel->channel_info->topic_mask
4712 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4713 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4715 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4718 if(channel->channel_info->topic)
4719 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4721 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4725 static MODCMD_FUNC(chan_opt_topicmask)
4729 struct chanData *cData = channel->channel_info;
4732 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4734 reply("CSMSG_TOPIC_LOCKED", channel->name);
4738 mask = unsplit_string(argv+1, argc-1, NULL);
4740 if(cData->topic_mask)
4741 free(cData->topic_mask);
4742 if(mask[0] == '*' && mask[1] == 0)
4744 cData->topic_mask = 0;
4748 cData->topic_mask = strdup(mask);
4750 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4751 else if(!match_ircglob(cData->topic, cData->topic_mask))
4752 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4756 if(channel->channel_info->topic_mask)
4757 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4759 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4763 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4767 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4771 if(greeting[0] == '*' && greeting[1] == 0)
4775 unsigned int length = strlen(greeting);
4776 if(length > chanserv_conf.greeting_length)
4778 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4781 *data = strdup(greeting);
4790 reply(name, user_find_message(user, "MSG_NONE"));
4794 static MODCMD_FUNC(chan_opt_greeting)
4796 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4799 static MODCMD_FUNC(chan_opt_usergreeting)
4801 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4804 static MODCMD_FUNC(chan_opt_modes)
4806 struct mod_chanmode *new_modes;
4807 char modes[MODELEN];
4811 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4813 reply("CSMSG_NO_ACCESS");
4816 if(argv[1][0] == '*' && argv[1][1] == 0)
4818 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4820 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE)))
4822 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4825 else if(new_modes->argc > 1)
4827 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4828 mod_chanmode_free(new_modes);
4833 channel->channel_info->modes = *new_modes;
4834 modcmd_chanmode_announce(new_modes);
4835 mod_chanmode_free(new_modes);
4839 mod_chanmode_format(&channel->channel_info->modes, modes);
4841 reply("CSMSG_SET_MODES", modes);
4843 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4847 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4849 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4851 struct chanData *cData = channel->channel_info;
4856 /* Set flag according to value. */
4857 if(enabled_string(argv[1]))
4859 cData->flags |= mask;
4862 else if(disabled_string(argv[1]))
4864 cData->flags &= ~mask;
4869 reply("MSG_INVALID_BINARY", argv[1]);
4875 /* Find current option value. */
4876 value = (cData->flags & mask) ? 1 : 0;
4880 reply(name, user_find_message(user, "MSG_ON"));
4882 reply(name, user_find_message(user, "MSG_OFF"));
4886 static MODCMD_FUNC(chan_opt_nodelete)
4888 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4890 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4894 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4897 static MODCMD_FUNC(chan_opt_dynlimit)
4899 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4902 static MODCMD_FUNC(chan_opt_defaults)
4904 struct userData *uData;
4905 struct chanData *cData;
4906 const char *confirm;
4907 enum levelOption lvlOpt;
4908 enum charOption chOpt;
4910 cData = channel->channel_info;
4911 uData = GetChannelUser(cData, user->handle_info);
4912 if(!uData || (uData->access < UL_OWNER))
4914 reply("CSMSG_OWNER_DEFAULTS", channel->name);
4917 confirm = make_confirmation_string(uData);
4918 if((argc < 2) || strcmp(argv[1], confirm))
4920 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
4923 cData->flags = CHANNEL_DEFAULT_FLAGS;
4924 cData->modes = chanserv_conf.default_modes;
4925 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
4926 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
4927 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
4928 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
4929 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
4934 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4936 struct chanData *cData = channel->channel_info;
4937 struct userData *uData;
4938 unsigned short value;
4942 if(!check_user_level(channel, user, option, 1, 1))
4944 reply("CSMSG_CANNOT_SET");
4947 value = user_level_from_name(argv[1], UL_OWNER+1);
4948 if(!value && strcmp(argv[1], "0"))
4950 reply("CSMSG_INVALID_ACCESS", argv[1]);
4953 uData = GetChannelUser(cData, user->handle_info);
4954 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
4956 reply("CSMSG_BAD_SETLEVEL");
4962 if(value > cData->lvlOpts[lvlGiveOps])
4964 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
4969 if(value < cData->lvlOpts[lvlGiveVoice])
4971 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
4976 /* This test only applies to owners, since non-owners
4977 * trying to set an option to above their level get caught
4978 * by the CSMSG_BAD_SETLEVEL test above.
4980 if(value > uData->access)
4982 reply("CSMSG_BAD_SETTERS");
4989 cData->lvlOpts[option] = value;
4991 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
4995 static MODCMD_FUNC(chan_opt_enfops)
4997 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5000 static MODCMD_FUNC(chan_opt_giveops)
5002 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5005 static MODCMD_FUNC(chan_opt_enfmodes)
5007 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5010 static MODCMD_FUNC(chan_opt_enftopic)
5012 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5015 static MODCMD_FUNC(chan_opt_pubcmd)
5017 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5020 static MODCMD_FUNC(chan_opt_setters)
5022 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5025 static MODCMD_FUNC(chan_opt_ctcpusers)
5027 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5030 static MODCMD_FUNC(chan_opt_userinfo)
5032 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5035 static MODCMD_FUNC(chan_opt_givevoice)
5037 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5040 static MODCMD_FUNC(chan_opt_topicsnarf)
5042 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5045 static MODCMD_FUNC(chan_opt_inviteme)
5047 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5051 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5053 struct chanData *cData = channel->channel_info;
5054 int count = charOptions[option].count, index;
5058 index = atoi(argv[1]);
5060 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5062 reply("CSMSG_INVALID_NUMERIC", index);
5063 /* Show possible values. */
5064 for(index = 0; index < count; index++)
5065 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5069 cData->chOpts[option] = charOptions[option].values[index].value;
5073 /* Find current option value. */
5076 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5080 /* Somehow, the option value is corrupt; reset it to the default. */
5081 cData->chOpts[option] = charOptions[option].default_value;
5086 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5090 static MODCMD_FUNC(chan_opt_protect)
5092 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5095 static MODCMD_FUNC(chan_opt_toys)
5097 return channel_multiple_option(chToys, CSFUNC_ARGS);
5100 static MODCMD_FUNC(chan_opt_ctcpreaction)
5102 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5105 static MODCMD_FUNC(chan_opt_topicrefresh)
5107 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5110 static struct svccmd_list set_shows_list;
5113 handle_svccmd_unbind(struct svccmd *target) {
5115 for(ii=0; ii<set_shows_list.used; ++ii)
5116 if(target == set_shows_list.list[ii])
5117 set_shows_list.used = 0;
5120 static CHANSERV_FUNC(cmd_set)
5122 struct svccmd *subcmd;
5126 /* Check if we need to (re-)initialize set_shows_list. */
5127 if(!set_shows_list.used)
5129 if(!set_shows_list.size)
5131 set_shows_list.size = chanserv_conf.set_shows->used;
5132 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5134 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5136 const char *name = chanserv_conf.set_shows->list[ii];
5137 sprintf(buf, "%s %s", argv[0], name);
5138 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5141 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5144 svccmd_list_append(&set_shows_list, subcmd);
5150 reply("CSMSG_CHANNEL_OPTIONS");
5151 for(ii = 0; ii < set_shows_list.used; ii++)
5153 subcmd = set_shows_list.list[ii];
5154 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5159 sprintf(buf, "%s %s", argv[0], argv[1]);
5160 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5163 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5166 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5168 reply("CSMSG_NO_ACCESS");
5172 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5176 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5178 struct userData *uData;
5180 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5183 reply("CSMSG_NOT_USER", channel->name);
5189 /* Just show current option value. */
5191 else if(enabled_string(argv[1]))
5193 uData->flags |= mask;
5195 else if(disabled_string(argv[1]))
5197 uData->flags &= ~mask;
5201 reply("MSG_INVALID_BINARY", argv[1]);
5205 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5209 static MODCMD_FUNC(user_opt_noautoop)
5211 struct userData *uData;
5213 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5216 reply("CSMSG_NOT_USER", channel->name);
5219 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5220 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5222 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5225 static MODCMD_FUNC(user_opt_autoinvite)
5227 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5230 static MODCMD_FUNC(user_opt_info)
5232 struct userData *uData;
5235 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5239 /* If they got past the command restrictions (which require access)
5240 * but fail this test, we have some fool with security override on.
5242 reply("CSMSG_NOT_USER", channel->name);
5248 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5251 if(infoline[0] == '*' && infoline[1] == 0)
5254 uData->info = strdup(infoline);
5257 reply("CSMSG_USET_INFO", uData->info);
5259 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5263 struct svccmd_list uset_shows_list;
5265 static CHANSERV_FUNC(cmd_uset)
5267 struct svccmd *subcmd;
5271 /* Check if we need to (re-)initialize uset_shows_list. */
5272 if(!uset_shows_list.used)
5276 "NoAutoOp", "AutoInvite", "Info"
5279 if(!uset_shows_list.size)
5281 uset_shows_list.size = ArrayLength(options);
5282 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5284 for(ii = 0; ii < ArrayLength(options); ii++)
5286 const char *name = options[ii];
5287 sprintf(buf, "%s %s", argv[0], name);
5288 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5291 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5294 svccmd_list_append(&uset_shows_list, subcmd);
5300 /* Do this so options are presented in a consistent order. */
5301 reply("CSMSG_USER_OPTIONS");
5302 for(ii = 0; ii < uset_shows_list.used; ii++)
5303 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5307 sprintf(buf, "%s %s", argv[0], argv[1]);
5308 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5311 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5315 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5318 static CHANSERV_FUNC(cmd_giveownership)
5320 struct handle_info *new_owner_hi;
5321 struct userData *new_owner, *curr_user;
5322 struct chanData *cData = channel->channel_info;
5323 struct do_not_register *dnr;
5325 unsigned short co_access;
5326 char reason[MAXLEN];
5329 curr_user = GetChannelAccess(cData, user->handle_info);
5330 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5331 if(!curr_user || (curr_user->access != UL_OWNER))
5333 struct userData *owner = NULL;
5334 for(curr_user = channel->channel_info->users;
5336 curr_user = curr_user->next)
5338 if(curr_user->access != UL_OWNER)
5342 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5349 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5351 if(new_owner_hi == user->handle_info)
5353 reply("CSMSG_NO_TRANSFER_SELF");
5356 new_owner = GetChannelAccess(cData, new_owner_hi);
5359 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5362 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5364 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5367 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5368 if(!IsHelping(user))
5369 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5371 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5374 if(new_owner->access >= UL_COOWNER)
5375 co_access = new_owner->access;
5377 co_access = UL_COOWNER;
5378 new_owner->access = UL_OWNER;
5380 curr_user->access = co_access;
5381 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5382 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5383 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5387 static CHANSERV_FUNC(cmd_suspend)
5389 struct handle_info *hi;
5390 struct userData *self, *target;
5393 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5394 self = GetChannelUser(channel->channel_info, user->handle_info);
5395 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5397 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5400 if(target->access >= self->access)
5402 reply("MSG_USER_OUTRANKED", hi->handle);
5405 if(target->flags & USER_SUSPENDED)
5407 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5412 target->present = 0;
5415 target->flags |= USER_SUSPENDED;
5416 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5420 static CHANSERV_FUNC(cmd_unsuspend)
5422 struct handle_info *hi;
5423 struct userData *self, *target;
5426 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5427 self = GetChannelUser(channel->channel_info, user->handle_info);
5428 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5430 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5433 if(target->access >= self->access)
5435 reply("MSG_USER_OUTRANKED", hi->handle);
5438 if(!(target->flags & USER_SUSPENDED))
5440 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5443 target->flags &= ~USER_SUSPENDED;
5444 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5448 static MODCMD_FUNC(cmd_deleteme)
5450 struct handle_info *hi;
5451 struct userData *target;
5452 const char *confirm_string;
5453 unsigned short access;
5456 hi = user->handle_info;
5457 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5459 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5462 if(target->access == UL_OWNER)
5464 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5467 confirm_string = make_confirmation_string(target);
5468 if((argc < 2) || strcmp(argv[1], confirm_string))
5470 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5473 access = target->access;
5474 channel_name = strdup(channel->name);
5475 del_channel_user(target, 1);
5476 reply("CSMSG_DELETED_YOU", access, channel_name);
5482 chanserv_refresh_topics(UNUSED_ARG(void *data))
5484 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5485 struct chanData *cData;
5488 for(cData = channelList; cData; cData = cData->next)
5490 if(IsSuspended(cData))
5492 opt = cData->chOpts[chTopicRefresh];
5495 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5498 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5499 cData->last_refresh = refresh_num;
5501 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5504 static CHANSERV_FUNC(cmd_unf)
5508 char response[MAXLEN];
5509 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5510 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5511 irc_privmsg(cmd->parent->bot, channel->name, response);
5514 reply("CSMSG_UNF_RESPONSE");
5518 static CHANSERV_FUNC(cmd_ping)
5522 char response[MAXLEN];
5523 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5524 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5525 irc_privmsg(cmd->parent->bot, channel->name, response);
5528 reply("CSMSG_PING_RESPONSE");
5532 static CHANSERV_FUNC(cmd_wut)
5536 char response[MAXLEN];
5537 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5538 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5539 irc_privmsg(cmd->parent->bot, channel->name, response);
5542 reply("CSMSG_WUT_RESPONSE");
5546 static CHANSERV_FUNC(cmd_8ball)
5548 unsigned int i, j, accum;
5553 for(i=1; i<argc; i++)
5554 for(j=0; argv[i][j]; j++)
5555 accum = (accum << 5) - accum + toupper(argv[i][j]);
5556 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5559 char response[MAXLEN];
5560 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5561 irc_privmsg(cmd->parent->bot, channel->name, response);
5564 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5568 static CHANSERV_FUNC(cmd_d)
5570 unsigned long sides, count, modifier, ii, total;
5571 char response[MAXLEN], *sep;
5575 if((count = strtoul(argv[1], &sep, 10)) < 1)
5585 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5586 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5590 else if((sep[0] == '-') && isdigit(sep[1]))
5591 modifier = strtoul(sep, NULL, 10);
5592 else if((sep[0] == '+') && isdigit(sep[1]))
5593 modifier = strtoul(sep+1, NULL, 10);
5600 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5605 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5608 for(total = ii = 0; ii < count; ++ii)
5609 total += (rand() % sides) + 1;
5612 if((count > 1) || modifier)
5614 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5615 sprintf(response, fmt, total, count, sides, modifier);
5619 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5620 sprintf(response, fmt, total, sides);
5623 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5625 send_message_type(4, user, cmd->parent->bot, "%s", response);
5629 static CHANSERV_FUNC(cmd_huggle)
5631 /* CTCP must be via PRIVMSG, never notice */
5633 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5635 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5640 chanserv_adjust_limit(void *data)
5642 struct mod_chanmode change;
5643 struct chanData *cData = data;
5644 struct chanNode *channel = cData->channel;
5647 if(IsSuspended(cData))
5650 cData->limitAdjusted = now;
5651 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5652 if(cData->modes.modes_set & MODE_LIMIT)
5654 if(limit > cData->modes.new_limit)
5655 limit = cData->modes.new_limit;
5656 else if(limit == cData->modes.new_limit)
5660 mod_chanmode_init(&change);
5661 change.modes_set = MODE_LIMIT;
5662 change.new_limit = limit;
5663 mod_chanmode_announce(chanserv, channel, &change);
5667 handle_new_channel(struct chanNode *channel)
5669 struct chanData *cData;
5671 if(!(cData = channel->channel_info))
5674 if(cData->modes.modes_set || cData->modes.modes_clear)
5675 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5677 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5678 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5681 /* Welcome to my worst nightmare. Warning: Read (or modify)
5682 the code below at your own risk. */
5684 handle_join(struct modeNode *mNode)
5686 struct mod_chanmode change;
5687 struct userNode *user = mNode->user;
5688 struct chanNode *channel = mNode->channel;
5689 struct chanData *cData;
5690 struct userData *uData = NULL;
5691 struct banData *bData;
5692 struct handle_info *handle;
5693 unsigned int modes = 0, info = 0;
5696 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5699 cData = channel->channel_info;
5700 if(channel->members.used > cData->max)
5701 cData->max = channel->members.used;
5703 /* Check for bans. If they're joining through a ban, one of two
5705 * 1: Join during a netburst, by riding the break. Kick them
5706 * unless they have ops or voice in the channel.
5707 * 2: They're allowed to join through the ban (an invite in
5708 * ircu2.10, or a +e on Hybrid, or something).
5709 * If they're not joining through a ban, and the banlist is not
5710 * full, see if they're on the banlist for the channel. If so,
5713 if(user->uplink->burst && !mNode->modes)
5716 for(ii = 0; ii < channel->banlist.used; ii++)
5718 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5720 /* Riding a netburst. Naughty. */
5721 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5727 mod_chanmode_init(&change);
5729 if(channel->banlist.used < MAXBANS)
5731 /* Not joining through a ban. */
5732 for(bData = cData->bans;
5733 bData && !user_matches_glob(user, bData->mask, 1);
5734 bData = bData->next);
5738 char kick_reason[MAXLEN];
5739 sprintf(kick_reason, "%s (%s)", bData->reason, bData->owner);
5741 bData->triggered = now;
5742 if(bData != cData->bans)
5744 /* Shuffle the ban to the head of the list. */
5746 bData->next->prev = bData->prev;
5748 bData->prev->next = bData->next;
5751 bData->next = cData->bans;
5754 cData->bans->prev = bData;
5755 cData->bans = bData;
5758 change.args[0].mode = MODE_BAN;
5759 change.args[0].hostmask = bData->mask;
5760 mod_chanmode_announce(chanserv, channel, &change);
5761 KickChannelUser(user, channel, chanserv, kick_reason);
5766 /* ChanServ will not modify the limits in join-flooded channels.
5767 It will also skip DynLimit processing when the user (or srvx)
5768 is bursting in, because there are likely more incoming. */
5769 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5770 && !user->uplink->burst
5771 && !channel->join_flooded
5772 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5774 /* The user count has begun "bumping" into the channel limit,
5775 so set a timer to raise the limit a bit. Any previous
5776 timers are removed so three incoming users within the delay
5777 results in one limit change, not three. */
5779 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5780 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5783 if(channel->join_flooded)
5785 /* don't automatically give ops or voice during a join flood */
5787 else if(cData->lvlOpts[lvlGiveOps] == 0)
5788 modes |= MODE_CHANOP;
5789 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5790 modes |= MODE_VOICE;
5792 greeting = cData->greeting;
5793 if(user->handle_info)
5795 handle = user->handle_info;
5797 if(IsHelper(user) && !IsHelping(user))
5800 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5802 if(channel == chanserv_conf.support_channels.list[ii])
5804 HANDLE_SET_FLAG(user->handle_info, HELPING);
5810 uData = GetTrueChannelAccess(cData, handle);
5811 if(uData && !IsUserSuspended(uData))
5813 /* Ops and above were handled by the above case. */
5814 if(IsUserAutoOp(uData))
5816 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5817 modes |= MODE_CHANOP;
5818 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5819 modes |= MODE_VOICE;
5821 if(uData->access >= UL_PRESENT)
5822 cData->visited = now;
5823 if(cData->user_greeting)
5824 greeting = cData->user_greeting;
5826 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5827 && ((now - uData->seen) >= chanserv_conf.info_delay)
5834 if(!user->uplink->burst)
5838 if(modes & MODE_CHANOP)
5839 modes &= ~MODE_VOICE;
5840 change.args[0].mode = modes;
5841 change.args[0].member = mNode;
5842 mod_chanmode_announce(chanserv, channel, &change);
5844 if(greeting && !user->uplink->burst)
5845 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5847 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5853 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5855 struct mod_chanmode change;
5856 struct userData *channel;
5857 unsigned int ii, jj;
5859 if(!user->handle_info)
5862 mod_chanmode_init(&change);
5864 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5866 struct chanNode *cn;
5867 struct modeNode *mn;
5868 if(IsUserSuspended(channel)
5869 || IsSuspended(channel->channel)
5870 || !(cn = channel->channel->channel))
5873 mn = GetUserMode(cn, user);
5876 if(!IsUserSuspended(channel)
5877 && IsUserAutoInvite(channel)
5878 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5880 irc_invite(chanserv, user, cn);
5884 if(channel->access >= UL_PRESENT)
5885 channel->channel->visited = now;
5887 if(IsUserAutoOp(channel))
5889 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
5890 change.args[0].mode = MODE_CHANOP;
5891 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
5892 change.args[0].mode = MODE_VOICE;
5894 change.args[0].mode = 0;
5895 change.args[0].member = mn;
5896 if(change.args[0].mode)
5897 mod_chanmode_announce(chanserv, cn, &change);
5900 channel->seen = now;
5901 channel->present = 1;
5904 for(ii = 0; ii < user->channels.used; ++ii)
5906 struct chanNode *channel = user->channels.list[ii]->channel;
5907 struct banData *ban;
5909 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
5910 || !channel->channel_info)
5912 for(jj = 0; jj < channel->banlist.used; ++jj)
5913 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
5915 if(jj < channel->banlist.used)
5917 for(ban = channel->channel_info->bans; ban; ban = ban->next)
5919 char kick_reason[MAXLEN];
5920 if(!user_matches_glob(user, ban->mask, 1))
5922 change.args[0].mode = MODE_BAN;
5923 change.args[0].hostmask = ban->mask;
5924 mod_chanmode_announce(chanserv, channel, &change);
5925 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
5926 KickChannelUser(user, channel, chanserv, kick_reason);
5927 ban->triggered = now;
5932 if(IsSupportHelper(user))
5934 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5936 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
5938 HANDLE_SET_FLAG(user->handle_info, HELPING);
5946 handle_part(struct userNode *user, struct chanNode *channel, UNUSED_ARG(const char *reason))
5948 struct chanData *cData;
5949 struct userData *uData;
5951 cData = channel->channel_info;
5952 if(!cData || IsSuspended(cData) || IsLocal(user))
5955 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !channel->join_flooded)
5957 /* Allow for a bit of padding so that the limit doesn't
5958 track the user count exactly, which could get annoying. */
5959 if((channel->limit - channel->members.used) > chanserv_conf.adjust_threshold + 5)
5961 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5962 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5966 if((uData = GetTrueChannelAccess(cData, user->handle_info)))
5968 scan_user_presence(uData, user);
5972 if(IsHelping(user) && IsSupportHelper(user))
5974 unsigned int ii, jj;
5975 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5977 for(jj = 0; jj < user->channels.used; ++jj)
5978 if(user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
5980 if(jj < user->channels.used)
5983 if(ii == chanserv_conf.support_channels.used)
5984 HANDLE_CLEAR_FLAG(user->handle_info, HELPING);
5989 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
5991 struct userData *uData;
5993 if(!channel->channel_info || !kicker || IsService(kicker)
5994 || (kicker == victim) || IsSuspended(channel->channel_info)
5995 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
5998 if(protect_user(victim, kicker, channel->channel_info))
6000 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6001 KickChannelUser(kicker, channel, chanserv, reason);
6004 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6009 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6011 struct chanData *cData;
6013 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6016 cData = channel->channel_info;
6017 if(bad_topic(channel, user, channel->topic))
6019 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6020 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6021 SetChannelTopic(channel, chanserv, old_topic, 1);
6022 else if(cData->topic)
6023 SetChannelTopic(channel, chanserv, cData->topic, 1);
6026 /* With topicsnarf, grab the topic and save it as the default topic. */
6027 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6030 cData->topic = strdup(channel->topic);
6036 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6038 struct mod_chanmode *bounce = NULL;
6039 unsigned int bnc, ii;
6042 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6045 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6046 && mode_lock_violated(&channel->channel_info->modes, change))
6048 char correct[MAXLEN];
6049 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6050 mod_chanmode_format(&channel->channel_info->modes, correct);
6051 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6053 for(ii = bnc = 0; ii < change->argc; ++ii)
6055 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6057 const struct userNode *victim = change->args[ii].member->user;
6058 if(!protect_user(victim, user, channel->channel_info))
6061 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6064 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6065 bounce->args[bnc].member = GetUserMode(channel, user);
6066 if(bounce->args[bnc].member)
6070 bounce->args[bnc].mode = MODE_CHANOP;
6071 bounce->args[bnc].member = change->args[ii].member;
6073 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6075 else if(change->args[ii].mode & MODE_CHANOP)
6077 const struct userNode *victim = change->args[ii].member->user;
6078 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6081 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6082 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6083 bounce->args[bnc].member = change->args[ii].member;
6086 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6088 const char *ban = change->args[ii].hostmask;
6089 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6092 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6093 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6094 bounce->args[bnc].hostmask = ban;
6096 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6101 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6102 mod_chanmode_announce(chanserv, channel, bounce);
6103 mod_chanmode_free(bounce);
6108 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6110 struct chanNode *channel;
6111 struct banData *bData;
6112 struct mod_chanmode change;
6113 unsigned int ii, jj;
6114 char kick_reason[MAXLEN];
6116 mod_chanmode_init(&change);
6118 change.args[0].mode = MODE_BAN;
6119 for(ii = 0; ii < user->channels.used; ++ii)
6121 channel = user->channels.list[ii]->channel;
6122 /* Need not check for bans if they're opped or voiced. */
6123 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6125 /* Need not check for bans unless channel registration is active. */
6126 if(!channel->channel_info || IsSuspended(channel->channel_info))
6128 /* Look for a matching ban already on the channel. */
6129 for(jj = 0; jj < channel->banlist.used; ++jj)
6130 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6132 /* Need not act if we found one. */
6133 if(jj < channel->banlist.used)
6135 /* Look for a matching ban in this channel. */
6136 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6138 if(!user_matches_glob(user, bData->mask, 1))
6140 change.args[0].hostmask = bData->mask;
6141 mod_chanmode_announce(chanserv, channel, &change);
6142 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6143 KickChannelUser(user, channel, chanserv, kick_reason);
6144 bData->triggered = now;
6145 break; /* we don't need to check any more bans in the channel */
6150 static void handle_rename(struct handle_info *handle, const char *old_handle)
6152 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6156 dict_remove2(handle_dnrs, old_handle, 1);
6157 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6158 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6163 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6165 struct userNode *h_user;
6167 if(handle->channels)
6169 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6170 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6172 while(handle->channels)
6173 del_channel_user(handle->channels, 1);
6178 handle_server_link(UNUSED_ARG(struct server *server))
6180 struct chanData *cData;
6182 for(cData = channelList; cData; cData = cData->next)
6184 if(!IsSuspended(cData))
6185 cData->may_opchan = 1;
6186 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6187 && !cData->channel->join_flooded
6188 && ((cData->channel->limit - cData->channel->members.used)
6189 < chanserv_conf.adjust_threshold))
6191 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6192 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6198 chanserv_conf_read(void)
6202 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6203 struct mod_chanmode *change;
6204 struct string_list *strlist;
6205 struct chanNode *chan;
6208 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6210 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6213 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6214 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6215 chanserv_conf.support_channels.used = 0;
6216 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6218 for(ii = 0; ii < strlist->used; ++ii)
6220 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6223 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6225 channelList_append(&chanserv_conf.support_channels, chan);
6228 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6231 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6234 chan = AddChannel(str, now, str2, NULL);
6236 channelList_append(&chanserv_conf.support_channels, chan);
6238 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6239 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6240 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6241 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6242 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6243 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6244 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6245 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6246 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6247 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6248 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6249 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6250 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6251 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6252 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6253 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6254 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6255 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6256 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6257 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6258 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6260 NickChange(chanserv, str, 0);
6261 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6262 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6263 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6264 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6265 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6266 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6267 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6268 chanserv_conf.max_owned = str ? atoi(str) : 5;
6269 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6270 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6271 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6272 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6273 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6274 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6275 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6278 safestrncpy(mode_line, str, sizeof(mode_line));
6279 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6280 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6282 chanserv_conf.default_modes = *change;
6283 mod_chanmode_free(change);
6285 free_string_list(chanserv_conf.set_shows);
6286 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6288 strlist = string_list_copy(strlist);
6291 static const char *list[] = {
6292 /* free form text */
6293 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6294 /* options based on user level */
6295 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6296 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6297 /* multiple choice options */
6298 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6299 /* binary options */
6300 "DynLimit", "NoDelete",
6305 strlist = alloc_string_list(ArrayLength(list)-1);
6306 for(ii=0; list[ii]; ii++)
6307 string_list_append(strlist, strdup(list[ii]));
6309 chanserv_conf.set_shows = strlist;
6310 /* We don't look things up now, in case the list refers to options
6311 * defined by modules initialized after this point. Just mark the
6312 * function list as invalid, so it will be initialized.
6314 set_shows_list.used = 0;
6315 free_string_list(chanserv_conf.eightball);
6316 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6319 strlist = string_list_copy(strlist);
6323 strlist = alloc_string_list(4);
6324 string_list_append(strlist, strdup("Yes."));
6325 string_list_append(strlist, strdup("No."));
6326 string_list_append(strlist, strdup("Maybe so."));
6328 chanserv_conf.eightball = strlist;
6329 free_string_list(chanserv_conf.old_ban_names);
6330 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6332 strlist = string_list_copy(strlist);
6334 strlist = alloc_string_list(2);
6335 chanserv_conf.old_ban_names = strlist;
6339 chanserv_note_type_read(const char *key, struct record_data *rd)
6342 struct note_type *ntype;
6345 if(!(obj = GET_RECORD_OBJECT(rd)))
6347 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6350 if(!(ntype = chanserv_create_note_type(key)))
6352 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6356 /* Figure out set access */
6357 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6359 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6360 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6362 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6364 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6365 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6367 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6369 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6373 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6374 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6375 ntype->set_access.min_opserv = 0;
6378 /* Figure out visibility */
6379 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6380 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6381 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6382 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6383 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6384 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6385 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6386 ntype->visible_type = NOTE_VIS_ALL;
6388 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6390 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6391 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6395 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6397 struct handle_info *handle;
6398 struct userData *uData;
6399 char *seen, *inf, *flags;
6401 unsigned short access;
6403 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6405 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6409 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6410 if(access > UL_OWNER)
6412 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6416 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6417 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6418 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6419 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6420 handle = get_handle_info(key);
6423 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6427 uData = add_channel_user(chan, handle, access, last_seen, inf);
6428 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6432 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6434 struct banData *bData;
6435 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6436 time_t set_time, triggered_time, expires_time;
6438 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6440 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6444 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6445 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6446 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6447 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6448 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6449 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6451 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6452 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6454 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6456 expires_time = set_time + atoi(s_duration);
6460 if(expires_time && (expires_time < now))
6463 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6466 static struct suspended *
6467 chanserv_read_suspended(dict_t obj)
6469 struct suspended *suspended = calloc(1, sizeof(*suspended));
6473 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6474 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6475 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6476 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6477 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6478 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6479 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6480 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6481 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6482 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6487 chanserv_channel_read(const char *key, struct record_data *hir)
6489 struct suspended *suspended;
6490 struct mod_chanmode *modes;
6491 struct chanNode *cNode;
6492 struct chanData *cData;
6493 struct dict *channel, *obj;
6494 char *str, *argv[10];
6498 channel = hir->d.object;
6500 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6503 cNode = AddChannel(key, now, NULL, NULL);
6506 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6509 cData = register_channel(cNode, str);
6512 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6516 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6518 enum levelOption lvlOpt;
6519 enum charOption chOpt;
6521 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6522 cData->flags = atoi(str);
6524 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6526 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6528 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6529 else if(levelOptions[lvlOpt].old_flag)
6531 if(cData->flags & levelOptions[lvlOpt].old_flag)
6532 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6534 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6538 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6540 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6542 cData->chOpts[chOpt] = str[0];
6545 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6547 enum levelOption lvlOpt;
6548 enum charOption chOpt;
6551 cData->flags = base64toint(str, 5);
6552 count = strlen(str += 5);
6553 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6556 if(levelOptions[lvlOpt].old_flag)
6558 if(cData->flags & levelOptions[lvlOpt].old_flag)
6559 lvl = levelOptions[lvlOpt].flag_value;
6561 lvl = levelOptions[lvlOpt].default_value;
6563 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6565 case 'c': lvl = UL_COOWNER; break;
6566 case 'm': lvl = UL_MASTER; break;
6567 case 'n': lvl = UL_OWNER+1; break;
6568 case 'o': lvl = UL_OP; break;
6569 case 'p': lvl = UL_PEON; break;
6570 case 'w': lvl = UL_OWNER; break;
6571 default: lvl = 0; break;
6573 cData->lvlOpts[lvlOpt] = lvl;
6575 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6576 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6579 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6581 suspended = chanserv_read_suspended(obj);
6582 cData->suspended = suspended;
6583 suspended->cData = cData;
6584 /* We could use suspended->expires and suspended->revoked to
6585 * set the CHANNEL_SUSPENDED flag, but we don't. */
6587 else if(cData->flags & CHANNEL_SUSPENDED)
6589 suspended = calloc(1, sizeof(*suspended));
6590 suspended->issued = 0;
6591 suspended->revoked = 0;
6592 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6593 suspended->expires = str ? atoi(str) : 0;
6594 suspended->suspender = strdup(database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING));
6595 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6596 suspended->reason = strdup(str ? str : "No reason");
6597 suspended->previous = NULL;
6598 cData->suspended = suspended;
6599 suspended->cData = cData;
6604 if((cData->flags & CHANNEL_SUSPENDED)
6605 && suspended->expires
6606 && (suspended->expires <= now))
6608 cData->flags &= ~CHANNEL_SUSPENDED;
6611 if(!(cData->flags & CHANNEL_SUSPENDED))
6613 struct mod_chanmode change;
6614 mod_chanmode_init(&change);
6616 change.args[0].mode = MODE_CHANOP;
6617 change.args[0].member = AddChannelUser(chanserv, cNode);
6618 mod_chanmode_announce(chanserv, cNode, &change);
6620 else if(suspended->expires > now)
6622 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6625 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6626 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6627 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6628 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6629 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6630 cData->max = str ? atoi(str) : 0;
6631 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6632 cData->greeting = str ? strdup(str) : NULL;
6633 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6634 cData->user_greeting = str ? strdup(str) : NULL;
6635 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6636 cData->topic_mask = str ? strdup(str) : NULL;
6637 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6638 cData->topic = str ? strdup(str) : NULL;
6640 if((str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6641 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6642 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6643 cData->modes = *modes;
6644 if(cData->modes.argc > 1)
6645 cData->modes.argc = 1;
6646 if(!IsSuspended(cData))
6647 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6648 mod_chanmode_free(modes);
6651 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6652 for(it = dict_first(obj); it; it = iter_next(it))
6653 user_read_helper(iter_key(it), iter_data(it), cData);
6655 if(!cData->users && !IsProtected(cData))
6657 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6658 unregister_channel(cData, "has empty user list.");
6662 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6663 for(it = dict_first(obj); it; it = iter_next(it))
6664 ban_read_helper(iter_key(it), iter_data(it), cData);
6666 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6667 for(it = dict_first(obj); it; it = iter_next(it))
6669 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6670 struct record_data *rd = iter_data(it);
6671 const char *note, *setter;
6673 if(rd->type != RECDB_OBJECT)
6675 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6679 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6681 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6683 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6687 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6688 if(!setter) setter = "<unknown>";
6689 chanserv_add_channel_note(cData, ntype, setter, note);
6697 chanserv_dnr_read(const char *key, struct record_data *hir)
6699 const char *setter, *reason, *str;
6700 struct do_not_register *dnr;
6702 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6705 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6708 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6711 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6714 dnr = chanserv_add_dnr(key, setter, reason);
6717 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6719 dnr->set = atoi(str);
6725 chanserv_saxdb_read(struct dict *database)
6727 struct dict *section;
6730 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6731 for(it = dict_first(section); it; it = iter_next(it))
6732 chanserv_note_type_read(iter_key(it), iter_data(it));
6734 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6735 for(it = dict_first(section); it; it = iter_next(it))
6736 chanserv_channel_read(iter_key(it), iter_data(it));
6738 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6739 for(it = dict_first(section); it; it = iter_next(it))
6740 chanserv_dnr_read(iter_key(it), iter_data(it));
6746 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6748 int high_present = 0;
6749 saxdb_start_record(ctx, KEY_USERS, 1);
6750 for(; uData; uData = uData->next)
6752 if((uData->access >= UL_PRESENT) && uData->present)
6754 saxdb_start_record(ctx, uData->handle->handle, 0);
6755 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6756 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6758 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6760 saxdb_write_string(ctx, KEY_INFO, uData->info);
6761 saxdb_end_record(ctx);
6763 saxdb_end_record(ctx);
6764 return high_present;
6768 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6772 saxdb_start_record(ctx, KEY_BANS, 1);
6773 for(; bData; bData = bData->next)
6775 saxdb_start_record(ctx, bData->mask, 0);
6776 saxdb_write_int(ctx, KEY_SET, bData->set);
6777 if(bData->triggered)
6778 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6780 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6782 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6784 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6785 saxdb_end_record(ctx);
6787 saxdb_end_record(ctx);
6791 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6793 saxdb_start_record(ctx, name, 0);
6794 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6795 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6797 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6799 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6801 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6803 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6804 saxdb_end_record(ctx);
6808 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6812 enum levelOption lvlOpt;
6813 enum charOption chOpt;
6815 saxdb_start_record(ctx, channel->channel->name, 1);
6817 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6818 saxdb_write_int(ctx, KEY_MAX, channel->max);
6820 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6821 if(channel->registrar)
6822 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6823 if(channel->greeting)
6824 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6825 if(channel->user_greeting)
6826 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6827 if(channel->topic_mask)
6828 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6829 if(channel->suspended)
6830 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6832 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6833 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6834 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6835 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6836 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6838 buf[0] = channel->chOpts[chOpt];
6840 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6842 saxdb_end_record(ctx);
6844 if(channel->modes.modes_set || channel->modes.modes_clear)
6846 mod_chanmode_format(&channel->modes, buf);
6847 saxdb_write_string(ctx, KEY_MODES, buf);
6850 high_present = chanserv_write_users(ctx, channel->users);
6851 chanserv_write_bans(ctx, channel->bans);
6853 if(dict_size(channel->notes))
6857 saxdb_start_record(ctx, KEY_NOTES, 1);
6858 for(it = dict_first(channel->notes); it; it = iter_next(it))
6860 struct note *note = iter_data(it);
6861 saxdb_start_record(ctx, iter_key(it), 0);
6862 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6863 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6864 saxdb_end_record(ctx);
6866 saxdb_end_record(ctx);
6869 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
6870 saxdb_end_record(ctx);
6874 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
6878 saxdb_start_record(ctx, ntype->name, 0);
6879 switch(ntype->set_access_type)
6881 case NOTE_SET_CHANNEL_ACCESS:
6882 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
6884 case NOTE_SET_CHANNEL_SETTER:
6885 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
6887 case NOTE_SET_PRIVILEGED: default:
6888 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
6891 switch(ntype->visible_type)
6893 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
6894 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
6895 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
6897 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
6898 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
6899 saxdb_end_record(ctx);
6903 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
6905 struct do_not_register *dnr;
6908 for(it = dict_first(dnrs); it; it = iter_next(it))
6910 dnr = iter_data(it);
6911 saxdb_start_record(ctx, dnr->chan_name, 0);
6913 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
6914 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
6915 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
6916 saxdb_end_record(ctx);
6921 chanserv_saxdb_write(struct saxdb_context *ctx)
6924 struct chanData *channel;
6927 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
6928 for(it = dict_first(note_types); it; it = iter_next(it))
6929 chanserv_write_note_type(ctx, iter_data(it));
6930 saxdb_end_record(ctx);
6933 saxdb_start_record(ctx, KEY_DNR, 1);
6934 write_dnrs_helper(ctx, handle_dnrs);
6935 write_dnrs_helper(ctx, plain_dnrs);
6936 write_dnrs_helper(ctx, mask_dnrs);
6937 saxdb_end_record(ctx);
6940 saxdb_start_record(ctx, KEY_CHANNELS, 1);
6941 for(channel = channelList; channel; channel = channel->next)
6942 chanserv_write_channel(ctx, channel);
6943 saxdb_end_record(ctx);
6949 chanserv_db_cleanup(void) {
6951 unreg_part_func(handle_part);
6953 unregister_channel(channelList, "terminating.");
6954 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6955 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6956 free(chanserv_conf.support_channels.list);
6957 dict_delete(handle_dnrs);
6958 dict_delete(plain_dnrs);
6959 dict_delete(mask_dnrs);
6960 dict_delete(note_types);
6961 free_string_list(chanserv_conf.eightball);
6962 free_string_list(chanserv_conf.old_ban_names);
6963 free_string_list(chanserv_conf.set_shows);
6964 free(set_shows_list.list);
6965 free(uset_shows_list.list);
6968 struct userData *helper = helperList;
6969 helperList = helperList->next;
6974 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
6975 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
6976 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
6979 init_chanserv(const char *nick)
6981 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
6982 conf_register_reload(chanserv_conf_read);
6984 reg_server_link_func(handle_server_link);
6986 reg_new_channel_func(handle_new_channel);
6987 reg_join_func(handle_join);
6988 reg_part_func(handle_part);
6989 reg_kick_func(handle_kick);
6990 reg_topic_func(handle_topic);
6991 reg_mode_change_func(handle_mode);
6992 reg_nick_change_func(handle_nick_change);
6994 reg_auth_func(handle_auth);
6995 reg_handle_rename_func(handle_rename);
6996 reg_unreg_func(handle_unreg);
6998 handle_dnrs = dict_new();
6999 dict_set_free_data(handle_dnrs, free);
7000 plain_dnrs = dict_new();
7001 dict_set_free_data(plain_dnrs, free);
7002 mask_dnrs = dict_new();
7003 dict_set_free_data(mask_dnrs, free);
7005 reg_svccmd_unbind_func(handle_svccmd_unbind);
7006 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7007 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7008 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7009 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7010 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7011 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7012 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7013 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7014 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7016 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7017 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7019 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7020 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7021 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7022 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7023 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7025 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7026 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7027 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7028 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7029 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7031 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7032 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7033 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7034 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7036 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7037 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7038 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7039 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7040 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7041 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7042 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7043 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7045 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7046 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7047 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7048 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7049 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7050 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7051 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7052 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7053 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7054 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7055 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7056 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7057 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7058 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7060 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7061 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7062 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7063 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7064 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7066 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7067 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7069 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7070 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7071 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7072 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7073 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7074 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7075 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7076 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7077 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7078 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7079 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7081 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7082 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7084 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7085 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7086 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7087 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7089 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7090 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7091 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7092 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7093 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7095 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7096 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7097 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7098 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7099 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7100 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7102 /* Channel options */
7103 DEFINE_CHANNEL_OPTION(defaulttopic);
7104 DEFINE_CHANNEL_OPTION(topicmask);
7105 DEFINE_CHANNEL_OPTION(greeting);
7106 DEFINE_CHANNEL_OPTION(usergreeting);
7107 DEFINE_CHANNEL_OPTION(modes);
7108 DEFINE_CHANNEL_OPTION(enfops);
7109 DEFINE_CHANNEL_OPTION(giveops);
7110 DEFINE_CHANNEL_OPTION(protect);
7111 DEFINE_CHANNEL_OPTION(enfmodes);
7112 DEFINE_CHANNEL_OPTION(enftopic);
7113 DEFINE_CHANNEL_OPTION(pubcmd);
7114 DEFINE_CHANNEL_OPTION(givevoice);
7115 DEFINE_CHANNEL_OPTION(userinfo);
7116 DEFINE_CHANNEL_OPTION(dynlimit);
7117 DEFINE_CHANNEL_OPTION(topicsnarf);
7118 DEFINE_CHANNEL_OPTION(nodelete);
7119 DEFINE_CHANNEL_OPTION(toys);
7120 DEFINE_CHANNEL_OPTION(setters);
7121 DEFINE_CHANNEL_OPTION(topicrefresh);
7122 DEFINE_CHANNEL_OPTION(ctcpusers);
7123 DEFINE_CHANNEL_OPTION(ctcpreaction);
7124 DEFINE_CHANNEL_OPTION(inviteme);
7125 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7127 /* Alias set topic to set defaulttopic for compatibility. */
7128 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7131 DEFINE_USER_OPTION(noautoop);
7132 DEFINE_USER_OPTION(autoinvite);
7133 DEFINE_USER_OPTION(info);
7135 /* Alias uset autovoice to uset autoop. */
7136 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7138 note_types = dict_new();
7139 dict_set_free_data(note_types, chanserv_deref_note_type);
7142 chanserv = AddService(nick, "Channel Services", NULL);
7143 service_register(chanserv)->trigger = '!';
7144 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7146 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7148 if(chanserv_conf.channel_expire_frequency)
7149 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7151 if(chanserv_conf.refresh_period)
7153 time_t next_refresh;
7154 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7155 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7158 reg_exit_func(chanserv_db_cleanup);
7159 message_register_table(msgtab);