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 is 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, int vacation)
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)
2470 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2473 if(((uData->access >= min_access) && (uData->access <= max_access))
2474 || (!max_access && (uData->access < actor->access)))
2476 del_channel_user(uData, 1);
2484 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2486 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2490 static CHANSERV_FUNC(cmd_trim)
2492 unsigned long duration;
2493 unsigned short min_level, max_level;
2498 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2499 duration = ParseInterval(argv[2]);
2502 reply("CSMSG_CANNOT_TRIM");
2506 if(!irccasecmp(argv[1], "bans"))
2508 cmd_trim_bans(user, channel, duration);
2511 else if(!irccasecmp(argv[1], "users"))
2513 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2516 else if(parse_level_range(&min_level, &max_level, argv[1]))
2518 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2521 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2523 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2528 reply("CSMSG_INVALID_TRIM", argv[1]);
2533 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2534 to the user. cmd_all takes advantage of this. */
2535 static CHANSERV_FUNC(cmd_up)
2537 struct mod_chanmode change;
2538 struct userData *uData;
2541 mod_chanmode_init(&change);
2543 change.args[0].u.member = GetUserMode(channel, user);
2544 if(!change.args[0].u.member)
2547 reply("MSG_CHANNEL_ABSENT", channel->name);
2551 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2555 reply("CSMSG_GODMODE_UP", argv[0]);
2558 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2560 change.args[0].mode = MODE_CHANOP;
2561 errmsg = "CSMSG_ALREADY_OPPED";
2563 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2565 change.args[0].mode = MODE_VOICE;
2566 errmsg = "CSMSG_ALREADY_VOICED";
2571 reply("CSMSG_NO_ACCESS");
2574 change.args[0].mode &= ~change.args[0].u.member->modes;
2575 if(!change.args[0].mode)
2578 reply(errmsg, channel->name);
2581 modcmd_chanmode_announce(&change);
2585 static CHANSERV_FUNC(cmd_down)
2587 struct mod_chanmode change;
2589 mod_chanmode_init(&change);
2591 change.args[0].u.member = GetUserMode(channel, user);
2592 if(!change.args[0].u.member)
2595 reply("MSG_CHANNEL_ABSENT", channel->name);
2599 if(!change.args[0].u.member->modes)
2602 reply("CSMSG_ALREADY_DOWN", channel->name);
2606 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2607 modcmd_chanmode_announce(&change);
2611 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)
2613 struct userData *cList;
2615 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2617 if(IsSuspended(cList->channel)
2618 || IsUserSuspended(cList)
2619 || !GetUserMode(cList->channel->channel, user))
2622 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2628 static CHANSERV_FUNC(cmd_upall)
2630 return cmd_all(CSFUNC_ARGS, cmd_up);
2633 static CHANSERV_FUNC(cmd_downall)
2635 return cmd_all(CSFUNC_ARGS, cmd_down);
2638 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2639 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2642 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)
2644 unsigned int ii, valid;
2645 struct userNode *victim;
2646 struct mod_chanmode *change;
2648 change = mod_chanmode_alloc(argc - 1);
2650 for(ii=valid=0; ++ii < argc; )
2652 if(!(victim = GetUserH(argv[ii])))
2654 change->args[valid].mode = mode;
2655 change->args[valid].u.member = GetUserMode(channel, victim);
2656 if(!change->args[valid].u.member)
2658 if(validate && !validate(user, channel, victim))
2663 change->argc = valid;
2664 if(valid < (argc-1))
2665 reply("CSMSG_PROCESS_FAILED");
2668 modcmd_chanmode_announce(change);
2669 reply(action, channel->name);
2671 mod_chanmode_free(change);
2675 static CHANSERV_FUNC(cmd_op)
2677 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2680 static CHANSERV_FUNC(cmd_deop)
2682 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2685 static CHANSERV_FUNC(cmd_voice)
2687 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2690 static CHANSERV_FUNC(cmd_devoice)
2692 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2696 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2702 for(ii=0; ii<channel->members.used; ii++)
2704 struct modeNode *mn = channel->members.list[ii];
2706 if(IsService(mn->user))
2709 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2712 if(protect_user(mn->user, user, channel->channel_info))
2716 victims[(*victimCount)++] = mn;
2722 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2724 struct userNode *victim;
2725 struct modeNode **victims;
2726 unsigned int offset, n, victimCount, duration = 0;
2727 char *reason = "Bye.", *ban, *name;
2728 char interval[INTERVALLEN];
2730 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2731 REQUIRE_PARAMS(offset);
2734 reason = unsplit_string(argv + offset, argc - offset, NULL);
2735 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2737 /* Truncate the reason to a length of TOPICLEN, as
2738 the ircd does; however, leave room for an ellipsis
2739 and the kicker's nick. */
2740 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2744 if((victim = GetUserH(argv[1])))
2746 victims = alloca(sizeof(victims[0]));
2747 victims[0] = GetUserMode(channel, victim);
2748 /* XXX: The comparison with ACTION_KICK is just because all
2749 * other actions can work on users outside the channel, and we
2750 * want to allow those (e.g. unbans) in that case. If we add
2751 * some other ejection action for in-channel users, change
2753 victimCount = victims[0] ? 1 : 0;
2755 if(IsService(victim))
2757 reply("MSG_SERVICE_IMMUNE", victim->nick);
2761 if((action == ACTION_KICK) && !victimCount)
2763 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2767 if(protect_user(victim, user, channel->channel_info))
2769 reply("CSMSG_USER_PROTECTED", victim->nick);
2773 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2774 name = victim->nick;
2778 if(!is_ircmask(argv[1]))
2780 reply("MSG_NICK_UNKNOWN", argv[1]);
2784 victims = alloca(sizeof(victims[0]) * channel->members.used);
2786 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2788 reply("CSMSG_MASK_PROTECTED", argv[1]);
2792 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2794 reply("CSMSG_LAME_MASK", argv[1]);
2798 if((action == ACTION_KICK) && (victimCount == 0))
2800 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2804 name = ban = strdup(argv[1]);
2807 /* Truncate the ban in place if necessary; we must ensure
2808 that 'ban' is a valid ban mask before sanitizing it. */
2809 sanitize_ircmask(ban);
2811 if(action & ACTION_ADD_BAN)
2813 struct banData *bData, *next;
2815 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2817 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2822 if(action & ACTION_ADD_TIMED_BAN)
2824 duration = ParseInterval(argv[2]);
2828 reply("CSMSG_DURATION_TOO_LOW");
2832 else if(duration > (86400 * 365 * 2))
2834 reply("CSMSG_DURATION_TOO_HIGH");
2840 for(bData = channel->channel_info->bans; bData; bData = next)
2842 if(match_ircglobs(bData->mask, ban))
2844 int exact = !irccasecmp(bData->mask, ban);
2846 /* The ban is redundant; there is already a ban
2847 with the same effect in place. */
2851 free(bData->reason);
2852 bData->reason = strdup(reason);
2853 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2855 reply("CSMSG_REASON_CHANGE", ban);
2859 if(exact && bData->expires)
2863 /* If the ban matches an existing one exactly,
2864 extend the expiration time if the provided
2865 duration is longer. */
2866 if(duration && ((time_t)(now + duration) > bData->expires))
2868 bData->expires = now + duration;
2879 /* Delete the expiration timeq entry and
2880 requeue if necessary. */
2881 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2884 timeq_add(bData->expires, expire_ban, bData);
2888 /* automated kickban */
2891 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2893 reply("CSMSG_BAN_ADDED", name, channel->name);
2899 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2906 if(match_ircglobs(ban, bData->mask))
2908 /* The ban we are adding makes previously existing
2909 bans redundant; silently remove them. */
2910 del_channel_ban(bData);
2914 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);
2916 name = ban = strdup(bData->mask);
2920 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2922 extern const char *hidden_host_suffix;
2923 const char *old_name = chanserv_conf.old_ban_names->list[n];
2925 unsigned int l1, l2;
2928 l2 = strlen(old_name);
2931 if(irccasecmp(ban + l1 - l2, old_name))
2933 new_mask = malloc(MAXLEN);
2934 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2936 name = ban = new_mask;
2941 if(action & ACTION_BAN)
2943 unsigned int exists;
2944 struct mod_chanmode *change;
2946 if(channel->banlist.used >= MAXBANS)
2949 reply("CSMSG_BANLIST_FULL", channel->name);
2954 exists = ChannelBanExists(channel, ban);
2955 change = mod_chanmode_alloc(victimCount + 1);
2956 for(n = 0; n < victimCount; ++n)
2958 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2959 change->args[n].u.member = victims[n];
2963 change->args[n].mode = MODE_BAN;
2964 change->args[n++].u.hostmask = ban;
2968 modcmd_chanmode_announce(change);
2970 mod_chanmode_announce(chanserv, channel, change);
2971 mod_chanmode_free(change);
2973 if(exists && (action == ACTION_BAN))
2976 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2982 if(action & ACTION_KICK)
2984 char kick_reason[MAXLEN];
2985 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2987 for(n = 0; n < victimCount; n++)
2988 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2993 /* No response, since it was automated. */
2995 else if(action & ACTION_ADD_BAN)
2998 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3000 reply("CSMSG_BAN_ADDED", name, channel->name);
3002 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3003 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3004 else if(action & ACTION_BAN)
3005 reply("CSMSG_BAN_DONE", name, channel->name);
3006 else if(action & ACTION_KICK && victimCount)
3007 reply("CSMSG_KICK_DONE", name, channel->name);
3013 static CHANSERV_FUNC(cmd_kickban)
3015 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3018 static CHANSERV_FUNC(cmd_kick)
3020 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3023 static CHANSERV_FUNC(cmd_ban)
3025 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3028 static CHANSERV_FUNC(cmd_addban)
3030 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3033 static CHANSERV_FUNC(cmd_addtimedban)
3035 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3038 static struct mod_chanmode *
3039 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3041 struct mod_chanmode *change;
3042 unsigned char *match;
3043 unsigned int ii, count;
3045 match = alloca(bans->used);
3048 for(ii = count = 0; ii < bans->used; ++ii)
3050 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3051 MATCH_USENICK | MATCH_VISIBLE);
3058 for(ii = count = 0; ii < bans->used; ++ii)
3060 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3067 change = mod_chanmode_alloc(count);
3068 for(ii = count = 0; ii < bans->used; ++ii)
3072 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3073 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3075 assert(count == change->argc);
3080 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3082 struct userNode *actee;
3088 /* may want to allow a comma delimited list of users... */
3089 if(!(actee = GetUserH(argv[1])))
3091 if(!is_ircmask(argv[1]))
3093 reply("MSG_NICK_UNKNOWN", argv[1]);
3097 mask = strdup(argv[1]);
3100 /* We don't sanitize the mask here because ircu
3102 if(action & ACTION_UNBAN)
3104 struct mod_chanmode *change;
3105 change = find_matching_bans(&channel->banlist, actee, mask);
3110 modcmd_chanmode_announce(change);
3111 for(ii = 0; ii < change->argc; ++ii)
3112 free((char*)change->args[ii].u.hostmask);
3113 mod_chanmode_free(change);
3118 if(action & ACTION_DEL_BAN)
3120 struct banData *ban, *next;
3122 ban = channel->channel_info->bans;
3126 for( ; ban && !user_matches_glob(actee, ban->mask,
3127 MATCH_USENICK | MATCH_VISIBLE);
3130 for( ; ban && !match_ircglobs(mask, ban->mask);
3135 del_channel_ban(ban);
3142 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3144 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3150 static CHANSERV_FUNC(cmd_unban)
3152 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3155 static CHANSERV_FUNC(cmd_delban)
3157 /* it doesn't necessarily have to remove the channel ban - may want
3158 to make that an option. */
3159 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3162 static CHANSERV_FUNC(cmd_unbanme)
3164 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3165 long flags = ACTION_UNBAN;
3167 /* remove permanent bans if the user has the proper access. */
3168 if(uData->access >= UL_MASTER)
3169 flags |= ACTION_DEL_BAN;
3171 argv[1] = user->nick;
3172 return unban_user(user, channel, 2, argv, cmd, flags);
3175 static CHANSERV_FUNC(cmd_unbanall)
3177 struct mod_chanmode *change;
3180 if(!channel->banlist.used)
3182 reply("CSMSG_NO_BANS", channel->name);
3186 change = mod_chanmode_alloc(channel->banlist.used);
3187 for(ii=0; ii<channel->banlist.used; ii++)
3189 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3190 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3192 modcmd_chanmode_announce(change);
3193 for(ii = 0; ii < change->argc; ++ii)
3194 free((char*)change->args[ii].u.hostmask);
3195 mod_chanmode_free(change);
3196 reply("CSMSG_BANS_REMOVED", channel->name);
3200 static CHANSERV_FUNC(cmd_open)
3202 struct mod_chanmode *change;
3205 change = find_matching_bans(&channel->banlist, user, NULL);
3207 change = mod_chanmode_alloc(0);
3208 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3209 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3210 && channel->channel_info->modes.modes_set)
3211 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3212 modcmd_chanmode_announce(change);
3213 reply("CSMSG_CHANNEL_OPENED", channel->name);
3214 for(ii = 0; ii < change->argc; ++ii)
3215 free((char*)change->args[ii].u.hostmask);
3216 mod_chanmode_free(change);
3220 static CHANSERV_FUNC(cmd_myaccess)
3222 static struct string_buffer sbuf;
3223 struct handle_info *target_handle;
3224 struct userData *uData;
3227 target_handle = user->handle_info;
3228 else if(!IsHelping(user))
3230 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3233 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3236 if(!target_handle->channels)
3238 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3242 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3243 for(uData = target_handle->channels; uData; uData = uData->u_next)
3245 struct chanData *cData = uData->channel;
3247 if(uData->access > UL_OWNER)
3249 if(IsProtected(cData)
3250 && (target_handle != user->handle_info)
3251 && !GetTrueChannelAccess(cData, user->handle_info))
3254 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3255 if(uData->flags != USER_AUTO_OP)
3256 string_buffer_append(&sbuf, ',');
3257 if(IsUserSuspended(uData))
3258 string_buffer_append(&sbuf, 's');
3259 if(IsUserAutoOp(uData))
3261 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3262 string_buffer_append(&sbuf, 'o');
3263 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3264 string_buffer_append(&sbuf, 'v');
3266 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3267 string_buffer_append(&sbuf, 'i');
3269 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3271 string_buffer_append_string(&sbuf, ")]");
3272 string_buffer_append(&sbuf, '\0');
3273 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3279 static CHANSERV_FUNC(cmd_access)
3281 struct userNode *target;
3282 struct handle_info *target_handle;
3283 struct userData *uData;
3285 char prefix[MAXLEN];
3290 target_handle = target->handle_info;
3292 else if((target = GetUserH(argv[1])))
3294 target_handle = target->handle_info;
3296 else if(argv[1][0] == '*')
3298 if(!(target_handle = get_handle_info(argv[1]+1)))
3300 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3306 reply("MSG_NICK_UNKNOWN", argv[1]);
3310 assert(target || target_handle);
3312 if(target == chanserv)
3314 reply("CSMSG_IS_CHANSERV");
3322 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3327 reply("MSG_USER_AUTHENTICATE", target->nick);
3330 reply("MSG_AUTHENTICATE");
3336 const char *epithet = NULL, *type = NULL;
3339 epithet = chanserv_conf.irc_operator_epithet;
3342 else if(IsNetworkHelper(target))
3344 epithet = chanserv_conf.network_helper_epithet;
3345 type = "network helper";
3347 else if(IsSupportHelper(target))
3349 epithet = chanserv_conf.support_helper_epithet;
3350 type = "support helper";
3354 if(target_handle->epithet)
3355 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3357 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3359 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3363 sprintf(prefix, "%s", target_handle->handle);
3366 if(!channel->channel_info)
3368 reply("CSMSG_NOT_REGISTERED", channel->name);
3372 helping = HANDLE_FLAGGED(target_handle, HELPING)
3373 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3374 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3376 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3377 /* To prevent possible information leaks, only show infolines
3378 * if the requestor is in the channel or it's their own
3380 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3382 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3384 /* Likewise, only say it's suspended if the user has active
3385 * access in that channel or it's their own entry. */
3386 if(IsUserSuspended(uData)
3387 && (GetChannelUser(channel->channel_info, user->handle_info)
3388 || (user->handle_info == uData->handle)))
3390 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3395 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3402 zoot_list(struct listData *list)
3404 struct userData *uData;
3405 unsigned int start, curr, highest, lowest;
3406 struct helpfile_table tmp_table;
3407 const char **temp, *msg;
3409 if(list->table.length == 1)
3412 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3414 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3415 msg = user_find_message(list->user, "MSG_NONE");
3416 send_message_type(4, list->user, list->bot, " %s", msg);
3418 tmp_table.width = list->table.width;
3419 tmp_table.flags = list->table.flags;
3420 list->table.contents[0][0] = " ";
3421 highest = list->highest;
3422 if(list->lowest != 0)
3423 lowest = list->lowest;
3424 else if(highest < 100)
3427 lowest = highest - 100;
3428 for(start = curr = 1; curr < list->table.length; )
3430 uData = list->users[curr-1];
3431 list->table.contents[curr++][0] = " ";
3432 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3435 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3437 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3438 temp = list->table.contents[--start];
3439 list->table.contents[start] = list->table.contents[0];
3440 tmp_table.contents = list->table.contents + start;
3441 tmp_table.length = curr - start;
3442 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3443 list->table.contents[start] = temp;
3445 highest = lowest - 1;
3446 lowest = (highest < 100) ? 0 : (highest - 99);
3452 def_list(struct listData *list)
3456 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3458 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3459 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3460 if(list->table.length == 1)
3462 msg = user_find_message(list->user, "MSG_NONE");
3463 send_message_type(4, list->user, list->bot, " %s", msg);
3468 userData_access_comp(const void *arg_a, const void *arg_b)
3470 const struct userData *a = *(struct userData**)arg_a;
3471 const struct userData *b = *(struct userData**)arg_b;
3473 if(a->access != b->access)
3474 res = b->access - a->access;
3476 res = irccasecmp(a->handle->handle, b->handle->handle);
3481 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3483 void (*send_list)(struct listData *);
3484 struct userData *uData;
3485 struct listData lData;
3486 unsigned int matches;
3490 lData.bot = cmd->parent->bot;
3491 lData.channel = channel;
3492 lData.lowest = lowest;
3493 lData.highest = highest;
3494 lData.search = (argc > 1) ? argv[1] : NULL;
3495 send_list = def_list;
3496 (void)zoot_list; /* since it doesn't show user levels */
3498 if(user->handle_info)
3500 switch(user->handle_info->userlist_style)
3502 case HI_STYLE_DEF: send_list = def_list; break;
3503 case HI_STYLE_ZOOT: send_list = def_list; break;
3507 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3509 for(uData = channel->channel_info->users; uData; uData = uData->next)
3511 if((uData->access < lowest)
3512 || (uData->access > highest)
3513 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3515 lData.users[matches++] = uData;
3517 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3519 lData.table.length = matches+1;
3520 lData.table.width = 4;
3521 lData.table.flags = TABLE_NO_FREE;
3522 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3523 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3524 lData.table.contents[0] = ary;
3527 ary[2] = "Last Seen";
3529 for(matches = 1; matches < lData.table.length; ++matches)
3531 struct userData *uData = lData.users[matches-1];
3532 char seen[INTERVALLEN];
3534 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3535 lData.table.contents[matches] = ary;
3536 ary[0] = strtab(uData->access);
3537 ary[1] = uData->handle->handle;
3540 else if(!uData->seen)
3543 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3544 ary[2] = strdup(ary[2]);
3545 if(IsUserSuspended(uData))
3546 ary[3] = "Suspended";
3547 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3548 ary[3] = "Vacation";
3553 for(matches = 1; matches < lData.table.length; ++matches)
3555 free((char*)lData.table.contents[matches][2]);
3556 free(lData.table.contents[matches]);
3558 free(lData.table.contents[0]);
3559 free(lData.table.contents);
3563 static CHANSERV_FUNC(cmd_users)
3565 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3568 static CHANSERV_FUNC(cmd_wlist)
3570 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3573 static CHANSERV_FUNC(cmd_clist)
3575 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3578 static CHANSERV_FUNC(cmd_mlist)
3580 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3583 static CHANSERV_FUNC(cmd_olist)
3585 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3588 static CHANSERV_FUNC(cmd_plist)
3590 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3593 static CHANSERV_FUNC(cmd_bans)
3595 struct userNode *search_u = NULL;
3596 struct helpfile_table tbl;
3597 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3598 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3599 const char *msg_never, *triggered, *expires;
3600 struct banData *ban, **bans;
3604 else if(strchr(search = argv[1], '!'))
3607 search_wilds = search[strcspn(search, "?*")];
3609 else if(!(search_u = GetUserH(search)))
3610 reply("MSG_NICK_UNKNOWN", search);
3612 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3614 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3618 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3623 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3626 bans[matches++] = ban;
3631 tbl.length = matches + 1;
3632 tbl.width = 4 + timed;
3634 tbl.flags = TABLE_NO_FREE;
3635 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3636 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3637 tbl.contents[0][0] = "Mask";
3638 tbl.contents[0][1] = "Set By";
3639 tbl.contents[0][2] = "Triggered";
3642 tbl.contents[0][3] = "Expires";
3643 tbl.contents[0][4] = "Reason";
3646 tbl.contents[0][3] = "Reason";
3649 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3651 free(tbl.contents[0]);
3656 msg_never = user_find_message(user, "MSG_NEVER");
3657 for(ii = 0; ii < matches; )
3663 else if(ban->expires)
3664 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3666 expires = msg_never;
3669 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3671 triggered = msg_never;
3673 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3674 tbl.contents[ii][0] = ban->mask;
3675 tbl.contents[ii][1] = ban->owner;
3676 tbl.contents[ii][2] = strdup(triggered);
3679 tbl.contents[ii][3] = strdup(expires);
3680 tbl.contents[ii][4] = ban->reason;
3683 tbl.contents[ii][3] = ban->reason;
3685 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3686 reply("MSG_MATCH_COUNT", matches);
3687 for(ii = 1; ii < tbl.length; ++ii)
3689 free((char*)tbl.contents[ii][2]);
3691 free((char*)tbl.contents[ii][3]);
3692 free(tbl.contents[ii]);
3694 free(tbl.contents[0]);
3700 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3702 struct chanData *cData = channel->channel_info;
3703 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3705 if(cData->topic_mask)
3706 return !match_ircglob(new_topic, cData->topic_mask);
3707 else if(cData->topic)
3708 return irccasecmp(new_topic, cData->topic);
3713 static CHANSERV_FUNC(cmd_topic)
3715 struct chanData *cData;
3718 cData = channel->channel_info;
3723 SetChannelTopic(channel, chanserv, cData->topic, 1);
3724 reply("CSMSG_TOPIC_SET", cData->topic);
3728 reply("CSMSG_NO_TOPIC", channel->name);
3732 topic = unsplit_string(argv + 1, argc - 1, NULL);
3733 /* If they say "!topic *", use an empty topic. */
3734 if((topic[0] == '*') && (topic[1] == 0))
3736 if(bad_topic(channel, user, topic))
3738 char *topic_mask = cData->topic_mask;
3741 char new_topic[TOPICLEN+1], tchar;
3742 int pos=0, starpos=-1, dpos=0, len;
3744 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3751 len = strlen(topic);
3752 if((dpos + len) > TOPICLEN)
3753 len = TOPICLEN + 1 - dpos;
3754 memcpy(new_topic+dpos, topic, len);
3758 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3759 default: new_topic[dpos++] = tchar; break;
3762 if((dpos > TOPICLEN) || tchar)
3765 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3766 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3769 new_topic[dpos] = 0;
3770 SetChannelTopic(channel, chanserv, new_topic, 1);
3772 reply("CSMSG_TOPIC_LOCKED", channel->name);
3777 SetChannelTopic(channel, chanserv, topic, 1);
3779 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3781 /* Grab the topic and save it as the default topic. */
3783 cData->topic = strdup(channel->topic);
3789 static CHANSERV_FUNC(cmd_mode)
3791 struct userData *uData;
3792 struct mod_chanmode *change;
3797 change = &channel->channel_info->modes;
3798 if(change->modes_set || change->modes_clear) {
3799 modcmd_chanmode_announce(change);
3800 reply("CSMSG_DEFAULTED_MODES", channel->name);
3802 reply("CSMSG_NO_MODES", channel->name);
3806 uData = GetChannelUser(channel->channel_info, user->handle_info);
3808 base_oplevel = MAXOPLEVEL;
3809 else if (uData->access >= UL_OWNER)
3812 base_oplevel = 1 + UL_OWNER - uData->access;
3813 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3816 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3820 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3821 && mode_lock_violated(&channel->channel_info->modes, change))
3824 mod_chanmode_format(&channel->channel_info->modes, modes);
3825 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3829 modcmd_chanmode_announce(change);
3830 mod_chanmode_free(change);
3831 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3835 static CHANSERV_FUNC(cmd_invite)
3837 struct userData *uData;
3838 struct userNode *invite;
3840 uData = GetChannelUser(channel->channel_info, user->handle_info);
3844 if(!(invite = GetUserH(argv[1])))
3846 reply("MSG_NICK_UNKNOWN", argv[1]);
3853 if(GetUserMode(channel, invite))
3855 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3863 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3864 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3867 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3869 irc_invite(chanserv, invite, channel);
3871 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3876 static CHANSERV_FUNC(cmd_inviteme)
3878 if(GetUserMode(channel, user))
3880 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3883 if(channel->channel_info
3884 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3886 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3889 irc_invite(cmd->parent->bot, user, channel);
3894 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3897 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3899 /* We display things based on two dimensions:
3900 * - Issue time: present or absent
3901 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3902 * (in order of precedence, so something both expired and revoked
3903 * only counts as revoked)
3905 combo = (suspended->issued ? 4 : 0)
3906 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3908 case 0: /* no issue time, indefinite expiration */
3909 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3911 case 1: /* no issue time, expires in future */
3912 intervalString(buf1, suspended->expires-now, user->handle_info);
3913 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3915 case 2: /* no issue time, expired */
3916 intervalString(buf1, now-suspended->expires, user->handle_info);
3917 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3919 case 3: /* no issue time, revoked */
3920 intervalString(buf1, now-suspended->revoked, user->handle_info);
3921 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3923 case 4: /* issue time set, indefinite expiration */
3924 intervalString(buf1, now-suspended->issued, user->handle_info);
3925 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3927 case 5: /* issue time set, expires in future */
3928 intervalString(buf1, now-suspended->issued, user->handle_info);
3929 intervalString(buf2, suspended->expires-now, user->handle_info);
3930 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3932 case 6: /* issue time set, expired */
3933 intervalString(buf1, now-suspended->issued, user->handle_info);
3934 intervalString(buf2, now-suspended->expires, user->handle_info);
3935 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3937 case 7: /* issue time set, revoked */
3938 intervalString(buf1, now-suspended->issued, user->handle_info);
3939 intervalString(buf2, now-suspended->revoked, user->handle_info);
3940 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3943 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3948 static CHANSERV_FUNC(cmd_info)
3950 char modes[MAXLEN], buffer[INTERVALLEN];
3951 struct userData *uData, *owner;
3952 struct chanData *cData;
3953 struct do_not_register *dnr;
3958 cData = channel->channel_info;
3959 reply("CSMSG_CHANNEL_INFO", channel->name);
3961 uData = GetChannelUser(cData, user->handle_info);
3962 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3964 mod_chanmode_format(&cData->modes, modes);
3965 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3966 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3969 for(it = dict_first(cData->notes); it; it = iter_next(it))
3973 note = iter_data(it);
3974 if(!note_type_visible_to_user(cData, note->type, user))
3977 padding = PADLEN - 1 - strlen(iter_key(it));
3978 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3981 reply("CSMSG_CHANNEL_MAX", cData->max);
3982 for(owner = cData->users; owner; owner = owner->next)
3983 if(owner->access == UL_OWNER)
3984 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3985 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3986 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3987 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3989 privileged = IsStaff(user);
3991 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3992 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3993 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3995 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3996 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3998 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4000 struct suspended *suspended;
4001 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4002 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4003 show_suspension_info(cmd, user, suspended);
4005 else if(IsSuspended(cData))
4007 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4008 show_suspension_info(cmd, user, cData->suspended);
4013 static CHANSERV_FUNC(cmd_netinfo)
4015 extern time_t boot_time;
4016 extern unsigned long burst_length;
4017 char interval[INTERVALLEN];
4019 reply("CSMSG_NETWORK_INFO");
4020 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4021 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4022 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4023 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4024 reply("CSMSG_NETWORK_BANS", banCount);
4025 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4026 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4027 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4032 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4034 struct helpfile_table table;
4036 struct userNode *user;
4041 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4042 table.contents = alloca(list->used*sizeof(*table.contents));
4043 for(nn=0; nn<list->used; nn++)
4045 user = list->list[nn];
4046 if(user->modes & skip_flags)
4050 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4053 nick = alloca(strlen(user->nick)+3);
4054 sprintf(nick, "(%s)", user->nick);
4058 table.contents[table.length][0] = nick;
4061 table_send(chanserv, to->nick, 0, NULL, table);
4064 static CHANSERV_FUNC(cmd_ircops)
4066 reply("CSMSG_STAFF_OPERS");
4067 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4071 static CHANSERV_FUNC(cmd_helpers)
4073 reply("CSMSG_STAFF_HELPERS");
4074 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4078 static CHANSERV_FUNC(cmd_staff)
4080 reply("CSMSG_NETWORK_STAFF");
4081 cmd_ircops(CSFUNC_ARGS);
4082 cmd_helpers(CSFUNC_ARGS);
4086 static CHANSERV_FUNC(cmd_peek)
4088 struct modeNode *mn;
4089 char modes[MODELEN];
4091 struct helpfile_table table;
4093 irc_make_chanmode(channel, modes);
4095 reply("CSMSG_PEEK_INFO", channel->name);
4096 reply("CSMSG_PEEK_TOPIC", channel->topic);
4097 reply("CSMSG_PEEK_MODES", modes);
4098 reply("CSMSG_PEEK_USERS", channel->members.used);
4102 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4103 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4104 for(n = 0; n < channel->members.used; n++)
4106 mn = channel->members.list[n];
4107 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4109 table.contents[table.length] = alloca(sizeof(**table.contents));
4110 table.contents[table.length][0] = mn->user->nick;
4115 reply("CSMSG_PEEK_OPS");
4116 table_send(chanserv, user->nick, 0, NULL, table);
4119 reply("CSMSG_PEEK_NO_OPS");
4123 static MODCMD_FUNC(cmd_wipeinfo)
4125 struct handle_info *victim;
4126 struct userData *ud, *actor;
4129 actor = GetChannelUser(channel->channel_info, user->handle_info);
4130 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4132 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4134 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4137 if((ud->access >= actor->access) && (ud != actor))
4139 reply("MSG_USER_OUTRANKED", victim->handle);
4145 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4149 static CHANSERV_FUNC(cmd_resync)
4151 struct mod_chanmode *changes;
4152 struct chanData *cData = channel->channel_info;
4153 unsigned int ii, used;
4155 changes = mod_chanmode_alloc(channel->members.used * 2);
4156 for(ii = used = 0; ii < channel->members.used; ++ii)
4158 struct modeNode *mn = channel->members.list[ii];
4159 struct userData *uData;
4161 if(IsService(mn->user))
4164 uData = GetChannelAccess(cData, mn->user->handle_info);
4165 if(!cData->lvlOpts[lvlGiveOps]
4166 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4168 if(!(mn->modes & MODE_CHANOP))
4170 changes->args[used].mode = MODE_CHANOP;
4171 changes->args[used++].u.member = mn;
4174 else if(!cData->lvlOpts[lvlGiveVoice]
4175 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4177 if(mn->modes & MODE_CHANOP)
4179 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4180 changes->args[used++].u.member = mn;
4182 if(!(mn->modes & MODE_VOICE))
4184 changes->args[used].mode = MODE_VOICE;
4185 changes->args[used++].u.member = mn;
4192 changes->args[used].mode = MODE_REMOVE | mn->modes;
4193 changes->args[used++].u.member = mn;
4197 changes->argc = used;
4198 modcmd_chanmode_announce(changes);
4199 mod_chanmode_free(changes);
4200 reply("CSMSG_RESYNCED_USERS", channel->name);
4204 static CHANSERV_FUNC(cmd_seen)
4206 struct userData *uData;
4207 struct handle_info *handle;
4208 char seen[INTERVALLEN];
4212 if(!irccasecmp(argv[1], chanserv->nick))
4214 reply("CSMSG_IS_CHANSERV");
4218 if(!(handle = get_handle_info(argv[1])))
4220 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4224 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4226 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4231 reply("CSMSG_USER_PRESENT", handle->handle);
4232 else if(uData->seen)
4233 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4235 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4237 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4238 reply("CSMSG_USER_VACATION", handle->handle);
4243 static MODCMD_FUNC(cmd_names)
4245 struct userNode *targ;
4246 struct userData *targData;
4247 unsigned int ii, pos;
4250 for(ii=pos=0; ii<channel->members.used; ++ii)
4252 targ = channel->members.list[ii]->user;
4253 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4256 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4259 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4263 if(IsUserSuspended(targData))
4265 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4268 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4269 reply("CSMSG_END_NAMES", channel->name);
4274 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4276 switch(ntype->visible_type)
4278 case NOTE_VIS_ALL: return 1;
4279 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4280 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4285 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4287 struct userData *uData;
4289 switch(ntype->set_access_type)
4291 case NOTE_SET_CHANNEL_ACCESS:
4292 if(!user->handle_info)
4294 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4296 return uData->access >= ntype->set_access.min_ulevel;
4297 case NOTE_SET_CHANNEL_SETTER:
4298 return check_user_level(channel, user, lvlSetters, 1, 0);
4299 case NOTE_SET_PRIVILEGED: default:
4300 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4304 static CHANSERV_FUNC(cmd_note)
4306 struct chanData *cData;
4308 struct note_type *ntype;
4310 cData = channel->channel_info;
4313 reply("CSMSG_NOT_REGISTERED", channel->name);
4317 /* If no arguments, show all visible notes for the channel. */
4323 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4325 note = iter_data(it);
4326 if(!note_type_visible_to_user(cData, note->type, user))
4329 reply("CSMSG_NOTELIST_HEADER", channel->name);
4330 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4333 reply("CSMSG_NOTELIST_END", channel->name);
4335 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4337 /* If one argument, show the named note. */
4340 if((note = dict_find(cData->notes, argv[1], NULL))
4341 && note_type_visible_to_user(cData, note->type, user))
4343 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4345 else if((ntype = dict_find(note_types, argv[1], NULL))
4346 && note_type_visible_to_user(NULL, ntype, user))
4348 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4353 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4357 /* Assume they're trying to set a note. */
4361 ntype = dict_find(note_types, argv[1], NULL);
4364 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4367 else if(note_type_settable_by_user(channel, ntype, user))
4369 note_text = unsplit_string(argv+2, argc-2, NULL);
4370 if((note = dict_find(cData->notes, argv[1], NULL)))
4371 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4372 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4373 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4375 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4377 /* The note is viewable to staff only, so return 0
4378 to keep the invocation from getting logged (or
4379 regular users can see it in !events). */
4385 reply("CSMSG_NO_ACCESS");
4392 static CHANSERV_FUNC(cmd_delnote)
4397 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4398 || !note_type_settable_by_user(channel, note->type, user))
4400 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4403 dict_remove(channel->channel_info->notes, note->type->name);
4404 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4408 static CHANSERV_FUNC(cmd_events)
4410 struct logSearch discrim;
4411 struct logReport report;
4412 unsigned int matches, limit;
4414 limit = (argc > 1) ? atoi(argv[1]) : 10;
4415 if(limit < 1 || limit > 200)
4418 memset(&discrim, 0, sizeof(discrim));
4419 discrim.masks.bot = chanserv;
4420 discrim.masks.channel_name = channel->name;
4422 discrim.masks.command = argv[2];
4423 discrim.limit = limit;
4424 discrim.max_time = INT_MAX;
4425 discrim.severities = 1 << LOG_COMMAND;
4426 report.reporter = chanserv;
4428 reply("CSMSG_EVENT_SEARCH_RESULTS");
4429 matches = log_entry_search(&discrim, log_report_entry, &report);
4431 reply("MSG_MATCH_COUNT", matches);
4433 reply("MSG_NO_MATCHES");
4437 static CHANSERV_FUNC(cmd_say)
4443 msg = unsplit_string(argv + 1, argc - 1, NULL);
4444 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4446 else if(GetUserH(argv[1]))
4449 msg = unsplit_string(argv + 2, argc - 2, NULL);
4450 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4454 reply("MSG_NOT_TARGET_NAME");
4460 static CHANSERV_FUNC(cmd_emote)
4466 /* CTCP is so annoying. */
4467 msg = unsplit_string(argv + 1, argc - 1, NULL);
4468 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4470 else if(GetUserH(argv[1]))
4472 msg = unsplit_string(argv + 2, argc - 2, NULL);
4473 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4477 reply("MSG_NOT_TARGET_NAME");
4483 struct channelList *
4484 chanserv_support_channels(void)
4486 return &chanserv_conf.support_channels;
4489 static CHANSERV_FUNC(cmd_expire)
4491 int channel_count = registered_channels;
4492 expire_channels(NULL);
4493 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4498 chanserv_expire_suspension(void *data)
4500 struct suspended *suspended = data;
4501 struct chanNode *channel;
4503 if(!suspended->expires || (now < suspended->expires))
4504 suspended->revoked = now;
4505 channel = suspended->cData->channel;
4506 suspended->cData->channel = channel;
4507 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4508 if(!IsOffChannel(suspended->cData))
4510 struct mod_chanmode change;
4511 mod_chanmode_init(&change);
4513 change.args[0].mode = MODE_CHANOP;
4514 change.args[0].u.member = AddChannelUser(chanserv, channel);
4515 mod_chanmode_announce(chanserv, channel, &change);
4519 static CHANSERV_FUNC(cmd_csuspend)
4521 struct suspended *suspended;
4522 char reason[MAXLEN];
4523 time_t expiry, duration;
4524 struct userData *uData;
4528 if(IsProtected(channel->channel_info))
4530 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4534 if(argv[1][0] == '!')
4536 else if(IsSuspended(channel->channel_info))
4538 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4539 show_suspension_info(cmd, user, channel->channel_info->suspended);
4543 if(!strcmp(argv[1], "0"))
4545 else if((duration = ParseInterval(argv[1])))
4546 expiry = now + duration;
4549 reply("MSG_INVALID_DURATION", argv[1]);
4553 unsplit_string(argv + 2, argc - 2, reason);
4555 suspended = calloc(1, sizeof(*suspended));
4556 suspended->revoked = 0;
4557 suspended->issued = now;
4558 suspended->suspender = strdup(user->handle_info->handle);
4559 suspended->expires = expiry;
4560 suspended->reason = strdup(reason);
4561 suspended->cData = channel->channel_info;
4562 suspended->previous = suspended->cData->suspended;
4563 suspended->cData->suspended = suspended;
4565 if(suspended->expires)
4566 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4568 if(IsSuspended(channel->channel_info))
4570 suspended->previous->revoked = now;
4571 if(suspended->previous->expires)
4572 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4573 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4574 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4578 /* Mark all users in channel as absent. */
4579 for(uData = channel->channel_info->users; uData; uData = uData->next)
4588 /* Mark the channel as suspended, then part. */
4589 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4590 DelChannelUser(chanserv, channel, suspended->reason, 0);
4591 reply("CSMSG_SUSPENDED", channel->name);
4592 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4593 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4598 static CHANSERV_FUNC(cmd_cunsuspend)
4600 struct suspended *suspended;
4601 char message[MAXLEN];
4603 if(!IsSuspended(channel->channel_info))
4605 reply("CSMSG_NOT_SUSPENDED", channel->name);
4609 suspended = channel->channel_info->suspended;
4611 /* Expire the suspension and join ChanServ to the channel. */
4612 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4613 chanserv_expire_suspension(suspended);
4614 reply("CSMSG_UNSUSPENDED", channel->name);
4615 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4616 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4620 typedef struct chanservSearch
4628 unsigned long flags;
4632 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4635 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4640 search = malloc(sizeof(struct chanservSearch));
4641 memset(search, 0, sizeof(*search));
4644 for(i = 0; i < argc; i++)
4646 /* Assume all criteria require arguments. */
4649 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4653 if(!irccasecmp(argv[i], "name"))
4654 search->name = argv[++i];
4655 else if(!irccasecmp(argv[i], "registrar"))
4656 search->registrar = argv[++i];
4657 else if(!irccasecmp(argv[i], "unvisited"))
4658 search->unvisited = ParseInterval(argv[++i]);
4659 else if(!irccasecmp(argv[i], "registered"))
4660 search->registered = ParseInterval(argv[++i]);
4661 else if(!irccasecmp(argv[i], "flags"))
4664 if(!irccasecmp(argv[i], "nodelete"))
4665 search->flags |= CHANNEL_NODELETE;
4666 else if(!irccasecmp(argv[i], "suspended"))
4667 search->flags |= CHANNEL_SUSPENDED;
4670 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4674 else if(!irccasecmp(argv[i], "limit"))
4675 search->limit = strtoul(argv[++i], NULL, 10);
4678 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4683 if(search->name && !strcmp(search->name, "*"))
4685 if(search->registrar && !strcmp(search->registrar, "*"))
4686 search->registrar = 0;
4695 chanserv_channel_match(struct chanData *channel, search_t search)
4697 const char *name = channel->channel->name;
4698 if((search->name && !match_ircglob(name, search->name)) ||
4699 (search->registrar && !channel->registrar) ||
4700 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4701 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4702 (search->registered && (now - channel->registered) > search->registered) ||
4703 (search->flags && ((search->flags & channel->flags) != search->flags)))
4710 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4712 struct chanData *channel;
4713 unsigned int matches = 0;
4715 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4717 if(!chanserv_channel_match(channel, search))
4727 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4732 search_print(struct chanData *channel, void *data)
4734 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4737 static CHANSERV_FUNC(cmd_search)
4740 unsigned int matches;
4741 channel_search_func action;
4745 if(!irccasecmp(argv[1], "count"))
4746 action = search_count;
4747 else if(!irccasecmp(argv[1], "print"))
4748 action = search_print;
4751 reply("CSMSG_ACTION_INVALID", argv[1]);
4755 search = chanserv_search_create(user, argc - 2, argv + 2);
4759 if(action == search_count)
4760 search->limit = INT_MAX;
4762 if(action == search_print)
4763 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4765 matches = chanserv_channel_search(search, action, user);
4768 reply("MSG_MATCH_COUNT", matches);
4770 reply("MSG_NO_MATCHES");
4776 static CHANSERV_FUNC(cmd_unvisited)
4778 struct chanData *cData;
4779 time_t interval = chanserv_conf.channel_expire_delay;
4780 char buffer[INTERVALLEN];
4781 unsigned int limit = 25, matches = 0;
4785 interval = ParseInterval(argv[1]);
4787 limit = atoi(argv[2]);
4790 intervalString(buffer, interval, user->handle_info);
4791 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4793 for(cData = channelList; cData && matches < limit; cData = cData->next)
4795 if((now - cData->visited) < interval)
4798 intervalString(buffer, now - cData->visited, user->handle_info);
4799 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4806 static MODCMD_FUNC(chan_opt_defaulttopic)
4812 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4814 reply("CSMSG_TOPIC_LOCKED", channel->name);
4818 topic = unsplit_string(argv+1, argc-1, NULL);
4820 free(channel->channel_info->topic);
4821 if(topic[0] == '*' && topic[1] == 0)
4823 topic = channel->channel_info->topic = NULL;
4827 topic = channel->channel_info->topic = strdup(topic);
4828 if(channel->channel_info->topic_mask
4829 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4830 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4832 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4835 if(channel->channel_info->topic)
4836 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4838 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4842 static MODCMD_FUNC(chan_opt_topicmask)
4846 struct chanData *cData = channel->channel_info;
4849 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4851 reply("CSMSG_TOPIC_LOCKED", channel->name);
4855 mask = unsplit_string(argv+1, argc-1, NULL);
4857 if(cData->topic_mask)
4858 free(cData->topic_mask);
4859 if(mask[0] == '*' && mask[1] == 0)
4861 cData->topic_mask = 0;
4865 cData->topic_mask = strdup(mask);
4867 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4868 else if(!match_ircglob(cData->topic, cData->topic_mask))
4869 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4873 if(channel->channel_info->topic_mask)
4874 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4876 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4880 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4884 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4888 if(greeting[0] == '*' && greeting[1] == 0)
4892 unsigned int length = strlen(greeting);
4893 if(length > chanserv_conf.greeting_length)
4895 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4898 *data = strdup(greeting);
4907 reply(name, user_find_message(user, "MSG_NONE"));
4911 static MODCMD_FUNC(chan_opt_greeting)
4913 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4916 static MODCMD_FUNC(chan_opt_usergreeting)
4918 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4921 static MODCMD_FUNC(chan_opt_modes)
4923 struct mod_chanmode *new_modes;
4924 char modes[MODELEN];
4928 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4930 reply("CSMSG_NO_ACCESS");
4933 if(argv[1][0] == '*' && argv[1][1] == 0)
4935 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4937 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4939 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4942 else if(new_modes->argc > 1)
4944 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4945 mod_chanmode_free(new_modes);
4950 channel->channel_info->modes = *new_modes;
4951 modcmd_chanmode_announce(new_modes);
4952 mod_chanmode_free(new_modes);
4956 mod_chanmode_format(&channel->channel_info->modes, modes);
4958 reply("CSMSG_SET_MODES", modes);
4960 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4964 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4966 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4968 struct chanData *cData = channel->channel_info;
4973 /* Set flag according to value. */
4974 if(enabled_string(argv[1]))
4976 cData->flags |= mask;
4979 else if(disabled_string(argv[1]))
4981 cData->flags &= ~mask;
4986 reply("MSG_INVALID_BINARY", argv[1]);
4992 /* Find current option value. */
4993 value = (cData->flags & mask) ? 1 : 0;
4997 reply(name, user_find_message(user, "MSG_ON"));
4999 reply(name, user_find_message(user, "MSG_OFF"));
5003 static MODCMD_FUNC(chan_opt_nodelete)
5005 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5007 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5011 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5014 static MODCMD_FUNC(chan_opt_dynlimit)
5016 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5019 static MODCMD_FUNC(chan_opt_offchannel)
5021 struct chanData *cData = channel->channel_info;
5026 /* Set flag according to value. */
5027 if(enabled_string(argv[1]))
5029 if(!IsOffChannel(cData))
5030 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5031 cData->flags |= CHANNEL_OFFCHANNEL;
5034 else if(disabled_string(argv[1]))
5036 if(IsOffChannel(cData))
5038 struct mod_chanmode change;
5039 mod_chanmode_init(&change);
5041 change.args[0].mode = MODE_CHANOP;
5042 change.args[0].u.member = AddChannelUser(chanserv, channel);
5043 mod_chanmode_announce(chanserv, channel, &change);
5045 cData->flags &= ~CHANNEL_OFFCHANNEL;
5050 reply("MSG_INVALID_BINARY", argv[1]);
5056 /* Find current option value. */
5057 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5061 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5063 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5067 static MODCMD_FUNC(chan_opt_defaults)
5069 struct userData *uData;
5070 struct chanData *cData;
5071 const char *confirm;
5072 enum levelOption lvlOpt;
5073 enum charOption chOpt;
5075 cData = channel->channel_info;
5076 uData = GetChannelUser(cData, user->handle_info);
5077 if(!uData || (uData->access < UL_OWNER))
5079 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5082 confirm = make_confirmation_string(uData);
5083 if((argc < 2) || strcmp(argv[1], confirm))
5085 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5088 cData->flags = CHANNEL_DEFAULT_FLAGS;
5089 cData->modes = chanserv_conf.default_modes;
5090 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5091 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5092 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5093 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5094 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5099 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5101 struct chanData *cData = channel->channel_info;
5102 struct userData *uData;
5103 unsigned short value;
5107 if(!check_user_level(channel, user, option, 1, 1))
5109 reply("CSMSG_CANNOT_SET");
5112 value = user_level_from_name(argv[1], UL_OWNER+1);
5113 if(!value && strcmp(argv[1], "0"))
5115 reply("CSMSG_INVALID_ACCESS", argv[1]);
5118 uData = GetChannelUser(cData, user->handle_info);
5119 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5121 reply("CSMSG_BAD_SETLEVEL");
5127 if(value > cData->lvlOpts[lvlGiveOps])
5129 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5134 if(value < cData->lvlOpts[lvlGiveVoice])
5136 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5141 /* This test only applies to owners, since non-owners
5142 * trying to set an option to above their level get caught
5143 * by the CSMSG_BAD_SETLEVEL test above.
5145 if(value > uData->access)
5147 reply("CSMSG_BAD_SETTERS");
5154 cData->lvlOpts[option] = value;
5156 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5160 static MODCMD_FUNC(chan_opt_enfops)
5162 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5165 static MODCMD_FUNC(chan_opt_giveops)
5167 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5170 static MODCMD_FUNC(chan_opt_enfmodes)
5172 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5175 static MODCMD_FUNC(chan_opt_enftopic)
5177 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5180 static MODCMD_FUNC(chan_opt_pubcmd)
5182 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5185 static MODCMD_FUNC(chan_opt_setters)
5187 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5190 static MODCMD_FUNC(chan_opt_ctcpusers)
5192 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5195 static MODCMD_FUNC(chan_opt_userinfo)
5197 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5200 static MODCMD_FUNC(chan_opt_givevoice)
5202 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5205 static MODCMD_FUNC(chan_opt_topicsnarf)
5207 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5210 static MODCMD_FUNC(chan_opt_inviteme)
5212 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5216 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5218 struct chanData *cData = channel->channel_info;
5219 int count = charOptions[option].count, index;
5223 index = atoi(argv[1]);
5225 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5227 reply("CSMSG_INVALID_NUMERIC", index);
5228 /* Show possible values. */
5229 for(index = 0; index < count; index++)
5230 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5234 cData->chOpts[option] = charOptions[option].values[index].value;
5238 /* Find current option value. */
5241 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5245 /* Somehow, the option value is corrupt; reset it to the default. */
5246 cData->chOpts[option] = charOptions[option].default_value;
5251 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5255 static MODCMD_FUNC(chan_opt_protect)
5257 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5260 static MODCMD_FUNC(chan_opt_toys)
5262 return channel_multiple_option(chToys, CSFUNC_ARGS);
5265 static MODCMD_FUNC(chan_opt_ctcpreaction)
5267 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5270 static MODCMD_FUNC(chan_opt_topicrefresh)
5272 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5275 static struct svccmd_list set_shows_list;
5278 handle_svccmd_unbind(struct svccmd *target) {
5280 for(ii=0; ii<set_shows_list.used; ++ii)
5281 if(target == set_shows_list.list[ii])
5282 set_shows_list.used = 0;
5285 static CHANSERV_FUNC(cmd_set)
5287 struct svccmd *subcmd;
5291 /* Check if we need to (re-)initialize set_shows_list. */
5292 if(!set_shows_list.used)
5294 if(!set_shows_list.size)
5296 set_shows_list.size = chanserv_conf.set_shows->used;
5297 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5299 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5301 const char *name = chanserv_conf.set_shows->list[ii];
5302 sprintf(buf, "%s %s", argv[0], name);
5303 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5306 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5309 svccmd_list_append(&set_shows_list, subcmd);
5315 reply("CSMSG_CHANNEL_OPTIONS");
5316 for(ii = 0; ii < set_shows_list.used; ii++)
5318 subcmd = set_shows_list.list[ii];
5319 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5324 sprintf(buf, "%s %s", argv[0], argv[1]);
5325 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5328 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5331 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5333 reply("CSMSG_NO_ACCESS");
5337 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5341 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5343 struct userData *uData;
5345 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5348 reply("CSMSG_NOT_USER", channel->name);
5354 /* Just show current option value. */
5356 else if(enabled_string(argv[1]))
5358 uData->flags |= mask;
5360 else if(disabled_string(argv[1]))
5362 uData->flags &= ~mask;
5366 reply("MSG_INVALID_BINARY", argv[1]);
5370 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5374 static MODCMD_FUNC(user_opt_noautoop)
5376 struct userData *uData;
5378 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5381 reply("CSMSG_NOT_USER", channel->name);
5384 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5385 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5387 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5390 static MODCMD_FUNC(user_opt_autoinvite)
5392 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5395 static MODCMD_FUNC(user_opt_info)
5397 struct userData *uData;
5400 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5404 /* If they got past the command restrictions (which require access)
5405 * but fail this test, we have some fool with security override on.
5407 reply("CSMSG_NOT_USER", channel->name);
5414 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5415 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5417 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5420 bp = strcspn(infoline, "\001");
5423 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5428 if(infoline[0] == '*' && infoline[1] == 0)
5431 uData->info = strdup(infoline);
5434 reply("CSMSG_USET_INFO", uData->info);
5436 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5440 struct svccmd_list uset_shows_list;
5442 static CHANSERV_FUNC(cmd_uset)
5444 struct svccmd *subcmd;
5448 /* Check if we need to (re-)initialize uset_shows_list. */
5449 if(!uset_shows_list.used)
5453 "NoAutoOp", "AutoInvite", "Info"
5456 if(!uset_shows_list.size)
5458 uset_shows_list.size = ArrayLength(options);
5459 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5461 for(ii = 0; ii < ArrayLength(options); ii++)
5463 const char *name = options[ii];
5464 sprintf(buf, "%s %s", argv[0], name);
5465 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5468 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5471 svccmd_list_append(&uset_shows_list, subcmd);
5477 /* Do this so options are presented in a consistent order. */
5478 reply("CSMSG_USER_OPTIONS");
5479 for(ii = 0; ii < uset_shows_list.used; ii++)
5480 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5484 sprintf(buf, "%s %s", argv[0], argv[1]);
5485 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5488 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5492 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5495 static CHANSERV_FUNC(cmd_giveownership)
5497 struct handle_info *new_owner_hi;
5498 struct userData *new_owner, *curr_user;
5499 struct chanData *cData = channel->channel_info;
5500 struct do_not_register *dnr;
5502 unsigned short co_access;
5503 char reason[MAXLEN];
5506 curr_user = GetChannelAccess(cData, user->handle_info);
5507 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5508 if(!curr_user || (curr_user->access != UL_OWNER))
5510 struct userData *owner = NULL;
5511 for(curr_user = channel->channel_info->users;
5513 curr_user = curr_user->next)
5515 if(curr_user->access != UL_OWNER)
5519 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5526 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5528 char delay[INTERVALLEN];
5529 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5530 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5533 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5535 if(new_owner_hi == user->handle_info)
5537 reply("CSMSG_NO_TRANSFER_SELF");
5540 new_owner = GetChannelAccess(cData, new_owner_hi);
5545 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5549 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5553 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5555 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5558 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5559 if(!IsHelping(user))
5560 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5562 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5565 if(new_owner->access >= UL_COOWNER)
5566 co_access = new_owner->access;
5568 co_access = UL_COOWNER;
5569 new_owner->access = UL_OWNER;
5571 curr_user->access = co_access;
5572 cData->ownerTransfer = now;
5573 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5574 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5575 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5579 static CHANSERV_FUNC(cmd_suspend)
5581 struct handle_info *hi;
5582 struct userData *self, *target;
5585 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5586 self = GetChannelUser(channel->channel_info, user->handle_info);
5587 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5589 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5592 if(target->access >= self->access)
5594 reply("MSG_USER_OUTRANKED", hi->handle);
5597 if(target->flags & USER_SUSPENDED)
5599 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5604 target->present = 0;
5607 target->flags |= USER_SUSPENDED;
5608 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5612 static CHANSERV_FUNC(cmd_unsuspend)
5614 struct handle_info *hi;
5615 struct userData *self, *target;
5618 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5619 self = GetChannelUser(channel->channel_info, user->handle_info);
5620 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5622 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5625 if(target->access >= self->access)
5627 reply("MSG_USER_OUTRANKED", hi->handle);
5630 if(!(target->flags & USER_SUSPENDED))
5632 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5635 target->flags &= ~USER_SUSPENDED;
5636 scan_user_presence(target, NULL);
5637 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5641 static MODCMD_FUNC(cmd_deleteme)
5643 struct handle_info *hi;
5644 struct userData *target;
5645 const char *confirm_string;
5646 unsigned short access;
5649 hi = user->handle_info;
5650 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5652 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5655 if(target->access == UL_OWNER)
5657 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5660 confirm_string = make_confirmation_string(target);
5661 if((argc < 2) || strcmp(argv[1], confirm_string))
5663 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5666 access = target->access;
5667 channel_name = strdup(channel->name);
5668 del_channel_user(target, 1);
5669 reply("CSMSG_DELETED_YOU", access, channel_name);
5675 chanserv_refresh_topics(UNUSED_ARG(void *data))
5677 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5678 struct chanData *cData;
5681 for(cData = channelList; cData; cData = cData->next)
5683 if(IsSuspended(cData))
5685 opt = cData->chOpts[chTopicRefresh];
5688 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5691 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5692 cData->last_refresh = refresh_num;
5694 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5697 static CHANSERV_FUNC(cmd_unf)
5701 char response[MAXLEN];
5702 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5703 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5704 irc_privmsg(cmd->parent->bot, channel->name, response);
5707 reply("CSMSG_UNF_RESPONSE");
5711 static CHANSERV_FUNC(cmd_ping)
5715 char response[MAXLEN];
5716 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5717 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5718 irc_privmsg(cmd->parent->bot, channel->name, response);
5721 reply("CSMSG_PING_RESPONSE");
5725 static CHANSERV_FUNC(cmd_wut)
5729 char response[MAXLEN];
5730 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5731 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5732 irc_privmsg(cmd->parent->bot, channel->name, response);
5735 reply("CSMSG_WUT_RESPONSE");
5739 static CHANSERV_FUNC(cmd_8ball)
5741 unsigned int i, j, accum;
5746 for(i=1; i<argc; i++)
5747 for(j=0; argv[i][j]; j++)
5748 accum = (accum << 5) - accum + toupper(argv[i][j]);
5749 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5752 char response[MAXLEN];
5753 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5754 irc_privmsg(cmd->parent->bot, channel->name, response);
5757 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5761 static CHANSERV_FUNC(cmd_d)
5763 unsigned long sides, count, modifier, ii, total;
5764 char response[MAXLEN], *sep;
5768 if((count = strtoul(argv[1], &sep, 10)) < 1)
5778 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5779 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5783 else if((sep[0] == '-') && isdigit(sep[1]))
5784 modifier = strtoul(sep, NULL, 10);
5785 else if((sep[0] == '+') && isdigit(sep[1]))
5786 modifier = strtoul(sep+1, NULL, 10);
5793 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5798 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5801 for(total = ii = 0; ii < count; ++ii)
5802 total += (rand() % sides) + 1;
5805 if((count > 1) || modifier)
5807 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5808 sprintf(response, fmt, total, count, sides, modifier);
5812 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5813 sprintf(response, fmt, total, sides);
5816 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5818 send_message_type(4, user, cmd->parent->bot, "%s", response);
5822 static CHANSERV_FUNC(cmd_huggle)
5824 /* CTCP must be via PRIVMSG, never notice */
5826 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5828 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5833 chanserv_adjust_limit(void *data)
5835 struct mod_chanmode change;
5836 struct chanData *cData = data;
5837 struct chanNode *channel = cData->channel;
5840 if(IsSuspended(cData))
5843 cData->limitAdjusted = now;
5844 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5845 if(cData->modes.modes_set & MODE_LIMIT)
5847 if(limit > cData->modes.new_limit)
5848 limit = cData->modes.new_limit;
5849 else if(limit == cData->modes.new_limit)
5853 mod_chanmode_init(&change);
5854 change.modes_set = MODE_LIMIT;
5855 change.new_limit = limit;
5856 mod_chanmode_announce(chanserv, channel, &change);
5860 handle_new_channel(struct chanNode *channel)
5862 struct chanData *cData;
5864 if(!(cData = channel->channel_info))
5867 if(cData->modes.modes_set || cData->modes.modes_clear)
5868 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5870 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5871 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5874 /* Welcome to my worst nightmare. Warning: Read (or modify)
5875 the code below at your own risk. */
5877 handle_join(struct modeNode *mNode)
5879 struct mod_chanmode change;
5880 struct userNode *user = mNode->user;
5881 struct chanNode *channel = mNode->channel;
5882 struct chanData *cData;
5883 struct userData *uData = NULL;
5884 struct banData *bData;
5885 struct handle_info *handle;
5886 unsigned int modes = 0, info = 0;
5889 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5892 cData = channel->channel_info;
5893 if(channel->members.used > cData->max)
5894 cData->max = channel->members.used;
5896 /* Check for bans. If they're joining through a ban, one of two
5898 * 1: Join during a netburst, by riding the break. Kick them
5899 * unless they have ops or voice in the channel.
5900 * 2: They're allowed to join through the ban (an invite in
5901 * ircu2.10, or a +e on Hybrid, or something).
5902 * If they're not joining through a ban, and the banlist is not
5903 * full, see if they're on the banlist for the channel. If so,
5906 if(user->uplink->burst && !mNode->modes)
5909 for(ii = 0; ii < channel->banlist.used; ii++)
5911 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5913 /* Riding a netburst. Naughty. */
5914 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5920 mod_chanmode_init(&change);
5922 if(channel->banlist.used < MAXBANS)
5924 /* Not joining through a ban. */
5925 for(bData = cData->bans;
5926 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5927 bData = bData->next);
5931 char kick_reason[MAXLEN];
5932 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5934 bData->triggered = now;
5935 if(bData != cData->bans)
5937 /* Shuffle the ban to the head of the list. */
5939 bData->next->prev = bData->prev;
5941 bData->prev->next = bData->next;
5944 bData->next = cData->bans;
5947 cData->bans->prev = bData;
5948 cData->bans = bData;
5951 change.args[0].mode = MODE_BAN;
5952 change.args[0].u.hostmask = bData->mask;
5953 mod_chanmode_announce(chanserv, channel, &change);
5954 KickChannelUser(user, channel, chanserv, kick_reason);
5959 /* ChanServ will not modify the limits in join-flooded channels.
5960 It will also skip DynLimit processing when the user (or srvx)
5961 is bursting in, because there are likely more incoming. */
5962 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5963 && !user->uplink->burst
5964 && !channel->join_flooded
5965 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5967 /* The user count has begun "bumping" into the channel limit,
5968 so set a timer to raise the limit a bit. Any previous
5969 timers are removed so three incoming users within the delay
5970 results in one limit change, not three. */
5972 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5973 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5976 if(channel->join_flooded)
5978 /* don't automatically give ops or voice during a join flood */
5980 else if(cData->lvlOpts[lvlGiveOps] == 0)
5981 modes |= MODE_CHANOP;
5982 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5983 modes |= MODE_VOICE;
5985 greeting = cData->greeting;
5986 if(user->handle_info)
5988 handle = user->handle_info;
5990 if(IsHelper(user) && !IsHelping(user))
5993 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5995 if(channel == chanserv_conf.support_channels.list[ii])
5997 HANDLE_SET_FLAG(user->handle_info, HELPING);
6003 uData = GetTrueChannelAccess(cData, handle);
6004 if(uData && !IsUserSuspended(uData))
6006 /* Ops and above were handled by the above case. */
6007 if(IsUserAutoOp(uData))
6009 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6010 modes |= MODE_CHANOP;
6011 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6012 modes |= MODE_VOICE;
6014 if(uData->access >= UL_PRESENT)
6015 cData->visited = now;
6016 if(cData->user_greeting)
6017 greeting = cData->user_greeting;
6019 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6020 && ((now - uData->seen) >= chanserv_conf.info_delay)
6028 /* If user joining normally (not during burst), apply op or voice,
6029 * and send greeting/userinfo as appropriate.
6031 if(!user->uplink->burst)
6035 if(modes & MODE_CHANOP)
6036 modes &= ~MODE_VOICE;
6037 change.args[0].mode = modes;
6038 change.args[0].u.member = mNode;
6039 mod_chanmode_announce(chanserv, channel, &change);
6042 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6044 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6050 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6052 struct mod_chanmode change;
6053 struct userData *channel;
6054 unsigned int ii, jj;
6056 if(!user->handle_info)
6059 mod_chanmode_init(&change);
6061 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6063 struct chanNode *cn;
6064 struct modeNode *mn;
6065 if(IsUserSuspended(channel)
6066 || IsSuspended(channel->channel)
6067 || !(cn = channel->channel->channel))
6070 mn = GetUserMode(cn, user);
6073 if(!IsUserSuspended(channel)
6074 && IsUserAutoInvite(channel)
6075 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6077 && !user->uplink->burst)
6078 irc_invite(chanserv, user, cn);
6082 if(channel->access >= UL_PRESENT)
6083 channel->channel->visited = now;
6085 if(IsUserAutoOp(channel))
6087 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6088 change.args[0].mode = MODE_CHANOP;
6089 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6090 change.args[0].mode = MODE_VOICE;
6092 change.args[0].mode = 0;
6093 change.args[0].u.member = mn;
6094 if(change.args[0].mode)
6095 mod_chanmode_announce(chanserv, cn, &change);
6098 channel->seen = now;
6099 channel->present = 1;
6102 for(ii = 0; ii < user->channels.used; ++ii)
6104 struct chanNode *channel = user->channels.list[ii]->channel;
6105 struct banData *ban;
6107 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6108 || !channel->channel_info
6109 || IsSuspended(channel->channel_info))
6111 for(jj = 0; jj < channel->banlist.used; ++jj)
6112 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6114 if(jj < channel->banlist.used)
6116 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6118 char kick_reason[MAXLEN];
6119 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6121 change.args[0].mode = MODE_BAN;
6122 change.args[0].u.hostmask = ban->mask;
6123 mod_chanmode_announce(chanserv, channel, &change);
6124 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6125 KickChannelUser(user, channel, chanserv, kick_reason);
6126 ban->triggered = now;
6131 if(IsSupportHelper(user))
6133 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6135 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6137 HANDLE_SET_FLAG(user->handle_info, HELPING);
6145 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6147 struct chanData *cData;
6148 struct userData *uData;
6150 cData = mn->channel->channel_info;
6151 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6154 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6156 /* Allow for a bit of padding so that the limit doesn't
6157 track the user count exactly, which could get annoying. */
6158 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6160 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6161 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6165 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6167 scan_user_presence(uData, mn->user);
6171 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6173 unsigned int ii, jj;
6174 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6176 for(jj = 0; jj < mn->user->channels.used; ++jj)
6177 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6179 if(jj < mn->user->channels.used)
6182 if(ii == chanserv_conf.support_channels.used)
6183 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6188 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6190 struct userData *uData;
6192 if(!channel->channel_info || !kicker || IsService(kicker)
6193 || (kicker == victim) || IsSuspended(channel->channel_info)
6194 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6197 if(protect_user(victim, kicker, channel->channel_info))
6199 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6200 KickChannelUser(kicker, channel, chanserv, reason);
6203 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6208 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6210 struct chanData *cData;
6212 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6215 cData = channel->channel_info;
6216 if(bad_topic(channel, user, channel->topic))
6218 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6219 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6220 SetChannelTopic(channel, chanserv, old_topic, 1);
6221 else if(cData->topic)
6222 SetChannelTopic(channel, chanserv, cData->topic, 1);
6225 /* With topicsnarf, grab the topic and save it as the default topic. */
6226 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6229 cData->topic = strdup(channel->topic);
6235 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6237 struct mod_chanmode *bounce = NULL;
6238 unsigned int bnc, ii;
6241 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6244 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6245 && mode_lock_violated(&channel->channel_info->modes, change))
6247 char correct[MAXLEN];
6248 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6249 mod_chanmode_format(&channel->channel_info->modes, correct);
6250 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6252 for(ii = bnc = 0; ii < change->argc; ++ii)
6254 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6256 const struct userNode *victim = change->args[ii].u.member->user;
6257 if(!protect_user(victim, user, channel->channel_info))
6260 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6263 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6264 bounce->args[bnc].u.member = GetUserMode(channel, user);
6265 if(bounce->args[bnc].u.member)
6269 bounce->args[bnc].mode = MODE_CHANOP;
6270 bounce->args[bnc].u.member = change->args[ii].u.member;
6272 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6274 else if(change->args[ii].mode & MODE_CHANOP)
6276 const struct userNode *victim = change->args[ii].u.member->user;
6277 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6280 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6281 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6282 bounce->args[bnc].u.member = change->args[ii].u.member;
6285 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6287 const char *ban = change->args[ii].u.hostmask;
6288 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6291 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6292 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6293 bounce->args[bnc].u.hostmask = strdup(ban);
6295 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6300 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6301 mod_chanmode_announce(chanserv, channel, bounce);
6302 for(ii = 0; ii < change->argc; ++ii)
6303 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6304 free((char*)bounce->args[ii].u.hostmask);
6305 mod_chanmode_free(bounce);
6310 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6312 struct chanNode *channel;
6313 struct banData *bData;
6314 struct mod_chanmode change;
6315 unsigned int ii, jj;
6316 char kick_reason[MAXLEN];
6318 mod_chanmode_init(&change);
6320 change.args[0].mode = MODE_BAN;
6321 for(ii = 0; ii < user->channels.used; ++ii)
6323 channel = user->channels.list[ii]->channel;
6324 /* Need not check for bans if they're opped or voiced. */
6325 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6327 /* Need not check for bans unless channel registration is active. */
6328 if(!channel->channel_info || IsSuspended(channel->channel_info))
6330 /* Look for a matching ban already on the channel. */
6331 for(jj = 0; jj < channel->banlist.used; ++jj)
6332 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6334 /* Need not act if we found one. */
6335 if(jj < channel->banlist.used)
6337 /* Look for a matching ban in this channel. */
6338 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6340 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6342 change.args[0].u.hostmask = bData->mask;
6343 mod_chanmode_announce(chanserv, channel, &change);
6344 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6345 KickChannelUser(user, channel, chanserv, kick_reason);
6346 bData->triggered = now;
6347 break; /* we don't need to check any more bans in the channel */
6352 static void handle_rename(struct handle_info *handle, const char *old_handle)
6354 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6358 dict_remove2(handle_dnrs, old_handle, 1);
6359 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6360 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6365 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6367 struct userNode *h_user;
6369 if(handle->channels)
6371 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6372 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6374 while(handle->channels)
6375 del_channel_user(handle->channels, 1);
6380 handle_server_link(UNUSED_ARG(struct server *server))
6382 struct chanData *cData;
6384 for(cData = channelList; cData; cData = cData->next)
6386 if(!IsSuspended(cData))
6387 cData->may_opchan = 1;
6388 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6389 && !cData->channel->join_flooded
6390 && ((cData->channel->limit - cData->channel->members.used)
6391 < chanserv_conf.adjust_threshold))
6393 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6394 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6400 chanserv_conf_read(void)
6404 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6405 struct mod_chanmode *change;
6406 struct string_list *strlist;
6407 struct chanNode *chan;
6410 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6412 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6415 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6416 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6417 chanserv_conf.support_channels.used = 0;
6418 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6420 for(ii = 0; ii < strlist->used; ++ii)
6422 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6425 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6427 channelList_append(&chanserv_conf.support_channels, chan);
6430 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6433 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6436 chan = AddChannel(str, now, str2, NULL);
6438 channelList_append(&chanserv_conf.support_channels, chan);
6440 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6441 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6442 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6443 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6444 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6445 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6446 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6447 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6448 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6449 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6450 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6451 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6452 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6453 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6454 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6455 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6456 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6457 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6458 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6459 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6460 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6461 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6462 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6464 NickChange(chanserv, str, 0);
6465 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6466 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6467 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6468 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6469 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6470 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6471 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6472 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6473 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6474 chanserv_conf.max_owned = str ? atoi(str) : 5;
6475 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6476 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6477 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6478 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6479 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6480 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6481 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6484 safestrncpy(mode_line, str, sizeof(mode_line));
6485 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6486 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6487 && (change->argc < 2))
6489 chanserv_conf.default_modes = *change;
6490 mod_chanmode_free(change);
6492 free_string_list(chanserv_conf.set_shows);
6493 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6495 strlist = string_list_copy(strlist);
6498 static const char *list[] = {
6499 /* free form text */
6500 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6501 /* options based on user level */
6502 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6503 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6504 /* multiple choice options */
6505 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6506 /* binary options */
6507 "DynLimit", "NoDelete",
6512 strlist = alloc_string_list(ArrayLength(list)-1);
6513 for(ii=0; list[ii]; ii++)
6514 string_list_append(strlist, strdup(list[ii]));
6516 chanserv_conf.set_shows = strlist;
6517 /* We don't look things up now, in case the list refers to options
6518 * defined by modules initialized after this point. Just mark the
6519 * function list as invalid, so it will be initialized.
6521 set_shows_list.used = 0;
6522 free_string_list(chanserv_conf.eightball);
6523 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6526 strlist = string_list_copy(strlist);
6530 strlist = alloc_string_list(4);
6531 string_list_append(strlist, strdup("Yes."));
6532 string_list_append(strlist, strdup("No."));
6533 string_list_append(strlist, strdup("Maybe so."));
6535 chanserv_conf.eightball = strlist;
6536 free_string_list(chanserv_conf.old_ban_names);
6537 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6539 strlist = string_list_copy(strlist);
6541 strlist = alloc_string_list(2);
6542 chanserv_conf.old_ban_names = strlist;
6543 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6544 off_channel = str ? atoi(str) : 0;
6548 chanserv_note_type_read(const char *key, struct record_data *rd)
6551 struct note_type *ntype;
6554 if(!(obj = GET_RECORD_OBJECT(rd)))
6556 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6559 if(!(ntype = chanserv_create_note_type(key)))
6561 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6565 /* Figure out set access */
6566 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6568 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6569 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6571 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6573 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6574 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6576 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6578 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6582 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6583 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6584 ntype->set_access.min_opserv = 0;
6587 /* Figure out visibility */
6588 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6589 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6590 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6591 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6592 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6593 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6594 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6595 ntype->visible_type = NOTE_VIS_ALL;
6597 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6599 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6600 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6604 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6606 struct handle_info *handle;
6607 struct userData *uData;
6608 char *seen, *inf, *flags;
6610 unsigned short access;
6612 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6614 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6618 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6619 if(access > UL_OWNER)
6621 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6625 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6626 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6627 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6628 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6629 handle = get_handle_info(key);
6632 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6636 uData = add_channel_user(chan, handle, access, last_seen, inf);
6637 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6641 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6643 struct banData *bData;
6644 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6645 time_t set_time, triggered_time, expires_time;
6647 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6649 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6653 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6654 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6655 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6656 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6657 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6658 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6659 if (!reason || !owner)
6662 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6663 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6665 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6667 expires_time = set_time + atoi(s_duration);
6671 if(!reason || (expires_time && (expires_time < now)))
6674 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6677 static struct suspended *
6678 chanserv_read_suspended(dict_t obj)
6680 struct suspended *suspended = calloc(1, sizeof(*suspended));
6684 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6685 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6686 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6687 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6688 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6689 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6690 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6691 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6692 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6693 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6698 chanserv_channel_read(const char *key, struct record_data *hir)
6700 struct suspended *suspended;
6701 struct mod_chanmode *modes;
6702 struct chanNode *cNode;
6703 struct chanData *cData;
6704 struct dict *channel, *obj;
6705 char *str, *argv[10];
6709 channel = hir->d.object;
6711 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6714 cNode = AddChannel(key, now, NULL, NULL);
6717 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6720 cData = register_channel(cNode, str);
6723 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6727 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6729 enum levelOption lvlOpt;
6730 enum charOption chOpt;
6732 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6733 cData->flags = atoi(str);
6735 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6737 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6739 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6740 else if(levelOptions[lvlOpt].old_flag)
6742 if(cData->flags & levelOptions[lvlOpt].old_flag)
6743 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6745 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6749 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6751 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6753 cData->chOpts[chOpt] = str[0];
6756 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6758 enum levelOption lvlOpt;
6759 enum charOption chOpt;
6762 cData->flags = base64toint(str, 5);
6763 count = strlen(str += 5);
6764 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6767 if(levelOptions[lvlOpt].old_flag)
6769 if(cData->flags & levelOptions[lvlOpt].old_flag)
6770 lvl = levelOptions[lvlOpt].flag_value;
6772 lvl = levelOptions[lvlOpt].default_value;
6774 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6776 case 'c': lvl = UL_COOWNER; break;
6777 case 'm': lvl = UL_MASTER; break;
6778 case 'n': lvl = UL_OWNER+1; break;
6779 case 'o': lvl = UL_OP; break;
6780 case 'p': lvl = UL_PEON; break;
6781 case 'w': lvl = UL_OWNER; break;
6782 default: lvl = 0; break;
6784 cData->lvlOpts[lvlOpt] = lvl;
6786 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6787 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6790 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6792 suspended = chanserv_read_suspended(obj);
6793 cData->suspended = suspended;
6794 suspended->cData = cData;
6795 /* We could use suspended->expires and suspended->revoked to
6796 * set the CHANNEL_SUSPENDED flag, but we don't. */
6798 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6800 suspended = calloc(1, sizeof(*suspended));
6801 suspended->issued = 0;
6802 suspended->revoked = 0;
6803 suspended->suspender = strdup(str);
6804 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6805 suspended->expires = str ? atoi(str) : 0;
6806 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6807 suspended->reason = strdup(str ? str : "No reason");
6808 suspended->previous = NULL;
6809 cData->suspended = suspended;
6810 suspended->cData = cData;
6814 cData->flags &= ~CHANNEL_SUSPENDED;
6815 suspended = NULL; /* to squelch a warning */
6818 if(IsSuspended(cData)) {
6819 if(suspended->expires > now)
6820 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6821 else if(suspended->expires)
6822 cData->flags &= ~CHANNEL_SUSPENDED;
6825 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6826 struct mod_chanmode change;
6827 mod_chanmode_init(&change);
6829 change.args[0].mode = MODE_CHANOP;
6830 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6831 mod_chanmode_announce(chanserv, cNode, &change);
6834 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6835 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6836 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6837 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6838 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6839 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6840 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6841 cData->max = str ? atoi(str) : 0;
6842 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6843 cData->greeting = str ? strdup(str) : NULL;
6844 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6845 cData->user_greeting = str ? strdup(str) : NULL;
6846 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6847 cData->topic_mask = str ? strdup(str) : NULL;
6848 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6849 cData->topic = str ? strdup(str) : NULL;
6851 if(!IsSuspended(cData)
6852 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6853 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6854 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6855 cData->modes = *modes;
6857 cData->modes.modes_set |= MODE_REGISTERED;
6858 if(cData->modes.argc > 1)
6859 cData->modes.argc = 1;
6860 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6861 mod_chanmode_free(modes);
6864 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6865 for(it = dict_first(obj); it; it = iter_next(it))
6866 user_read_helper(iter_key(it), iter_data(it), cData);
6868 if(!cData->users && !IsProtected(cData))
6870 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6871 unregister_channel(cData, "has empty user list.");
6875 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6876 for(it = dict_first(obj); it; it = iter_next(it))
6877 ban_read_helper(iter_key(it), iter_data(it), cData);
6879 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6880 for(it = dict_first(obj); it; it = iter_next(it))
6882 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6883 struct record_data *rd = iter_data(it);
6884 const char *note, *setter;
6886 if(rd->type != RECDB_OBJECT)
6888 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6892 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6894 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6896 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6900 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6901 if(!setter) setter = "<unknown>";
6902 chanserv_add_channel_note(cData, ntype, setter, note);
6910 chanserv_dnr_read(const char *key, struct record_data *hir)
6912 const char *setter, *reason, *str;
6913 struct do_not_register *dnr;
6915 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6918 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6921 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6924 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6927 dnr = chanserv_add_dnr(key, setter, reason);
6930 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6932 dnr->set = atoi(str);
6938 chanserv_saxdb_read(struct dict *database)
6940 struct dict *section;
6943 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6944 for(it = dict_first(section); it; it = iter_next(it))
6945 chanserv_note_type_read(iter_key(it), iter_data(it));
6947 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6948 for(it = dict_first(section); it; it = iter_next(it))
6949 chanserv_channel_read(iter_key(it), iter_data(it));
6951 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6952 for(it = dict_first(section); it; it = iter_next(it))
6953 chanserv_dnr_read(iter_key(it), iter_data(it));
6959 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6961 int high_present = 0;
6962 saxdb_start_record(ctx, KEY_USERS, 1);
6963 for(; uData; uData = uData->next)
6965 if((uData->access >= UL_PRESENT) && uData->present)
6967 saxdb_start_record(ctx, uData->handle->handle, 0);
6968 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6969 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6971 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6973 saxdb_write_string(ctx, KEY_INFO, uData->info);
6974 saxdb_end_record(ctx);
6976 saxdb_end_record(ctx);
6977 return high_present;
6981 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6985 saxdb_start_record(ctx, KEY_BANS, 1);
6986 for(; bData; bData = bData->next)
6988 saxdb_start_record(ctx, bData->mask, 0);
6989 saxdb_write_int(ctx, KEY_SET, bData->set);
6990 if(bData->triggered)
6991 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6993 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6995 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6997 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6998 saxdb_end_record(ctx);
7000 saxdb_end_record(ctx);
7004 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7006 saxdb_start_record(ctx, name, 0);
7007 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7008 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7010 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7012 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7014 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7016 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7017 saxdb_end_record(ctx);
7021 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7025 enum levelOption lvlOpt;
7026 enum charOption chOpt;
7028 saxdb_start_record(ctx, channel->channel->name, 1);
7030 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7031 saxdb_write_int(ctx, KEY_MAX, channel->max);
7033 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7034 if(channel->registrar)
7035 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7036 if(channel->greeting)
7037 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7038 if(channel->user_greeting)
7039 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7040 if(channel->topic_mask)
7041 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7042 if(channel->suspended)
7043 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7045 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7046 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7047 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7048 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7049 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7051 buf[0] = channel->chOpts[chOpt];
7053 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7055 saxdb_end_record(ctx);
7057 if(channel->modes.modes_set || channel->modes.modes_clear)
7059 mod_chanmode_format(&channel->modes, buf);
7060 saxdb_write_string(ctx, KEY_MODES, buf);
7063 high_present = chanserv_write_users(ctx, channel->users);
7064 chanserv_write_bans(ctx, channel->bans);
7066 if(dict_size(channel->notes))
7070 saxdb_start_record(ctx, KEY_NOTES, 1);
7071 for(it = dict_first(channel->notes); it; it = iter_next(it))
7073 struct note *note = iter_data(it);
7074 saxdb_start_record(ctx, iter_key(it), 0);
7075 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7076 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7077 saxdb_end_record(ctx);
7079 saxdb_end_record(ctx);
7082 if(channel->ownerTransfer)
7083 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7084 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7085 saxdb_end_record(ctx);
7089 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7093 saxdb_start_record(ctx, ntype->name, 0);
7094 switch(ntype->set_access_type)
7096 case NOTE_SET_CHANNEL_ACCESS:
7097 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7099 case NOTE_SET_CHANNEL_SETTER:
7100 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7102 case NOTE_SET_PRIVILEGED: default:
7103 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7106 switch(ntype->visible_type)
7108 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7109 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7110 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7112 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7113 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7114 saxdb_end_record(ctx);
7118 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7120 struct do_not_register *dnr;
7123 for(it = dict_first(dnrs); it; it = iter_next(it))
7125 dnr = iter_data(it);
7126 saxdb_start_record(ctx, dnr->chan_name, 0);
7128 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7129 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7130 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7131 saxdb_end_record(ctx);
7136 chanserv_saxdb_write(struct saxdb_context *ctx)
7139 struct chanData *channel;
7142 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7143 for(it = dict_first(note_types); it; it = iter_next(it))
7144 chanserv_write_note_type(ctx, iter_data(it));
7145 saxdb_end_record(ctx);
7148 saxdb_start_record(ctx, KEY_DNR, 1);
7149 write_dnrs_helper(ctx, handle_dnrs);
7150 write_dnrs_helper(ctx, plain_dnrs);
7151 write_dnrs_helper(ctx, mask_dnrs);
7152 saxdb_end_record(ctx);
7155 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7156 for(channel = channelList; channel; channel = channel->next)
7157 chanserv_write_channel(ctx, channel);
7158 saxdb_end_record(ctx);
7164 chanserv_db_cleanup(void) {
7166 unreg_part_func(handle_part);
7168 unregister_channel(channelList, "terminating.");
7169 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7170 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7171 free(chanserv_conf.support_channels.list);
7172 dict_delete(handle_dnrs);
7173 dict_delete(plain_dnrs);
7174 dict_delete(mask_dnrs);
7175 dict_delete(note_types);
7176 free_string_list(chanserv_conf.eightball);
7177 free_string_list(chanserv_conf.old_ban_names);
7178 free_string_list(chanserv_conf.set_shows);
7179 free(set_shows_list.list);
7180 free(uset_shows_list.list);
7183 struct userData *helper = helperList;
7184 helperList = helperList->next;
7189 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7190 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7191 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7194 init_chanserv(const char *nick)
7196 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7197 conf_register_reload(chanserv_conf_read);
7199 reg_server_link_func(handle_server_link);
7201 reg_new_channel_func(handle_new_channel);
7202 reg_join_func(handle_join);
7203 reg_part_func(handle_part);
7204 reg_kick_func(handle_kick);
7205 reg_topic_func(handle_topic);
7206 reg_mode_change_func(handle_mode);
7207 reg_nick_change_func(handle_nick_change);
7209 reg_auth_func(handle_auth);
7210 reg_handle_rename_func(handle_rename);
7211 reg_unreg_func(handle_unreg);
7213 handle_dnrs = dict_new();
7214 dict_set_free_data(handle_dnrs, free);
7215 plain_dnrs = dict_new();
7216 dict_set_free_data(plain_dnrs, free);
7217 mask_dnrs = dict_new();
7218 dict_set_free_data(mask_dnrs, free);
7220 reg_svccmd_unbind_func(handle_svccmd_unbind);
7221 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7222 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7223 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7224 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7225 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7226 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7227 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7228 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7229 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7231 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7232 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7234 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7235 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7236 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7237 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7238 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7240 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7241 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7242 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7243 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7244 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7246 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7247 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7248 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7249 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7251 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7252 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7253 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7254 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7255 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7256 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7257 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7258 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7260 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7261 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7262 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7263 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7264 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7265 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7266 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7267 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7268 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7269 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7270 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7271 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7272 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7273 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7275 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7276 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7277 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7278 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7279 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7281 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7282 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7284 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7285 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7286 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7287 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7288 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7289 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7290 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7291 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7292 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7293 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7294 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7296 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7297 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7299 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7300 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7301 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7302 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7304 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7305 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7306 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7307 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7308 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7310 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7311 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7312 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7313 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7314 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7315 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7317 /* Channel options */
7318 DEFINE_CHANNEL_OPTION(defaulttopic);
7319 DEFINE_CHANNEL_OPTION(topicmask);
7320 DEFINE_CHANNEL_OPTION(greeting);
7321 DEFINE_CHANNEL_OPTION(usergreeting);
7322 DEFINE_CHANNEL_OPTION(modes);
7323 DEFINE_CHANNEL_OPTION(enfops);
7324 DEFINE_CHANNEL_OPTION(giveops);
7325 DEFINE_CHANNEL_OPTION(protect);
7326 DEFINE_CHANNEL_OPTION(enfmodes);
7327 DEFINE_CHANNEL_OPTION(enftopic);
7328 DEFINE_CHANNEL_OPTION(pubcmd);
7329 DEFINE_CHANNEL_OPTION(givevoice);
7330 DEFINE_CHANNEL_OPTION(userinfo);
7331 DEFINE_CHANNEL_OPTION(dynlimit);
7332 DEFINE_CHANNEL_OPTION(topicsnarf);
7333 DEFINE_CHANNEL_OPTION(nodelete);
7334 DEFINE_CHANNEL_OPTION(toys);
7335 DEFINE_CHANNEL_OPTION(setters);
7336 DEFINE_CHANNEL_OPTION(topicrefresh);
7337 DEFINE_CHANNEL_OPTION(ctcpusers);
7338 DEFINE_CHANNEL_OPTION(ctcpreaction);
7339 DEFINE_CHANNEL_OPTION(inviteme);
7341 DEFINE_CHANNEL_OPTION(offchannel);
7342 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7344 /* Alias set topic to set defaulttopic for compatibility. */
7345 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7348 DEFINE_USER_OPTION(noautoop);
7349 DEFINE_USER_OPTION(autoinvite);
7350 DEFINE_USER_OPTION(info);
7352 /* Alias uset autovoice to uset autoop. */
7353 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7355 note_types = dict_new();
7356 dict_set_free_data(note_types, chanserv_deref_note_type);
7359 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7360 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7361 service_register(chanserv)->trigger = '!';
7362 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7364 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7366 if(chanserv_conf.channel_expire_frequency)
7367 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7369 if(chanserv_conf.refresh_period)
7371 time_t next_refresh;
7372 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7373 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7376 reg_exit_func(chanserv_db_cleanup);
7377 message_register_table(msgtab);