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_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
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"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "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." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "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." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "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" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
424 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
427 { "CSMSG_USER_OPTIONS", "User Options:" },
428 { "CSMSG_USER_PROTECTED", "That user is protected." },
431 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
432 { "CSMSG_PING_RESPONSE", "Pong!" },
433 { "CSMSG_WUT_RESPONSE", "wut" },
434 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
435 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
436 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
437 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
438 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
439 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
440 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
443 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
447 /* eject_user and unban_user flags */
448 #define ACTION_KICK 0x0001
449 #define ACTION_BAN 0x0002
450 #define ACTION_ADD_BAN 0x0004
451 #define ACTION_ADD_TIMED_BAN 0x0008
452 #define ACTION_UNBAN 0x0010
453 #define ACTION_DEL_BAN 0x0020
455 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
456 #define MODELEN 40 + KEYLEN
460 #define CSFUNC_ARGS user, channel, argc, argv, cmd
462 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
463 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
464 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
465 reply("MSG_MISSING_PARAMS", argv[0]); \
469 DECLARE_LIST(dnrList, struct do_not_register *);
470 DEFINE_LIST(dnrList, struct do_not_register *);
472 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
474 struct userNode *chanserv;
477 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
478 static struct log_type *CS_LOG;
482 struct channelList support_channels;
483 struct mod_chanmode default_modes;
485 unsigned long db_backup_frequency;
486 unsigned long channel_expire_frequency;
489 unsigned int adjust_delay;
490 long channel_expire_delay;
491 unsigned int nodelete_level;
493 unsigned int adjust_threshold;
494 int join_flood_threshold;
496 unsigned int greeting_length;
497 unsigned int refresh_period;
498 unsigned int giveownership_period;
500 unsigned int max_owned;
501 unsigned int max_chan_users;
502 unsigned int max_chan_bans;
503 unsigned int max_userinfo_length;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->ownerTransfer = now;
1084 channel->flags = CHANNEL_DEFAULT_FLAGS;
1085 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1086 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1088 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1090 channel->prev = NULL;
1091 channel->next = channelList;
1094 channelList->prev = channel;
1095 channelList = channel;
1096 registered_channels++;
1098 channel->channel = cNode;
1100 cNode->channel_info = channel;
1105 static struct userData*
1106 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1108 struct userData *ud;
1110 if(access > UL_OWNER)
1113 ud = calloc(1, sizeof(*ud));
1114 ud->channel = channel;
1115 ud->handle = handle;
1117 ud->access = access;
1118 ud->info = info ? strdup(info) : NULL;
1121 ud->next = channel->users;
1123 channel->users->prev = ud;
1124 channel->users = ud;
1126 channel->userCount++;
1130 ud->u_next = ud->handle->channels;
1132 ud->u_next->u_prev = ud;
1133 ud->handle->channels = ud;
1138 static void unregister_channel(struct chanData *channel, const char *reason);
1141 del_channel_user(struct userData *user, int do_gc)
1143 struct chanData *channel = user->channel;
1145 channel->userCount--;
1149 user->prev->next = user->next;
1151 channel->users = user->next;
1153 user->next->prev = user->prev;
1156 user->u_prev->u_next = user->u_next;
1158 user->handle->channels = user->u_next;
1160 user->u_next->u_prev = user->u_prev;
1164 if(do_gc && !channel->users && !IsProtected(channel))
1165 unregister_channel(channel, "lost all users.");
1168 static void expire_ban(void *data);
1170 static struct banData*
1171 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1174 unsigned int ii, l1, l2;
1179 bd = malloc(sizeof(struct banData));
1181 bd->channel = channel;
1183 bd->triggered = triggered;
1184 bd->expires = expires;
1186 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1188 extern const char *hidden_host_suffix;
1189 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1193 l2 = strlen(old_name);
1196 if(irccasecmp(mask + l1 - l2, old_name))
1198 new_mask = alloca(MAXLEN);
1199 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1202 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1204 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1205 bd->reason = strdup(reason);
1208 timeq_add(expires, expire_ban, bd);
1211 bd->next = channel->bans;
1213 channel->bans->prev = bd;
1215 channel->banCount++;
1222 del_channel_ban(struct banData *ban)
1224 ban->channel->banCount--;
1228 ban->prev->next = ban->next;
1230 ban->channel->bans = ban->next;
1233 ban->next->prev = ban->prev;
1236 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1245 expire_ban(void *data)
1247 struct banData *bd = data;
1248 if(!IsSuspended(bd->channel))
1250 struct banList bans;
1251 struct mod_chanmode change;
1253 bans = bd->channel->channel->banlist;
1254 mod_chanmode_init(&change);
1255 for(ii=0; ii<bans.used; ii++)
1257 if(!strcmp(bans.list[ii]->ban, bd->mask))
1260 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1261 change.args[0].u.hostmask = bd->mask;
1262 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1268 del_channel_ban(bd);
1271 static void chanserv_expire_suspension(void *data);
1274 unregister_channel(struct chanData *channel, const char *reason)
1276 struct mod_chanmode change;
1277 char msgbuf[MAXLEN];
1279 /* After channel unregistration, the following must be cleaned
1281 - Channel information.
1284 - Channel suspension data.
1285 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1291 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 mod_chanmode_init(&change);
1296 change.modes_clear |= MODE_REGISTERED;
1297 mod_chanmode_announce(chanserv, channel->channel, &change);
1300 while(channel->users)
1301 del_channel_user(channel->users, 0);
1303 while(channel->bans)
1304 del_channel_ban(channel->bans);
1306 free(channel->topic);
1307 free(channel->registrar);
1308 free(channel->greeting);
1309 free(channel->user_greeting);
1310 free(channel->topic_mask);
1313 channel->prev->next = channel->next;
1315 channelList = channel->next;
1318 channel->next->prev = channel->prev;
1320 if(channel->suspended)
1322 struct chanNode *cNode = channel->channel;
1323 struct suspended *suspended, *next_suspended;
1325 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1327 next_suspended = suspended->previous;
1328 free(suspended->suspender);
1329 free(suspended->reason);
1330 if(suspended->expires)
1331 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1336 cNode->channel_info = NULL;
1338 channel->channel->channel_info = NULL;
1340 dict_delete(channel->notes);
1341 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1342 if(!IsSuspended(channel))
1343 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1345 UnlockChannel(channel->channel);
1347 registered_channels--;
1351 expire_channels(UNUSED_ARG(void *data))
1353 struct chanData *channel, *next;
1354 struct userData *user;
1355 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1357 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1358 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1360 for(channel = channelList; channel; channel = next)
1362 next = channel->next;
1364 /* See if the channel can be expired. */
1365 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1366 || IsProtected(channel))
1369 /* Make sure there are no high-ranking users still in the channel. */
1370 for(user=channel->users; user; user=user->next)
1371 if(user->present && (user->access >= UL_PRESENT))
1376 /* Unregister the channel */
1377 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1378 unregister_channel(channel, "registration expired.");
1381 if(chanserv_conf.channel_expire_frequency)
1382 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1386 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1388 char protect = channel->chOpts[chProtect];
1389 struct userData *cs_victim, *cs_aggressor;
1391 /* Don't protect if no one is to be protected, someone is attacking
1392 himself, or if the aggressor is an IRC Operator. */
1393 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1396 /* Don't protect if the victim isn't authenticated (because they
1397 can't be a channel user), unless we are to protect non-users
1399 cs_victim = GetChannelAccess(channel, victim->handle_info);
1400 if(protect != 'a' && !cs_victim)
1403 /* Protect if the aggressor isn't a user because at this point,
1404 the aggressor can only be less than or equal to the victim. */
1405 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1409 /* If the aggressor was a user, then the victim can't be helped. */
1416 if(cs_victim->access > cs_aggressor->access)
1421 if(cs_victim->access >= cs_aggressor->access)
1430 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1432 struct chanData *cData = channel->channel_info;
1433 struct userData *cs_victim;
1435 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1436 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1437 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1439 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1447 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1449 if(IsService(victim))
1451 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1455 if(protect_user(victim, user, channel->channel_info))
1457 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1464 static struct do_not_register *
1465 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1467 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1468 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1469 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1470 strcpy(dnr->reason, reason);
1472 if(dnr->chan_name[0] == '*')
1473 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1474 else if(strpbrk(dnr->chan_name, "*?"))
1475 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1477 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1481 static struct dnrList
1482 chanserv_find_dnrs(const char *chan_name, const char *handle)
1484 struct dnrList list;
1486 struct do_not_register *dnr;
1488 dnrList_init(&list);
1489 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1490 dnrList_append(&list, dnr);
1491 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1492 dnrList_append(&list, dnr);
1494 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1495 if(match_ircglob(chan_name, iter_key(it)))
1496 dnrList_append(&list, iter_data(it));
1501 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1503 struct dnrList list;
1504 struct do_not_register *dnr;
1506 char buf[INTERVALLEN];
1508 list = chanserv_find_dnrs(chan_name, handle);
1509 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1511 dnr = list.list[ii];
1514 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1515 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1518 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1521 reply("CSMSG_MORE_DNRS", list.used - ii);
1526 struct do_not_register *
1527 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1529 struct do_not_register *dnr;
1532 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1536 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1538 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1539 if(match_ircglob(chan_name, iter_key(it)))
1540 return iter_data(it);
1545 static CHANSERV_FUNC(cmd_noregister)
1548 struct do_not_register *dnr;
1549 char buf[INTERVALLEN];
1550 unsigned int matches;
1556 reply("CSMSG_DNR_SEARCH_RESULTS");
1558 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1560 dnr = iter_data(it);
1562 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1564 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1569 dnr = iter_data(it);
1571 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1573 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1576 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1578 dnr = iter_data(it);
1580 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1582 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587 reply("MSG_MATCH_COUNT", matches);
1589 reply("MSG_NO_MATCHES");
1595 if(!IsChannelName(target) && (*target != '*'))
1597 reply("CSMSG_NOT_DNR", target);
1603 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1604 if((*target == '*') && !get_handle_info(target + 1))
1606 reply("MSG_HANDLE_UNKNOWN", target + 1);
1609 chanserv_add_dnr(target, user->handle_info->handle, reason);
1610 reply("CSMSG_NOREGISTER_CHANNEL", target);
1614 reply("CSMSG_DNR_SEARCH_RESULTS");
1616 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1618 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1620 reply("MSG_NO_MATCHES");
1624 static CHANSERV_FUNC(cmd_allowregister)
1626 const char *chan_name = argv[1];
1628 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1630 dict_remove(handle_dnrs, chan_name+1);
1631 reply("CSMSG_DNR_REMOVED", chan_name);
1633 else if(dict_find(plain_dnrs, chan_name, NULL))
1635 dict_remove(plain_dnrs, chan_name);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(mask_dnrs, chan_name, NULL))
1640 dict_remove(mask_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1645 reply("CSMSG_NO_SUCH_DNR", chan_name);
1652 chanserv_get_owned_count(struct handle_info *hi)
1654 struct userData *cList;
1657 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1658 if(cList->access == UL_OWNER)
1663 static CHANSERV_FUNC(cmd_register)
1665 struct handle_info *handle;
1666 struct chanData *cData;
1667 struct modeNode *mn;
1668 char reason[MAXLEN];
1670 unsigned int new_channel, force=0;
1671 struct do_not_register *dnr;
1675 if(channel->channel_info)
1677 reply("CSMSG_ALREADY_REGGED", channel->name);
1681 if(channel->bad_channel)
1683 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1688 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1690 reply("CSMSG_MUST_BE_OPPED", channel->name);
1695 chan_name = channel->name;
1699 if((argc < 2) || !IsChannelName(argv[1]))
1701 reply("MSG_NOT_CHANNEL_NAME");
1705 if(opserv_bad_channel(argv[1]))
1707 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1712 chan_name = argv[1];
1715 if(argc >= (new_channel+2))
1717 if(!IsHelping(user))
1719 reply("CSMSG_PROXY_FORBIDDEN");
1723 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1725 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1726 dnr = chanserv_is_dnr(chan_name, handle);
1730 handle = user->handle_info;
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 if(!IsHelping(user))
1736 reply("CSMSG_DNR_CHANNEL", chan_name);
1738 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1742 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1744 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1749 channel = AddChannel(argv[1], now, NULL, NULL);
1751 cData = register_channel(channel, user->handle_info->handle);
1752 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1753 cData->modes = chanserv_conf.default_modes;
1755 cData->modes.modes_set |= MODE_REGISTERED;
1756 if (IsOffChannel(cData))
1758 mod_chanmode_announce(chanserv, channel, &cData->modes);
1762 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1763 change->args[change->argc].mode = MODE_CHANOP;
1764 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1766 mod_chanmode_announce(chanserv, channel, change);
1767 mod_chanmode_free(change);
1770 /* Initialize the channel's max user record. */
1771 cData->max = channel->members.used;
1773 if(handle != user->handle_info)
1774 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1776 reply("CSMSG_REG_SUCCESS", channel->name);
1778 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1779 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1784 make_confirmation_string(struct userData *uData)
1786 static char strbuf[16];
1791 for(src = uData->handle->handle; *src; )
1792 accum = accum * 31 + toupper(*src++);
1794 for(src = uData->channel->channel->name; *src; )
1795 accum = accum * 31 + toupper(*src++);
1796 sprintf(strbuf, "%08x", accum);
1800 static CHANSERV_FUNC(cmd_unregister)
1803 char reason[MAXLEN];
1804 struct chanData *cData;
1805 struct userData *uData;
1807 cData = channel->channel_info;
1810 reply("CSMSG_NOT_REGISTERED", channel->name);
1814 uData = GetChannelUser(cData, user->handle_info);
1815 if(!uData || (uData->access < UL_OWNER))
1817 reply("CSMSG_NO_ACCESS");
1821 if(IsProtected(cData))
1823 reply("CSMSG_UNREG_NODELETE", channel->name);
1827 if(!IsHelping(user))
1829 const char *confirm_string;
1830 if(IsSuspended(cData))
1832 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1835 confirm_string = make_confirmation_string(uData);
1836 if((argc < 2) || strcmp(argv[1], confirm_string))
1838 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1843 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1844 name = strdup(channel->name);
1845 unregister_channel(cData, reason);
1846 reply("CSMSG_UNREG_SUCCESS", name);
1851 static CHANSERV_FUNC(cmd_move)
1853 struct mod_chanmode change;
1854 struct chanNode *target;
1855 struct modeNode *mn;
1856 struct userData *uData;
1857 char reason[MAXLEN];
1858 struct do_not_register *dnr;
1862 if(IsProtected(channel->channel_info))
1864 reply("CSMSG_MOVE_NODELETE", channel->name);
1868 if(!IsChannelName(argv[1]))
1870 reply("MSG_NOT_CHANNEL_NAME");
1874 if(opserv_bad_channel(argv[1]))
1876 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1880 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1882 for(uData = channel->channel_info->users; uData; uData = uData->next)
1884 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1886 if(!IsHelping(user))
1887 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1889 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1895 mod_chanmode_init(&change);
1896 if(!(target = GetChannel(argv[1])))
1898 target = AddChannel(argv[1], now, NULL, NULL);
1899 if(!IsSuspended(channel->channel_info))
1900 AddChannelUser(chanserv, target);
1902 else if(target->channel_info)
1904 reply("CSMSG_ALREADY_REGGED", target->name);
1907 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1908 && !IsHelping(user))
1910 reply("CSMSG_MUST_BE_OPPED", target->name);
1913 else if(!IsSuspended(channel->channel_info))
1916 change.args[0].mode = MODE_CHANOP;
1917 change.args[0].u.member = AddChannelUser(chanserv, target);
1918 mod_chanmode_announce(chanserv, target, &change);
1923 /* Clear MODE_REGISTERED from old channel, add it to new. */
1925 change.modes_clear = MODE_REGISTERED;
1926 mod_chanmode_announce(chanserv, channel, &change);
1927 change.modes_clear = 0;
1928 change.modes_set = MODE_REGISTERED;
1929 mod_chanmode_announce(chanserv, target, &change);
1932 /* Move the channel_info to the target channel; it
1933 shouldn't be necessary to clear timeq callbacks
1934 for the old channel. */
1935 target->channel_info = channel->channel_info;
1936 target->channel_info->channel = target;
1937 channel->channel_info = NULL;
1939 reply("CSMSG_MOVE_SUCCESS", target->name);
1941 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1942 if(!IsSuspended(target->channel_info))
1944 char reason2[MAXLEN];
1945 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1946 DelChannelUser(chanserv, channel, reason2, 0);
1948 UnlockChannel(channel);
1949 LockChannel(target);
1950 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1955 merge_users(struct chanData *source, struct chanData *target)
1957 struct userData *suData, *tuData, *next;
1963 /* Insert the source's users into the scratch area. */
1964 for(suData = source->users; suData; suData = suData->next)
1965 dict_insert(merge, suData->handle->handle, suData);
1967 /* Iterate through the target's users, looking for
1968 users common to both channels. The lower access is
1969 removed from either the scratch area or target user
1971 for(tuData = target->users; tuData; tuData = next)
1973 struct userData *choice;
1975 next = tuData->next;
1977 /* If a source user exists with the same handle as a target
1978 channel's user, resolve the conflict by removing one. */
1979 suData = dict_find(merge, tuData->handle->handle, NULL);
1983 /* Pick the data we want to keep. */
1984 /* If the access is the same, use the later seen time. */
1985 if(suData->access == tuData->access)
1986 choice = (suData->seen > tuData->seen) ? suData : tuData;
1987 else /* Otherwise, keep the higher access level. */
1988 choice = (suData->access > tuData->access) ? suData : tuData;
1990 /* Remove the user that wasn't picked. */
1991 if(choice == tuData)
1993 dict_remove(merge, suData->handle->handle);
1994 del_channel_user(suData, 0);
1997 del_channel_user(tuData, 0);
2000 /* Move the remaining users to the target channel. */
2001 for(it = dict_first(merge); it; it = iter_next(it))
2003 suData = iter_data(it);
2005 /* Insert the user into the target channel's linked list. */
2006 suData->prev = NULL;
2007 suData->next = target->users;
2008 suData->channel = target;
2011 target->users->prev = suData;
2012 target->users = suData;
2014 /* Update the user counts for the target channel; the
2015 source counts are left alone. */
2016 target->userCount++;
2019 /* Possible to assert (source->users == NULL) here. */
2020 source->users = NULL;
2025 merge_bans(struct chanData *source, struct chanData *target)
2027 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2029 /* Hold on to the original head of the target ban list
2030 to avoid comparing source bans with source bans. */
2031 tFront = target->bans;
2033 /* Perform a totally expensive O(n*m) merge, ick. */
2034 for(sbData = source->bans; sbData; sbData = sNext)
2036 /* Flag to track whether the ban's been moved
2037 to the destination yet. */
2040 /* Possible to assert (sbData->prev == NULL) here. */
2041 sNext = sbData->next;
2043 for(tbData = tFront; tbData; tbData = tNext)
2045 tNext = tbData->next;
2047 /* Perform two comparisons between each source
2048 and target ban, conflicts are resolved by
2049 keeping the broader ban and copying the later
2050 expiration and triggered time. */
2051 if(match_ircglobs(tbData->mask, sbData->mask))
2053 /* There is a broader ban in the target channel that
2054 overrides one in the source channel; remove the
2055 source ban and break. */
2056 if(sbData->expires > tbData->expires)
2057 tbData->expires = sbData->expires;
2058 if(sbData->triggered > tbData->triggered)
2059 tbData->triggered = sbData->triggered;
2060 del_channel_ban(sbData);
2063 else if(match_ircglobs(sbData->mask, tbData->mask))
2065 /* There is a broader ban in the source channel that
2066 overrides one in the target channel; remove the
2067 target ban, fall through and move the source over. */
2068 if(tbData->expires > sbData->expires)
2069 sbData->expires = tbData->expires;
2070 if(tbData->triggered > sbData->triggered)
2071 sbData->triggered = tbData->triggered;
2072 if(tbData == tFront)
2074 del_channel_ban(tbData);
2077 /* Source bans can override multiple target bans, so
2078 we allow a source to run through this loop multiple
2079 times, but we can only move it once. */
2084 /* Remove the source ban from the source ban list. */
2086 sbData->next->prev = sbData->prev;
2088 /* Modify the source ban's associated channel. */
2089 sbData->channel = target;
2091 /* Insert the ban into the target channel's linked list. */
2092 sbData->prev = NULL;
2093 sbData->next = target->bans;
2096 target->bans->prev = sbData;
2097 target->bans = sbData;
2099 /* Update the user counts for the target channel. */
2104 /* Possible to assert (source->bans == NULL) here. */
2105 source->bans = NULL;
2109 merge_data(struct chanData *source, struct chanData *target)
2111 /* Use more recent visited and owner-transfer time; use older
2112 * registered time. Bitwise or may_opchan. Use higher max.
2113 * Do not touch last_refresh, ban count or user counts.
2115 if(source->visited > target->visited)
2116 target->visited = source->visited;
2117 if(source->registered < target->registered)
2118 target->registered = source->registered;
2119 if(source->ownerTransfer > target->ownerTransfer)
2120 target->ownerTransfer = source->ownerTransfer;
2121 if(source->may_opchan)
2122 target->may_opchan = 1;
2123 if(source->max > target->max)
2124 target->max = source->max;
2128 merge_channel(struct chanData *source, struct chanData *target)
2130 merge_users(source, target);
2131 merge_bans(source, target);
2132 merge_data(source, target);
2135 static CHANSERV_FUNC(cmd_merge)
2137 struct userData *target_user;
2138 struct chanNode *target;
2139 char reason[MAXLEN];
2143 /* Make sure the target channel exists and is registered to the user
2144 performing the command. */
2145 if(!(target = GetChannel(argv[1])))
2147 reply("MSG_INVALID_CHANNEL");
2151 if(!target->channel_info)
2153 reply("CSMSG_NOT_REGISTERED", target->name);
2157 if(IsProtected(channel->channel_info))
2159 reply("CSMSG_MERGE_NODELETE");
2163 if(IsSuspended(target->channel_info))
2165 reply("CSMSG_MERGE_SUSPENDED");
2169 if(channel == target)
2171 reply("CSMSG_MERGE_SELF");
2175 target_user = GetChannelUser(target->channel_info, user->handle_info);
2176 if(!target_user || (target_user->access < UL_OWNER))
2178 reply("CSMSG_MERGE_NOT_OWNER");
2182 /* Merge the channel structures and associated data. */
2183 merge_channel(channel->channel_info, target->channel_info);
2184 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2185 unregister_channel(channel->channel_info, reason);
2186 reply("CSMSG_MERGE_SUCCESS", target->name);
2190 static CHANSERV_FUNC(cmd_opchan)
2192 struct mod_chanmode change;
2193 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2195 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2198 channel->channel_info->may_opchan = 0;
2199 mod_chanmode_init(&change);
2201 change.args[0].mode = MODE_CHANOP;
2202 change.args[0].u.member = GetUserMode(channel, chanserv);
2203 mod_chanmode_announce(chanserv, channel, &change);
2204 reply("CSMSG_OPCHAN_DONE", channel->name);
2208 static CHANSERV_FUNC(cmd_adduser)
2210 struct userData *actee;
2211 struct userData *actor;
2212 struct handle_info *handle;
2213 unsigned short access;
2217 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2219 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2223 access = user_level_from_name(argv[2], UL_OWNER);
2226 reply("CSMSG_INVALID_ACCESS", argv[2]);
2230 actor = GetChannelUser(channel->channel_info, user->handle_info);
2231 if(actor->access <= access)
2233 reply("CSMSG_NO_BUMP_ACCESS");
2237 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2240 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2242 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2246 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2247 scan_user_presence(actee, NULL);
2248 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2252 static CHANSERV_FUNC(cmd_clvl)
2254 struct handle_info *handle;
2255 struct userData *victim;
2256 struct userData *actor;
2257 unsigned short new_access;
2258 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2262 actor = GetChannelUser(channel->channel_info, user->handle_info);
2264 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2267 if(handle == user->handle_info && !privileged)
2269 reply("CSMSG_NO_SELF_CLVL");
2273 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2275 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2279 if(actor->access <= victim->access && !privileged)
2281 reply("MSG_USER_OUTRANKED", handle->handle);
2285 new_access = user_level_from_name(argv[2], UL_OWNER);
2289 reply("CSMSG_INVALID_ACCESS", argv[2]);
2293 if(new_access >= actor->access && !privileged)
2295 reply("CSMSG_NO_BUMP_ACCESS");
2299 victim->access = new_access;
2300 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2304 static CHANSERV_FUNC(cmd_deluser)
2306 struct handle_info *handle;
2307 struct userData *victim;
2308 struct userData *actor;
2309 unsigned short access;
2314 actor = GetChannelUser(channel->channel_info, user->handle_info);
2316 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2319 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2321 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2327 access = user_level_from_name(argv[1], UL_OWNER);
2330 reply("CSMSG_INVALID_ACCESS", argv[1]);
2333 if(access != victim->access)
2335 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2341 access = victim->access;
2344 if((actor->access <= victim->access) && !IsHelping(user))
2346 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2350 chan_name = strdup(channel->name);
2351 del_channel_user(victim, 1);
2352 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2358 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2360 struct userData *actor, *uData, *next;
2362 actor = GetChannelUser(channel->channel_info, user->handle_info);
2364 if(min_access > max_access)
2366 reply("CSMSG_BAD_RANGE", min_access, max_access);
2370 if((actor->access <= max_access) && !IsHelping(user))
2372 reply("CSMSG_NO_ACCESS");
2376 for(uData = channel->channel_info->users; uData; uData = next)
2380 if((uData->access >= min_access)
2381 && (uData->access <= max_access)
2382 && match_ircglob(uData->handle->handle, mask))
2383 del_channel_user(uData, 1);
2386 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2390 static CHANSERV_FUNC(cmd_mdelowner)
2392 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2395 static CHANSERV_FUNC(cmd_mdelcoowner)
2397 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2400 static CHANSERV_FUNC(cmd_mdelmaster)
2402 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2405 static CHANSERV_FUNC(cmd_mdelop)
2407 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2410 static CHANSERV_FUNC(cmd_mdelpeon)
2412 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2416 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2418 struct banData *bData, *next;
2419 char interval[INTERVALLEN];
2424 limit = now - duration;
2425 for(bData = channel->channel_info->bans; bData; bData = next)
2429 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2432 del_channel_ban(bData);
2436 intervalString(interval, duration, user->handle_info);
2437 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2442 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2444 struct userData *actor, *uData, *next;
2445 char interval[INTERVALLEN];
2449 actor = GetChannelUser(channel->channel_info, user->handle_info);
2450 if(min_access > max_access)
2452 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2456 if((actor->access <= max_access) && !IsHelping(user))
2458 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2463 limit = now - duration;
2464 for(uData = channel->channel_info->users; uData; uData = next)
2468 if((uData->seen > limit) || uData->present)
2471 if(((uData->access >= min_access) && (uData->access <= max_access))
2472 || (!max_access && (uData->access < actor->access)))
2474 del_channel_user(uData, 1);
2482 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2484 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2488 static CHANSERV_FUNC(cmd_trim)
2490 unsigned long duration;
2491 unsigned short min_level, max_level;
2495 duration = ParseInterval(argv[2]);
2498 reply("CSMSG_CANNOT_TRIM");
2502 if(!irccasecmp(argv[1], "bans"))
2504 cmd_trim_bans(user, channel, duration);
2507 else if(!irccasecmp(argv[1], "users"))
2509 cmd_trim_users(user, channel, 0, 0, duration);
2512 else if(parse_level_range(&min_level, &max_level, argv[1]))
2514 cmd_trim_users(user, channel, min_level, max_level, duration);
2517 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2519 cmd_trim_users(user, channel, min_level, min_level, duration);
2524 reply("CSMSG_INVALID_TRIM", argv[1]);
2529 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2530 to the user. cmd_all takes advantage of this. */
2531 static CHANSERV_FUNC(cmd_up)
2533 struct mod_chanmode change;
2534 struct userData *uData;
2537 mod_chanmode_init(&change);
2539 change.args[0].u.member = GetUserMode(channel, user);
2540 if(!change.args[0].u.member)
2543 reply("MSG_CHANNEL_ABSENT", channel->name);
2547 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2551 reply("CSMSG_GODMODE_UP", argv[0]);
2554 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2556 change.args[0].mode = MODE_CHANOP;
2557 errmsg = "CSMSG_ALREADY_OPPED";
2559 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2561 change.args[0].mode = MODE_VOICE;
2562 errmsg = "CSMSG_ALREADY_VOICED";
2567 reply("CSMSG_NO_ACCESS");
2570 change.args[0].mode &= ~change.args[0].u.member->modes;
2571 if(!change.args[0].mode)
2574 reply(errmsg, channel->name);
2577 modcmd_chanmode_announce(&change);
2581 static CHANSERV_FUNC(cmd_down)
2583 struct mod_chanmode change;
2585 mod_chanmode_init(&change);
2587 change.args[0].u.member = GetUserMode(channel, user);
2588 if(!change.args[0].u.member)
2591 reply("MSG_CHANNEL_ABSENT", channel->name);
2595 if(!change.args[0].u.member->modes)
2598 reply("CSMSG_ALREADY_DOWN", channel->name);
2602 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2603 modcmd_chanmode_announce(&change);
2607 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)
2609 struct userData *cList;
2611 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2613 if(IsSuspended(cList->channel)
2614 || IsUserSuspended(cList)
2615 || !GetUserMode(cList->channel->channel, user))
2618 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2624 static CHANSERV_FUNC(cmd_upall)
2626 return cmd_all(CSFUNC_ARGS, cmd_up);
2629 static CHANSERV_FUNC(cmd_downall)
2631 return cmd_all(CSFUNC_ARGS, cmd_down);
2634 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2635 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2638 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)
2640 unsigned int ii, valid;
2641 struct userNode *victim;
2642 struct mod_chanmode *change;
2644 change = mod_chanmode_alloc(argc - 1);
2646 for(ii=valid=0; ++ii < argc; )
2648 if(!(victim = GetUserH(argv[ii])))
2650 change->args[valid].mode = mode;
2651 change->args[valid].u.member = GetUserMode(channel, victim);
2652 if(!change->args[valid].u.member)
2654 if(validate && !validate(user, channel, victim))
2659 change->argc = valid;
2660 if(valid < (argc-1))
2661 reply("CSMSG_PROCESS_FAILED");
2664 modcmd_chanmode_announce(change);
2665 reply(action, channel->name);
2667 mod_chanmode_free(change);
2671 static CHANSERV_FUNC(cmd_op)
2673 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2676 static CHANSERV_FUNC(cmd_deop)
2678 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2681 static CHANSERV_FUNC(cmd_voice)
2683 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2686 static CHANSERV_FUNC(cmd_devoice)
2688 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2692 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2698 for(ii=0; ii<channel->members.used; ii++)
2700 struct modeNode *mn = channel->members.list[ii];
2702 if(IsService(mn->user))
2705 if(!user_matches_glob(mn->user, ban, 1))
2708 if(protect_user(mn->user, user, channel->channel_info))
2712 victims[(*victimCount)++] = mn;
2718 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2720 struct userNode *victim;
2721 struct modeNode **victims;
2722 unsigned int offset, n, victimCount, duration = 0;
2723 char *reason = "Bye.", *ban, *name;
2724 char interval[INTERVALLEN];
2726 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2727 REQUIRE_PARAMS(offset);
2730 reason = unsplit_string(argv + offset, argc - offset, NULL);
2731 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2733 /* Truncate the reason to a length of TOPICLEN, as
2734 the ircd does; however, leave room for an ellipsis
2735 and the kicker's nick. */
2736 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2740 if((victim = GetUserH(argv[1])))
2742 victims = alloca(sizeof(victims[0]));
2743 victims[0] = GetUserMode(channel, victim);
2744 /* XXX: The comparison with ACTION_KICK is just because all
2745 * other actions can work on users outside the channel, and we
2746 * want to allow those (e.g. unbans) in that case. If we add
2747 * some other ejection action for in-channel users, change
2749 victimCount = victims[0] ? 1 : 0;
2751 if(IsService(victim))
2753 reply("MSG_SERVICE_IMMUNE", victim->nick);
2757 if((action == ACTION_KICK) && !victimCount)
2759 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2763 if(protect_user(victim, user, channel->channel_info))
2765 reply("CSMSG_USER_PROTECTED", victim->nick);
2769 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2770 name = victim->nick;
2774 if(!is_ircmask(argv[1]))
2776 reply("MSG_NICK_UNKNOWN", argv[1]);
2780 victims = alloca(sizeof(victims[0]) * channel->members.used);
2782 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2784 reply("CSMSG_MASK_PROTECTED", argv[1]);
2788 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2790 reply("CSMSG_LAME_MASK", argv[1]);
2794 if((action == ACTION_KICK) && (victimCount == 0))
2796 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2800 name = ban = strdup(argv[1]);
2803 /* Truncate the ban in place if necessary; we must ensure
2804 that 'ban' is a valid ban mask before sanitizing it. */
2805 sanitize_ircmask(ban);
2807 if(action & ACTION_ADD_BAN)
2809 struct banData *bData, *next;
2811 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2813 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2818 if(action & ACTION_ADD_TIMED_BAN)
2820 duration = ParseInterval(argv[2]);
2824 reply("CSMSG_DURATION_TOO_LOW");
2828 else if(duration > (86400 * 365 * 2))
2830 reply("CSMSG_DURATION_TOO_HIGH");
2836 for(bData = channel->channel_info->bans; bData; bData = next)
2838 if(match_ircglobs(bData->mask, ban))
2840 int exact = !irccasecmp(bData->mask, ban);
2842 /* The ban is redundant; there is already a ban
2843 with the same effect in place. */
2847 free(bData->reason);
2848 bData->reason = strdup(reason);
2849 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2851 reply("CSMSG_REASON_CHANGE", ban);
2855 if(exact && bData->expires)
2859 /* If the ban matches an existing one exactly,
2860 extend the expiration time if the provided
2861 duration is longer. */
2862 if(duration && ((time_t)(now + duration) > bData->expires))
2864 bData->expires = now + duration;
2875 /* Delete the expiration timeq entry and
2876 requeue if necessary. */
2877 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2880 timeq_add(bData->expires, expire_ban, bData);
2884 /* automated kickban */
2887 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2889 reply("CSMSG_BAN_ADDED", name, channel->name);
2895 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2902 if(match_ircglobs(ban, bData->mask))
2904 /* The ban we are adding makes previously existing
2905 bans redundant; silently remove them. */
2906 del_channel_ban(bData);
2910 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);
2912 name = ban = strdup(bData->mask);
2916 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2918 extern const char *hidden_host_suffix;
2919 const char *old_name = chanserv_conf.old_ban_names->list[n];
2921 unsigned int l1, l2;
2924 l2 = strlen(old_name);
2927 if(irccasecmp(ban + l1 - l2, old_name))
2929 new_mask = malloc(MAXLEN);
2930 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2932 name = ban = new_mask;
2937 if(action & ACTION_BAN)
2939 unsigned int exists;
2940 struct mod_chanmode *change;
2942 if(channel->banlist.used >= MAXBANS)
2945 reply("CSMSG_BANLIST_FULL", channel->name);
2950 exists = ChannelBanExists(channel, ban);
2951 change = mod_chanmode_alloc(victimCount + 1);
2952 for(n = 0; n < victimCount; ++n)
2954 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2955 change->args[n].u.member = victims[n];
2959 change->args[n].mode = MODE_BAN;
2960 change->args[n++].u.hostmask = ban;
2964 modcmd_chanmode_announce(change);
2966 mod_chanmode_announce(chanserv, channel, change);
2967 mod_chanmode_free(change);
2969 if(exists && (action == ACTION_BAN))
2972 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2978 if(action & ACTION_KICK)
2980 char kick_reason[MAXLEN];
2981 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2983 for(n = 0; n < victimCount; n++)
2984 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2989 /* No response, since it was automated. */
2991 else if(action & ACTION_ADD_BAN)
2994 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2996 reply("CSMSG_BAN_ADDED", name, channel->name);
2998 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2999 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3000 else if(action & ACTION_BAN)
3001 reply("CSMSG_BAN_DONE", name, channel->name);
3002 else if(action & ACTION_KICK && victimCount)
3003 reply("CSMSG_KICK_DONE", name, channel->name);
3009 static CHANSERV_FUNC(cmd_kickban)
3011 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3014 static CHANSERV_FUNC(cmd_kick)
3016 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3019 static CHANSERV_FUNC(cmd_ban)
3021 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3024 static CHANSERV_FUNC(cmd_addban)
3026 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3029 static CHANSERV_FUNC(cmd_addtimedban)
3031 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3034 static struct mod_chanmode *
3035 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3037 struct mod_chanmode *change;
3038 unsigned char *match;
3039 unsigned int ii, count;
3041 match = alloca(bans->used);
3044 for(ii = count = 0; ii < bans->used; ++ii)
3046 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3053 for(ii = count = 0; ii < bans->used; ++ii)
3055 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3062 change = mod_chanmode_alloc(count);
3063 for(ii = count = 0; ii < bans->used; ++ii)
3067 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3068 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3070 assert(count == change->argc);
3075 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3077 struct userNode *actee;
3083 /* may want to allow a comma delimited list of users... */
3084 if(!(actee = GetUserH(argv[1])))
3086 if(!is_ircmask(argv[1]))
3088 reply("MSG_NICK_UNKNOWN", argv[1]);
3092 mask = strdup(argv[1]);
3095 /* We don't sanitize the mask here because ircu
3097 if(action & ACTION_UNBAN)
3099 struct mod_chanmode *change;
3100 change = find_matching_bans(&channel->banlist, actee, mask);
3105 modcmd_chanmode_announce(change);
3106 for(ii = 0; ii < change->argc; ++ii)
3107 free((char*)change->args[ii].u.hostmask);
3108 mod_chanmode_free(change);
3113 if(action & ACTION_DEL_BAN)
3115 struct banData *ban, *next;
3117 ban = channel->channel_info->bans;
3121 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3124 for( ; ban && !match_ircglobs(mask, ban->mask);
3129 del_channel_ban(ban);
3136 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3138 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3144 static CHANSERV_FUNC(cmd_unban)
3146 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3149 static CHANSERV_FUNC(cmd_delban)
3151 /* it doesn't necessarily have to remove the channel ban - may want
3152 to make that an option. */
3153 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3156 static CHANSERV_FUNC(cmd_unbanme)
3158 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3159 long flags = ACTION_UNBAN;
3161 /* remove permanent bans if the user has the proper access. */
3162 if(uData->access >= UL_MASTER)
3163 flags |= ACTION_DEL_BAN;
3165 argv[1] = user->nick;
3166 return unban_user(user, channel, 2, argv, cmd, flags);
3169 static CHANSERV_FUNC(cmd_unbanall)
3171 struct mod_chanmode *change;
3174 if(!channel->banlist.used)
3176 reply("CSMSG_NO_BANS", channel->name);
3180 change = mod_chanmode_alloc(channel->banlist.used);
3181 for(ii=0; ii<channel->banlist.used; ii++)
3183 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3184 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3186 modcmd_chanmode_announce(change);
3187 for(ii = 0; ii < change->argc; ++ii)
3188 free((char*)change->args[ii].u.hostmask);
3189 mod_chanmode_free(change);
3190 reply("CSMSG_BANS_REMOVED", channel->name);
3194 static CHANSERV_FUNC(cmd_open)
3196 struct mod_chanmode *change;
3199 change = find_matching_bans(&channel->banlist, user, NULL);
3201 change = mod_chanmode_alloc(0);
3202 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3203 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3204 && channel->channel_info->modes.modes_set)
3205 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3206 modcmd_chanmode_announce(change);
3207 reply("CSMSG_CHANNEL_OPENED", channel->name);
3208 for(ii = 0; ii < change->argc; ++ii)
3209 free((char*)change->args[ii].u.hostmask);
3210 mod_chanmode_free(change);
3214 static CHANSERV_FUNC(cmd_myaccess)
3216 static struct string_buffer sbuf;
3217 struct handle_info *target_handle;
3218 struct userData *uData;
3221 target_handle = user->handle_info;
3222 else if(!IsHelping(user))
3224 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3227 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3230 if(!target_handle->channels)
3232 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3236 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3237 for(uData = target_handle->channels; uData; uData = uData->u_next)
3239 struct chanData *cData = uData->channel;
3241 if(uData->access > UL_OWNER)
3243 if(IsProtected(cData)
3244 && (target_handle != user->handle_info)
3245 && !GetTrueChannelAccess(cData, user->handle_info))
3248 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3249 if(uData->flags != USER_AUTO_OP)
3250 string_buffer_append(&sbuf, ',');
3251 if(IsUserSuspended(uData))
3252 string_buffer_append(&sbuf, 's');
3253 if(IsUserAutoOp(uData))
3255 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3256 string_buffer_append(&sbuf, 'o');
3257 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3258 string_buffer_append(&sbuf, 'v');
3260 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3261 string_buffer_append(&sbuf, 'i');
3263 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3265 string_buffer_append_string(&sbuf, ")]");
3266 string_buffer_append(&sbuf, '\0');
3267 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3273 static CHANSERV_FUNC(cmd_access)
3275 struct userNode *target;
3276 struct handle_info *target_handle;
3277 struct userData *uData;
3279 char prefix[MAXLEN];
3284 target_handle = target->handle_info;
3286 else if((target = GetUserH(argv[1])))
3288 target_handle = target->handle_info;
3290 else if(argv[1][0] == '*')
3292 if(!(target_handle = get_handle_info(argv[1]+1)))
3294 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3300 reply("MSG_NICK_UNKNOWN", argv[1]);
3304 assert(target || target_handle);
3306 if(target == chanserv)
3308 reply("CSMSG_IS_CHANSERV");
3316 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3321 reply("MSG_USER_AUTHENTICATE", target->nick);
3324 reply("MSG_AUTHENTICATE");
3330 const char *epithet = NULL, *type = NULL;
3333 epithet = chanserv_conf.irc_operator_epithet;
3336 else if(IsNetworkHelper(target))
3338 epithet = chanserv_conf.network_helper_epithet;
3339 type = "network helper";
3341 else if(IsSupportHelper(target))
3343 epithet = chanserv_conf.support_helper_epithet;
3344 type = "support helper";
3348 if(target_handle->epithet)
3349 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3351 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3353 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3357 sprintf(prefix, "%s", target_handle->handle);
3360 if(!channel->channel_info)
3362 reply("CSMSG_NOT_REGISTERED", channel->name);
3366 helping = HANDLE_FLAGGED(target_handle, HELPING)
3367 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3368 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3370 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3371 /* To prevent possible information leaks, only show infolines
3372 * if the requestor is in the channel or it's their own
3374 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3376 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3378 /* Likewise, only say it's suspended if the user has active
3379 * access in that channel or it's their own entry. */
3380 if(IsUserSuspended(uData)
3381 && (GetChannelUser(channel->channel_info, user->handle_info)
3382 || (user->handle_info == uData->handle)))
3384 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3389 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3396 zoot_list(struct listData *list)
3398 struct userData *uData;
3399 unsigned int start, curr, highest, lowest;
3400 struct helpfile_table tmp_table;
3401 const char **temp, *msg;
3403 if(list->table.length == 1)
3406 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3408 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3409 msg = user_find_message(list->user, "MSG_NONE");
3410 send_message_type(4, list->user, list->bot, " %s", msg);
3412 tmp_table.width = list->table.width;
3413 tmp_table.flags = list->table.flags;
3414 list->table.contents[0][0] = " ";
3415 highest = list->highest;
3416 if(list->lowest != 0)
3417 lowest = list->lowest;
3418 else if(highest < 100)
3421 lowest = highest - 100;
3422 for(start = curr = 1; curr < list->table.length; )
3424 uData = list->users[curr-1];
3425 list->table.contents[curr++][0] = " ";
3426 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3429 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3431 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3432 temp = list->table.contents[--start];
3433 list->table.contents[start] = list->table.contents[0];
3434 tmp_table.contents = list->table.contents + start;
3435 tmp_table.length = curr - start;
3436 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3437 list->table.contents[start] = temp;
3439 highest = lowest - 1;
3440 lowest = (highest < 100) ? 0 : (highest - 99);
3446 def_list(struct listData *list)
3450 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3452 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3453 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3454 if(list->table.length == 1)
3456 msg = user_find_message(list->user, "MSG_NONE");
3457 send_message_type(4, list->user, list->bot, " %s", msg);
3462 userData_access_comp(const void *arg_a, const void *arg_b)
3464 const struct userData *a = *(struct userData**)arg_a;
3465 const struct userData *b = *(struct userData**)arg_b;
3467 if(a->access != b->access)
3468 res = b->access - a->access;
3470 res = irccasecmp(a->handle->handle, b->handle->handle);
3475 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3477 void (*send_list)(struct listData *);
3478 struct userData *uData;
3479 struct listData lData;
3480 unsigned int matches;
3484 lData.bot = cmd->parent->bot;
3485 lData.channel = channel;
3486 lData.lowest = lowest;
3487 lData.highest = highest;
3488 lData.search = (argc > 1) ? argv[1] : NULL;
3489 send_list = def_list;
3490 (void)zoot_list; /* since it doesn't show user levels */
3492 if(user->handle_info)
3494 switch(user->handle_info->userlist_style)
3496 case HI_STYLE_DEF: send_list = def_list; break;
3497 case HI_STYLE_ZOOT: send_list = def_list; break;
3501 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3503 for(uData = channel->channel_info->users; uData; uData = uData->next)
3505 if((uData->access < lowest)
3506 || (uData->access > highest)
3507 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3509 lData.users[matches++] = uData;
3511 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3513 lData.table.length = matches+1;
3514 lData.table.width = 4;
3515 lData.table.flags = TABLE_NO_FREE;
3516 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3517 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3518 lData.table.contents[0] = ary;
3521 ary[2] = "Last Seen";
3523 for(matches = 1; matches < lData.table.length; ++matches)
3525 struct userData *uData = lData.users[matches-1];
3526 char seen[INTERVALLEN];
3528 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3529 lData.table.contents[matches] = ary;
3530 ary[0] = strtab(uData->access);
3531 ary[1] = uData->handle->handle;
3534 else if(!uData->seen)
3537 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3538 ary[2] = strdup(ary[2]);
3539 if(IsUserSuspended(uData))
3540 ary[3] = "Suspended";
3541 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3542 ary[3] = "Vacation";
3547 for(matches = 1; matches < lData.table.length; ++matches)
3549 free((char*)lData.table.contents[matches][2]);
3550 free(lData.table.contents[matches]);
3552 free(lData.table.contents[0]);
3553 free(lData.table.contents);
3557 static CHANSERV_FUNC(cmd_users)
3559 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3562 static CHANSERV_FUNC(cmd_wlist)
3564 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3567 static CHANSERV_FUNC(cmd_clist)
3569 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3572 static CHANSERV_FUNC(cmd_mlist)
3574 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3577 static CHANSERV_FUNC(cmd_olist)
3579 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3582 static CHANSERV_FUNC(cmd_plist)
3584 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3587 static CHANSERV_FUNC(cmd_bans)
3589 struct helpfile_table tbl;
3590 unsigned int matches = 0, timed = 0, ii;
3591 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3592 const char *msg_never, *triggered, *expires;
3593 struct banData *ban, **bans;
3600 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3602 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3604 if(search && !match_ircglobs(search, ban->mask))
3606 bans[matches++] = ban;
3611 tbl.length = matches + 1;
3612 tbl.width = 4 + timed;
3614 tbl.flags = TABLE_NO_FREE;
3615 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3616 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3617 tbl.contents[0][0] = "Mask";
3618 tbl.contents[0][1] = "Set By";
3619 tbl.contents[0][2] = "Triggered";
3622 tbl.contents[0][3] = "Expires";
3623 tbl.contents[0][4] = "Reason";
3626 tbl.contents[0][3] = "Reason";
3629 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3631 free(tbl.contents[0]);
3636 msg_never = user_find_message(user, "MSG_NEVER");
3637 for(ii = 0; ii < matches; )
3643 else if(ban->expires)
3644 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3646 expires = msg_never;
3649 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3651 triggered = msg_never;
3653 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3654 tbl.contents[ii][0] = ban->mask;
3655 tbl.contents[ii][1] = ban->owner;
3656 tbl.contents[ii][2] = strdup(triggered);
3659 tbl.contents[ii][3] = strdup(expires);
3660 tbl.contents[ii][4] = ban->reason;
3663 tbl.contents[ii][3] = ban->reason;
3665 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3666 reply("MSG_MATCH_COUNT", matches);
3667 for(ii = 1; ii < tbl.length; ++ii)
3669 free((char*)tbl.contents[ii][2]);
3671 free((char*)tbl.contents[ii][3]);
3672 free(tbl.contents[ii]);
3674 free(tbl.contents[0]);
3680 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3682 struct chanData *cData = channel->channel_info;
3683 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3685 if(cData->topic_mask)
3686 return !match_ircglob(new_topic, cData->topic_mask);
3687 else if(cData->topic)
3688 return irccasecmp(new_topic, cData->topic);
3693 static CHANSERV_FUNC(cmd_topic)
3695 struct chanData *cData;
3698 cData = channel->channel_info;
3703 SetChannelTopic(channel, chanserv, cData->topic, 1);
3704 reply("CSMSG_TOPIC_SET", cData->topic);
3708 reply("CSMSG_NO_TOPIC", channel->name);
3712 topic = unsplit_string(argv + 1, argc - 1, NULL);
3713 /* If they say "!topic *", use an empty topic. */
3714 if((topic[0] == '*') && (topic[1] == 0))
3716 if(bad_topic(channel, user, topic))
3718 char *topic_mask = cData->topic_mask;
3721 char new_topic[TOPICLEN+1], tchar;
3722 int pos=0, starpos=-1, dpos=0, len;
3724 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3731 len = strlen(topic);
3732 if((dpos + len) > TOPICLEN)
3733 len = TOPICLEN + 1 - dpos;
3734 memcpy(new_topic+dpos, topic, len);
3738 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3739 default: new_topic[dpos++] = tchar; break;
3742 if((dpos > TOPICLEN) || tchar)
3745 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3746 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3749 new_topic[dpos] = 0;
3750 SetChannelTopic(channel, chanserv, new_topic, 1);
3752 reply("CSMSG_TOPIC_LOCKED", channel->name);
3757 SetChannelTopic(channel, chanserv, topic, 1);
3759 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3761 /* Grab the topic and save it as the default topic. */
3763 cData->topic = strdup(channel->topic);
3769 static CHANSERV_FUNC(cmd_mode)
3771 struct userData *uData;
3772 struct mod_chanmode *change;
3777 change = &channel->channel_info->modes;
3778 if(change->modes_set || change->modes_clear) {
3779 modcmd_chanmode_announce(change);
3780 reply("CSMSG_DEFAULTED_MODES", channel->name);
3782 reply("CSMSG_NO_MODES", channel->name);
3786 uData = GetChannelUser(channel->channel_info, user->handle_info);
3788 base_oplevel = MAXOPLEVEL;
3789 else if (uData->access >= UL_OWNER)
3792 base_oplevel = 1 + UL_OWNER - uData->access;
3793 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3796 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3800 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3801 && mode_lock_violated(&channel->channel_info->modes, change))
3804 mod_chanmode_format(&channel->channel_info->modes, modes);
3805 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3809 modcmd_chanmode_announce(change);
3810 mod_chanmode_free(change);
3811 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3815 static CHANSERV_FUNC(cmd_invite)
3817 struct userData *uData;
3818 struct userNode *invite;
3820 uData = GetChannelUser(channel->channel_info, user->handle_info);
3824 if(!(invite = GetUserH(argv[1])))
3826 reply("MSG_NICK_UNKNOWN", argv[1]);
3833 if(GetUserMode(channel, invite))
3835 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3843 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3844 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3847 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3849 irc_invite(chanserv, invite, channel);
3851 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3856 static CHANSERV_FUNC(cmd_inviteme)
3858 if(GetUserMode(channel, user))
3860 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3863 if(channel->channel_info
3864 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3866 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3869 irc_invite(cmd->parent->bot, user, channel);
3874 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3877 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3879 /* We display things based on two dimensions:
3880 * - Issue time: present or absent
3881 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3882 * (in order of precedence, so something both expired and revoked
3883 * only counts as revoked)
3885 combo = (suspended->issued ? 4 : 0)
3886 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3888 case 0: /* no issue time, indefinite expiration */
3889 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3891 case 1: /* no issue time, expires in future */
3892 intervalString(buf1, suspended->expires-now, user->handle_info);
3893 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3895 case 2: /* no issue time, expired */
3896 intervalString(buf1, now-suspended->expires, user->handle_info);
3897 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3899 case 3: /* no issue time, revoked */
3900 intervalString(buf1, now-suspended->revoked, user->handle_info);
3901 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3903 case 4: /* issue time set, indefinite expiration */
3904 intervalString(buf1, now-suspended->issued, user->handle_info);
3905 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3907 case 5: /* issue time set, expires in future */
3908 intervalString(buf1, now-suspended->issued, user->handle_info);
3909 intervalString(buf2, suspended->expires-now, user->handle_info);
3910 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3912 case 6: /* issue time set, expired */
3913 intervalString(buf1, now-suspended->issued, user->handle_info);
3914 intervalString(buf2, now-suspended->expires, user->handle_info);
3915 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3917 case 7: /* issue time set, revoked */
3918 intervalString(buf1, now-suspended->issued, user->handle_info);
3919 intervalString(buf2, now-suspended->revoked, user->handle_info);
3920 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3923 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3928 static CHANSERV_FUNC(cmd_info)
3930 char modes[MAXLEN], buffer[INTERVALLEN];
3931 struct userData *uData, *owner;
3932 struct chanData *cData;
3933 struct do_not_register *dnr;
3938 cData = channel->channel_info;
3939 reply("CSMSG_CHANNEL_INFO", channel->name);
3941 uData = GetChannelUser(cData, user->handle_info);
3942 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3944 mod_chanmode_format(&cData->modes, modes);
3945 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3946 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3949 for(it = dict_first(cData->notes); it; it = iter_next(it))
3953 note = iter_data(it);
3954 if(!note_type_visible_to_user(cData, note->type, user))
3957 padding = PADLEN - 1 - strlen(iter_key(it));
3958 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3961 reply("CSMSG_CHANNEL_MAX", cData->max);
3962 for(owner = cData->users; owner; owner = owner->next)
3963 if(owner->access == UL_OWNER)
3964 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3965 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3966 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3967 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3969 privileged = IsStaff(user);
3971 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3972 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3973 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3975 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3976 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3978 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3980 struct suspended *suspended;
3981 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3982 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3983 show_suspension_info(cmd, user, suspended);
3985 else if(IsSuspended(cData))
3987 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3988 show_suspension_info(cmd, user, cData->suspended);
3993 static CHANSERV_FUNC(cmd_netinfo)
3995 extern time_t boot_time;
3996 extern unsigned long burst_length;
3997 char interval[INTERVALLEN];
3999 reply("CSMSG_NETWORK_INFO");
4000 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4001 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4002 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4003 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4004 reply("CSMSG_NETWORK_BANS", banCount);
4005 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4006 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4007 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4012 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4014 struct helpfile_table table;
4016 struct userNode *user;
4021 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4022 table.contents = alloca(list->used*sizeof(*table.contents));
4023 for(nn=0; nn<list->used; nn++)
4025 user = list->list[nn];
4026 if(user->modes & skip_flags)
4030 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4033 nick = alloca(strlen(user->nick)+3);
4034 sprintf(nick, "(%s)", user->nick);
4038 table.contents[table.length][0] = nick;
4041 table_send(chanserv, to->nick, 0, NULL, table);
4044 static CHANSERV_FUNC(cmd_ircops)
4046 reply("CSMSG_STAFF_OPERS");
4047 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4051 static CHANSERV_FUNC(cmd_helpers)
4053 reply("CSMSG_STAFF_HELPERS");
4054 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4058 static CHANSERV_FUNC(cmd_staff)
4060 reply("CSMSG_NETWORK_STAFF");
4061 cmd_ircops(CSFUNC_ARGS);
4062 cmd_helpers(CSFUNC_ARGS);
4066 static CHANSERV_FUNC(cmd_peek)
4068 struct modeNode *mn;
4069 char modes[MODELEN];
4071 struct helpfile_table table;
4073 irc_make_chanmode(channel, modes);
4075 reply("CSMSG_PEEK_INFO", channel->name);
4076 reply("CSMSG_PEEK_TOPIC", channel->topic);
4077 reply("CSMSG_PEEK_MODES", modes);
4078 reply("CSMSG_PEEK_USERS", channel->members.used);
4082 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4083 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4084 for(n = 0; n < channel->members.used; n++)
4086 mn = channel->members.list[n];
4087 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4089 table.contents[table.length] = alloca(sizeof(**table.contents));
4090 table.contents[table.length][0] = mn->user->nick;
4095 reply("CSMSG_PEEK_OPS");
4096 table_send(chanserv, user->nick, 0, NULL, table);
4099 reply("CSMSG_PEEK_NO_OPS");
4103 static MODCMD_FUNC(cmd_wipeinfo)
4105 struct handle_info *victim;
4106 struct userData *ud, *actor;
4109 actor = GetChannelUser(channel->channel_info, user->handle_info);
4110 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4112 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4114 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4117 if((ud->access >= actor->access) && (ud != actor))
4119 reply("MSG_USER_OUTRANKED", victim->handle);
4125 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4129 static CHANSERV_FUNC(cmd_resync)
4131 struct mod_chanmode *changes;
4132 struct chanData *cData = channel->channel_info;
4133 unsigned int ii, used;
4135 changes = mod_chanmode_alloc(channel->members.used * 2);
4136 for(ii = used = 0; ii < channel->members.used; ++ii)
4138 struct modeNode *mn = channel->members.list[ii];
4139 struct userData *uData;
4141 if(IsService(mn->user))
4144 uData = GetChannelAccess(cData, mn->user->handle_info);
4145 if(!cData->lvlOpts[lvlGiveOps]
4146 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4148 if(!(mn->modes & MODE_CHANOP))
4150 changes->args[used].mode = MODE_CHANOP;
4151 changes->args[used++].u.member = mn;
4154 else if(!cData->lvlOpts[lvlGiveVoice]
4155 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4157 if(mn->modes & MODE_CHANOP)
4159 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4160 changes->args[used++].u.member = mn;
4162 if(!(mn->modes & MODE_VOICE))
4164 changes->args[used].mode = MODE_VOICE;
4165 changes->args[used++].u.member = mn;
4172 changes->args[used].mode = MODE_REMOVE | mn->modes;
4173 changes->args[used++].u.member = mn;
4177 changes->argc = used;
4178 modcmd_chanmode_announce(changes);
4179 mod_chanmode_free(changes);
4180 reply("CSMSG_RESYNCED_USERS", channel->name);
4184 static CHANSERV_FUNC(cmd_seen)
4186 struct userData *uData;
4187 struct handle_info *handle;
4188 char seen[INTERVALLEN];
4192 if(!irccasecmp(argv[1], chanserv->nick))
4194 reply("CSMSG_IS_CHANSERV");
4198 if(!(handle = get_handle_info(argv[1])))
4200 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4204 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4206 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4211 reply("CSMSG_USER_PRESENT", handle->handle);
4212 else if(uData->seen)
4213 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4215 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4217 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4218 reply("CSMSG_USER_VACATION", handle->handle);
4223 static MODCMD_FUNC(cmd_names)
4225 struct userNode *targ;
4226 struct userData *targData;
4227 unsigned int ii, pos;
4230 for(ii=pos=0; ii<channel->members.used; ++ii)
4232 targ = channel->members.list[ii]->user;
4233 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4236 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4239 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4243 if(IsUserSuspended(targData))
4245 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4248 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4249 reply("CSMSG_END_NAMES", channel->name);
4254 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4256 switch(ntype->visible_type)
4258 case NOTE_VIS_ALL: return 1;
4259 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4260 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4265 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4267 struct userData *uData;
4269 switch(ntype->set_access_type)
4271 case NOTE_SET_CHANNEL_ACCESS:
4272 if(!user->handle_info)
4274 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4276 return uData->access >= ntype->set_access.min_ulevel;
4277 case NOTE_SET_CHANNEL_SETTER:
4278 return check_user_level(channel, user, lvlSetters, 1, 0);
4279 case NOTE_SET_PRIVILEGED: default:
4280 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4284 static CHANSERV_FUNC(cmd_note)
4286 struct chanData *cData;
4288 struct note_type *ntype;
4290 cData = channel->channel_info;
4293 reply("CSMSG_NOT_REGISTERED", channel->name);
4297 /* If no arguments, show all visible notes for the channel. */
4303 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4305 note = iter_data(it);
4306 if(!note_type_visible_to_user(cData, note->type, user))
4309 reply("CSMSG_NOTELIST_HEADER", channel->name);
4310 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4313 reply("CSMSG_NOTELIST_END", channel->name);
4315 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4317 /* If one argument, show the named note. */
4320 if((note = dict_find(cData->notes, argv[1], NULL))
4321 && note_type_visible_to_user(cData, note->type, user))
4323 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4325 else if((ntype = dict_find(note_types, argv[1], NULL))
4326 && note_type_visible_to_user(NULL, ntype, user))
4328 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4333 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4337 /* Assume they're trying to set a note. */
4341 ntype = dict_find(note_types, argv[1], NULL);
4344 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4347 else if(note_type_settable_by_user(channel, ntype, user))
4349 note_text = unsplit_string(argv+2, argc-2, NULL);
4350 if((note = dict_find(cData->notes, argv[1], NULL)))
4351 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4352 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4353 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4355 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4357 /* The note is viewable to staff only, so return 0
4358 to keep the invocation from getting logged (or
4359 regular users can see it in !events). */
4365 reply("CSMSG_NO_ACCESS");
4372 static CHANSERV_FUNC(cmd_delnote)
4377 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4378 || !note_type_settable_by_user(channel, note->type, user))
4380 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4383 dict_remove(channel->channel_info->notes, note->type->name);
4384 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4388 static CHANSERV_FUNC(cmd_events)
4390 struct logSearch discrim;
4391 struct logReport report;
4392 unsigned int matches, limit;
4394 limit = (argc > 1) ? atoi(argv[1]) : 10;
4395 if(limit < 1 || limit > 200)
4398 memset(&discrim, 0, sizeof(discrim));
4399 discrim.masks.bot = chanserv;
4400 discrim.masks.channel_name = channel->name;
4402 discrim.masks.command = argv[2];
4403 discrim.limit = limit;
4404 discrim.max_time = INT_MAX;
4405 discrim.severities = 1 << LOG_COMMAND;
4406 report.reporter = chanserv;
4408 reply("CSMSG_EVENT_SEARCH_RESULTS");
4409 matches = log_entry_search(&discrim, log_report_entry, &report);
4411 reply("MSG_MATCH_COUNT", matches);
4413 reply("MSG_NO_MATCHES");
4417 static CHANSERV_FUNC(cmd_say)
4423 msg = unsplit_string(argv + 1, argc - 1, NULL);
4424 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4426 else if(GetUserH(argv[1]))
4429 msg = unsplit_string(argv + 2, argc - 2, NULL);
4430 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4434 reply("MSG_NOT_TARGET_NAME");
4440 static CHANSERV_FUNC(cmd_emote)
4446 /* CTCP is so annoying. */
4447 msg = unsplit_string(argv + 1, argc - 1, NULL);
4448 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4450 else if(GetUserH(argv[1]))
4452 msg = unsplit_string(argv + 2, argc - 2, NULL);
4453 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4457 reply("MSG_NOT_TARGET_NAME");
4463 struct channelList *
4464 chanserv_support_channels(void)
4466 return &chanserv_conf.support_channels;
4469 static CHANSERV_FUNC(cmd_expire)
4471 int channel_count = registered_channels;
4472 expire_channels(NULL);
4473 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4478 chanserv_expire_suspension(void *data)
4480 struct suspended *suspended = data;
4481 struct chanNode *channel;
4483 if(!suspended->expires || (now < suspended->expires))
4484 suspended->revoked = now;
4485 channel = suspended->cData->channel;
4486 suspended->cData->channel = channel;
4487 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4488 if(!IsOffChannel(suspended->cData))
4490 struct mod_chanmode change;
4491 mod_chanmode_init(&change);
4493 change.args[0].mode = MODE_CHANOP;
4494 change.args[0].u.member = AddChannelUser(chanserv, channel);
4495 mod_chanmode_announce(chanserv, channel, &change);
4499 static CHANSERV_FUNC(cmd_csuspend)
4501 struct suspended *suspended;
4502 char reason[MAXLEN];
4503 time_t expiry, duration;
4504 struct userData *uData;
4508 if(IsProtected(channel->channel_info))
4510 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4514 if(argv[1][0] == '!')
4516 else if(IsSuspended(channel->channel_info))
4518 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4519 show_suspension_info(cmd, user, channel->channel_info->suspended);
4523 if(!strcmp(argv[1], "0"))
4525 else if((duration = ParseInterval(argv[1])))
4526 expiry = now + duration;
4529 reply("MSG_INVALID_DURATION", argv[1]);
4533 unsplit_string(argv + 2, argc - 2, reason);
4535 suspended = calloc(1, sizeof(*suspended));
4536 suspended->revoked = 0;
4537 suspended->issued = now;
4538 suspended->suspender = strdup(user->handle_info->handle);
4539 suspended->expires = expiry;
4540 suspended->reason = strdup(reason);
4541 suspended->cData = channel->channel_info;
4542 suspended->previous = suspended->cData->suspended;
4543 suspended->cData->suspended = suspended;
4545 if(suspended->expires)
4546 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4548 if(IsSuspended(channel->channel_info))
4550 suspended->previous->revoked = now;
4551 if(suspended->previous->expires)
4552 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4553 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4554 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4558 /* Mark all users in channel as absent. */
4559 for(uData = channel->channel_info->users; uData; uData = uData->next)
4568 /* Mark the channel as suspended, then part. */
4569 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4570 DelChannelUser(chanserv, channel, suspended->reason, 0);
4571 reply("CSMSG_SUSPENDED", channel->name);
4572 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4573 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4578 static CHANSERV_FUNC(cmd_cunsuspend)
4580 struct suspended *suspended;
4581 char message[MAXLEN];
4583 if(!IsSuspended(channel->channel_info))
4585 reply("CSMSG_NOT_SUSPENDED", channel->name);
4589 suspended = channel->channel_info->suspended;
4591 /* Expire the suspension and join ChanServ to the channel. */
4592 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4593 chanserv_expire_suspension(suspended);
4594 reply("CSMSG_UNSUSPENDED", channel->name);
4595 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4596 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4600 typedef struct chanservSearch
4608 unsigned long flags;
4612 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4615 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4620 search = malloc(sizeof(struct chanservSearch));
4621 memset(search, 0, sizeof(*search));
4624 for(i = 0; i < argc; i++)
4626 /* Assume all criteria require arguments. */
4629 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4633 if(!irccasecmp(argv[i], "name"))
4634 search->name = argv[++i];
4635 else if(!irccasecmp(argv[i], "registrar"))
4636 search->registrar = argv[++i];
4637 else if(!irccasecmp(argv[i], "unvisited"))
4638 search->unvisited = ParseInterval(argv[++i]);
4639 else if(!irccasecmp(argv[i], "registered"))
4640 search->registered = ParseInterval(argv[++i]);
4641 else if(!irccasecmp(argv[i], "flags"))
4644 if(!irccasecmp(argv[i], "nodelete"))
4645 search->flags |= CHANNEL_NODELETE;
4646 else if(!irccasecmp(argv[i], "suspended"))
4647 search->flags |= CHANNEL_SUSPENDED;
4650 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4654 else if(!irccasecmp(argv[i], "limit"))
4655 search->limit = strtoul(argv[++i], NULL, 10);
4658 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4663 if(search->name && !strcmp(search->name, "*"))
4665 if(search->registrar && !strcmp(search->registrar, "*"))
4666 search->registrar = 0;
4675 chanserv_channel_match(struct chanData *channel, search_t search)
4677 const char *name = channel->channel->name;
4678 if((search->name && !match_ircglob(name, search->name)) ||
4679 (search->registrar && !channel->registrar) ||
4680 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4681 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4682 (search->registered && (now - channel->registered) > search->registered) ||
4683 (search->flags && ((search->flags & channel->flags) != search->flags)))
4690 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4692 struct chanData *channel;
4693 unsigned int matches = 0;
4695 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4697 if(!chanserv_channel_match(channel, search))
4707 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4712 search_print(struct chanData *channel, void *data)
4714 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4717 static CHANSERV_FUNC(cmd_search)
4720 unsigned int matches;
4721 channel_search_func action;
4725 if(!irccasecmp(argv[1], "count"))
4726 action = search_count;
4727 else if(!irccasecmp(argv[1], "print"))
4728 action = search_print;
4731 reply("CSMSG_ACTION_INVALID", argv[1]);
4735 search = chanserv_search_create(user, argc - 2, argv + 2);
4739 if(action == search_count)
4740 search->limit = INT_MAX;
4742 if(action == search_print)
4743 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4745 matches = chanserv_channel_search(search, action, user);
4748 reply("MSG_MATCH_COUNT", matches);
4750 reply("MSG_NO_MATCHES");
4756 static CHANSERV_FUNC(cmd_unvisited)
4758 struct chanData *cData;
4759 time_t interval = chanserv_conf.channel_expire_delay;
4760 char buffer[INTERVALLEN];
4761 unsigned int limit = 25, matches = 0;
4765 interval = ParseInterval(argv[1]);
4767 limit = atoi(argv[2]);
4770 intervalString(buffer, interval, user->handle_info);
4771 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4773 for(cData = channelList; cData && matches < limit; cData = cData->next)
4775 if((now - cData->visited) < interval)
4778 intervalString(buffer, now - cData->visited, user->handle_info);
4779 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4786 static MODCMD_FUNC(chan_opt_defaulttopic)
4792 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4794 reply("CSMSG_TOPIC_LOCKED", channel->name);
4798 topic = unsplit_string(argv+1, argc-1, NULL);
4800 free(channel->channel_info->topic);
4801 if(topic[0] == '*' && topic[1] == 0)
4803 topic = channel->channel_info->topic = NULL;
4807 topic = channel->channel_info->topic = strdup(topic);
4808 if(channel->channel_info->topic_mask
4809 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4810 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4812 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4815 if(channel->channel_info->topic)
4816 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4818 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4822 static MODCMD_FUNC(chan_opt_topicmask)
4826 struct chanData *cData = channel->channel_info;
4829 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4831 reply("CSMSG_TOPIC_LOCKED", channel->name);
4835 mask = unsplit_string(argv+1, argc-1, NULL);
4837 if(cData->topic_mask)
4838 free(cData->topic_mask);
4839 if(mask[0] == '*' && mask[1] == 0)
4841 cData->topic_mask = 0;
4845 cData->topic_mask = strdup(mask);
4847 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4848 else if(!match_ircglob(cData->topic, cData->topic_mask))
4849 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4853 if(channel->channel_info->topic_mask)
4854 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4856 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4860 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4864 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4868 if(greeting[0] == '*' && greeting[1] == 0)
4872 unsigned int length = strlen(greeting);
4873 if(length > chanserv_conf.greeting_length)
4875 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4878 *data = strdup(greeting);
4887 reply(name, user_find_message(user, "MSG_NONE"));
4891 static MODCMD_FUNC(chan_opt_greeting)
4893 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4896 static MODCMD_FUNC(chan_opt_usergreeting)
4898 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4901 static MODCMD_FUNC(chan_opt_modes)
4903 struct mod_chanmode *new_modes;
4904 char modes[MODELEN];
4908 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4910 reply("CSMSG_NO_ACCESS");
4913 if(argv[1][0] == '*' && argv[1][1] == 0)
4915 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4917 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4919 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4922 else if(new_modes->argc > 1)
4924 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4925 mod_chanmode_free(new_modes);
4930 channel->channel_info->modes = *new_modes;
4931 modcmd_chanmode_announce(new_modes);
4932 mod_chanmode_free(new_modes);
4936 mod_chanmode_format(&channel->channel_info->modes, modes);
4938 reply("CSMSG_SET_MODES", modes);
4940 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4944 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4946 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4948 struct chanData *cData = channel->channel_info;
4953 /* Set flag according to value. */
4954 if(enabled_string(argv[1]))
4956 cData->flags |= mask;
4959 else if(disabled_string(argv[1]))
4961 cData->flags &= ~mask;
4966 reply("MSG_INVALID_BINARY", argv[1]);
4972 /* Find current option value. */
4973 value = (cData->flags & mask) ? 1 : 0;
4977 reply(name, user_find_message(user, "MSG_ON"));
4979 reply(name, user_find_message(user, "MSG_OFF"));
4983 static MODCMD_FUNC(chan_opt_nodelete)
4985 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4987 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4991 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4994 static MODCMD_FUNC(chan_opt_dynlimit)
4996 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4999 static MODCMD_FUNC(chan_opt_offchannel)
5001 struct chanData *cData = channel->channel_info;
5006 /* Set flag according to value. */
5007 if(enabled_string(argv[1]))
5009 if(!IsOffChannel(cData))
5010 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5011 cData->flags |= CHANNEL_OFFCHANNEL;
5014 else if(disabled_string(argv[1]))
5016 if(IsOffChannel(cData))
5018 struct mod_chanmode change;
5019 mod_chanmode_init(&change);
5021 change.args[0].mode = MODE_CHANOP;
5022 change.args[0].u.member = AddChannelUser(chanserv, channel);
5023 mod_chanmode_announce(chanserv, channel, &change);
5025 cData->flags &= ~CHANNEL_OFFCHANNEL;
5030 reply("MSG_INVALID_BINARY", argv[1]);
5036 /* Find current option value. */
5037 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5041 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5043 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5047 static MODCMD_FUNC(chan_opt_defaults)
5049 struct userData *uData;
5050 struct chanData *cData;
5051 const char *confirm;
5052 enum levelOption lvlOpt;
5053 enum charOption chOpt;
5055 cData = channel->channel_info;
5056 uData = GetChannelUser(cData, user->handle_info);
5057 if(!uData || (uData->access < UL_OWNER))
5059 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5062 confirm = make_confirmation_string(uData);
5063 if((argc < 2) || strcmp(argv[1], confirm))
5065 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5068 cData->flags = CHANNEL_DEFAULT_FLAGS;
5069 cData->modes = chanserv_conf.default_modes;
5070 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5071 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5072 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5073 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5074 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5079 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5081 struct chanData *cData = channel->channel_info;
5082 struct userData *uData;
5083 unsigned short value;
5087 if(!check_user_level(channel, user, option, 1, 1))
5089 reply("CSMSG_CANNOT_SET");
5092 value = user_level_from_name(argv[1], UL_OWNER+1);
5093 if(!value && strcmp(argv[1], "0"))
5095 reply("CSMSG_INVALID_ACCESS", argv[1]);
5098 uData = GetChannelUser(cData, user->handle_info);
5099 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5101 reply("CSMSG_BAD_SETLEVEL");
5107 if(value > cData->lvlOpts[lvlGiveOps])
5109 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5114 if(value < cData->lvlOpts[lvlGiveVoice])
5116 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5121 /* This test only applies to owners, since non-owners
5122 * trying to set an option to above their level get caught
5123 * by the CSMSG_BAD_SETLEVEL test above.
5125 if(value > uData->access)
5127 reply("CSMSG_BAD_SETTERS");
5134 cData->lvlOpts[option] = value;
5136 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5140 static MODCMD_FUNC(chan_opt_enfops)
5142 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5145 static MODCMD_FUNC(chan_opt_giveops)
5147 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5150 static MODCMD_FUNC(chan_opt_enfmodes)
5152 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5155 static MODCMD_FUNC(chan_opt_enftopic)
5157 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5160 static MODCMD_FUNC(chan_opt_pubcmd)
5162 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5165 static MODCMD_FUNC(chan_opt_setters)
5167 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5170 static MODCMD_FUNC(chan_opt_ctcpusers)
5172 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5175 static MODCMD_FUNC(chan_opt_userinfo)
5177 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5180 static MODCMD_FUNC(chan_opt_givevoice)
5182 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5185 static MODCMD_FUNC(chan_opt_topicsnarf)
5187 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5190 static MODCMD_FUNC(chan_opt_inviteme)
5192 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5196 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5198 struct chanData *cData = channel->channel_info;
5199 int count = charOptions[option].count, index;
5203 index = atoi(argv[1]);
5205 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5207 reply("CSMSG_INVALID_NUMERIC", index);
5208 /* Show possible values. */
5209 for(index = 0; index < count; index++)
5210 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5214 cData->chOpts[option] = charOptions[option].values[index].value;
5218 /* Find current option value. */
5221 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5225 /* Somehow, the option value is corrupt; reset it to the default. */
5226 cData->chOpts[option] = charOptions[option].default_value;
5231 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5235 static MODCMD_FUNC(chan_opt_protect)
5237 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5240 static MODCMD_FUNC(chan_opt_toys)
5242 return channel_multiple_option(chToys, CSFUNC_ARGS);
5245 static MODCMD_FUNC(chan_opt_ctcpreaction)
5247 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5250 static MODCMD_FUNC(chan_opt_topicrefresh)
5252 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5255 static struct svccmd_list set_shows_list;
5258 handle_svccmd_unbind(struct svccmd *target) {
5260 for(ii=0; ii<set_shows_list.used; ++ii)
5261 if(target == set_shows_list.list[ii])
5262 set_shows_list.used = 0;
5265 static CHANSERV_FUNC(cmd_set)
5267 struct svccmd *subcmd;
5271 /* Check if we need to (re-)initialize set_shows_list. */
5272 if(!set_shows_list.used)
5274 if(!set_shows_list.size)
5276 set_shows_list.size = chanserv_conf.set_shows->used;
5277 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5279 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5281 const char *name = chanserv_conf.set_shows->list[ii];
5282 sprintf(buf, "%s %s", argv[0], name);
5283 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5286 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5289 svccmd_list_append(&set_shows_list, subcmd);
5295 reply("CSMSG_CHANNEL_OPTIONS");
5296 for(ii = 0; ii < set_shows_list.used; ii++)
5298 subcmd = set_shows_list.list[ii];
5299 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5304 sprintf(buf, "%s %s", argv[0], argv[1]);
5305 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5308 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5311 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5313 reply("CSMSG_NO_ACCESS");
5317 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5321 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5323 struct userData *uData;
5325 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5328 reply("CSMSG_NOT_USER", channel->name);
5334 /* Just show current option value. */
5336 else if(enabled_string(argv[1]))
5338 uData->flags |= mask;
5340 else if(disabled_string(argv[1]))
5342 uData->flags &= ~mask;
5346 reply("MSG_INVALID_BINARY", argv[1]);
5350 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5354 static MODCMD_FUNC(user_opt_noautoop)
5356 struct userData *uData;
5358 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5361 reply("CSMSG_NOT_USER", channel->name);
5364 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5365 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5367 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5370 static MODCMD_FUNC(user_opt_autoinvite)
5372 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5375 static MODCMD_FUNC(user_opt_info)
5377 struct userData *uData;
5380 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5384 /* If they got past the command restrictions (which require access)
5385 * but fail this test, we have some fool with security override on.
5387 reply("CSMSG_NOT_USER", channel->name);
5394 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5395 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5397 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5400 bp = strcspn(infoline, "\001");
5403 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5408 if(infoline[0] == '*' && infoline[1] == 0)
5411 uData->info = strdup(infoline);
5414 reply("CSMSG_USET_INFO", uData->info);
5416 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5420 struct svccmd_list uset_shows_list;
5422 static CHANSERV_FUNC(cmd_uset)
5424 struct svccmd *subcmd;
5428 /* Check if we need to (re-)initialize uset_shows_list. */
5429 if(!uset_shows_list.used)
5433 "NoAutoOp", "AutoInvite", "Info"
5436 if(!uset_shows_list.size)
5438 uset_shows_list.size = ArrayLength(options);
5439 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5441 for(ii = 0; ii < ArrayLength(options); ii++)
5443 const char *name = options[ii];
5444 sprintf(buf, "%s %s", argv[0], name);
5445 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5448 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5451 svccmd_list_append(&uset_shows_list, subcmd);
5457 /* Do this so options are presented in a consistent order. */
5458 reply("CSMSG_USER_OPTIONS");
5459 for(ii = 0; ii < uset_shows_list.used; ii++)
5460 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5464 sprintf(buf, "%s %s", argv[0], argv[1]);
5465 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5468 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5472 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5475 static CHANSERV_FUNC(cmd_giveownership)
5477 struct handle_info *new_owner_hi;
5478 struct userData *new_owner, *curr_user;
5479 struct chanData *cData = channel->channel_info;
5480 struct do_not_register *dnr;
5482 unsigned short co_access;
5483 char reason[MAXLEN];
5486 curr_user = GetChannelAccess(cData, user->handle_info);
5487 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5488 if(!curr_user || (curr_user->access != UL_OWNER))
5490 struct userData *owner = NULL;
5491 for(curr_user = channel->channel_info->users;
5493 curr_user = curr_user->next)
5495 if(curr_user->access != UL_OWNER)
5499 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5506 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5508 char delay[INTERVALLEN];
5509 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5510 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5513 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5515 if(new_owner_hi == user->handle_info)
5517 reply("CSMSG_NO_TRANSFER_SELF");
5520 new_owner = GetChannelAccess(cData, new_owner_hi);
5525 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5529 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5533 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5535 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5538 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5539 if(!IsHelping(user))
5540 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5542 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5545 if(new_owner->access >= UL_COOWNER)
5546 co_access = new_owner->access;
5548 co_access = UL_COOWNER;
5549 new_owner->access = UL_OWNER;
5551 curr_user->access = co_access;
5552 cData->ownerTransfer = now;
5553 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5554 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5555 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5559 static CHANSERV_FUNC(cmd_suspend)
5561 struct handle_info *hi;
5562 struct userData *self, *target;
5565 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5566 self = GetChannelUser(channel->channel_info, user->handle_info);
5567 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5569 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5572 if(target->access >= self->access)
5574 reply("MSG_USER_OUTRANKED", hi->handle);
5577 if(target->flags & USER_SUSPENDED)
5579 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5584 target->present = 0;
5587 target->flags |= USER_SUSPENDED;
5588 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5592 static CHANSERV_FUNC(cmd_unsuspend)
5594 struct handle_info *hi;
5595 struct userData *self, *target;
5598 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5599 self = GetChannelUser(channel->channel_info, user->handle_info);
5600 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5602 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5605 if(target->access >= self->access)
5607 reply("MSG_USER_OUTRANKED", hi->handle);
5610 if(!(target->flags & USER_SUSPENDED))
5612 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5615 target->flags &= ~USER_SUSPENDED;
5616 scan_user_presence(target, NULL);
5617 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5621 static MODCMD_FUNC(cmd_deleteme)
5623 struct handle_info *hi;
5624 struct userData *target;
5625 const char *confirm_string;
5626 unsigned short access;
5629 hi = user->handle_info;
5630 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5632 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5635 if(target->access == UL_OWNER)
5637 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5640 confirm_string = make_confirmation_string(target);
5641 if((argc < 2) || strcmp(argv[1], confirm_string))
5643 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5646 access = target->access;
5647 channel_name = strdup(channel->name);
5648 del_channel_user(target, 1);
5649 reply("CSMSG_DELETED_YOU", access, channel_name);
5655 chanserv_refresh_topics(UNUSED_ARG(void *data))
5657 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5658 struct chanData *cData;
5661 for(cData = channelList; cData; cData = cData->next)
5663 if(IsSuspended(cData))
5665 opt = cData->chOpts[chTopicRefresh];
5668 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5671 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5672 cData->last_refresh = refresh_num;
5674 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5677 static CHANSERV_FUNC(cmd_unf)
5681 char response[MAXLEN];
5682 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5683 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5684 irc_privmsg(cmd->parent->bot, channel->name, response);
5687 reply("CSMSG_UNF_RESPONSE");
5691 static CHANSERV_FUNC(cmd_ping)
5695 char response[MAXLEN];
5696 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5697 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5698 irc_privmsg(cmd->parent->bot, channel->name, response);
5701 reply("CSMSG_PING_RESPONSE");
5705 static CHANSERV_FUNC(cmd_wut)
5709 char response[MAXLEN];
5710 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5711 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5712 irc_privmsg(cmd->parent->bot, channel->name, response);
5715 reply("CSMSG_WUT_RESPONSE");
5719 static CHANSERV_FUNC(cmd_8ball)
5721 unsigned int i, j, accum;
5726 for(i=1; i<argc; i++)
5727 for(j=0; argv[i][j]; j++)
5728 accum = (accum << 5) - accum + toupper(argv[i][j]);
5729 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5732 char response[MAXLEN];
5733 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5734 irc_privmsg(cmd->parent->bot, channel->name, response);
5737 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5741 static CHANSERV_FUNC(cmd_d)
5743 unsigned long sides, count, modifier, ii, total;
5744 char response[MAXLEN], *sep;
5748 if((count = strtoul(argv[1], &sep, 10)) < 1)
5758 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5759 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5763 else if((sep[0] == '-') && isdigit(sep[1]))
5764 modifier = strtoul(sep, NULL, 10);
5765 else if((sep[0] == '+') && isdigit(sep[1]))
5766 modifier = strtoul(sep+1, NULL, 10);
5773 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5778 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5781 for(total = ii = 0; ii < count; ++ii)
5782 total += (rand() % sides) + 1;
5785 if((count > 1) || modifier)
5787 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5788 sprintf(response, fmt, total, count, sides, modifier);
5792 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5793 sprintf(response, fmt, total, sides);
5796 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5798 send_message_type(4, user, cmd->parent->bot, "%s", response);
5802 static CHANSERV_FUNC(cmd_huggle)
5804 /* CTCP must be via PRIVMSG, never notice */
5806 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5808 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5813 chanserv_adjust_limit(void *data)
5815 struct mod_chanmode change;
5816 struct chanData *cData = data;
5817 struct chanNode *channel = cData->channel;
5820 if(IsSuspended(cData))
5823 cData->limitAdjusted = now;
5824 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5825 if(cData->modes.modes_set & MODE_LIMIT)
5827 if(limit > cData->modes.new_limit)
5828 limit = cData->modes.new_limit;
5829 else if(limit == cData->modes.new_limit)
5833 mod_chanmode_init(&change);
5834 change.modes_set = MODE_LIMIT;
5835 change.new_limit = limit;
5836 mod_chanmode_announce(chanserv, channel, &change);
5840 handle_new_channel(struct chanNode *channel)
5842 struct chanData *cData;
5844 if(!(cData = channel->channel_info))
5847 if(cData->modes.modes_set || cData->modes.modes_clear)
5848 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5850 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5851 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5854 /* Welcome to my worst nightmare. Warning: Read (or modify)
5855 the code below at your own risk. */
5857 handle_join(struct modeNode *mNode)
5859 struct mod_chanmode change;
5860 struct userNode *user = mNode->user;
5861 struct chanNode *channel = mNode->channel;
5862 struct chanData *cData;
5863 struct userData *uData = NULL;
5864 struct banData *bData;
5865 struct handle_info *handle;
5866 unsigned int modes = 0, info = 0;
5869 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5872 cData = channel->channel_info;
5873 if(channel->members.used > cData->max)
5874 cData->max = channel->members.used;
5876 /* Check for bans. If they're joining through a ban, one of two
5878 * 1: Join during a netburst, by riding the break. Kick them
5879 * unless they have ops or voice in the channel.
5880 * 2: They're allowed to join through the ban (an invite in
5881 * ircu2.10, or a +e on Hybrid, or something).
5882 * If they're not joining through a ban, and the banlist is not
5883 * full, see if they're on the banlist for the channel. If so,
5886 if(user->uplink->burst && !mNode->modes)
5889 for(ii = 0; ii < channel->banlist.used; ii++)
5891 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5893 /* Riding a netburst. Naughty. */
5894 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5900 mod_chanmode_init(&change);
5902 if(channel->banlist.used < MAXBANS)
5904 /* Not joining through a ban. */
5905 for(bData = cData->bans;
5906 bData && !user_matches_glob(user, bData->mask, 1);
5907 bData = bData->next);
5911 char kick_reason[MAXLEN];
5912 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5914 bData->triggered = now;
5915 if(bData != cData->bans)
5917 /* Shuffle the ban to the head of the list. */
5919 bData->next->prev = bData->prev;
5921 bData->prev->next = bData->next;
5924 bData->next = cData->bans;
5927 cData->bans->prev = bData;
5928 cData->bans = bData;
5931 change.args[0].mode = MODE_BAN;
5932 change.args[0].u.hostmask = bData->mask;
5933 mod_chanmode_announce(chanserv, channel, &change);
5934 KickChannelUser(user, channel, chanserv, kick_reason);
5939 /* ChanServ will not modify the limits in join-flooded channels.
5940 It will also skip DynLimit processing when the user (or srvx)
5941 is bursting in, because there are likely more incoming. */
5942 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5943 && !user->uplink->burst
5944 && !channel->join_flooded
5945 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5947 /* The user count has begun "bumping" into the channel limit,
5948 so set a timer to raise the limit a bit. Any previous
5949 timers are removed so three incoming users within the delay
5950 results in one limit change, not three. */
5952 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5953 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5956 if(channel->join_flooded)
5958 /* don't automatically give ops or voice during a join flood */
5960 else if(cData->lvlOpts[lvlGiveOps] == 0)
5961 modes |= MODE_CHANOP;
5962 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5963 modes |= MODE_VOICE;
5965 greeting = cData->greeting;
5966 if(user->handle_info)
5968 handle = user->handle_info;
5970 if(IsHelper(user) && !IsHelping(user))
5973 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5975 if(channel == chanserv_conf.support_channels.list[ii])
5977 HANDLE_SET_FLAG(user->handle_info, HELPING);
5983 uData = GetTrueChannelAccess(cData, handle);
5984 if(uData && !IsUserSuspended(uData))
5986 /* Ops and above were handled by the above case. */
5987 if(IsUserAutoOp(uData))
5989 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5990 modes |= MODE_CHANOP;
5991 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5992 modes |= MODE_VOICE;
5994 if(uData->access >= UL_PRESENT)
5995 cData->visited = now;
5996 if(cData->user_greeting)
5997 greeting = cData->user_greeting;
5999 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6000 && ((now - uData->seen) >= chanserv_conf.info_delay)
6007 if(!user->uplink->burst)
6011 if(modes & MODE_CHANOP)
6012 modes &= ~MODE_VOICE;
6013 change.args[0].mode = modes;
6014 change.args[0].u.member = mNode;
6015 mod_chanmode_announce(chanserv, channel, &change);
6017 if(greeting && !user->uplink->burst)
6018 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6020 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6026 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6028 struct mod_chanmode change;
6029 struct userData *channel;
6030 unsigned int ii, jj;
6032 if(!user->handle_info)
6035 mod_chanmode_init(&change);
6037 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6039 struct chanNode *cn;
6040 struct modeNode *mn;
6041 if(IsUserSuspended(channel)
6042 || IsSuspended(channel->channel)
6043 || !(cn = channel->channel->channel))
6046 mn = GetUserMode(cn, user);
6049 if(!IsUserSuspended(channel)
6050 && IsUserAutoInvite(channel)
6051 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6053 && !user->uplink->burst)
6054 irc_invite(chanserv, user, cn);
6058 if(channel->access >= UL_PRESENT)
6059 channel->channel->visited = now;
6061 if(IsUserAutoOp(channel))
6063 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6064 change.args[0].mode = MODE_CHANOP;
6065 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6066 change.args[0].mode = MODE_VOICE;
6068 change.args[0].mode = 0;
6069 change.args[0].u.member = mn;
6070 if(change.args[0].mode)
6071 mod_chanmode_announce(chanserv, cn, &change);
6074 channel->seen = now;
6075 channel->present = 1;
6078 for(ii = 0; ii < user->channels.used; ++ii)
6080 struct chanNode *channel = user->channels.list[ii]->channel;
6081 struct banData *ban;
6083 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6084 || !channel->channel_info
6085 || IsSuspended(channel->channel_info))
6087 for(jj = 0; jj < channel->banlist.used; ++jj)
6088 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6090 if(jj < channel->banlist.used)
6092 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6094 char kick_reason[MAXLEN];
6095 if(!user_matches_glob(user, ban->mask, 1))
6097 change.args[0].mode = MODE_BAN;
6098 change.args[0].u.hostmask = ban->mask;
6099 mod_chanmode_announce(chanserv, channel, &change);
6100 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6101 KickChannelUser(user, channel, chanserv, kick_reason);
6102 ban->triggered = now;
6107 if(IsSupportHelper(user))
6109 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6111 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6113 HANDLE_SET_FLAG(user->handle_info, HELPING);
6121 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6123 struct chanData *cData;
6124 struct userData *uData;
6126 cData = mn->channel->channel_info;
6127 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6130 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6132 /* Allow for a bit of padding so that the limit doesn't
6133 track the user count exactly, which could get annoying. */
6134 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6136 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6137 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6141 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6143 scan_user_presence(uData, mn->user);
6147 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6149 unsigned int ii, jj;
6150 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6152 for(jj = 0; jj < mn->user->channels.used; ++jj)
6153 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6155 if(jj < mn->user->channels.used)
6158 if(ii == chanserv_conf.support_channels.used)
6159 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6164 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6166 struct userData *uData;
6168 if(!channel->channel_info || !kicker || IsService(kicker)
6169 || (kicker == victim) || IsSuspended(channel->channel_info)
6170 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6173 if(protect_user(victim, kicker, channel->channel_info))
6175 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6176 KickChannelUser(kicker, channel, chanserv, reason);
6179 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6184 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6186 struct chanData *cData;
6188 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6191 cData = channel->channel_info;
6192 if(bad_topic(channel, user, channel->topic))
6194 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6195 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6196 SetChannelTopic(channel, chanserv, old_topic, 1);
6197 else if(cData->topic)
6198 SetChannelTopic(channel, chanserv, cData->topic, 1);
6201 /* With topicsnarf, grab the topic and save it as the default topic. */
6202 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6205 cData->topic = strdup(channel->topic);
6211 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6213 struct mod_chanmode *bounce = NULL;
6214 unsigned int bnc, ii;
6217 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6220 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6221 && mode_lock_violated(&channel->channel_info->modes, change))
6223 char correct[MAXLEN];
6224 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6225 mod_chanmode_format(&channel->channel_info->modes, correct);
6226 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6228 for(ii = bnc = 0; ii < change->argc; ++ii)
6230 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6232 const struct userNode *victim = change->args[ii].u.member->user;
6233 if(!protect_user(victim, user, channel->channel_info))
6236 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6239 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6240 bounce->args[bnc].u.member = GetUserMode(channel, user);
6241 if(bounce->args[bnc].u.member)
6245 bounce->args[bnc].mode = MODE_CHANOP;
6246 bounce->args[bnc].u.member = change->args[ii].u.member;
6248 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6250 else if(change->args[ii].mode & MODE_CHANOP)
6252 const struct userNode *victim = change->args[ii].u.member->user;
6253 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6256 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6257 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6258 bounce->args[bnc].u.member = change->args[ii].u.member;
6261 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6263 const char *ban = change->args[ii].u.hostmask;
6264 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6267 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6268 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6269 bounce->args[bnc].u.hostmask = strdup(ban);
6271 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6276 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6277 mod_chanmode_announce(chanserv, channel, bounce);
6278 for(ii = 0; ii < change->argc; ++ii)
6279 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6280 free((char*)bounce->args[ii].u.hostmask);
6281 mod_chanmode_free(bounce);
6286 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6288 struct chanNode *channel;
6289 struct banData *bData;
6290 struct mod_chanmode change;
6291 unsigned int ii, jj;
6292 char kick_reason[MAXLEN];
6294 mod_chanmode_init(&change);
6296 change.args[0].mode = MODE_BAN;
6297 for(ii = 0; ii < user->channels.used; ++ii)
6299 channel = user->channels.list[ii]->channel;
6300 /* Need not check for bans if they're opped or voiced. */
6301 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6303 /* Need not check for bans unless channel registration is active. */
6304 if(!channel->channel_info || IsSuspended(channel->channel_info))
6306 /* Look for a matching ban already on the channel. */
6307 for(jj = 0; jj < channel->banlist.used; ++jj)
6308 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6310 /* Need not act if we found one. */
6311 if(jj < channel->banlist.used)
6313 /* Look for a matching ban in this channel. */
6314 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6316 if(!user_matches_glob(user, bData->mask, 1))
6318 change.args[0].u.hostmask = bData->mask;
6319 mod_chanmode_announce(chanserv, channel, &change);
6320 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6321 KickChannelUser(user, channel, chanserv, kick_reason);
6322 bData->triggered = now;
6323 break; /* we don't need to check any more bans in the channel */
6328 static void handle_rename(struct handle_info *handle, const char *old_handle)
6330 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6334 dict_remove2(handle_dnrs, old_handle, 1);
6335 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6336 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6341 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6343 struct userNode *h_user;
6345 if(handle->channels)
6347 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6348 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6350 while(handle->channels)
6351 del_channel_user(handle->channels, 1);
6356 handle_server_link(UNUSED_ARG(struct server *server))
6358 struct chanData *cData;
6360 for(cData = channelList; cData; cData = cData->next)
6362 if(!IsSuspended(cData))
6363 cData->may_opchan = 1;
6364 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6365 && !cData->channel->join_flooded
6366 && ((cData->channel->limit - cData->channel->members.used)
6367 < chanserv_conf.adjust_threshold))
6369 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6370 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6376 chanserv_conf_read(void)
6380 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6381 struct mod_chanmode *change;
6382 struct string_list *strlist;
6383 struct chanNode *chan;
6386 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6388 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6391 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6392 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6393 chanserv_conf.support_channels.used = 0;
6394 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6396 for(ii = 0; ii < strlist->used; ++ii)
6398 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6401 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6403 channelList_append(&chanserv_conf.support_channels, chan);
6406 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6409 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6412 chan = AddChannel(str, now, str2, NULL);
6414 channelList_append(&chanserv_conf.support_channels, chan);
6416 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6417 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6418 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6419 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6420 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6421 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6422 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6423 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6424 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6425 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6426 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6427 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6428 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6429 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6430 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6431 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6432 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6433 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6434 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6435 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6436 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6437 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6438 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6440 NickChange(chanserv, str, 0);
6441 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6442 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6443 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6444 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6445 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6446 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6447 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6448 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6449 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6450 chanserv_conf.max_owned = str ? atoi(str) : 5;
6451 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6452 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6453 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6454 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6455 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6456 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6457 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6460 safestrncpy(mode_line, str, sizeof(mode_line));
6461 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6462 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6463 && (change->argc < 2))
6465 chanserv_conf.default_modes = *change;
6466 mod_chanmode_free(change);
6468 free_string_list(chanserv_conf.set_shows);
6469 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6471 strlist = string_list_copy(strlist);
6474 static const char *list[] = {
6475 /* free form text */
6476 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6477 /* options based on user level */
6478 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6479 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6480 /* multiple choice options */
6481 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6482 /* binary options */
6483 "DynLimit", "NoDelete",
6488 strlist = alloc_string_list(ArrayLength(list)-1);
6489 for(ii=0; list[ii]; ii++)
6490 string_list_append(strlist, strdup(list[ii]));
6492 chanserv_conf.set_shows = strlist;
6493 /* We don't look things up now, in case the list refers to options
6494 * defined by modules initialized after this point. Just mark the
6495 * function list as invalid, so it will be initialized.
6497 set_shows_list.used = 0;
6498 free_string_list(chanserv_conf.eightball);
6499 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6502 strlist = string_list_copy(strlist);
6506 strlist = alloc_string_list(4);
6507 string_list_append(strlist, strdup("Yes."));
6508 string_list_append(strlist, strdup("No."));
6509 string_list_append(strlist, strdup("Maybe so."));
6511 chanserv_conf.eightball = strlist;
6512 free_string_list(chanserv_conf.old_ban_names);
6513 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6515 strlist = string_list_copy(strlist);
6517 strlist = alloc_string_list(2);
6518 chanserv_conf.old_ban_names = strlist;
6519 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6520 off_channel = str ? atoi(str) : 0;
6524 chanserv_note_type_read(const char *key, struct record_data *rd)
6527 struct note_type *ntype;
6530 if(!(obj = GET_RECORD_OBJECT(rd)))
6532 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6535 if(!(ntype = chanserv_create_note_type(key)))
6537 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6541 /* Figure out set access */
6542 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6544 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6545 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6547 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6549 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6550 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6552 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6554 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6558 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6559 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6560 ntype->set_access.min_opserv = 0;
6563 /* Figure out visibility */
6564 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6565 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6566 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6567 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6568 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6569 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6570 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6571 ntype->visible_type = NOTE_VIS_ALL;
6573 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6575 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6576 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6580 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6582 struct handle_info *handle;
6583 struct userData *uData;
6584 char *seen, *inf, *flags;
6586 unsigned short access;
6588 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6590 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6594 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6595 if(access > UL_OWNER)
6597 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6601 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6602 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6603 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6604 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6605 handle = get_handle_info(key);
6608 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6612 uData = add_channel_user(chan, handle, access, last_seen, inf);
6613 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6617 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6619 struct banData *bData;
6620 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6621 time_t set_time, triggered_time, expires_time;
6623 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6625 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6629 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6630 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6631 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6632 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6633 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6634 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6635 if (!reason || !owner)
6638 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6639 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6641 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6643 expires_time = set_time + atoi(s_duration);
6647 if(!reason || (expires_time && (expires_time < now)))
6650 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6653 static struct suspended *
6654 chanserv_read_suspended(dict_t obj)
6656 struct suspended *suspended = calloc(1, sizeof(*suspended));
6660 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6661 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6662 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6663 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6664 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6665 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6666 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6667 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6668 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6669 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6674 chanserv_channel_read(const char *key, struct record_data *hir)
6676 struct suspended *suspended;
6677 struct mod_chanmode *modes;
6678 struct chanNode *cNode;
6679 struct chanData *cData;
6680 struct dict *channel, *obj;
6681 char *str, *argv[10];
6685 channel = hir->d.object;
6687 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6690 cNode = AddChannel(key, now, NULL, NULL);
6693 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6696 cData = register_channel(cNode, str);
6699 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6703 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6705 enum levelOption lvlOpt;
6706 enum charOption chOpt;
6708 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6709 cData->flags = atoi(str);
6711 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6713 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6715 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6716 else if(levelOptions[lvlOpt].old_flag)
6718 if(cData->flags & levelOptions[lvlOpt].old_flag)
6719 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6721 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6725 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6727 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6729 cData->chOpts[chOpt] = str[0];
6732 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6734 enum levelOption lvlOpt;
6735 enum charOption chOpt;
6738 cData->flags = base64toint(str, 5);
6739 count = strlen(str += 5);
6740 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6743 if(levelOptions[lvlOpt].old_flag)
6745 if(cData->flags & levelOptions[lvlOpt].old_flag)
6746 lvl = levelOptions[lvlOpt].flag_value;
6748 lvl = levelOptions[lvlOpt].default_value;
6750 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6752 case 'c': lvl = UL_COOWNER; break;
6753 case 'm': lvl = UL_MASTER; break;
6754 case 'n': lvl = UL_OWNER+1; break;
6755 case 'o': lvl = UL_OP; break;
6756 case 'p': lvl = UL_PEON; break;
6757 case 'w': lvl = UL_OWNER; break;
6758 default: lvl = 0; break;
6760 cData->lvlOpts[lvlOpt] = lvl;
6762 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6763 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6766 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6768 suspended = chanserv_read_suspended(obj);
6769 cData->suspended = suspended;
6770 suspended->cData = cData;
6771 /* We could use suspended->expires and suspended->revoked to
6772 * set the CHANNEL_SUSPENDED flag, but we don't. */
6774 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6776 suspended = calloc(1, sizeof(*suspended));
6777 suspended->issued = 0;
6778 suspended->revoked = 0;
6779 suspended->suspender = strdup(str);
6780 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6781 suspended->expires = str ? atoi(str) : 0;
6782 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6783 suspended->reason = strdup(str ? str : "No reason");
6784 suspended->previous = NULL;
6785 cData->suspended = suspended;
6786 suspended->cData = cData;
6790 cData->flags &= ~CHANNEL_SUSPENDED;
6791 suspended = NULL; /* to squelch a warning */
6794 if(IsSuspended(cData)) {
6795 if(suspended->expires > now)
6796 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6797 else if(suspended->expires)
6798 cData->flags &= ~CHANNEL_SUSPENDED;
6801 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6802 struct mod_chanmode change;
6803 mod_chanmode_init(&change);
6805 change.args[0].mode = MODE_CHANOP;
6806 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6807 mod_chanmode_announce(chanserv, cNode, &change);
6810 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6811 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6812 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6813 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6814 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6815 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6816 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6817 cData->max = str ? atoi(str) : 0;
6818 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6819 cData->greeting = str ? strdup(str) : NULL;
6820 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6821 cData->user_greeting = str ? strdup(str) : NULL;
6822 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6823 cData->topic_mask = str ? strdup(str) : NULL;
6824 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6825 cData->topic = str ? strdup(str) : NULL;
6827 if(!IsSuspended(cData)
6828 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6829 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6830 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6831 cData->modes = *modes;
6833 cData->modes.modes_set |= MODE_REGISTERED;
6834 if(cData->modes.argc > 1)
6835 cData->modes.argc = 1;
6836 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6837 mod_chanmode_free(modes);
6840 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6841 for(it = dict_first(obj); it; it = iter_next(it))
6842 user_read_helper(iter_key(it), iter_data(it), cData);
6844 if(!cData->users && !IsProtected(cData))
6846 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6847 unregister_channel(cData, "has empty user list.");
6851 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6852 for(it = dict_first(obj); it; it = iter_next(it))
6853 ban_read_helper(iter_key(it), iter_data(it), cData);
6855 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6856 for(it = dict_first(obj); it; it = iter_next(it))
6858 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6859 struct record_data *rd = iter_data(it);
6860 const char *note, *setter;
6862 if(rd->type != RECDB_OBJECT)
6864 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6868 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6870 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6872 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6876 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6877 if(!setter) setter = "<unknown>";
6878 chanserv_add_channel_note(cData, ntype, setter, note);
6886 chanserv_dnr_read(const char *key, struct record_data *hir)
6888 const char *setter, *reason, *str;
6889 struct do_not_register *dnr;
6891 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6894 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6897 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6900 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6903 dnr = chanserv_add_dnr(key, setter, reason);
6906 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6908 dnr->set = atoi(str);
6914 chanserv_saxdb_read(struct dict *database)
6916 struct dict *section;
6919 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6920 for(it = dict_first(section); it; it = iter_next(it))
6921 chanserv_note_type_read(iter_key(it), iter_data(it));
6923 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6924 for(it = dict_first(section); it; it = iter_next(it))
6925 chanserv_channel_read(iter_key(it), iter_data(it));
6927 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6928 for(it = dict_first(section); it; it = iter_next(it))
6929 chanserv_dnr_read(iter_key(it), iter_data(it));
6935 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6937 int high_present = 0;
6938 saxdb_start_record(ctx, KEY_USERS, 1);
6939 for(; uData; uData = uData->next)
6941 if((uData->access >= UL_PRESENT) && uData->present)
6943 saxdb_start_record(ctx, uData->handle->handle, 0);
6944 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6945 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6947 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6949 saxdb_write_string(ctx, KEY_INFO, uData->info);
6950 saxdb_end_record(ctx);
6952 saxdb_end_record(ctx);
6953 return high_present;
6957 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6961 saxdb_start_record(ctx, KEY_BANS, 1);
6962 for(; bData; bData = bData->next)
6964 saxdb_start_record(ctx, bData->mask, 0);
6965 saxdb_write_int(ctx, KEY_SET, bData->set);
6966 if(bData->triggered)
6967 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6969 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6971 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6973 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6974 saxdb_end_record(ctx);
6976 saxdb_end_record(ctx);
6980 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6982 saxdb_start_record(ctx, name, 0);
6983 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6984 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6986 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6988 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6990 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6992 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6993 saxdb_end_record(ctx);
6997 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7001 enum levelOption lvlOpt;
7002 enum charOption chOpt;
7004 saxdb_start_record(ctx, channel->channel->name, 1);
7006 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7007 saxdb_write_int(ctx, KEY_MAX, channel->max);
7009 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7010 if(channel->registrar)
7011 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7012 if(channel->greeting)
7013 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7014 if(channel->user_greeting)
7015 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7016 if(channel->topic_mask)
7017 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7018 if(channel->suspended)
7019 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7021 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7022 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7023 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7024 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7025 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7027 buf[0] = channel->chOpts[chOpt];
7029 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7031 saxdb_end_record(ctx);
7033 if(channel->modes.modes_set || channel->modes.modes_clear)
7035 mod_chanmode_format(&channel->modes, buf);
7036 saxdb_write_string(ctx, KEY_MODES, buf);
7039 high_present = chanserv_write_users(ctx, channel->users);
7040 chanserv_write_bans(ctx, channel->bans);
7042 if(dict_size(channel->notes))
7046 saxdb_start_record(ctx, KEY_NOTES, 1);
7047 for(it = dict_first(channel->notes); it; it = iter_next(it))
7049 struct note *note = iter_data(it);
7050 saxdb_start_record(ctx, iter_key(it), 0);
7051 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7052 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7053 saxdb_end_record(ctx);
7055 saxdb_end_record(ctx);
7058 if(channel->ownerTransfer)
7059 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7060 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7061 saxdb_end_record(ctx);
7065 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7069 saxdb_start_record(ctx, ntype->name, 0);
7070 switch(ntype->set_access_type)
7072 case NOTE_SET_CHANNEL_ACCESS:
7073 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7075 case NOTE_SET_CHANNEL_SETTER:
7076 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7078 case NOTE_SET_PRIVILEGED: default:
7079 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7082 switch(ntype->visible_type)
7084 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7085 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7086 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7088 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7089 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7090 saxdb_end_record(ctx);
7094 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7096 struct do_not_register *dnr;
7099 for(it = dict_first(dnrs); it; it = iter_next(it))
7101 dnr = iter_data(it);
7102 saxdb_start_record(ctx, dnr->chan_name, 0);
7104 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7105 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7106 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7107 saxdb_end_record(ctx);
7112 chanserv_saxdb_write(struct saxdb_context *ctx)
7115 struct chanData *channel;
7118 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7119 for(it = dict_first(note_types); it; it = iter_next(it))
7120 chanserv_write_note_type(ctx, iter_data(it));
7121 saxdb_end_record(ctx);
7124 saxdb_start_record(ctx, KEY_DNR, 1);
7125 write_dnrs_helper(ctx, handle_dnrs);
7126 write_dnrs_helper(ctx, plain_dnrs);
7127 write_dnrs_helper(ctx, mask_dnrs);
7128 saxdb_end_record(ctx);
7131 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7132 for(channel = channelList; channel; channel = channel->next)
7133 chanserv_write_channel(ctx, channel);
7134 saxdb_end_record(ctx);
7140 chanserv_db_cleanup(void) {
7142 unreg_part_func(handle_part);
7144 unregister_channel(channelList, "terminating.");
7145 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7146 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7147 free(chanserv_conf.support_channels.list);
7148 dict_delete(handle_dnrs);
7149 dict_delete(plain_dnrs);
7150 dict_delete(mask_dnrs);
7151 dict_delete(note_types);
7152 free_string_list(chanserv_conf.eightball);
7153 free_string_list(chanserv_conf.old_ban_names);
7154 free_string_list(chanserv_conf.set_shows);
7155 free(set_shows_list.list);
7156 free(uset_shows_list.list);
7159 struct userData *helper = helperList;
7160 helperList = helperList->next;
7165 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7166 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7167 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7170 init_chanserv(const char *nick)
7172 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7173 conf_register_reload(chanserv_conf_read);
7175 reg_server_link_func(handle_server_link);
7177 reg_new_channel_func(handle_new_channel);
7178 reg_join_func(handle_join);
7179 reg_part_func(handle_part);
7180 reg_kick_func(handle_kick);
7181 reg_topic_func(handle_topic);
7182 reg_mode_change_func(handle_mode);
7183 reg_nick_change_func(handle_nick_change);
7185 reg_auth_func(handle_auth);
7186 reg_handle_rename_func(handle_rename);
7187 reg_unreg_func(handle_unreg);
7189 handle_dnrs = dict_new();
7190 dict_set_free_data(handle_dnrs, free);
7191 plain_dnrs = dict_new();
7192 dict_set_free_data(plain_dnrs, free);
7193 mask_dnrs = dict_new();
7194 dict_set_free_data(mask_dnrs, free);
7196 reg_svccmd_unbind_func(handle_svccmd_unbind);
7197 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7198 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7199 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7200 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7201 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7202 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7203 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7204 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7205 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7207 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7208 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7210 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7211 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7212 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7213 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7214 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7216 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7217 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7218 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7219 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7220 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7222 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7223 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7224 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7225 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7227 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7228 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7229 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7230 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7231 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7232 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7233 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7234 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7236 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7237 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7238 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7239 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7240 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7241 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7242 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7243 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7244 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7245 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7246 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7247 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7248 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7249 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7251 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7252 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7253 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7254 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7255 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7257 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7258 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7260 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7261 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7262 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7263 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7264 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7265 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7266 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7267 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7268 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7269 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7270 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7272 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7273 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7275 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7276 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7277 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7278 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7280 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7281 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7282 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7283 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7284 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7286 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7287 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7288 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7289 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7290 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7291 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7293 /* Channel options */
7294 DEFINE_CHANNEL_OPTION(defaulttopic);
7295 DEFINE_CHANNEL_OPTION(topicmask);
7296 DEFINE_CHANNEL_OPTION(greeting);
7297 DEFINE_CHANNEL_OPTION(usergreeting);
7298 DEFINE_CHANNEL_OPTION(modes);
7299 DEFINE_CHANNEL_OPTION(enfops);
7300 DEFINE_CHANNEL_OPTION(giveops);
7301 DEFINE_CHANNEL_OPTION(protect);
7302 DEFINE_CHANNEL_OPTION(enfmodes);
7303 DEFINE_CHANNEL_OPTION(enftopic);
7304 DEFINE_CHANNEL_OPTION(pubcmd);
7305 DEFINE_CHANNEL_OPTION(givevoice);
7306 DEFINE_CHANNEL_OPTION(userinfo);
7307 DEFINE_CHANNEL_OPTION(dynlimit);
7308 DEFINE_CHANNEL_OPTION(topicsnarf);
7309 DEFINE_CHANNEL_OPTION(nodelete);
7310 DEFINE_CHANNEL_OPTION(toys);
7311 DEFINE_CHANNEL_OPTION(setters);
7312 DEFINE_CHANNEL_OPTION(topicrefresh);
7313 DEFINE_CHANNEL_OPTION(ctcpusers);
7314 DEFINE_CHANNEL_OPTION(ctcpreaction);
7315 DEFINE_CHANNEL_OPTION(inviteme);
7317 DEFINE_CHANNEL_OPTION(offchannel);
7318 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7320 /* Alias set topic to set defaulttopic for compatibility. */
7321 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7324 DEFINE_USER_OPTION(noautoop);
7325 DEFINE_USER_OPTION(autoinvite);
7326 DEFINE_USER_OPTION(info);
7328 /* Alias uset autovoice to uset autoop. */
7329 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7331 note_types = dict_new();
7332 dict_set_free_data(note_types, chanserv_deref_note_type);
7335 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7336 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7337 service_register(chanserv)->trigger = '!';
7338 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7340 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7342 if(chanserv_conf.channel_expire_frequency)
7343 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7345 if(chanserv_conf.refresh_period)
7347 time_t next_refresh;
7348 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7349 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7352 reg_exit_func(chanserv_db_cleanup);
7353 message_register_table(msgtab);