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 mod_chanmode *change;
3775 change = &channel->channel_info->modes;
3776 if(change->modes_set || change->modes_clear) {
3777 modcmd_chanmode_announce(change);
3778 reply("CSMSG_DEFAULTED_MODES", channel->name);
3780 reply("CSMSG_NO_MODES", channel->name);
3784 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3787 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3791 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3792 && mode_lock_violated(&channel->channel_info->modes, change))
3795 mod_chanmode_format(&channel->channel_info->modes, modes);
3796 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3800 modcmd_chanmode_announce(change);
3801 mod_chanmode_free(change);
3802 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3806 static CHANSERV_FUNC(cmd_invite)
3808 struct userData *uData;
3809 struct userNode *invite;
3811 uData = GetChannelUser(channel->channel_info, user->handle_info);
3815 if(!(invite = GetUserH(argv[1])))
3817 reply("MSG_NICK_UNKNOWN", argv[1]);
3824 if(GetUserMode(channel, invite))
3826 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3834 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3835 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3838 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3840 irc_invite(chanserv, invite, channel);
3842 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3847 static CHANSERV_FUNC(cmd_inviteme)
3849 if(GetUserMode(channel, user))
3851 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3854 if(channel->channel_info
3855 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3857 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3860 irc_invite(cmd->parent->bot, user, channel);
3865 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3868 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3870 /* We display things based on two dimensions:
3871 * - Issue time: present or absent
3872 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3873 * (in order of precedence, so something both expired and revoked
3874 * only counts as revoked)
3876 combo = (suspended->issued ? 4 : 0)
3877 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3879 case 0: /* no issue time, indefinite expiration */
3880 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3882 case 1: /* no issue time, expires in future */
3883 intervalString(buf1, suspended->expires-now, user->handle_info);
3884 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3886 case 2: /* no issue time, expired */
3887 intervalString(buf1, now-suspended->expires, user->handle_info);
3888 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3890 case 3: /* no issue time, revoked */
3891 intervalString(buf1, now-suspended->revoked, user->handle_info);
3892 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3894 case 4: /* issue time set, indefinite expiration */
3895 intervalString(buf1, now-suspended->issued, user->handle_info);
3896 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3898 case 5: /* issue time set, expires in future */
3899 intervalString(buf1, now-suspended->issued, user->handle_info);
3900 intervalString(buf2, suspended->expires-now, user->handle_info);
3901 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3903 case 6: /* issue time set, expired */
3904 intervalString(buf1, now-suspended->issued, user->handle_info);
3905 intervalString(buf2, now-suspended->expires, user->handle_info);
3906 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3908 case 7: /* issue time set, revoked */
3909 intervalString(buf1, now-suspended->issued, user->handle_info);
3910 intervalString(buf2, now-suspended->revoked, user->handle_info);
3911 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3914 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3919 static CHANSERV_FUNC(cmd_info)
3921 char modes[MAXLEN], buffer[INTERVALLEN];
3922 struct userData *uData, *owner;
3923 struct chanData *cData;
3924 struct do_not_register *dnr;
3929 cData = channel->channel_info;
3930 reply("CSMSG_CHANNEL_INFO", channel->name);
3932 uData = GetChannelUser(cData, user->handle_info);
3933 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3935 mod_chanmode_format(&cData->modes, modes);
3936 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3937 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3940 for(it = dict_first(cData->notes); it; it = iter_next(it))
3944 note = iter_data(it);
3945 if(!note_type_visible_to_user(cData, note->type, user))
3948 padding = PADLEN - 1 - strlen(iter_key(it));
3949 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3952 reply("CSMSG_CHANNEL_MAX", cData->max);
3953 for(owner = cData->users; owner; owner = owner->next)
3954 if(owner->access == UL_OWNER)
3955 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3956 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3957 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3958 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3960 privileged = IsStaff(user);
3962 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3963 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3964 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3966 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3967 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3969 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3971 struct suspended *suspended;
3972 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3973 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3974 show_suspension_info(cmd, user, suspended);
3976 else if(IsSuspended(cData))
3978 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3979 show_suspension_info(cmd, user, cData->suspended);
3984 static CHANSERV_FUNC(cmd_netinfo)
3986 extern time_t boot_time;
3987 extern unsigned long burst_length;
3988 char interval[INTERVALLEN];
3990 reply("CSMSG_NETWORK_INFO");
3991 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3992 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3993 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3994 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3995 reply("CSMSG_NETWORK_BANS", banCount);
3996 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3997 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3998 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4003 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4005 struct helpfile_table table;
4007 struct userNode *user;
4012 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4013 table.contents = alloca(list->used*sizeof(*table.contents));
4014 for(nn=0; nn<list->used; nn++)
4016 user = list->list[nn];
4017 if(user->modes & skip_flags)
4021 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4024 nick = alloca(strlen(user->nick)+3);
4025 sprintf(nick, "(%s)", user->nick);
4029 table.contents[table.length][0] = nick;
4032 table_send(chanserv, to->nick, 0, NULL, table);
4035 static CHANSERV_FUNC(cmd_ircops)
4037 reply("CSMSG_STAFF_OPERS");
4038 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4042 static CHANSERV_FUNC(cmd_helpers)
4044 reply("CSMSG_STAFF_HELPERS");
4045 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4049 static CHANSERV_FUNC(cmd_staff)
4051 reply("CSMSG_NETWORK_STAFF");
4052 cmd_ircops(CSFUNC_ARGS);
4053 cmd_helpers(CSFUNC_ARGS);
4057 static CHANSERV_FUNC(cmd_peek)
4059 struct modeNode *mn;
4060 char modes[MODELEN];
4062 struct helpfile_table table;
4064 irc_make_chanmode(channel, modes);
4066 reply("CSMSG_PEEK_INFO", channel->name);
4067 reply("CSMSG_PEEK_TOPIC", channel->topic);
4068 reply("CSMSG_PEEK_MODES", modes);
4069 reply("CSMSG_PEEK_USERS", channel->members.used);
4073 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4074 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4075 for(n = 0; n < channel->members.used; n++)
4077 mn = channel->members.list[n];
4078 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4080 table.contents[table.length] = alloca(sizeof(**table.contents));
4081 table.contents[table.length][0] = mn->user->nick;
4086 reply("CSMSG_PEEK_OPS");
4087 table_send(chanserv, user->nick, 0, NULL, table);
4090 reply("CSMSG_PEEK_NO_OPS");
4094 static MODCMD_FUNC(cmd_wipeinfo)
4096 struct handle_info *victim;
4097 struct userData *ud, *actor;
4100 actor = GetChannelUser(channel->channel_info, user->handle_info);
4101 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4103 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4105 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4108 if((ud->access >= actor->access) && (ud != actor))
4110 reply("MSG_USER_OUTRANKED", victim->handle);
4116 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4120 static CHANSERV_FUNC(cmd_resync)
4122 struct mod_chanmode *changes;
4123 struct chanData *cData = channel->channel_info;
4124 unsigned int ii, used;
4126 changes = mod_chanmode_alloc(channel->members.used * 2);
4127 for(ii = used = 0; ii < channel->members.used; ++ii)
4129 struct modeNode *mn = channel->members.list[ii];
4130 struct userData *uData;
4132 if(IsService(mn->user))
4135 uData = GetChannelAccess(cData, mn->user->handle_info);
4136 if(!cData->lvlOpts[lvlGiveOps]
4137 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4139 if(!(mn->modes & MODE_CHANOP))
4141 changes->args[used].mode = MODE_CHANOP;
4142 changes->args[used++].u.member = mn;
4145 else if(!cData->lvlOpts[lvlGiveVoice]
4146 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4148 if(mn->modes & MODE_CHANOP)
4150 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4151 changes->args[used++].u.member = mn;
4153 if(!(mn->modes & MODE_VOICE))
4155 changes->args[used].mode = MODE_VOICE;
4156 changes->args[used++].u.member = mn;
4163 changes->args[used].mode = MODE_REMOVE | mn->modes;
4164 changes->args[used++].u.member = mn;
4168 changes->argc = used;
4169 modcmd_chanmode_announce(changes);
4170 mod_chanmode_free(changes);
4171 reply("CSMSG_RESYNCED_USERS", channel->name);
4175 static CHANSERV_FUNC(cmd_seen)
4177 struct userData *uData;
4178 struct handle_info *handle;
4179 char seen[INTERVALLEN];
4183 if(!irccasecmp(argv[1], chanserv->nick))
4185 reply("CSMSG_IS_CHANSERV");
4189 if(!(handle = get_handle_info(argv[1])))
4191 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4195 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4197 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4202 reply("CSMSG_USER_PRESENT", handle->handle);
4203 else if(uData->seen)
4204 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4206 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4208 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4209 reply("CSMSG_USER_VACATION", handle->handle);
4214 static MODCMD_FUNC(cmd_names)
4216 struct userNode *targ;
4217 struct userData *targData;
4218 unsigned int ii, pos;
4221 for(ii=pos=0; ii<channel->members.used; ++ii)
4223 targ = channel->members.list[ii]->user;
4224 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4227 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4230 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4234 if(IsUserSuspended(targData))
4236 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4239 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4240 reply("CSMSG_END_NAMES", channel->name);
4245 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4247 switch(ntype->visible_type)
4249 case NOTE_VIS_ALL: return 1;
4250 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4251 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4256 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4258 struct userData *uData;
4260 switch(ntype->set_access_type)
4262 case NOTE_SET_CHANNEL_ACCESS:
4263 if(!user->handle_info)
4265 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4267 return uData->access >= ntype->set_access.min_ulevel;
4268 case NOTE_SET_CHANNEL_SETTER:
4269 return check_user_level(channel, user, lvlSetters, 1, 0);
4270 case NOTE_SET_PRIVILEGED: default:
4271 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4275 static CHANSERV_FUNC(cmd_note)
4277 struct chanData *cData;
4279 struct note_type *ntype;
4281 cData = channel->channel_info;
4284 reply("CSMSG_NOT_REGISTERED", channel->name);
4288 /* If no arguments, show all visible notes for the channel. */
4294 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4296 note = iter_data(it);
4297 if(!note_type_visible_to_user(cData, note->type, user))
4300 reply("CSMSG_NOTELIST_HEADER", channel->name);
4301 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4304 reply("CSMSG_NOTELIST_END", channel->name);
4306 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4308 /* If one argument, show the named note. */
4311 if((note = dict_find(cData->notes, argv[1], NULL))
4312 && note_type_visible_to_user(cData, note->type, user))
4314 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4316 else if((ntype = dict_find(note_types, argv[1], NULL))
4317 && note_type_visible_to_user(NULL, ntype, user))
4319 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4324 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4328 /* Assume they're trying to set a note. */
4332 ntype = dict_find(note_types, argv[1], NULL);
4335 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4338 else if(note_type_settable_by_user(channel, ntype, user))
4340 note_text = unsplit_string(argv+2, argc-2, NULL);
4341 if((note = dict_find(cData->notes, argv[1], NULL)))
4342 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4343 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4344 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4346 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4348 /* The note is viewable to staff only, so return 0
4349 to keep the invocation from getting logged (or
4350 regular users can see it in !events). */
4356 reply("CSMSG_NO_ACCESS");
4363 static CHANSERV_FUNC(cmd_delnote)
4368 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4369 || !note_type_settable_by_user(channel, note->type, user))
4371 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4374 dict_remove(channel->channel_info->notes, note->type->name);
4375 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4379 static CHANSERV_FUNC(cmd_events)
4381 struct logSearch discrim;
4382 struct logReport report;
4383 unsigned int matches, limit;
4385 limit = (argc > 1) ? atoi(argv[1]) : 10;
4386 if(limit < 1 || limit > 200)
4389 memset(&discrim, 0, sizeof(discrim));
4390 discrim.masks.bot = chanserv;
4391 discrim.masks.channel_name = channel->name;
4393 discrim.masks.command = argv[2];
4394 discrim.limit = limit;
4395 discrim.max_time = INT_MAX;
4396 discrim.severities = 1 << LOG_COMMAND;
4397 report.reporter = chanserv;
4399 reply("CSMSG_EVENT_SEARCH_RESULTS");
4400 matches = log_entry_search(&discrim, log_report_entry, &report);
4402 reply("MSG_MATCH_COUNT", matches);
4404 reply("MSG_NO_MATCHES");
4408 static CHANSERV_FUNC(cmd_say)
4414 msg = unsplit_string(argv + 1, argc - 1, NULL);
4415 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4417 else if(GetUserH(argv[1]))
4420 msg = unsplit_string(argv + 2, argc - 2, NULL);
4421 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4425 reply("MSG_NOT_TARGET_NAME");
4431 static CHANSERV_FUNC(cmd_emote)
4437 /* CTCP is so annoying. */
4438 msg = unsplit_string(argv + 1, argc - 1, NULL);
4439 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4441 else if(GetUserH(argv[1]))
4443 msg = unsplit_string(argv + 2, argc - 2, NULL);
4444 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4448 reply("MSG_NOT_TARGET_NAME");
4454 struct channelList *
4455 chanserv_support_channels(void)
4457 return &chanserv_conf.support_channels;
4460 static CHANSERV_FUNC(cmd_expire)
4462 int channel_count = registered_channels;
4463 expire_channels(NULL);
4464 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4469 chanserv_expire_suspension(void *data)
4471 struct suspended *suspended = data;
4472 struct chanNode *channel;
4474 if(!suspended->expires || (now < suspended->expires))
4475 suspended->revoked = now;
4476 channel = suspended->cData->channel;
4477 suspended->cData->channel = channel;
4478 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4479 if(!IsOffChannel(suspended->cData))
4481 struct mod_chanmode change;
4482 mod_chanmode_init(&change);
4484 change.args[0].mode = MODE_CHANOP;
4485 change.args[0].u.member = AddChannelUser(chanserv, channel);
4486 mod_chanmode_announce(chanserv, channel, &change);
4490 static CHANSERV_FUNC(cmd_csuspend)
4492 struct suspended *suspended;
4493 char reason[MAXLEN];
4494 time_t expiry, duration;
4495 struct userData *uData;
4499 if(IsProtected(channel->channel_info))
4501 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4505 if(argv[1][0] == '!')
4507 else if(IsSuspended(channel->channel_info))
4509 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4510 show_suspension_info(cmd, user, channel->channel_info->suspended);
4514 if(!strcmp(argv[1], "0"))
4516 else if((duration = ParseInterval(argv[1])))
4517 expiry = now + duration;
4520 reply("MSG_INVALID_DURATION", argv[1]);
4524 unsplit_string(argv + 2, argc - 2, reason);
4526 suspended = calloc(1, sizeof(*suspended));
4527 suspended->revoked = 0;
4528 suspended->issued = now;
4529 suspended->suspender = strdup(user->handle_info->handle);
4530 suspended->expires = expiry;
4531 suspended->reason = strdup(reason);
4532 suspended->cData = channel->channel_info;
4533 suspended->previous = suspended->cData->suspended;
4534 suspended->cData->suspended = suspended;
4536 if(suspended->expires)
4537 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4539 if(IsSuspended(channel->channel_info))
4541 suspended->previous->revoked = now;
4542 if(suspended->previous->expires)
4543 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4544 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4545 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4549 /* Mark all users in channel as absent. */
4550 for(uData = channel->channel_info->users; uData; uData = uData->next)
4559 /* Mark the channel as suspended, then part. */
4560 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4561 DelChannelUser(chanserv, channel, suspended->reason, 0);
4562 reply("CSMSG_SUSPENDED", channel->name);
4563 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4564 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4569 static CHANSERV_FUNC(cmd_cunsuspend)
4571 struct suspended *suspended;
4572 char message[MAXLEN];
4574 if(!IsSuspended(channel->channel_info))
4576 reply("CSMSG_NOT_SUSPENDED", channel->name);
4580 suspended = channel->channel_info->suspended;
4582 /* Expire the suspension and join ChanServ to the channel. */
4583 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4584 chanserv_expire_suspension(suspended);
4585 reply("CSMSG_UNSUSPENDED", channel->name);
4586 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4587 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4591 typedef struct chanservSearch
4599 unsigned long flags;
4603 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4606 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4611 search = malloc(sizeof(struct chanservSearch));
4612 memset(search, 0, sizeof(*search));
4615 for(i = 0; i < argc; i++)
4617 /* Assume all criteria require arguments. */
4620 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4624 if(!irccasecmp(argv[i], "name"))
4625 search->name = argv[++i];
4626 else if(!irccasecmp(argv[i], "registrar"))
4627 search->registrar = argv[++i];
4628 else if(!irccasecmp(argv[i], "unvisited"))
4629 search->unvisited = ParseInterval(argv[++i]);
4630 else if(!irccasecmp(argv[i], "registered"))
4631 search->registered = ParseInterval(argv[++i]);
4632 else if(!irccasecmp(argv[i], "flags"))
4635 if(!irccasecmp(argv[i], "nodelete"))
4636 search->flags |= CHANNEL_NODELETE;
4637 else if(!irccasecmp(argv[i], "suspended"))
4638 search->flags |= CHANNEL_SUSPENDED;
4641 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4645 else if(!irccasecmp(argv[i], "limit"))
4646 search->limit = strtoul(argv[++i], NULL, 10);
4649 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4654 if(search->name && !strcmp(search->name, "*"))
4656 if(search->registrar && !strcmp(search->registrar, "*"))
4657 search->registrar = 0;
4666 chanserv_channel_match(struct chanData *channel, search_t search)
4668 const char *name = channel->channel->name;
4669 if((search->name && !match_ircglob(name, search->name)) ||
4670 (search->registrar && !channel->registrar) ||
4671 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4672 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4673 (search->registered && (now - channel->registered) > search->registered) ||
4674 (search->flags && ((search->flags & channel->flags) != search->flags)))
4681 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4683 struct chanData *channel;
4684 unsigned int matches = 0;
4686 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4688 if(!chanserv_channel_match(channel, search))
4698 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4703 search_print(struct chanData *channel, void *data)
4705 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4708 static CHANSERV_FUNC(cmd_search)
4711 unsigned int matches;
4712 channel_search_func action;
4716 if(!irccasecmp(argv[1], "count"))
4717 action = search_count;
4718 else if(!irccasecmp(argv[1], "print"))
4719 action = search_print;
4722 reply("CSMSG_ACTION_INVALID", argv[1]);
4726 search = chanserv_search_create(user, argc - 2, argv + 2);
4730 if(action == search_count)
4731 search->limit = INT_MAX;
4733 if(action == search_print)
4734 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4736 matches = chanserv_channel_search(search, action, user);
4739 reply("MSG_MATCH_COUNT", matches);
4741 reply("MSG_NO_MATCHES");
4747 static CHANSERV_FUNC(cmd_unvisited)
4749 struct chanData *cData;
4750 time_t interval = chanserv_conf.channel_expire_delay;
4751 char buffer[INTERVALLEN];
4752 unsigned int limit = 25, matches = 0;
4756 interval = ParseInterval(argv[1]);
4758 limit = atoi(argv[2]);
4761 intervalString(buffer, interval, user->handle_info);
4762 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4764 for(cData = channelList; cData && matches < limit; cData = cData->next)
4766 if((now - cData->visited) < interval)
4769 intervalString(buffer, now - cData->visited, user->handle_info);
4770 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4777 static MODCMD_FUNC(chan_opt_defaulttopic)
4783 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4785 reply("CSMSG_TOPIC_LOCKED", channel->name);
4789 topic = unsplit_string(argv+1, argc-1, NULL);
4791 free(channel->channel_info->topic);
4792 if(topic[0] == '*' && topic[1] == 0)
4794 topic = channel->channel_info->topic = NULL;
4798 topic = channel->channel_info->topic = strdup(topic);
4799 if(channel->channel_info->topic_mask
4800 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4801 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4803 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4806 if(channel->channel_info->topic)
4807 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4809 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4813 static MODCMD_FUNC(chan_opt_topicmask)
4817 struct chanData *cData = channel->channel_info;
4820 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4822 reply("CSMSG_TOPIC_LOCKED", channel->name);
4826 mask = unsplit_string(argv+1, argc-1, NULL);
4828 if(cData->topic_mask)
4829 free(cData->topic_mask);
4830 if(mask[0] == '*' && mask[1] == 0)
4832 cData->topic_mask = 0;
4836 cData->topic_mask = strdup(mask);
4838 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4839 else if(!match_ircglob(cData->topic, cData->topic_mask))
4840 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4844 if(channel->channel_info->topic_mask)
4845 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4847 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4851 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4855 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4859 if(greeting[0] == '*' && greeting[1] == 0)
4863 unsigned int length = strlen(greeting);
4864 if(length > chanserv_conf.greeting_length)
4866 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4869 *data = strdup(greeting);
4878 reply(name, user_find_message(user, "MSG_NONE"));
4882 static MODCMD_FUNC(chan_opt_greeting)
4884 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4887 static MODCMD_FUNC(chan_opt_usergreeting)
4889 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4892 static MODCMD_FUNC(chan_opt_modes)
4894 struct mod_chanmode *new_modes;
4895 char modes[MODELEN];
4899 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4901 reply("CSMSG_NO_ACCESS");
4904 if(argv[1][0] == '*' && argv[1][1] == 0)
4906 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4908 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4910 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4913 else if(new_modes->argc > 1)
4915 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4916 mod_chanmode_free(new_modes);
4921 channel->channel_info->modes = *new_modes;
4922 modcmd_chanmode_announce(new_modes);
4923 mod_chanmode_free(new_modes);
4927 mod_chanmode_format(&channel->channel_info->modes, modes);
4929 reply("CSMSG_SET_MODES", modes);
4931 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4935 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4937 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4939 struct chanData *cData = channel->channel_info;
4944 /* Set flag according to value. */
4945 if(enabled_string(argv[1]))
4947 cData->flags |= mask;
4950 else if(disabled_string(argv[1]))
4952 cData->flags &= ~mask;
4957 reply("MSG_INVALID_BINARY", argv[1]);
4963 /* Find current option value. */
4964 value = (cData->flags & mask) ? 1 : 0;
4968 reply(name, user_find_message(user, "MSG_ON"));
4970 reply(name, user_find_message(user, "MSG_OFF"));
4974 static MODCMD_FUNC(chan_opt_nodelete)
4976 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4978 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4982 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4985 static MODCMD_FUNC(chan_opt_dynlimit)
4987 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4990 static MODCMD_FUNC(chan_opt_offchannel)
4992 struct chanData *cData = channel->channel_info;
4997 /* Set flag according to value. */
4998 if(enabled_string(argv[1]))
5000 if(!IsOffChannel(cData))
5001 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5002 cData->flags |= CHANNEL_OFFCHANNEL;
5005 else if(disabled_string(argv[1]))
5007 if(IsOffChannel(cData))
5009 struct mod_chanmode change;
5010 mod_chanmode_init(&change);
5012 change.args[0].mode = MODE_CHANOP;
5013 change.args[0].u.member = AddChannelUser(chanserv, channel);
5014 mod_chanmode_announce(chanserv, channel, &change);
5016 cData->flags &= ~CHANNEL_OFFCHANNEL;
5021 reply("MSG_INVALID_BINARY", argv[1]);
5027 /* Find current option value. */
5028 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5032 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5034 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5038 static MODCMD_FUNC(chan_opt_defaults)
5040 struct userData *uData;
5041 struct chanData *cData;
5042 const char *confirm;
5043 enum levelOption lvlOpt;
5044 enum charOption chOpt;
5046 cData = channel->channel_info;
5047 uData = GetChannelUser(cData, user->handle_info);
5048 if(!uData || (uData->access < UL_OWNER))
5050 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5053 confirm = make_confirmation_string(uData);
5054 if((argc < 2) || strcmp(argv[1], confirm))
5056 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5059 cData->flags = CHANNEL_DEFAULT_FLAGS;
5060 cData->modes = chanserv_conf.default_modes;
5061 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5062 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5063 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5064 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5065 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5070 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5072 struct chanData *cData = channel->channel_info;
5073 struct userData *uData;
5074 unsigned short value;
5078 if(!check_user_level(channel, user, option, 1, 1))
5080 reply("CSMSG_CANNOT_SET");
5083 value = user_level_from_name(argv[1], UL_OWNER+1);
5084 if(!value && strcmp(argv[1], "0"))
5086 reply("CSMSG_INVALID_ACCESS", argv[1]);
5089 uData = GetChannelUser(cData, user->handle_info);
5090 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5092 reply("CSMSG_BAD_SETLEVEL");
5098 if(value > cData->lvlOpts[lvlGiveOps])
5100 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5105 if(value < cData->lvlOpts[lvlGiveVoice])
5107 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5112 /* This test only applies to owners, since non-owners
5113 * trying to set an option to above their level get caught
5114 * by the CSMSG_BAD_SETLEVEL test above.
5116 if(value > uData->access)
5118 reply("CSMSG_BAD_SETTERS");
5125 cData->lvlOpts[option] = value;
5127 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5131 static MODCMD_FUNC(chan_opt_enfops)
5133 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5136 static MODCMD_FUNC(chan_opt_giveops)
5138 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5141 static MODCMD_FUNC(chan_opt_enfmodes)
5143 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5146 static MODCMD_FUNC(chan_opt_enftopic)
5148 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5151 static MODCMD_FUNC(chan_opt_pubcmd)
5153 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5156 static MODCMD_FUNC(chan_opt_setters)
5158 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5161 static MODCMD_FUNC(chan_opt_ctcpusers)
5163 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5166 static MODCMD_FUNC(chan_opt_userinfo)
5168 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5171 static MODCMD_FUNC(chan_opt_givevoice)
5173 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5176 static MODCMD_FUNC(chan_opt_topicsnarf)
5178 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5181 static MODCMD_FUNC(chan_opt_inviteme)
5183 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5187 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5189 struct chanData *cData = channel->channel_info;
5190 int count = charOptions[option].count, index;
5194 index = atoi(argv[1]);
5196 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5198 reply("CSMSG_INVALID_NUMERIC", index);
5199 /* Show possible values. */
5200 for(index = 0; index < count; index++)
5201 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5205 cData->chOpts[option] = charOptions[option].values[index].value;
5209 /* Find current option value. */
5212 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5216 /* Somehow, the option value is corrupt; reset it to the default. */
5217 cData->chOpts[option] = charOptions[option].default_value;
5222 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5226 static MODCMD_FUNC(chan_opt_protect)
5228 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5231 static MODCMD_FUNC(chan_opt_toys)
5233 return channel_multiple_option(chToys, CSFUNC_ARGS);
5236 static MODCMD_FUNC(chan_opt_ctcpreaction)
5238 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5241 static MODCMD_FUNC(chan_opt_topicrefresh)
5243 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5246 static struct svccmd_list set_shows_list;
5249 handle_svccmd_unbind(struct svccmd *target) {
5251 for(ii=0; ii<set_shows_list.used; ++ii)
5252 if(target == set_shows_list.list[ii])
5253 set_shows_list.used = 0;
5256 static CHANSERV_FUNC(cmd_set)
5258 struct svccmd *subcmd;
5262 /* Check if we need to (re-)initialize set_shows_list. */
5263 if(!set_shows_list.used)
5265 if(!set_shows_list.size)
5267 set_shows_list.size = chanserv_conf.set_shows->used;
5268 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5270 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5272 const char *name = chanserv_conf.set_shows->list[ii];
5273 sprintf(buf, "%s %s", argv[0], name);
5274 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5277 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5280 svccmd_list_append(&set_shows_list, subcmd);
5286 reply("CSMSG_CHANNEL_OPTIONS");
5287 for(ii = 0; ii < set_shows_list.used; ii++)
5289 subcmd = set_shows_list.list[ii];
5290 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5295 sprintf(buf, "%s %s", argv[0], argv[1]);
5296 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5299 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5302 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5304 reply("CSMSG_NO_ACCESS");
5308 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5312 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5314 struct userData *uData;
5316 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5319 reply("CSMSG_NOT_USER", channel->name);
5325 /* Just show current option value. */
5327 else if(enabled_string(argv[1]))
5329 uData->flags |= mask;
5331 else if(disabled_string(argv[1]))
5333 uData->flags &= ~mask;
5337 reply("MSG_INVALID_BINARY", argv[1]);
5341 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5345 static MODCMD_FUNC(user_opt_noautoop)
5347 struct userData *uData;
5349 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5352 reply("CSMSG_NOT_USER", channel->name);
5355 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5356 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5358 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5361 static MODCMD_FUNC(user_opt_autoinvite)
5363 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5366 static MODCMD_FUNC(user_opt_info)
5368 struct userData *uData;
5371 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5375 /* If they got past the command restrictions (which require access)
5376 * but fail this test, we have some fool with security override on.
5378 reply("CSMSG_NOT_USER", channel->name);
5385 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5386 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5388 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5391 bp = strcspn(infoline, "\001");
5394 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5399 if(infoline[0] == '*' && infoline[1] == 0)
5402 uData->info = strdup(infoline);
5405 reply("CSMSG_USET_INFO", uData->info);
5407 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5411 struct svccmd_list uset_shows_list;
5413 static CHANSERV_FUNC(cmd_uset)
5415 struct svccmd *subcmd;
5419 /* Check if we need to (re-)initialize uset_shows_list. */
5420 if(!uset_shows_list.used)
5424 "NoAutoOp", "AutoInvite", "Info"
5427 if(!uset_shows_list.size)
5429 uset_shows_list.size = ArrayLength(options);
5430 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5432 for(ii = 0; ii < ArrayLength(options); ii++)
5434 const char *name = options[ii];
5435 sprintf(buf, "%s %s", argv[0], name);
5436 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5439 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5442 svccmd_list_append(&uset_shows_list, subcmd);
5448 /* Do this so options are presented in a consistent order. */
5449 reply("CSMSG_USER_OPTIONS");
5450 for(ii = 0; ii < uset_shows_list.used; ii++)
5451 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5455 sprintf(buf, "%s %s", argv[0], argv[1]);
5456 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5459 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5463 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5466 static CHANSERV_FUNC(cmd_giveownership)
5468 struct handle_info *new_owner_hi;
5469 struct userData *new_owner, *curr_user;
5470 struct chanData *cData = channel->channel_info;
5471 struct do_not_register *dnr;
5473 unsigned short co_access;
5474 char reason[MAXLEN];
5477 curr_user = GetChannelAccess(cData, user->handle_info);
5478 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5479 if(!curr_user || (curr_user->access != UL_OWNER))
5481 struct userData *owner = NULL;
5482 for(curr_user = channel->channel_info->users;
5484 curr_user = curr_user->next)
5486 if(curr_user->access != UL_OWNER)
5490 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5497 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5499 char delay[INTERVALLEN];
5500 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5501 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5504 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5506 if(new_owner_hi == user->handle_info)
5508 reply("CSMSG_NO_TRANSFER_SELF");
5511 new_owner = GetChannelAccess(cData, new_owner_hi);
5516 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5520 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5524 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5526 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5529 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5530 if(!IsHelping(user))
5531 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5533 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5536 if(new_owner->access >= UL_COOWNER)
5537 co_access = new_owner->access;
5539 co_access = UL_COOWNER;
5540 new_owner->access = UL_OWNER;
5542 curr_user->access = co_access;
5543 cData->ownerTransfer = now;
5544 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5545 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5546 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5550 static CHANSERV_FUNC(cmd_suspend)
5552 struct handle_info *hi;
5553 struct userData *self, *target;
5556 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5557 self = GetChannelUser(channel->channel_info, user->handle_info);
5558 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5560 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5563 if(target->access >= self->access)
5565 reply("MSG_USER_OUTRANKED", hi->handle);
5568 if(target->flags & USER_SUSPENDED)
5570 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5575 target->present = 0;
5578 target->flags |= USER_SUSPENDED;
5579 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5583 static CHANSERV_FUNC(cmd_unsuspend)
5585 struct handle_info *hi;
5586 struct userData *self, *target;
5589 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5590 self = GetChannelUser(channel->channel_info, user->handle_info);
5591 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5593 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5596 if(target->access >= self->access)
5598 reply("MSG_USER_OUTRANKED", hi->handle);
5601 if(!(target->flags & USER_SUSPENDED))
5603 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5606 target->flags &= ~USER_SUSPENDED;
5607 scan_user_presence(target, NULL);
5608 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5612 static MODCMD_FUNC(cmd_deleteme)
5614 struct handle_info *hi;
5615 struct userData *target;
5616 const char *confirm_string;
5617 unsigned short access;
5620 hi = user->handle_info;
5621 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5623 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5626 if(target->access == UL_OWNER)
5628 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5631 confirm_string = make_confirmation_string(target);
5632 if((argc < 2) || strcmp(argv[1], confirm_string))
5634 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5637 access = target->access;
5638 channel_name = strdup(channel->name);
5639 del_channel_user(target, 1);
5640 reply("CSMSG_DELETED_YOU", access, channel_name);
5646 chanserv_refresh_topics(UNUSED_ARG(void *data))
5648 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5649 struct chanData *cData;
5652 for(cData = channelList; cData; cData = cData->next)
5654 if(IsSuspended(cData))
5656 opt = cData->chOpts[chTopicRefresh];
5659 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5662 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5663 cData->last_refresh = refresh_num;
5665 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5668 static CHANSERV_FUNC(cmd_unf)
5672 char response[MAXLEN];
5673 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5674 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5675 irc_privmsg(cmd->parent->bot, channel->name, response);
5678 reply("CSMSG_UNF_RESPONSE");
5682 static CHANSERV_FUNC(cmd_ping)
5686 char response[MAXLEN];
5687 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5688 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5689 irc_privmsg(cmd->parent->bot, channel->name, response);
5692 reply("CSMSG_PING_RESPONSE");
5696 static CHANSERV_FUNC(cmd_wut)
5700 char response[MAXLEN];
5701 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5702 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5703 irc_privmsg(cmd->parent->bot, channel->name, response);
5706 reply("CSMSG_WUT_RESPONSE");
5710 static CHANSERV_FUNC(cmd_8ball)
5712 unsigned int i, j, accum;
5717 for(i=1; i<argc; i++)
5718 for(j=0; argv[i][j]; j++)
5719 accum = (accum << 5) - accum + toupper(argv[i][j]);
5720 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5723 char response[MAXLEN];
5724 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5725 irc_privmsg(cmd->parent->bot, channel->name, response);
5728 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5732 static CHANSERV_FUNC(cmd_d)
5734 unsigned long sides, count, modifier, ii, total;
5735 char response[MAXLEN], *sep;
5739 if((count = strtoul(argv[1], &sep, 10)) < 1)
5749 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5750 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5754 else if((sep[0] == '-') && isdigit(sep[1]))
5755 modifier = strtoul(sep, NULL, 10);
5756 else if((sep[0] == '+') && isdigit(sep[1]))
5757 modifier = strtoul(sep+1, NULL, 10);
5764 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5769 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5772 for(total = ii = 0; ii < count; ++ii)
5773 total += (rand() % sides) + 1;
5776 if((count > 1) || modifier)
5778 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5779 sprintf(response, fmt, total, count, sides, modifier);
5783 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5784 sprintf(response, fmt, total, sides);
5787 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5789 send_message_type(4, user, cmd->parent->bot, "%s", response);
5793 static CHANSERV_FUNC(cmd_huggle)
5795 /* CTCP must be via PRIVMSG, never notice */
5797 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5799 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5804 chanserv_adjust_limit(void *data)
5806 struct mod_chanmode change;
5807 struct chanData *cData = data;
5808 struct chanNode *channel = cData->channel;
5811 if(IsSuspended(cData))
5814 cData->limitAdjusted = now;
5815 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5816 if(cData->modes.modes_set & MODE_LIMIT)
5818 if(limit > cData->modes.new_limit)
5819 limit = cData->modes.new_limit;
5820 else if(limit == cData->modes.new_limit)
5824 mod_chanmode_init(&change);
5825 change.modes_set = MODE_LIMIT;
5826 change.new_limit = limit;
5827 mod_chanmode_announce(chanserv, channel, &change);
5831 handle_new_channel(struct chanNode *channel)
5833 struct chanData *cData;
5835 if(!(cData = channel->channel_info))
5838 if(cData->modes.modes_set || cData->modes.modes_clear)
5839 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5841 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5842 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5845 /* Welcome to my worst nightmare. Warning: Read (or modify)
5846 the code below at your own risk. */
5848 handle_join(struct modeNode *mNode)
5850 struct mod_chanmode change;
5851 struct userNode *user = mNode->user;
5852 struct chanNode *channel = mNode->channel;
5853 struct chanData *cData;
5854 struct userData *uData = NULL;
5855 struct banData *bData;
5856 struct handle_info *handle;
5857 unsigned int modes = 0, info = 0;
5860 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5863 cData = channel->channel_info;
5864 if(channel->members.used > cData->max)
5865 cData->max = channel->members.used;
5867 /* Check for bans. If they're joining through a ban, one of two
5869 * 1: Join during a netburst, by riding the break. Kick them
5870 * unless they have ops or voice in the channel.
5871 * 2: They're allowed to join through the ban (an invite in
5872 * ircu2.10, or a +e on Hybrid, or something).
5873 * If they're not joining through a ban, and the banlist is not
5874 * full, see if they're on the banlist for the channel. If so,
5877 if(user->uplink->burst && !mNode->modes)
5880 for(ii = 0; ii < channel->banlist.used; ii++)
5882 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5884 /* Riding a netburst. Naughty. */
5885 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5891 mod_chanmode_init(&change);
5893 if(channel->banlist.used < MAXBANS)
5895 /* Not joining through a ban. */
5896 for(bData = cData->bans;
5897 bData && !user_matches_glob(user, bData->mask, 1);
5898 bData = bData->next);
5902 char kick_reason[MAXLEN];
5903 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5905 bData->triggered = now;
5906 if(bData != cData->bans)
5908 /* Shuffle the ban to the head of the list. */
5910 bData->next->prev = bData->prev;
5912 bData->prev->next = bData->next;
5915 bData->next = cData->bans;
5918 cData->bans->prev = bData;
5919 cData->bans = bData;
5922 change.args[0].mode = MODE_BAN;
5923 change.args[0].u.hostmask = bData->mask;
5924 mod_chanmode_announce(chanserv, channel, &change);
5925 KickChannelUser(user, channel, chanserv, kick_reason);
5930 /* ChanServ will not modify the limits in join-flooded channels.
5931 It will also skip DynLimit processing when the user (or srvx)
5932 is bursting in, because there are likely more incoming. */
5933 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5934 && !user->uplink->burst
5935 && !channel->join_flooded
5936 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5938 /* The user count has begun "bumping" into the channel limit,
5939 so set a timer to raise the limit a bit. Any previous
5940 timers are removed so three incoming users within the delay
5941 results in one limit change, not three. */
5943 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5944 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5947 if(channel->join_flooded)
5949 /* don't automatically give ops or voice during a join flood */
5951 else if(cData->lvlOpts[lvlGiveOps] == 0)
5952 modes |= MODE_CHANOP;
5953 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5954 modes |= MODE_VOICE;
5956 greeting = cData->greeting;
5957 if(user->handle_info)
5959 handle = user->handle_info;
5961 if(IsHelper(user) && !IsHelping(user))
5964 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5966 if(channel == chanserv_conf.support_channels.list[ii])
5968 HANDLE_SET_FLAG(user->handle_info, HELPING);
5974 uData = GetTrueChannelAccess(cData, handle);
5975 if(uData && !IsUserSuspended(uData))
5977 /* Ops and above were handled by the above case. */
5978 if(IsUserAutoOp(uData))
5980 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5981 modes |= MODE_CHANOP;
5982 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5983 modes |= MODE_VOICE;
5985 if(uData->access >= UL_PRESENT)
5986 cData->visited = now;
5987 if(cData->user_greeting)
5988 greeting = cData->user_greeting;
5990 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5991 && ((now - uData->seen) >= chanserv_conf.info_delay)
5998 if(!user->uplink->burst)
6002 if(modes & MODE_CHANOP)
6003 modes &= ~MODE_VOICE;
6004 change.args[0].mode = modes;
6005 change.args[0].u.member = mNode;
6006 mod_chanmode_announce(chanserv, channel, &change);
6008 if(greeting && !user->uplink->burst)
6009 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6011 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6017 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6019 struct mod_chanmode change;
6020 struct userData *channel;
6021 unsigned int ii, jj;
6023 if(!user->handle_info)
6026 mod_chanmode_init(&change);
6028 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6030 struct chanNode *cn;
6031 struct modeNode *mn;
6032 if(IsUserSuspended(channel)
6033 || IsSuspended(channel->channel)
6034 || !(cn = channel->channel->channel))
6037 mn = GetUserMode(cn, user);
6040 if(!IsUserSuspended(channel)
6041 && IsUserAutoInvite(channel)
6042 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6044 && !user->uplink->burst)
6045 irc_invite(chanserv, user, cn);
6049 if(channel->access >= UL_PRESENT)
6050 channel->channel->visited = now;
6052 if(IsUserAutoOp(channel))
6054 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6055 change.args[0].mode = MODE_CHANOP;
6056 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6057 change.args[0].mode = MODE_VOICE;
6059 change.args[0].mode = 0;
6060 change.args[0].u.member = mn;
6061 if(change.args[0].mode)
6062 mod_chanmode_announce(chanserv, cn, &change);
6065 channel->seen = now;
6066 channel->present = 1;
6069 for(ii = 0; ii < user->channels.used; ++ii)
6071 struct chanNode *channel = user->channels.list[ii]->channel;
6072 struct banData *ban;
6074 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6075 || !channel->channel_info
6076 || IsSuspended(channel->channel_info))
6078 for(jj = 0; jj < channel->banlist.used; ++jj)
6079 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6081 if(jj < channel->banlist.used)
6083 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6085 char kick_reason[MAXLEN];
6086 if(!user_matches_glob(user, ban->mask, 1))
6088 change.args[0].mode = MODE_BAN;
6089 change.args[0].u.hostmask = ban->mask;
6090 mod_chanmode_announce(chanserv, channel, &change);
6091 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6092 KickChannelUser(user, channel, chanserv, kick_reason);
6093 ban->triggered = now;
6098 if(IsSupportHelper(user))
6100 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6102 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6104 HANDLE_SET_FLAG(user->handle_info, HELPING);
6112 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6114 struct chanData *cData;
6115 struct userData *uData;
6117 cData = mn->channel->channel_info;
6118 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6121 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6123 /* Allow for a bit of padding so that the limit doesn't
6124 track the user count exactly, which could get annoying. */
6125 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6127 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6128 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6132 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6134 scan_user_presence(uData, mn->user);
6138 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6140 unsigned int ii, jj;
6141 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6143 for(jj = 0; jj < mn->user->channels.used; ++jj)
6144 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6146 if(jj < mn->user->channels.used)
6149 if(ii == chanserv_conf.support_channels.used)
6150 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6155 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6157 struct userData *uData;
6159 if(!channel->channel_info || !kicker || IsService(kicker)
6160 || (kicker == victim) || IsSuspended(channel->channel_info)
6161 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6164 if(protect_user(victim, kicker, channel->channel_info))
6166 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6167 KickChannelUser(kicker, channel, chanserv, reason);
6170 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6175 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6177 struct chanData *cData;
6179 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6182 cData = channel->channel_info;
6183 if(bad_topic(channel, user, channel->topic))
6185 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6186 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6187 SetChannelTopic(channel, chanserv, old_topic, 1);
6188 else if(cData->topic)
6189 SetChannelTopic(channel, chanserv, cData->topic, 1);
6192 /* With topicsnarf, grab the topic and save it as the default topic. */
6193 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6196 cData->topic = strdup(channel->topic);
6202 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6204 struct mod_chanmode *bounce = NULL;
6205 unsigned int bnc, ii;
6208 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6211 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6212 && mode_lock_violated(&channel->channel_info->modes, change))
6214 char correct[MAXLEN];
6215 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6216 mod_chanmode_format(&channel->channel_info->modes, correct);
6217 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6219 for(ii = bnc = 0; ii < change->argc; ++ii)
6221 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6223 const struct userNode *victim = change->args[ii].u.member->user;
6224 if(!protect_user(victim, user, channel->channel_info))
6227 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6230 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6231 bounce->args[bnc].u.member = GetUserMode(channel, user);
6232 if(bounce->args[bnc].u.member)
6236 bounce->args[bnc].mode = MODE_CHANOP;
6237 bounce->args[bnc].u.member = change->args[ii].u.member;
6239 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6241 else if(change->args[ii].mode & MODE_CHANOP)
6243 const struct userNode *victim = change->args[ii].u.member->user;
6244 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6247 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6248 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6249 bounce->args[bnc].u.member = change->args[ii].u.member;
6252 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6254 const char *ban = change->args[ii].u.hostmask;
6255 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6258 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6259 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6260 bounce->args[bnc].u.hostmask = strdup(ban);
6262 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6267 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6268 mod_chanmode_announce(chanserv, channel, bounce);
6269 for(ii = 0; ii < change->argc; ++ii)
6270 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6271 free((char*)bounce->args[ii].u.hostmask);
6272 mod_chanmode_free(bounce);
6277 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6279 struct chanNode *channel;
6280 struct banData *bData;
6281 struct mod_chanmode change;
6282 unsigned int ii, jj;
6283 char kick_reason[MAXLEN];
6285 mod_chanmode_init(&change);
6287 change.args[0].mode = MODE_BAN;
6288 for(ii = 0; ii < user->channels.used; ++ii)
6290 channel = user->channels.list[ii]->channel;
6291 /* Need not check for bans if they're opped or voiced. */
6292 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6294 /* Need not check for bans unless channel registration is active. */
6295 if(!channel->channel_info || IsSuspended(channel->channel_info))
6297 /* Look for a matching ban already on the channel. */
6298 for(jj = 0; jj < channel->banlist.used; ++jj)
6299 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6301 /* Need not act if we found one. */
6302 if(jj < channel->banlist.used)
6304 /* Look for a matching ban in this channel. */
6305 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6307 if(!user_matches_glob(user, bData->mask, 1))
6309 change.args[0].u.hostmask = bData->mask;
6310 mod_chanmode_announce(chanserv, channel, &change);
6311 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6312 KickChannelUser(user, channel, chanserv, kick_reason);
6313 bData->triggered = now;
6314 break; /* we don't need to check any more bans in the channel */
6319 static void handle_rename(struct handle_info *handle, const char *old_handle)
6321 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6325 dict_remove2(handle_dnrs, old_handle, 1);
6326 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6327 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6332 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6334 struct userNode *h_user;
6336 if(handle->channels)
6338 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6339 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6341 while(handle->channels)
6342 del_channel_user(handle->channels, 1);
6347 handle_server_link(UNUSED_ARG(struct server *server))
6349 struct chanData *cData;
6351 for(cData = channelList; cData; cData = cData->next)
6353 if(!IsSuspended(cData))
6354 cData->may_opchan = 1;
6355 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6356 && !cData->channel->join_flooded
6357 && ((cData->channel->limit - cData->channel->members.used)
6358 < chanserv_conf.adjust_threshold))
6360 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6361 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6367 chanserv_conf_read(void)
6371 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6372 struct mod_chanmode *change;
6373 struct string_list *strlist;
6374 struct chanNode *chan;
6377 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6379 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6382 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6383 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6384 chanserv_conf.support_channels.used = 0;
6385 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6387 for(ii = 0; ii < strlist->used; ++ii)
6389 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6392 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6394 channelList_append(&chanserv_conf.support_channels, chan);
6397 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6400 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6403 chan = AddChannel(str, now, str2, NULL);
6405 channelList_append(&chanserv_conf.support_channels, chan);
6407 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6408 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6409 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6410 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6411 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6412 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6413 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6414 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6415 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6416 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6417 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6418 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6419 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6420 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6421 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6422 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6423 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6424 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6425 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6426 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6427 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6428 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6429 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6431 NickChange(chanserv, str, 0);
6432 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6433 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6434 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6435 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6436 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6437 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6438 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6439 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6440 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6441 chanserv_conf.max_owned = str ? atoi(str) : 5;
6442 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6443 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6444 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6445 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6446 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6447 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6448 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6451 safestrncpy(mode_line, str, sizeof(mode_line));
6452 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6453 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6455 chanserv_conf.default_modes = *change;
6456 mod_chanmode_free(change);
6458 free_string_list(chanserv_conf.set_shows);
6459 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6461 strlist = string_list_copy(strlist);
6464 static const char *list[] = {
6465 /* free form text */
6466 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6467 /* options based on user level */
6468 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6469 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6470 /* multiple choice options */
6471 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6472 /* binary options */
6473 "DynLimit", "NoDelete",
6478 strlist = alloc_string_list(ArrayLength(list)-1);
6479 for(ii=0; list[ii]; ii++)
6480 string_list_append(strlist, strdup(list[ii]));
6482 chanserv_conf.set_shows = strlist;
6483 /* We don't look things up now, in case the list refers to options
6484 * defined by modules initialized after this point. Just mark the
6485 * function list as invalid, so it will be initialized.
6487 set_shows_list.used = 0;
6488 free_string_list(chanserv_conf.eightball);
6489 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6492 strlist = string_list_copy(strlist);
6496 strlist = alloc_string_list(4);
6497 string_list_append(strlist, strdup("Yes."));
6498 string_list_append(strlist, strdup("No."));
6499 string_list_append(strlist, strdup("Maybe so."));
6501 chanserv_conf.eightball = strlist;
6502 free_string_list(chanserv_conf.old_ban_names);
6503 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6505 strlist = string_list_copy(strlist);
6507 strlist = alloc_string_list(2);
6508 chanserv_conf.old_ban_names = strlist;
6509 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6510 off_channel = str ? atoi(str) : 0;
6514 chanserv_note_type_read(const char *key, struct record_data *rd)
6517 struct note_type *ntype;
6520 if(!(obj = GET_RECORD_OBJECT(rd)))
6522 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6525 if(!(ntype = chanserv_create_note_type(key)))
6527 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6531 /* Figure out set access */
6532 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6534 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6535 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6537 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6539 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6540 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6542 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6544 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6548 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6549 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6550 ntype->set_access.min_opserv = 0;
6553 /* Figure out visibility */
6554 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6555 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6556 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6557 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6558 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6559 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6560 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6561 ntype->visible_type = NOTE_VIS_ALL;
6563 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6565 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6566 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6570 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6572 struct handle_info *handle;
6573 struct userData *uData;
6574 char *seen, *inf, *flags;
6576 unsigned short access;
6578 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6580 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6584 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6585 if(access > UL_OWNER)
6587 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6591 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6592 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6593 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6594 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6595 handle = get_handle_info(key);
6598 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6602 uData = add_channel_user(chan, handle, access, last_seen, inf);
6603 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6607 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6609 struct banData *bData;
6610 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6611 time_t set_time, triggered_time, expires_time;
6613 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6615 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6619 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6620 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6621 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6622 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6623 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6624 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6625 if (!reason || !owner)
6628 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6629 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6631 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6633 expires_time = set_time + atoi(s_duration);
6637 if(!reason || (expires_time && (expires_time < now)))
6640 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6643 static struct suspended *
6644 chanserv_read_suspended(dict_t obj)
6646 struct suspended *suspended = calloc(1, sizeof(*suspended));
6650 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6651 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6652 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6653 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6654 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6655 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6656 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6657 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6658 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6659 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6664 chanserv_channel_read(const char *key, struct record_data *hir)
6666 struct suspended *suspended;
6667 struct mod_chanmode *modes;
6668 struct chanNode *cNode;
6669 struct chanData *cData;
6670 struct dict *channel, *obj;
6671 char *str, *argv[10];
6675 channel = hir->d.object;
6677 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6680 cNode = AddChannel(key, now, NULL, NULL);
6683 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6686 cData = register_channel(cNode, str);
6689 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6693 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6695 enum levelOption lvlOpt;
6696 enum charOption chOpt;
6698 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6699 cData->flags = atoi(str);
6701 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6703 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6705 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6706 else if(levelOptions[lvlOpt].old_flag)
6708 if(cData->flags & levelOptions[lvlOpt].old_flag)
6709 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6711 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6715 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6717 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6719 cData->chOpts[chOpt] = str[0];
6722 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6724 enum levelOption lvlOpt;
6725 enum charOption chOpt;
6728 cData->flags = base64toint(str, 5);
6729 count = strlen(str += 5);
6730 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6733 if(levelOptions[lvlOpt].old_flag)
6735 if(cData->flags & levelOptions[lvlOpt].old_flag)
6736 lvl = levelOptions[lvlOpt].flag_value;
6738 lvl = levelOptions[lvlOpt].default_value;
6740 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6742 case 'c': lvl = UL_COOWNER; break;
6743 case 'm': lvl = UL_MASTER; break;
6744 case 'n': lvl = UL_OWNER+1; break;
6745 case 'o': lvl = UL_OP; break;
6746 case 'p': lvl = UL_PEON; break;
6747 case 'w': lvl = UL_OWNER; break;
6748 default: lvl = 0; break;
6750 cData->lvlOpts[lvlOpt] = lvl;
6752 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6753 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6756 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6758 suspended = chanserv_read_suspended(obj);
6759 cData->suspended = suspended;
6760 suspended->cData = cData;
6761 /* We could use suspended->expires and suspended->revoked to
6762 * set the CHANNEL_SUSPENDED flag, but we don't. */
6764 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6766 suspended = calloc(1, sizeof(*suspended));
6767 suspended->issued = 0;
6768 suspended->revoked = 0;
6769 suspended->suspender = strdup(str);
6770 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6771 suspended->expires = str ? atoi(str) : 0;
6772 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6773 suspended->reason = strdup(str ? str : "No reason");
6774 suspended->previous = NULL;
6775 cData->suspended = suspended;
6776 suspended->cData = cData;
6780 cData->flags &= ~CHANNEL_SUSPENDED;
6781 suspended = NULL; /* to squelch a warning */
6784 if(IsSuspended(cData)) {
6785 if(suspended->expires > now)
6786 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6787 else if(suspended->expires)
6788 cData->flags &= ~CHANNEL_SUSPENDED;
6791 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6792 struct mod_chanmode change;
6793 mod_chanmode_init(&change);
6795 change.args[0].mode = MODE_CHANOP;
6796 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6797 mod_chanmode_announce(chanserv, cNode, &change);
6800 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6801 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6802 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6803 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6804 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6805 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6806 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6807 cData->max = str ? atoi(str) : 0;
6808 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6809 cData->greeting = str ? strdup(str) : NULL;
6810 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6811 cData->user_greeting = str ? strdup(str) : NULL;
6812 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6813 cData->topic_mask = str ? strdup(str) : NULL;
6814 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6815 cData->topic = str ? strdup(str) : NULL;
6817 if(!IsSuspended(cData)
6818 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6819 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6820 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6821 cData->modes = *modes;
6823 cData->modes.modes_set |= MODE_REGISTERED;
6824 if(cData->modes.argc > 1)
6825 cData->modes.argc = 1;
6826 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6827 mod_chanmode_free(modes);
6830 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6831 for(it = dict_first(obj); it; it = iter_next(it))
6832 user_read_helper(iter_key(it), iter_data(it), cData);
6834 if(!cData->users && !IsProtected(cData))
6836 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6837 unregister_channel(cData, "has empty user list.");
6841 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6842 for(it = dict_first(obj); it; it = iter_next(it))
6843 ban_read_helper(iter_key(it), iter_data(it), cData);
6845 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6846 for(it = dict_first(obj); it; it = iter_next(it))
6848 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6849 struct record_data *rd = iter_data(it);
6850 const char *note, *setter;
6852 if(rd->type != RECDB_OBJECT)
6854 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6858 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6860 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6862 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6866 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6867 if(!setter) setter = "<unknown>";
6868 chanserv_add_channel_note(cData, ntype, setter, note);
6876 chanserv_dnr_read(const char *key, struct record_data *hir)
6878 const char *setter, *reason, *str;
6879 struct do_not_register *dnr;
6881 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6884 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6887 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6890 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6893 dnr = chanserv_add_dnr(key, setter, reason);
6896 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6898 dnr->set = atoi(str);
6904 chanserv_saxdb_read(struct dict *database)
6906 struct dict *section;
6909 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6910 for(it = dict_first(section); it; it = iter_next(it))
6911 chanserv_note_type_read(iter_key(it), iter_data(it));
6913 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6914 for(it = dict_first(section); it; it = iter_next(it))
6915 chanserv_channel_read(iter_key(it), iter_data(it));
6917 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6918 for(it = dict_first(section); it; it = iter_next(it))
6919 chanserv_dnr_read(iter_key(it), iter_data(it));
6925 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6927 int high_present = 0;
6928 saxdb_start_record(ctx, KEY_USERS, 1);
6929 for(; uData; uData = uData->next)
6931 if((uData->access >= UL_PRESENT) && uData->present)
6933 saxdb_start_record(ctx, uData->handle->handle, 0);
6934 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6935 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6937 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6939 saxdb_write_string(ctx, KEY_INFO, uData->info);
6940 saxdb_end_record(ctx);
6942 saxdb_end_record(ctx);
6943 return high_present;
6947 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6951 saxdb_start_record(ctx, KEY_BANS, 1);
6952 for(; bData; bData = bData->next)
6954 saxdb_start_record(ctx, bData->mask, 0);
6955 saxdb_write_int(ctx, KEY_SET, bData->set);
6956 if(bData->triggered)
6957 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6959 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6961 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6963 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6964 saxdb_end_record(ctx);
6966 saxdb_end_record(ctx);
6970 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6972 saxdb_start_record(ctx, name, 0);
6973 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6974 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6976 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6978 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6980 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6982 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6983 saxdb_end_record(ctx);
6987 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6991 enum levelOption lvlOpt;
6992 enum charOption chOpt;
6994 saxdb_start_record(ctx, channel->channel->name, 1);
6996 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6997 saxdb_write_int(ctx, KEY_MAX, channel->max);
6999 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7000 if(channel->registrar)
7001 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7002 if(channel->greeting)
7003 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7004 if(channel->user_greeting)
7005 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7006 if(channel->topic_mask)
7007 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7008 if(channel->suspended)
7009 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7011 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7012 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7013 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7014 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7015 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7017 buf[0] = channel->chOpts[chOpt];
7019 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7021 saxdb_end_record(ctx);
7023 if(channel->modes.modes_set || channel->modes.modes_clear)
7025 mod_chanmode_format(&channel->modes, buf);
7026 saxdb_write_string(ctx, KEY_MODES, buf);
7029 high_present = chanserv_write_users(ctx, channel->users);
7030 chanserv_write_bans(ctx, channel->bans);
7032 if(dict_size(channel->notes))
7036 saxdb_start_record(ctx, KEY_NOTES, 1);
7037 for(it = dict_first(channel->notes); it; it = iter_next(it))
7039 struct note *note = iter_data(it);
7040 saxdb_start_record(ctx, iter_key(it), 0);
7041 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7042 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7043 saxdb_end_record(ctx);
7045 saxdb_end_record(ctx);
7048 if(channel->ownerTransfer)
7049 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7050 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7051 saxdb_end_record(ctx);
7055 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7059 saxdb_start_record(ctx, ntype->name, 0);
7060 switch(ntype->set_access_type)
7062 case NOTE_SET_CHANNEL_ACCESS:
7063 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7065 case NOTE_SET_CHANNEL_SETTER:
7066 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7068 case NOTE_SET_PRIVILEGED: default:
7069 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7072 switch(ntype->visible_type)
7074 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7075 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7076 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7078 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7079 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7080 saxdb_end_record(ctx);
7084 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7086 struct do_not_register *dnr;
7089 for(it = dict_first(dnrs); it; it = iter_next(it))
7091 dnr = iter_data(it);
7092 saxdb_start_record(ctx, dnr->chan_name, 0);
7094 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7095 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7096 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7097 saxdb_end_record(ctx);
7102 chanserv_saxdb_write(struct saxdb_context *ctx)
7105 struct chanData *channel;
7108 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7109 for(it = dict_first(note_types); it; it = iter_next(it))
7110 chanserv_write_note_type(ctx, iter_data(it));
7111 saxdb_end_record(ctx);
7114 saxdb_start_record(ctx, KEY_DNR, 1);
7115 write_dnrs_helper(ctx, handle_dnrs);
7116 write_dnrs_helper(ctx, plain_dnrs);
7117 write_dnrs_helper(ctx, mask_dnrs);
7118 saxdb_end_record(ctx);
7121 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7122 for(channel = channelList; channel; channel = channel->next)
7123 chanserv_write_channel(ctx, channel);
7124 saxdb_end_record(ctx);
7130 chanserv_db_cleanup(void) {
7132 unreg_part_func(handle_part);
7134 unregister_channel(channelList, "terminating.");
7135 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7136 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7137 free(chanserv_conf.support_channels.list);
7138 dict_delete(handle_dnrs);
7139 dict_delete(plain_dnrs);
7140 dict_delete(mask_dnrs);
7141 dict_delete(note_types);
7142 free_string_list(chanserv_conf.eightball);
7143 free_string_list(chanserv_conf.old_ban_names);
7144 free_string_list(chanserv_conf.set_shows);
7145 free(set_shows_list.list);
7146 free(uset_shows_list.list);
7149 struct userData *helper = helperList;
7150 helperList = helperList->next;
7155 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7156 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7157 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7160 init_chanserv(const char *nick)
7162 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7163 conf_register_reload(chanserv_conf_read);
7165 reg_server_link_func(handle_server_link);
7167 reg_new_channel_func(handle_new_channel);
7168 reg_join_func(handle_join);
7169 reg_part_func(handle_part);
7170 reg_kick_func(handle_kick);
7171 reg_topic_func(handle_topic);
7172 reg_mode_change_func(handle_mode);
7173 reg_nick_change_func(handle_nick_change);
7175 reg_auth_func(handle_auth);
7176 reg_handle_rename_func(handle_rename);
7177 reg_unreg_func(handle_unreg);
7179 handle_dnrs = dict_new();
7180 dict_set_free_data(handle_dnrs, free);
7181 plain_dnrs = dict_new();
7182 dict_set_free_data(plain_dnrs, free);
7183 mask_dnrs = dict_new();
7184 dict_set_free_data(mask_dnrs, free);
7186 reg_svccmd_unbind_func(handle_svccmd_unbind);
7187 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7188 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7189 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7190 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7191 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7192 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7193 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7194 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7195 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7197 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7198 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7200 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7201 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7202 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7203 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7204 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7206 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7207 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7208 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7209 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7210 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7212 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7213 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7214 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7215 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7217 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7218 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7219 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7220 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7221 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7222 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7223 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7224 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7226 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7227 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7228 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7229 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7230 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7231 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7232 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7233 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7234 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7235 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7236 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7237 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7238 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7239 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7241 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7242 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7243 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7244 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7245 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7247 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7248 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7250 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7251 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7252 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7253 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7254 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7255 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7256 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7257 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7258 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7259 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7260 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7262 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7263 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7265 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7266 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7267 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7268 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7270 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7271 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7272 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7273 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7274 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7276 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7277 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7278 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7279 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7280 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7281 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7283 /* Channel options */
7284 DEFINE_CHANNEL_OPTION(defaulttopic);
7285 DEFINE_CHANNEL_OPTION(topicmask);
7286 DEFINE_CHANNEL_OPTION(greeting);
7287 DEFINE_CHANNEL_OPTION(usergreeting);
7288 DEFINE_CHANNEL_OPTION(modes);
7289 DEFINE_CHANNEL_OPTION(enfops);
7290 DEFINE_CHANNEL_OPTION(giveops);
7291 DEFINE_CHANNEL_OPTION(protect);
7292 DEFINE_CHANNEL_OPTION(enfmodes);
7293 DEFINE_CHANNEL_OPTION(enftopic);
7294 DEFINE_CHANNEL_OPTION(pubcmd);
7295 DEFINE_CHANNEL_OPTION(givevoice);
7296 DEFINE_CHANNEL_OPTION(userinfo);
7297 DEFINE_CHANNEL_OPTION(dynlimit);
7298 DEFINE_CHANNEL_OPTION(topicsnarf);
7299 DEFINE_CHANNEL_OPTION(nodelete);
7300 DEFINE_CHANNEL_OPTION(toys);
7301 DEFINE_CHANNEL_OPTION(setters);
7302 DEFINE_CHANNEL_OPTION(topicrefresh);
7303 DEFINE_CHANNEL_OPTION(ctcpusers);
7304 DEFINE_CHANNEL_OPTION(ctcpreaction);
7305 DEFINE_CHANNEL_OPTION(inviteme);
7307 DEFINE_CHANNEL_OPTION(offchannel);
7308 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7310 /* Alias set topic to set defaulttopic for compatibility. */
7311 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7314 DEFINE_USER_OPTION(noautoop);
7315 DEFINE_USER_OPTION(autoinvite);
7316 DEFINE_USER_OPTION(info);
7318 /* Alias uset autovoice to uset autoop. */
7319 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7321 note_types = dict_new();
7322 dict_set_free_data(note_types, chanserv_deref_note_type);
7325 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7326 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7327 service_register(chanserv)->trigger = '!';
7328 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7330 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7332 if(chanserv_conf.channel_expire_frequency)
7333 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7335 if(chanserv_conf.refresh_period)
7337 time_t next_refresh;
7338 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7339 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7342 reg_exit_func(chanserv_db_cleanup);
7343 message_register_table(msgtab);