1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 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_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
201 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
204 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
205 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
206 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
207 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
208 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
209 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
210 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
211 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
212 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
213 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
214 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
215 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
216 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
217 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
218 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
219 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
221 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
223 /* Channel management */
224 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
225 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
226 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
228 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
229 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
230 { "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" },
231 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
232 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
233 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
234 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
236 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
237 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
238 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
239 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
240 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
241 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
242 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
243 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
244 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
245 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
246 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
247 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
248 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
249 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
250 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
251 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
252 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
253 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
254 { "CSMSG_SET_MODES", "$bModes $b %s" },
255 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
256 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
257 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
258 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
259 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
260 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
261 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
262 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
263 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
264 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
265 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
266 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
267 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
268 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
269 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
270 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
271 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
272 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
273 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
274 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
275 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
276 { "CSMSG_USET_INFO", "$bInfo $b %s" },
278 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
279 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
280 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
281 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
282 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
283 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
284 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
285 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
287 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
288 { "CSMSG_PROTECT_NONE", "No users will be protected." },
289 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
290 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
291 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
292 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
293 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
294 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
295 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
296 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
297 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
300 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
302 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
303 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
304 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
305 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
306 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
307 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
308 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
309 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
311 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
312 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
313 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
315 /* Channel userlist */
316 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
317 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
318 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
319 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
321 /* Channel note list */
322 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
323 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
324 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
325 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
326 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
327 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
328 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
329 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
330 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
331 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
332 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
333 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
334 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
335 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
336 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
338 /* Channel [un]suspension */
339 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
340 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
341 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
342 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
343 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
344 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
345 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
347 /* Access information */
348 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
349 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
350 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
351 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
352 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
353 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
354 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
355 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
356 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
357 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
358 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
359 { "CSMSG_UC_H_TITLE", "network helper" },
360 { "CSMSG_LC_H_TITLE", "support helper" },
361 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
363 /* Seen information */
364 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
365 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
366 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
367 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
369 /* Names information */
370 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
371 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
373 /* Channel information */
374 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
375 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
376 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
377 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
378 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
379 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
380 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
381 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
382 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
383 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
384 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
385 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
393 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
394 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
396 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
397 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
398 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
399 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
400 { "CSMSG_PEEK_OPS", "$bOps:$b" },
401 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
403 /* Network information */
404 { "CSMSG_NETWORK_INFO", "Network Information:" },
405 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
406 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
407 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
408 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
409 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
410 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
411 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
412 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
415 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
416 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
417 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
419 /* Channel searches */
420 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
421 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
422 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
423 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
425 /* Channel configuration */
426 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
427 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
428 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
429 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
432 { "CSMSG_USER_OPTIONS", "User Options:" },
433 { "CSMSG_USER_PROTECTED", "That user is protected." },
436 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
437 { "CSMSG_PING_RESPONSE", "Pong!" },
438 { "CSMSG_WUT_RESPONSE", "wut" },
439 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
440 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
441 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
442 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
443 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
444 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
445 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
448 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
452 /* eject_user and unban_user flags */
453 #define ACTION_KICK 0x0001
454 #define ACTION_BAN 0x0002
455 #define ACTION_ADD_BAN 0x0004
456 #define ACTION_ADD_TIMED_BAN 0x0008
457 #define ACTION_UNBAN 0x0010
458 #define ACTION_DEL_BAN 0x0020
460 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
461 #define MODELEN 40 + KEYLEN
465 #define CSFUNC_ARGS user, channel, argc, argv, cmd
467 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
468 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
469 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
470 reply("MSG_MISSING_PARAMS", argv[0]); \
474 DECLARE_LIST(dnrList, struct do_not_register *);
475 DEFINE_LIST(dnrList, struct do_not_register *);
477 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
479 struct userNode *chanserv;
482 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
483 static struct log_type *CS_LOG;
487 struct channelList support_channels;
488 struct mod_chanmode default_modes;
490 unsigned long db_backup_frequency;
491 unsigned long channel_expire_frequency;
494 unsigned int adjust_delay;
495 long channel_expire_delay;
496 unsigned int nodelete_level;
498 unsigned int adjust_threshold;
499 int join_flood_threshold;
501 unsigned int greeting_length;
502 unsigned int refresh_period;
503 unsigned int giveownership_period;
505 unsigned int max_owned;
506 unsigned int max_chan_users;
507 unsigned int max_chan_bans;
508 unsigned int max_userinfo_length;
510 struct string_list *set_shows;
511 struct string_list *eightball;
512 struct string_list *old_ban_names;
514 const char *ctcp_short_ban_duration;
515 const char *ctcp_long_ban_duration;
517 const char *irc_operator_epithet;
518 const char *network_helper_epithet;
519 const char *support_helper_epithet;
524 struct userNode *user;
525 struct userNode *bot;
526 struct chanNode *channel;
528 unsigned short lowest;
529 unsigned short highest;
530 struct userData **users;
531 struct helpfile_table table;
534 enum note_access_type
536 NOTE_SET_CHANNEL_ACCESS,
537 NOTE_SET_CHANNEL_SETTER,
541 enum note_visible_type
544 NOTE_VIS_CHANNEL_USERS,
550 enum note_access_type set_access_type;
552 unsigned int min_opserv;
553 unsigned short min_ulevel;
555 enum note_visible_type visible_type;
556 unsigned int max_length;
563 struct note_type *type;
564 char setter[NICKSERV_HANDLE_LEN+1];
568 static unsigned int registered_channels;
569 static unsigned int banCount;
571 static const struct {
574 unsigned short level;
577 { "peon", "Peon", UL_PEON, '+' },
578 { "op", "Op", UL_OP, '@' },
579 { "master", "Master", UL_MASTER, '%' },
580 { "coowner", "Coowner", UL_COOWNER, '*' },
581 { "owner", "Owner", UL_OWNER, '!' },
582 { "helper", "BUG:", UL_HELPER, 'X' }
585 static const struct {
588 unsigned short default_value;
589 unsigned int old_idx;
590 unsigned int old_flag;
591 unsigned short flag_value;
593 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
594 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
595 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
596 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
597 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
598 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
599 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
600 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
601 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
602 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
603 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
606 struct charOptionValues {
609 } protectValues[] = {
610 { 'a', "CSMSG_PROTECT_ALL" },
611 { 'e', "CSMSG_PROTECT_EQUAL" },
612 { 'l', "CSMSG_PROTECT_LOWER" },
613 { 'n', "CSMSG_PROTECT_NONE" }
615 { 'd', "CSMSG_TOYS_DISABLED" },
616 { 'n', "CSMSG_TOYS_PRIVATE" },
617 { 'p', "CSMSG_TOYS_PUBLIC" }
618 }, topicRefreshValues[] = {
619 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
620 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
621 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
622 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
623 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
624 }, ctcpReactionValues[] = {
625 { 'k', "CSMSG_CTCPREACTION_KICK" },
626 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
627 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
628 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
631 static const struct {
635 unsigned int old_idx;
637 struct charOptionValues *values;
639 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
640 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
641 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
642 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
645 struct userData *helperList;
646 struct chanData *channelList;
647 static struct module *chanserv_module;
648 static unsigned int userCount;
650 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
651 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
652 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
655 user_level_from_name(const char *name, unsigned short clamp_level)
657 unsigned int level = 0, ii;
659 level = strtoul(name, NULL, 10);
660 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
661 if(!irccasecmp(name, accessLevels[ii].name))
662 level = accessLevels[ii].level;
663 if(level > clamp_level)
669 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
672 *minl = strtoul(arg, &sep, 10);
680 *maxl = strtoul(sep+1, &sep, 10);
688 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
690 struct userData *uData, **head;
692 if(!channel || !handle)
695 if(override && HANDLE_FLAGGED(handle, HELPING)
696 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
698 for(uData = helperList;
699 uData && uData->handle != handle;
700 uData = uData->next);
704 uData = calloc(1, sizeof(struct userData));
705 uData->handle = handle;
707 uData->access = UL_HELPER;
713 uData->next = helperList;
715 helperList->prev = uData;
723 for(uData = channel->users; uData; uData = uData->next)
724 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
727 head = &(channel->users);
730 if(uData && (uData != *head))
732 /* Shuffle the user to the head of whatever list he was in. */
734 uData->next->prev = uData->prev;
736 uData->prev->next = uData->next;
742 (**head).prev = uData;
749 /* Returns non-zero if user has at least the minimum access.
750 * exempt_owner is set when handling !set, so the owner can set things
753 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
755 struct userData *uData;
756 struct chanData *cData = channel->channel_info;
757 unsigned short minimum = cData->lvlOpts[opt];
760 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
763 if(minimum <= uData->access)
765 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
770 /* Scan for other users authenticated to the same handle
771 still in the channel. If so, keep them listed as present.
773 user is optional, if not null, it skips checking that userNode
774 (for the handle_part function) */
776 scan_user_presence(struct userData *uData, struct userNode *user)
780 if(IsSuspended(uData->channel)
781 || IsUserSuspended(uData)
782 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
794 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
796 unsigned int eflags, argc;
798 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
800 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
801 if(!channel->channel_info
802 || IsSuspended(channel->channel_info)
804 || !ircncasecmp(text, "ACTION ", 7))
806 /* Figure out the minimum level needed to CTCP the channel */
807 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
809 /* We need to enforce against them; do so. */
811 argv[0] = (char*)text;
812 argv[1] = user->nick;
814 if(GetUserMode(channel, user))
815 eflags |= ACTION_KICK;
816 switch(channel->channel_info->chOpts[chCTCPReaction]) {
817 default: case 'k': /* just do the kick */ break;
819 eflags |= ACTION_BAN;
822 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
823 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
826 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
827 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
830 argv[argc++] = bad_ctcp_reason;
831 eject_user(chanserv, channel, argc, argv, NULL, eflags);
835 chanserv_create_note_type(const char *name)
837 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
838 strcpy(ntype->name, name);
840 dict_insert(note_types, ntype->name, ntype);
845 chanserv_deref_note_type(void *data)
847 struct note_type *ntype = data;
849 if(--ntype->refs > 0)
855 chanserv_flush_note_type(struct note_type *ntype)
857 struct chanData *cData;
858 for(cData = channelList; cData; cData = cData->next)
859 dict_remove(cData->notes, ntype->name);
863 chanserv_truncate_notes(struct note_type *ntype)
865 struct chanData *cData;
867 unsigned int size = sizeof(*note) + ntype->max_length;
869 for(cData = channelList; cData; cData = cData->next) {
870 note = dict_find(cData->notes, ntype->name, NULL);
873 if(strlen(note->note) <= ntype->max_length)
875 dict_remove2(cData->notes, ntype->name, 1);
876 note = realloc(note, size);
877 note->note[ntype->max_length] = 0;
878 dict_insert(cData->notes, ntype->name, note);
882 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
885 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
888 unsigned int len = strlen(text);
890 if(len > type->max_length) len = type->max_length;
891 note = calloc(1, sizeof(*note) + len);
893 strncpy(note->setter, setter, sizeof(note->setter)-1);
894 memcpy(note->note, text, len);
896 dict_insert(channel->notes, type->name, note);
902 chanserv_free_note(void *data)
904 struct note *note = data;
906 chanserv_deref_note_type(note->type);
907 assert(note->type->refs > 0); /* must use delnote to remove the type */
911 static MODCMD_FUNC(cmd_createnote) {
912 struct note_type *ntype;
913 unsigned int arg = 1, existed = 0, max_length;
915 if((ntype = dict_find(note_types, argv[1], NULL)))
918 ntype = chanserv_create_note_type(argv[arg]);
919 if(!irccasecmp(argv[++arg], "privileged"))
922 ntype->set_access_type = NOTE_SET_PRIVILEGED;
923 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
925 else if(!irccasecmp(argv[arg], "channel"))
927 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
930 reply("CSMSG_INVALID_ACCESS", argv[arg]);
933 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
934 ntype->set_access.min_ulevel = ulvl;
936 else if(!irccasecmp(argv[arg], "setter"))
938 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
942 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
946 if(!irccasecmp(argv[++arg], "privileged"))
947 ntype->visible_type = NOTE_VIS_PRIVILEGED;
948 else if(!irccasecmp(argv[arg], "channel_users"))
949 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
950 else if(!irccasecmp(argv[arg], "all"))
951 ntype->visible_type = NOTE_VIS_ALL;
953 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
957 if((arg+1) >= argc) {
958 reply("MSG_MISSING_PARAMS", argv[0]);
961 max_length = strtoul(argv[++arg], NULL, 0);
962 if(max_length < 20 || max_length > 450)
964 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
967 if(existed && (max_length < ntype->max_length))
969 ntype->max_length = max_length;
970 chanserv_truncate_notes(ntype);
972 ntype->max_length = max_length;
975 reply("CSMSG_NOTE_MODIFIED", ntype->name);
977 reply("CSMSG_NOTE_CREATED", ntype->name);
982 dict_remove(note_types, ntype->name);
986 static MODCMD_FUNC(cmd_removenote) {
987 struct note_type *ntype;
990 ntype = dict_find(note_types, argv[1], NULL);
991 force = (argc > 2) && !irccasecmp(argv[2], "force");
994 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1001 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1004 chanserv_flush_note_type(ntype);
1006 dict_remove(note_types, argv[1]);
1007 reply("CSMSG_NOTE_DELETED", argv[1]);
1012 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1016 if(orig->modes_set & change->modes_clear)
1018 if(orig->modes_clear & change->modes_set)
1020 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1021 && strcmp(orig->new_key, change->new_key))
1023 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1024 && (orig->new_limit != change->new_limit))
1029 static char max_length_text[MAXLEN+1][16];
1031 static struct helpfile_expansion
1032 chanserv_expand_variable(const char *variable)
1034 struct helpfile_expansion exp;
1036 if(!irccasecmp(variable, "notes"))
1039 exp.type = HF_TABLE;
1040 exp.value.table.length = 1;
1041 exp.value.table.width = 3;
1042 exp.value.table.flags = 0;
1043 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1044 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1045 exp.value.table.contents[0][0] = "Note Type";
1046 exp.value.table.contents[0][1] = "Visibility";
1047 exp.value.table.contents[0][2] = "Max Length";
1048 for(it=dict_first(note_types); it; it=iter_next(it))
1050 struct note_type *ntype = iter_data(it);
1053 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1054 row = exp.value.table.length++;
1055 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1056 exp.value.table.contents[row][0] = ntype->name;
1057 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1058 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1060 if(!max_length_text[ntype->max_length][0])
1061 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1062 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1067 exp.type = HF_STRING;
1068 exp.value.str = NULL;
1072 static struct chanData*
1073 register_channel(struct chanNode *cNode, char *registrar)
1075 struct chanData *channel;
1076 enum levelOption lvlOpt;
1077 enum charOption chOpt;
1079 channel = calloc(1, sizeof(struct chanData));
1081 channel->notes = dict_new();
1082 dict_set_free_data(channel->notes, chanserv_free_note);
1084 channel->registrar = strdup(registrar);
1085 channel->registered = now;
1086 channel->visited = now;
1087 channel->limitAdjusted = now;
1088 channel->ownerTransfer = now;
1089 channel->flags = CHANNEL_DEFAULT_FLAGS;
1090 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1091 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1092 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1093 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1095 channel->prev = NULL;
1096 channel->next = channelList;
1099 channelList->prev = channel;
1100 channelList = channel;
1101 registered_channels++;
1103 channel->channel = cNode;
1105 cNode->channel_info = channel;
1110 static struct userData*
1111 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1113 struct userData *ud;
1115 if(access > UL_OWNER)
1118 ud = calloc(1, sizeof(*ud));
1119 ud->channel = channel;
1120 ud->handle = handle;
1122 ud->access = access;
1123 ud->info = info ? strdup(info) : NULL;
1126 ud->next = channel->users;
1128 channel->users->prev = ud;
1129 channel->users = ud;
1131 channel->userCount++;
1135 ud->u_next = ud->handle->channels;
1137 ud->u_next->u_prev = ud;
1138 ud->handle->channels = ud;
1143 static void unregister_channel(struct chanData *channel, const char *reason);
1146 del_channel_user(struct userData *user, int do_gc)
1148 struct chanData *channel = user->channel;
1150 channel->userCount--;
1154 user->prev->next = user->next;
1156 channel->users = user->next;
1158 user->next->prev = user->prev;
1161 user->u_prev->u_next = user->u_next;
1163 user->handle->channels = user->u_next;
1165 user->u_next->u_prev = user->u_prev;
1169 if(do_gc && !channel->users && !IsProtected(channel))
1170 unregister_channel(channel, "lost all users.");
1173 static void expire_ban(void *data);
1175 static struct banData*
1176 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1179 unsigned int ii, l1, l2;
1184 bd = malloc(sizeof(struct banData));
1186 bd->channel = channel;
1188 bd->triggered = triggered;
1189 bd->expires = expires;
1191 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1193 extern const char *hidden_host_suffix;
1194 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1198 l2 = strlen(old_name);
1201 if(irccasecmp(mask + l1 - l2, old_name))
1203 new_mask = alloca(MAXLEN);
1204 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1207 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1209 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1210 bd->reason = strdup(reason);
1213 timeq_add(expires, expire_ban, bd);
1216 bd->next = channel->bans;
1218 channel->bans->prev = bd;
1220 channel->banCount++;
1227 del_channel_ban(struct banData *ban)
1229 ban->channel->banCount--;
1233 ban->prev->next = ban->next;
1235 ban->channel->bans = ban->next;
1238 ban->next->prev = ban->prev;
1241 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1250 expire_ban(void *data)
1252 struct banData *bd = data;
1253 if(!IsSuspended(bd->channel))
1255 struct banList bans;
1256 struct mod_chanmode change;
1258 bans = bd->channel->channel->banlist;
1259 mod_chanmode_init(&change);
1260 for(ii=0; ii<bans.used; ii++)
1262 if(!strcmp(bans.list[ii]->ban, bd->mask))
1265 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1266 change.args[0].u.hostmask = bd->mask;
1267 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1273 del_channel_ban(bd);
1276 static void chanserv_expire_suspension(void *data);
1279 unregister_channel(struct chanData *channel, const char *reason)
1281 struct mod_chanmode change;
1282 char msgbuf[MAXLEN];
1284 /* After channel unregistration, the following must be cleaned
1286 - Channel information.
1289 - Channel suspension data.
1290 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1296 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1300 mod_chanmode_init(&change);
1301 change.modes_clear |= MODE_REGISTERED;
1302 mod_chanmode_announce(chanserv, channel->channel, &change);
1305 while(channel->users)
1306 del_channel_user(channel->users, 0);
1308 while(channel->bans)
1309 del_channel_ban(channel->bans);
1311 free(channel->topic);
1312 free(channel->registrar);
1313 free(channel->greeting);
1314 free(channel->user_greeting);
1315 free(channel->topic_mask);
1318 channel->prev->next = channel->next;
1320 channelList = channel->next;
1323 channel->next->prev = channel->prev;
1325 if(channel->suspended)
1327 struct chanNode *cNode = channel->channel;
1328 struct suspended *suspended, *next_suspended;
1330 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1332 next_suspended = suspended->previous;
1333 free(suspended->suspender);
1334 free(suspended->reason);
1335 if(suspended->expires)
1336 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1341 cNode->channel_info = NULL;
1343 channel->channel->channel_info = NULL;
1345 dict_delete(channel->notes);
1346 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1347 if(!IsSuspended(channel))
1348 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1349 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1350 UnlockChannel(channel->channel);
1352 registered_channels--;
1356 expire_channels(UNUSED_ARG(void *data))
1358 struct chanData *channel, *next;
1359 struct userData *user;
1360 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1362 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1363 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1365 for(channel = channelList; channel; channel = next)
1367 next = channel->next;
1369 /* See if the channel can be expired. */
1370 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1371 || IsProtected(channel))
1374 /* Make sure there are no high-ranking users still in the channel. */
1375 for(user=channel->users; user; user=user->next)
1376 if(user->present && (user->access >= UL_PRESENT))
1381 /* Unregister the channel */
1382 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1383 unregister_channel(channel, "registration expired.");
1386 if(chanserv_conf.channel_expire_frequency)
1387 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1391 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1393 char protect = channel->chOpts[chProtect];
1394 struct userData *cs_victim, *cs_aggressor;
1396 /* Don't protect if no one is to be protected, someone is attacking
1397 himself, or if the aggressor is an IRC Operator. */
1398 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1401 /* Don't protect if the victim isn't authenticated (because they
1402 can't be a channel user), unless we are to protect non-users
1404 cs_victim = GetChannelAccess(channel, victim->handle_info);
1405 if(protect != 'a' && !cs_victim)
1408 /* Protect if the aggressor isn't a user because at this point,
1409 the aggressor can only be less than or equal to the victim. */
1410 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1414 /* If the aggressor was a user, then the victim can't be helped. */
1421 if(cs_victim->access > cs_aggressor->access)
1426 if(cs_victim->access >= cs_aggressor->access)
1435 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1437 struct chanData *cData = channel->channel_info;
1438 struct userData *cs_victim;
1440 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1441 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1442 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1444 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1452 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1454 if(IsService(victim))
1456 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1460 if(protect_user(victim, user, channel->channel_info))
1462 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1469 static struct do_not_register *
1470 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1472 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1473 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1474 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1475 strcpy(dnr->reason, reason);
1477 if(dnr->chan_name[0] == '*')
1478 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1479 else if(strpbrk(dnr->chan_name, "*?"))
1480 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1482 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1486 static struct dnrList
1487 chanserv_find_dnrs(const char *chan_name, const char *handle)
1489 struct dnrList list;
1491 struct do_not_register *dnr;
1493 dnrList_init(&list);
1494 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1495 dnrList_append(&list, dnr);
1496 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1497 dnrList_append(&list, dnr);
1499 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1500 if(match_ircglob(chan_name, iter_key(it)))
1501 dnrList_append(&list, iter_data(it));
1506 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1508 struct dnrList list;
1509 struct do_not_register *dnr;
1511 char buf[INTERVALLEN];
1513 list = chanserv_find_dnrs(chan_name, handle);
1514 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1516 dnr = list.list[ii];
1519 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1520 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1523 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1526 reply("CSMSG_MORE_DNRS", list.used - ii);
1531 struct do_not_register *
1532 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1534 struct do_not_register *dnr;
1537 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1541 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1543 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1544 if(match_ircglob(chan_name, iter_key(it)))
1545 return iter_data(it);
1550 static CHANSERV_FUNC(cmd_noregister)
1553 struct do_not_register *dnr;
1554 char buf[INTERVALLEN];
1555 unsigned int matches;
1561 reply("CSMSG_DNR_SEARCH_RESULTS");
1563 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1565 dnr = iter_data(it);
1567 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1569 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1572 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1574 dnr = iter_data(it);
1576 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1578 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1581 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1583 dnr = iter_data(it);
1585 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1587 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1592 reply("MSG_MATCH_COUNT", matches);
1594 reply("MSG_NO_MATCHES");
1600 if(!IsChannelName(target) && (*target != '*'))
1602 reply("CSMSG_NOT_DNR", target);
1608 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1609 if((*target == '*') && !get_handle_info(target + 1))
1611 reply("MSG_HANDLE_UNKNOWN", target + 1);
1614 chanserv_add_dnr(target, user->handle_info->handle, reason);
1615 reply("CSMSG_NOREGISTER_CHANNEL", target);
1619 reply("CSMSG_DNR_SEARCH_RESULTS");
1621 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1623 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1625 reply("MSG_NO_MATCHES");
1629 static CHANSERV_FUNC(cmd_allowregister)
1631 const char *chan_name = argv[1];
1633 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1635 dict_remove(handle_dnrs, chan_name+1);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(plain_dnrs, chan_name, NULL))
1640 dict_remove(plain_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1643 else if(dict_find(mask_dnrs, chan_name, NULL))
1645 dict_remove(mask_dnrs, chan_name);
1646 reply("CSMSG_DNR_REMOVED", chan_name);
1650 reply("CSMSG_NO_SUCH_DNR", chan_name);
1657 chanserv_get_owned_count(struct handle_info *hi)
1659 struct userData *cList;
1662 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1663 if(cList->access == UL_OWNER)
1668 static CHANSERV_FUNC(cmd_register)
1670 struct handle_info *handle;
1671 struct chanData *cData;
1672 struct modeNode *mn;
1673 char reason[MAXLEN];
1675 unsigned int new_channel, force=0;
1676 struct do_not_register *dnr;
1680 if(channel->channel_info)
1682 reply("CSMSG_ALREADY_REGGED", channel->name);
1686 if(channel->bad_channel)
1688 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1693 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1695 reply("CSMSG_MUST_BE_OPPED", channel->name);
1700 chan_name = channel->name;
1704 if((argc < 2) || !IsChannelName(argv[1]))
1706 reply("MSG_NOT_CHANNEL_NAME");
1710 if(opserv_bad_channel(argv[1]))
1712 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1717 chan_name = argv[1];
1720 if(argc >= (new_channel+2))
1722 if(!IsHelping(user))
1724 reply("CSMSG_PROXY_FORBIDDEN");
1728 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1730 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 handle = user->handle_info;
1736 dnr = chanserv_is_dnr(chan_name, handle);
1740 if(!IsHelping(user))
1741 reply("CSMSG_DNR_CHANNEL", chan_name);
1743 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1747 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1749 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1754 channel = AddChannel(argv[1], now, NULL, NULL);
1756 cData = register_channel(channel, user->handle_info->handle);
1757 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1758 cData->modes = chanserv_conf.default_modes;
1760 cData->modes.modes_set |= MODE_REGISTERED;
1761 if (IsOffChannel(cData))
1763 mod_chanmode_announce(chanserv, channel, &cData->modes);
1767 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1768 change->args[change->argc].mode = MODE_CHANOP;
1769 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1771 mod_chanmode_announce(chanserv, channel, change);
1772 mod_chanmode_free(change);
1775 /* Initialize the channel's max user record. */
1776 cData->max = channel->members.used;
1778 if(handle != user->handle_info)
1779 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1781 reply("CSMSG_REG_SUCCESS", channel->name);
1783 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1784 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1789 make_confirmation_string(struct userData *uData)
1791 static char strbuf[16];
1796 for(src = uData->handle->handle; *src; )
1797 accum = accum * 31 + toupper(*src++);
1799 for(src = uData->channel->channel->name; *src; )
1800 accum = accum * 31 + toupper(*src++);
1801 sprintf(strbuf, "%08x", accum);
1805 static CHANSERV_FUNC(cmd_unregister)
1808 char reason[MAXLEN];
1809 struct chanData *cData;
1810 struct userData *uData;
1812 cData = channel->channel_info;
1815 reply("CSMSG_NOT_REGISTERED", channel->name);
1819 uData = GetChannelUser(cData, user->handle_info);
1820 if(!uData || (uData->access < UL_OWNER))
1822 reply("CSMSG_NO_ACCESS");
1826 if(IsProtected(cData))
1828 reply("CSMSG_UNREG_NODELETE", channel->name);
1832 if(!IsHelping(user))
1834 const char *confirm_string;
1835 if(IsSuspended(cData))
1837 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1840 confirm_string = make_confirmation_string(uData);
1841 if((argc < 2) || strcmp(argv[1], confirm_string))
1843 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1848 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1849 name = strdup(channel->name);
1850 unregister_channel(cData, reason);
1851 reply("CSMSG_UNREG_SUCCESS", name);
1856 static CHANSERV_FUNC(cmd_move)
1858 struct mod_chanmode change;
1859 struct chanNode *target;
1860 struct modeNode *mn;
1861 struct userData *uData;
1862 char reason[MAXLEN];
1863 struct do_not_register *dnr;
1867 if(IsProtected(channel->channel_info))
1869 reply("CSMSG_MOVE_NODELETE", channel->name);
1873 if(!IsChannelName(argv[1]))
1875 reply("MSG_NOT_CHANNEL_NAME");
1879 if(opserv_bad_channel(argv[1]))
1881 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1885 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1887 for(uData = channel->channel_info->users; uData; uData = uData->next)
1889 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1891 if(!IsHelping(user))
1892 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1894 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1900 mod_chanmode_init(&change);
1901 if(!(target = GetChannel(argv[1])))
1903 target = AddChannel(argv[1], now, NULL, NULL);
1904 if(!IsSuspended(channel->channel_info))
1905 AddChannelUser(chanserv, target);
1907 else if(target->channel_info)
1909 reply("CSMSG_ALREADY_REGGED", target->name);
1912 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1913 && !IsHelping(user))
1915 reply("CSMSG_MUST_BE_OPPED", target->name);
1918 else if(!IsSuspended(channel->channel_info))
1921 change.args[0].mode = MODE_CHANOP;
1922 change.args[0].u.member = AddChannelUser(chanserv, target);
1923 mod_chanmode_announce(chanserv, target, &change);
1928 /* Clear MODE_REGISTERED from old channel, add it to new. */
1930 change.modes_clear = MODE_REGISTERED;
1931 mod_chanmode_announce(chanserv, channel, &change);
1932 change.modes_clear = 0;
1933 change.modes_set = MODE_REGISTERED;
1934 mod_chanmode_announce(chanserv, target, &change);
1937 /* Move the channel_info to the target channel; it
1938 shouldn't be necessary to clear timeq callbacks
1939 for the old channel. */
1940 target->channel_info = channel->channel_info;
1941 target->channel_info->channel = target;
1942 channel->channel_info = NULL;
1944 reply("CSMSG_MOVE_SUCCESS", target->name);
1946 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1947 if(!IsSuspended(target->channel_info))
1949 char reason2[MAXLEN];
1950 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1951 DelChannelUser(chanserv, channel, reason2, 0);
1953 UnlockChannel(channel);
1954 LockChannel(target);
1955 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1960 merge_users(struct chanData *source, struct chanData *target)
1962 struct userData *suData, *tuData, *next;
1968 /* Insert the source's users into the scratch area. */
1969 for(suData = source->users; suData; suData = suData->next)
1970 dict_insert(merge, suData->handle->handle, suData);
1972 /* Iterate through the target's users, looking for
1973 users common to both channels. The lower access is
1974 removed from either the scratch area or target user
1976 for(tuData = target->users; tuData; tuData = next)
1978 struct userData *choice;
1980 next = tuData->next;
1982 /* If a source user exists with the same handle as a target
1983 channel's user, resolve the conflict by removing one. */
1984 suData = dict_find(merge, tuData->handle->handle, NULL);
1988 /* Pick the data we want to keep. */
1989 /* If the access is the same, use the later seen time. */
1990 if(suData->access == tuData->access)
1991 choice = (suData->seen > tuData->seen) ? suData : tuData;
1992 else /* Otherwise, keep the higher access level. */
1993 choice = (suData->access > tuData->access) ? suData : tuData;
1995 /* Remove the user that wasn't picked. */
1996 if(choice == tuData)
1998 dict_remove(merge, suData->handle->handle);
1999 del_channel_user(suData, 0);
2002 del_channel_user(tuData, 0);
2005 /* Move the remaining users to the target channel. */
2006 for(it = dict_first(merge); it; it = iter_next(it))
2008 suData = iter_data(it);
2010 /* Insert the user into the target channel's linked list. */
2011 suData->prev = NULL;
2012 suData->next = target->users;
2013 suData->channel = target;
2016 target->users->prev = suData;
2017 target->users = suData;
2019 /* Update the user counts for the target channel; the
2020 source counts are left alone. */
2021 target->userCount++;
2024 /* Possible to assert (source->users == NULL) here. */
2025 source->users = NULL;
2030 merge_bans(struct chanData *source, struct chanData *target)
2032 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2034 /* Hold on to the original head of the target ban list
2035 to avoid comparing source bans with source bans. */
2036 tFront = target->bans;
2038 /* Perform a totally expensive O(n*m) merge, ick. */
2039 for(sbData = source->bans; sbData; sbData = sNext)
2041 /* Flag to track whether the ban's been moved
2042 to the destination yet. */
2045 /* Possible to assert (sbData->prev == NULL) here. */
2046 sNext = sbData->next;
2048 for(tbData = tFront; tbData; tbData = tNext)
2050 tNext = tbData->next;
2052 /* Perform two comparisons between each source
2053 and target ban, conflicts are resolved by
2054 keeping the broader ban and copying the later
2055 expiration and triggered time. */
2056 if(match_ircglobs(tbData->mask, sbData->mask))
2058 /* There is a broader ban in the target channel that
2059 overrides one in the source channel; remove the
2060 source ban and break. */
2061 if(sbData->expires > tbData->expires)
2062 tbData->expires = sbData->expires;
2063 if(sbData->triggered > tbData->triggered)
2064 tbData->triggered = sbData->triggered;
2065 del_channel_ban(sbData);
2068 else if(match_ircglobs(sbData->mask, tbData->mask))
2070 /* There is a broader ban in the source channel that
2071 overrides one in the target channel; remove the
2072 target ban, fall through and move the source over. */
2073 if(tbData->expires > sbData->expires)
2074 sbData->expires = tbData->expires;
2075 if(tbData->triggered > sbData->triggered)
2076 sbData->triggered = tbData->triggered;
2077 if(tbData == tFront)
2079 del_channel_ban(tbData);
2082 /* Source bans can override multiple target bans, so
2083 we allow a source to run through this loop multiple
2084 times, but we can only move it once. */
2089 /* Remove the source ban from the source ban list. */
2091 sbData->next->prev = sbData->prev;
2093 /* Modify the source ban's associated channel. */
2094 sbData->channel = target;
2096 /* Insert the ban into the target channel's linked list. */
2097 sbData->prev = NULL;
2098 sbData->next = target->bans;
2101 target->bans->prev = sbData;
2102 target->bans = sbData;
2104 /* Update the user counts for the target channel. */
2109 /* Possible to assert (source->bans == NULL) here. */
2110 source->bans = NULL;
2114 merge_data(struct chanData *source, struct chanData *target)
2116 /* Use more recent visited and owner-transfer time; use older
2117 * registered time. Bitwise or may_opchan. Use higher max.
2118 * Do not touch last_refresh, ban count or user counts.
2120 if(source->visited > target->visited)
2121 target->visited = source->visited;
2122 if(source->registered < target->registered)
2123 target->registered = source->registered;
2124 if(source->ownerTransfer > target->ownerTransfer)
2125 target->ownerTransfer = source->ownerTransfer;
2126 if(source->may_opchan)
2127 target->may_opchan = 1;
2128 if(source->max > target->max)
2129 target->max = source->max;
2133 merge_channel(struct chanData *source, struct chanData *target)
2135 merge_users(source, target);
2136 merge_bans(source, target);
2137 merge_data(source, target);
2140 static CHANSERV_FUNC(cmd_merge)
2142 struct userData *target_user;
2143 struct chanNode *target;
2144 char reason[MAXLEN];
2148 /* Make sure the target channel exists and is registered to the user
2149 performing the command. */
2150 if(!(target = GetChannel(argv[1])))
2152 reply("MSG_INVALID_CHANNEL");
2156 if(!target->channel_info)
2158 reply("CSMSG_NOT_REGISTERED", target->name);
2162 if(IsProtected(channel->channel_info))
2164 reply("CSMSG_MERGE_NODELETE");
2168 if(IsSuspended(target->channel_info))
2170 reply("CSMSG_MERGE_SUSPENDED");
2174 if(channel == target)
2176 reply("CSMSG_MERGE_SELF");
2180 target_user = GetChannelUser(target->channel_info, user->handle_info);
2181 if(!target_user || (target_user->access < UL_OWNER))
2183 reply("CSMSG_MERGE_NOT_OWNER");
2187 /* Merge the channel structures and associated data. */
2188 merge_channel(channel->channel_info, target->channel_info);
2189 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2190 unregister_channel(channel->channel_info, reason);
2191 reply("CSMSG_MERGE_SUCCESS", target->name);
2195 static CHANSERV_FUNC(cmd_opchan)
2197 struct mod_chanmode change;
2198 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2200 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2203 channel->channel_info->may_opchan = 0;
2204 mod_chanmode_init(&change);
2206 change.args[0].mode = MODE_CHANOP;
2207 change.args[0].u.member = GetUserMode(channel, chanserv);
2208 mod_chanmode_announce(chanserv, channel, &change);
2209 reply("CSMSG_OPCHAN_DONE", channel->name);
2213 static CHANSERV_FUNC(cmd_adduser)
2215 struct userData *actee;
2216 struct userData *actor;
2217 struct handle_info *handle;
2218 unsigned short access;
2222 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2224 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2228 access = user_level_from_name(argv[2], UL_OWNER);
2231 reply("CSMSG_INVALID_ACCESS", argv[2]);
2235 actor = GetChannelUser(channel->channel_info, user->handle_info);
2236 if(actor->access <= access)
2238 reply("CSMSG_NO_BUMP_ACCESS");
2242 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2245 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2247 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2251 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2252 scan_user_presence(actee, NULL);
2253 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2257 static CHANSERV_FUNC(cmd_clvl)
2259 struct handle_info *handle;
2260 struct userData *victim;
2261 struct userData *actor;
2262 unsigned short new_access;
2263 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2267 actor = GetChannelUser(channel->channel_info, user->handle_info);
2269 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2272 if(handle == user->handle_info && !privileged)
2274 reply("CSMSG_NO_SELF_CLVL");
2278 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2280 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2284 if(actor->access <= victim->access && !privileged)
2286 reply("MSG_USER_OUTRANKED", handle->handle);
2290 new_access = user_level_from_name(argv[2], UL_OWNER);
2294 reply("CSMSG_INVALID_ACCESS", argv[2]);
2298 if(new_access >= actor->access && !privileged)
2300 reply("CSMSG_NO_BUMP_ACCESS");
2304 victim->access = new_access;
2305 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2309 static CHANSERV_FUNC(cmd_deluser)
2311 struct handle_info *handle;
2312 struct userData *victim;
2313 struct userData *actor;
2314 unsigned short access;
2319 actor = GetChannelUser(channel->channel_info, user->handle_info);
2321 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2324 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2326 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2332 access = user_level_from_name(argv[1], UL_OWNER);
2335 reply("CSMSG_INVALID_ACCESS", argv[1]);
2338 if(access != victim->access)
2340 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2346 access = victim->access;
2349 if((actor->access <= victim->access) && !IsHelping(user))
2351 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2355 chan_name = strdup(channel->name);
2356 del_channel_user(victim, 1);
2357 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2363 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2365 struct userData *actor, *uData, *next;
2367 actor = GetChannelUser(channel->channel_info, user->handle_info);
2369 if(min_access > max_access)
2371 reply("CSMSG_BAD_RANGE", min_access, max_access);
2375 if((actor->access <= max_access) && !IsHelping(user))
2377 reply("CSMSG_NO_ACCESS");
2381 for(uData = channel->channel_info->users; uData; uData = next)
2385 if((uData->access >= min_access)
2386 && (uData->access <= max_access)
2387 && match_ircglob(uData->handle->handle, mask))
2388 del_channel_user(uData, 1);
2391 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2395 static CHANSERV_FUNC(cmd_mdelowner)
2397 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2400 static CHANSERV_FUNC(cmd_mdelcoowner)
2402 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2405 static CHANSERV_FUNC(cmd_mdelmaster)
2407 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2410 static CHANSERV_FUNC(cmd_mdelop)
2412 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2415 static CHANSERV_FUNC(cmd_mdelpeon)
2417 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2421 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2423 struct banData *bData, *next;
2424 char interval[INTERVALLEN];
2429 limit = now - duration;
2430 for(bData = channel->channel_info->bans; bData; bData = next)
2434 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2437 del_channel_ban(bData);
2441 intervalString(interval, duration, user->handle_info);
2442 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2447 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2449 struct userData *actor, *uData, *next;
2450 char interval[INTERVALLEN];
2454 actor = GetChannelUser(channel->channel_info, user->handle_info);
2455 if(min_access > max_access)
2457 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2461 if((actor->access <= max_access) && !IsHelping(user))
2463 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2468 limit = now - duration;
2469 for(uData = channel->channel_info->users; uData; uData = next)
2473 if((uData->seen > limit)
2475 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2478 if(((uData->access >= min_access) && (uData->access <= max_access))
2479 || (!max_access && (uData->access < actor->access)))
2481 del_channel_user(uData, 1);
2489 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2491 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2495 static CHANSERV_FUNC(cmd_trim)
2497 unsigned long duration;
2498 unsigned short min_level, max_level;
2503 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2504 duration = ParseInterval(argv[2]);
2507 reply("CSMSG_CANNOT_TRIM");
2511 if(!irccasecmp(argv[1], "bans"))
2513 cmd_trim_bans(user, channel, duration);
2516 else if(!irccasecmp(argv[1], "users"))
2518 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2521 else if(parse_level_range(&min_level, &max_level, argv[1]))
2523 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2526 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2528 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2533 reply("CSMSG_INVALID_TRIM", argv[1]);
2538 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2539 to the user. cmd_all takes advantage of this. */
2540 static CHANSERV_FUNC(cmd_up)
2542 struct mod_chanmode change;
2543 struct userData *uData;
2546 mod_chanmode_init(&change);
2548 change.args[0].u.member = GetUserMode(channel, user);
2549 if(!change.args[0].u.member)
2552 reply("MSG_CHANNEL_ABSENT", channel->name);
2556 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2560 reply("CSMSG_GODMODE_UP", argv[0]);
2563 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2565 change.args[0].mode = MODE_CHANOP;
2566 errmsg = "CSMSG_ALREADY_OPPED";
2568 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2570 change.args[0].mode = MODE_VOICE;
2571 errmsg = "CSMSG_ALREADY_VOICED";
2576 reply("CSMSG_NO_ACCESS");
2579 change.args[0].mode &= ~change.args[0].u.member->modes;
2580 if(!change.args[0].mode)
2583 reply(errmsg, channel->name);
2586 modcmd_chanmode_announce(&change);
2590 static CHANSERV_FUNC(cmd_down)
2592 struct mod_chanmode change;
2594 mod_chanmode_init(&change);
2596 change.args[0].u.member = GetUserMode(channel, user);
2597 if(!change.args[0].u.member)
2600 reply("MSG_CHANNEL_ABSENT", channel->name);
2604 if(!change.args[0].u.member->modes)
2607 reply("CSMSG_ALREADY_DOWN", channel->name);
2611 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2612 modcmd_chanmode_announce(&change);
2616 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)
2618 struct userData *cList;
2620 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2622 if(IsSuspended(cList->channel)
2623 || IsUserSuspended(cList)
2624 || !GetUserMode(cList->channel->channel, user))
2627 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2633 static CHANSERV_FUNC(cmd_upall)
2635 return cmd_all(CSFUNC_ARGS, cmd_up);
2638 static CHANSERV_FUNC(cmd_downall)
2640 return cmd_all(CSFUNC_ARGS, cmd_down);
2643 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2644 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2647 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)
2649 unsigned int ii, valid;
2650 struct userNode *victim;
2651 struct mod_chanmode *change;
2653 change = mod_chanmode_alloc(argc - 1);
2655 for(ii=valid=0; ++ii < argc; )
2657 if(!(victim = GetUserH(argv[ii])))
2659 change->args[valid].mode = mode;
2660 change->args[valid].u.member = GetUserMode(channel, victim);
2661 if(!change->args[valid].u.member)
2663 if(validate && !validate(user, channel, victim))
2668 change->argc = valid;
2669 if(valid < (argc-1))
2670 reply("CSMSG_PROCESS_FAILED");
2673 modcmd_chanmode_announce(change);
2674 reply(action, channel->name);
2676 mod_chanmode_free(change);
2680 static CHANSERV_FUNC(cmd_op)
2682 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2685 static CHANSERV_FUNC(cmd_deop)
2687 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2690 static CHANSERV_FUNC(cmd_voice)
2692 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2695 static CHANSERV_FUNC(cmd_devoice)
2697 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2701 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2707 for(ii=0; ii<channel->members.used; ii++)
2709 struct modeNode *mn = channel->members.list[ii];
2711 if(IsService(mn->user))
2714 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2717 if(protect_user(mn->user, user, channel->channel_info))
2721 victims[(*victimCount)++] = mn;
2727 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2729 struct userNode *victim;
2730 struct modeNode **victims;
2731 unsigned int offset, n, victimCount, duration = 0;
2732 char *reason = "Bye.", *ban, *name;
2733 char interval[INTERVALLEN];
2735 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2736 REQUIRE_PARAMS(offset);
2739 reason = unsplit_string(argv + offset, argc - offset, NULL);
2740 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2742 /* Truncate the reason to a length of TOPICLEN, as
2743 the ircd does; however, leave room for an ellipsis
2744 and the kicker's nick. */
2745 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2749 if((victim = GetUserH(argv[1])))
2751 victims = alloca(sizeof(victims[0]));
2752 victims[0] = GetUserMode(channel, victim);
2753 /* XXX: The comparison with ACTION_KICK is just because all
2754 * other actions can work on users outside the channel, and we
2755 * want to allow those (e.g. unbans) in that case. If we add
2756 * some other ejection action for in-channel users, change
2758 victimCount = victims[0] ? 1 : 0;
2760 if(IsService(victim))
2762 reply("MSG_SERVICE_IMMUNE", victim->nick);
2766 if((action == ACTION_KICK) && !victimCount)
2768 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2772 if(protect_user(victim, user, channel->channel_info))
2774 reply("CSMSG_USER_PROTECTED", victim->nick);
2778 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2779 name = victim->nick;
2783 if(!is_ircmask(argv[1]))
2785 reply("MSG_NICK_UNKNOWN", argv[1]);
2789 victims = alloca(sizeof(victims[0]) * channel->members.used);
2791 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2793 reply("CSMSG_MASK_PROTECTED", argv[1]);
2797 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2799 reply("CSMSG_LAME_MASK", argv[1]);
2803 if((action == ACTION_KICK) && (victimCount == 0))
2805 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2809 name = ban = strdup(argv[1]);
2812 /* Truncate the ban in place if necessary; we must ensure
2813 that 'ban' is a valid ban mask before sanitizing it. */
2814 sanitize_ircmask(ban);
2816 if(action & ACTION_ADD_BAN)
2818 struct banData *bData, *next;
2820 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2822 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2827 if(action & ACTION_ADD_TIMED_BAN)
2829 duration = ParseInterval(argv[2]);
2833 reply("CSMSG_DURATION_TOO_LOW");
2837 else if(duration > (86400 * 365 * 2))
2839 reply("CSMSG_DURATION_TOO_HIGH");
2845 for(bData = channel->channel_info->bans; bData; bData = next)
2847 if(match_ircglobs(bData->mask, ban))
2849 int exact = !irccasecmp(bData->mask, ban);
2851 /* The ban is redundant; there is already a ban
2852 with the same effect in place. */
2856 free(bData->reason);
2857 bData->reason = strdup(reason);
2858 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2860 reply("CSMSG_REASON_CHANGE", ban);
2864 if(exact && bData->expires)
2868 /* If the ban matches an existing one exactly,
2869 extend the expiration time if the provided
2870 duration is longer. */
2871 if(duration && ((time_t)(now + duration) > bData->expires))
2873 bData->expires = now + duration;
2884 /* Delete the expiration timeq entry and
2885 requeue if necessary. */
2886 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2889 timeq_add(bData->expires, expire_ban, bData);
2893 /* automated kickban */
2896 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2898 reply("CSMSG_BAN_ADDED", name, channel->name);
2904 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2911 if(match_ircglobs(ban, bData->mask))
2913 /* The ban we are adding makes previously existing
2914 bans redundant; silently remove them. */
2915 del_channel_ban(bData);
2919 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);
2921 name = ban = strdup(bData->mask);
2925 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2927 extern const char *hidden_host_suffix;
2928 const char *old_name = chanserv_conf.old_ban_names->list[n];
2930 unsigned int l1, l2;
2933 l2 = strlen(old_name);
2936 if(irccasecmp(ban + l1 - l2, old_name))
2938 new_mask = malloc(MAXLEN);
2939 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2941 name = ban = new_mask;
2946 if(action & ACTION_BAN)
2948 unsigned int exists;
2949 struct mod_chanmode *change;
2951 if(channel->banlist.used >= MAXBANS)
2954 reply("CSMSG_BANLIST_FULL", channel->name);
2959 exists = ChannelBanExists(channel, ban);
2960 change = mod_chanmode_alloc(victimCount + 1);
2961 for(n = 0; n < victimCount; ++n)
2963 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2964 change->args[n].u.member = victims[n];
2968 change->args[n].mode = MODE_BAN;
2969 change->args[n++].u.hostmask = ban;
2973 modcmd_chanmode_announce(change);
2975 mod_chanmode_announce(chanserv, channel, change);
2976 mod_chanmode_free(change);
2978 if(exists && (action == ACTION_BAN))
2981 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2987 if(action & ACTION_KICK)
2989 char kick_reason[MAXLEN];
2990 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2992 for(n = 0; n < victimCount; n++)
2993 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2998 /* No response, since it was automated. */
3000 else if(action & ACTION_ADD_BAN)
3003 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3005 reply("CSMSG_BAN_ADDED", name, channel->name);
3007 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3008 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3009 else if(action & ACTION_BAN)
3010 reply("CSMSG_BAN_DONE", name, channel->name);
3011 else if(action & ACTION_KICK && victimCount)
3012 reply("CSMSG_KICK_DONE", name, channel->name);
3018 static CHANSERV_FUNC(cmd_kickban)
3020 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3023 static CHANSERV_FUNC(cmd_kick)
3025 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3028 static CHANSERV_FUNC(cmd_ban)
3030 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3033 static CHANSERV_FUNC(cmd_addban)
3035 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3038 static CHANSERV_FUNC(cmd_addtimedban)
3040 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3043 static struct mod_chanmode *
3044 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3046 struct mod_chanmode *change;
3047 unsigned char *match;
3048 unsigned int ii, count;
3050 match = alloca(bans->used);
3053 for(ii = count = 0; ii < bans->used; ++ii)
3055 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3056 MATCH_USENICK | MATCH_VISIBLE);
3063 for(ii = count = 0; ii < bans->used; ++ii)
3065 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3072 change = mod_chanmode_alloc(count);
3073 for(ii = count = 0; ii < bans->used; ++ii)
3077 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3078 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3080 assert(count == change->argc);
3085 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3087 struct userNode *actee;
3093 /* may want to allow a comma delimited list of users... */
3094 if(!(actee = GetUserH(argv[1])))
3096 if(!is_ircmask(argv[1]))
3098 reply("MSG_NICK_UNKNOWN", argv[1]);
3102 mask = strdup(argv[1]);
3105 /* We don't sanitize the mask here because ircu
3107 if(action & ACTION_UNBAN)
3109 struct mod_chanmode *change;
3110 change = find_matching_bans(&channel->banlist, actee, mask);
3115 modcmd_chanmode_announce(change);
3116 for(ii = 0; ii < change->argc; ++ii)
3117 free((char*)change->args[ii].u.hostmask);
3118 mod_chanmode_free(change);
3123 if(action & ACTION_DEL_BAN)
3125 struct banData *ban, *next;
3127 ban = channel->channel_info->bans;
3131 for( ; ban && !user_matches_glob(actee, ban->mask,
3132 MATCH_USENICK | MATCH_VISIBLE);
3135 for( ; ban && !match_ircglobs(mask, ban->mask);
3140 del_channel_ban(ban);
3147 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3149 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3155 static CHANSERV_FUNC(cmd_unban)
3157 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3160 static CHANSERV_FUNC(cmd_delban)
3162 /* it doesn't necessarily have to remove the channel ban - may want
3163 to make that an option. */
3164 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3167 static CHANSERV_FUNC(cmd_unbanme)
3169 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3170 long flags = ACTION_UNBAN;
3172 /* remove permanent bans if the user has the proper access. */
3173 if(uData->access >= UL_MASTER)
3174 flags |= ACTION_DEL_BAN;
3176 argv[1] = user->nick;
3177 return unban_user(user, channel, 2, argv, cmd, flags);
3180 static CHANSERV_FUNC(cmd_unbanall)
3182 struct mod_chanmode *change;
3185 if(!channel->banlist.used)
3187 reply("CSMSG_NO_BANS", channel->name);
3191 change = mod_chanmode_alloc(channel->banlist.used);
3192 for(ii=0; ii<channel->banlist.used; ii++)
3194 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3195 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3197 modcmd_chanmode_announce(change);
3198 for(ii = 0; ii < change->argc; ++ii)
3199 free((char*)change->args[ii].u.hostmask);
3200 mod_chanmode_free(change);
3201 reply("CSMSG_BANS_REMOVED", channel->name);
3205 static CHANSERV_FUNC(cmd_open)
3207 struct mod_chanmode *change;
3210 change = find_matching_bans(&channel->banlist, user, NULL);
3212 change = mod_chanmode_alloc(0);
3213 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3214 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3215 && channel->channel_info->modes.modes_set)
3216 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3217 modcmd_chanmode_announce(change);
3218 reply("CSMSG_CHANNEL_OPENED", channel->name);
3219 for(ii = 0; ii < change->argc; ++ii)
3220 free((char*)change->args[ii].u.hostmask);
3221 mod_chanmode_free(change);
3225 static CHANSERV_FUNC(cmd_myaccess)
3227 static struct string_buffer sbuf;
3228 struct handle_info *target_handle;
3229 struct userData *uData;
3232 target_handle = user->handle_info;
3233 else if(!IsHelping(user))
3235 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3238 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3241 if(!target_handle->channels)
3243 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3247 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3248 for(uData = target_handle->channels; uData; uData = uData->u_next)
3250 struct chanData *cData = uData->channel;
3252 if(uData->access > UL_OWNER)
3254 if(IsProtected(cData)
3255 && (target_handle != user->handle_info)
3256 && !GetTrueChannelAccess(cData, user->handle_info))
3259 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3260 if(uData->flags != USER_AUTO_OP)
3261 string_buffer_append(&sbuf, ',');
3262 if(IsUserSuspended(uData))
3263 string_buffer_append(&sbuf, 's');
3264 if(IsUserAutoOp(uData))
3266 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3267 string_buffer_append(&sbuf, 'o');
3268 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3269 string_buffer_append(&sbuf, 'v');
3271 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3272 string_buffer_append(&sbuf, 'i');
3274 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3276 string_buffer_append_string(&sbuf, ")]");
3277 string_buffer_append(&sbuf, '\0');
3278 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3284 static CHANSERV_FUNC(cmd_access)
3286 struct userNode *target;
3287 struct handle_info *target_handle;
3288 struct userData *uData;
3290 char prefix[MAXLEN];
3295 target_handle = target->handle_info;
3297 else if((target = GetUserH(argv[1])))
3299 target_handle = target->handle_info;
3301 else if(argv[1][0] == '*')
3303 if(!(target_handle = get_handle_info(argv[1]+1)))
3305 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3311 reply("MSG_NICK_UNKNOWN", argv[1]);
3315 assert(target || target_handle);
3317 if(target == chanserv)
3319 reply("CSMSG_IS_CHANSERV");
3327 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3332 reply("MSG_USER_AUTHENTICATE", target->nick);
3335 reply("MSG_AUTHENTICATE");
3341 const char *epithet = NULL, *type = NULL;
3344 epithet = chanserv_conf.irc_operator_epithet;
3345 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3347 else if(IsNetworkHelper(target))
3349 epithet = chanserv_conf.network_helper_epithet;
3350 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3352 else if(IsSupportHelper(target))
3354 epithet = chanserv_conf.support_helper_epithet;
3355 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3359 if(target_handle->epithet)
3360 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3362 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3364 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3368 sprintf(prefix, "%s", target_handle->handle);
3371 if(!channel->channel_info)
3373 reply("CSMSG_NOT_REGISTERED", channel->name);
3377 helping = HANDLE_FLAGGED(target_handle, HELPING)
3378 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3379 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3381 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3382 /* To prevent possible information leaks, only show infolines
3383 * if the requestor is in the channel or it's their own
3385 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3387 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3389 /* Likewise, only say it's suspended if the user has active
3390 * access in that channel or it's their own entry. */
3391 if(IsUserSuspended(uData)
3392 && (GetChannelUser(channel->channel_info, user->handle_info)
3393 || (user->handle_info == uData->handle)))
3395 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3400 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3407 zoot_list(struct listData *list)
3409 struct userData *uData;
3410 unsigned int start, curr, highest, lowest;
3411 struct helpfile_table tmp_table;
3412 const char **temp, *msg;
3414 if(list->table.length == 1)
3417 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3419 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3420 msg = user_find_message(list->user, "MSG_NONE");
3421 send_message_type(4, list->user, list->bot, " %s", msg);
3423 tmp_table.width = list->table.width;
3424 tmp_table.flags = list->table.flags;
3425 list->table.contents[0][0] = " ";
3426 highest = list->highest;
3427 if(list->lowest != 0)
3428 lowest = list->lowest;
3429 else if(highest < 100)
3432 lowest = highest - 100;
3433 for(start = curr = 1; curr < list->table.length; )
3435 uData = list->users[curr-1];
3436 list->table.contents[curr++][0] = " ";
3437 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3440 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3442 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3443 temp = list->table.contents[--start];
3444 list->table.contents[start] = list->table.contents[0];
3445 tmp_table.contents = list->table.contents + start;
3446 tmp_table.length = curr - start;
3447 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3448 list->table.contents[start] = temp;
3450 highest = lowest - 1;
3451 lowest = (highest < 100) ? 0 : (highest - 99);
3457 def_list(struct listData *list)
3461 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3463 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3464 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3465 if(list->table.length == 1)
3467 msg = user_find_message(list->user, "MSG_NONE");
3468 send_message_type(4, list->user, list->bot, " %s", msg);
3473 userData_access_comp(const void *arg_a, const void *arg_b)
3475 const struct userData *a = *(struct userData**)arg_a;
3476 const struct userData *b = *(struct userData**)arg_b;
3478 if(a->access != b->access)
3479 res = b->access - a->access;
3481 res = irccasecmp(a->handle->handle, b->handle->handle);
3486 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3488 void (*send_list)(struct listData *);
3489 struct userData *uData;
3490 struct listData lData;
3491 unsigned int matches;
3495 lData.bot = cmd->parent->bot;
3496 lData.channel = channel;
3497 lData.lowest = lowest;
3498 lData.highest = highest;
3499 lData.search = (argc > 1) ? argv[1] : NULL;
3500 send_list = def_list;
3501 (void)zoot_list; /* since it doesn't show user levels */
3503 if(user->handle_info)
3505 switch(user->handle_info->userlist_style)
3507 case HI_STYLE_DEF: send_list = def_list; break;
3508 case HI_STYLE_ZOOT: send_list = def_list; break;
3512 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3514 for(uData = channel->channel_info->users; uData; uData = uData->next)
3516 if((uData->access < lowest)
3517 || (uData->access > highest)
3518 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3520 lData.users[matches++] = uData;
3522 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3524 lData.table.length = matches+1;
3525 lData.table.width = 4;
3526 lData.table.flags = TABLE_NO_FREE;
3527 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3528 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3529 lData.table.contents[0] = ary;
3532 ary[2] = "Last Seen";
3534 for(matches = 1; matches < lData.table.length; ++matches)
3536 struct userData *uData = lData.users[matches-1];
3537 char seen[INTERVALLEN];
3539 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3540 lData.table.contents[matches] = ary;
3541 ary[0] = strtab(uData->access);
3542 ary[1] = uData->handle->handle;
3545 else if(!uData->seen)
3548 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3549 ary[2] = strdup(ary[2]);
3550 if(IsUserSuspended(uData))
3551 ary[3] = "Suspended";
3552 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3553 ary[3] = "Vacation";
3558 for(matches = 1; matches < lData.table.length; ++matches)
3560 free((char*)lData.table.contents[matches][2]);
3561 free(lData.table.contents[matches]);
3563 free(lData.table.contents[0]);
3564 free(lData.table.contents);
3568 static CHANSERV_FUNC(cmd_users)
3570 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3573 static CHANSERV_FUNC(cmd_wlist)
3575 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3578 static CHANSERV_FUNC(cmd_clist)
3580 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3583 static CHANSERV_FUNC(cmd_mlist)
3585 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3588 static CHANSERV_FUNC(cmd_olist)
3590 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3593 static CHANSERV_FUNC(cmd_plist)
3595 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3598 static CHANSERV_FUNC(cmd_bans)
3600 struct userNode *search_u = NULL;
3601 struct helpfile_table tbl;
3602 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3603 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3604 const char *msg_never, *triggered, *expires;
3605 struct banData *ban, **bans;
3609 else if(strchr(search = argv[1], '!'))
3612 search_wilds = search[strcspn(search, "?*")];
3614 else if(!(search_u = GetUserH(search)))
3615 reply("MSG_NICK_UNKNOWN", search);
3617 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3619 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3623 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3628 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3631 bans[matches++] = ban;
3636 tbl.length = matches + 1;
3637 tbl.width = 4 + timed;
3639 tbl.flags = TABLE_NO_FREE;
3640 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3641 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3642 tbl.contents[0][0] = "Mask";
3643 tbl.contents[0][1] = "Set By";
3644 tbl.contents[0][2] = "Triggered";
3647 tbl.contents[0][3] = "Expires";
3648 tbl.contents[0][4] = "Reason";
3651 tbl.contents[0][3] = "Reason";
3654 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3656 free(tbl.contents[0]);
3661 msg_never = user_find_message(user, "MSG_NEVER");
3662 for(ii = 0; ii < matches; )
3668 else if(ban->expires)
3669 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3671 expires = msg_never;
3674 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3676 triggered = msg_never;
3678 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3679 tbl.contents[ii][0] = ban->mask;
3680 tbl.contents[ii][1] = ban->owner;
3681 tbl.contents[ii][2] = strdup(triggered);
3684 tbl.contents[ii][3] = strdup(expires);
3685 tbl.contents[ii][4] = ban->reason;
3688 tbl.contents[ii][3] = ban->reason;
3690 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3691 reply("MSG_MATCH_COUNT", matches);
3692 for(ii = 1; ii < tbl.length; ++ii)
3694 free((char*)tbl.contents[ii][2]);
3696 free((char*)tbl.contents[ii][3]);
3697 free(tbl.contents[ii]);
3699 free(tbl.contents[0]);
3705 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3707 struct chanData *cData = channel->channel_info;
3708 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3710 if(cData->topic_mask)
3711 return !match_ircglob(new_topic, cData->topic_mask);
3712 else if(cData->topic)
3713 return irccasecmp(new_topic, cData->topic);
3718 static CHANSERV_FUNC(cmd_topic)
3720 struct chanData *cData;
3723 cData = channel->channel_info;
3728 SetChannelTopic(channel, chanserv, cData->topic, 1);
3729 reply("CSMSG_TOPIC_SET", cData->topic);
3733 reply("CSMSG_NO_TOPIC", channel->name);
3737 topic = unsplit_string(argv + 1, argc - 1, NULL);
3738 /* If they say "!topic *", use an empty topic. */
3739 if((topic[0] == '*') && (topic[1] == 0))
3741 if(bad_topic(channel, user, topic))
3743 char *topic_mask = cData->topic_mask;
3746 char new_topic[TOPICLEN+1], tchar;
3747 int pos=0, starpos=-1, dpos=0, len;
3749 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3756 len = strlen(topic);
3757 if((dpos + len) > TOPICLEN)
3758 len = TOPICLEN + 1 - dpos;
3759 memcpy(new_topic+dpos, topic, len);
3763 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3764 default: new_topic[dpos++] = tchar; break;
3767 if((dpos > TOPICLEN) || tchar)
3770 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3771 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3774 new_topic[dpos] = 0;
3775 SetChannelTopic(channel, chanserv, new_topic, 1);
3777 reply("CSMSG_TOPIC_LOCKED", channel->name);
3782 SetChannelTopic(channel, chanserv, topic, 1);
3784 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3786 /* Grab the topic and save it as the default topic. */
3788 cData->topic = strdup(channel->topic);
3794 static CHANSERV_FUNC(cmd_mode)
3796 struct userData *uData;
3797 struct mod_chanmode *change;
3802 change = &channel->channel_info->modes;
3803 if(change->modes_set || change->modes_clear) {
3804 modcmd_chanmode_announce(change);
3805 reply("CSMSG_DEFAULTED_MODES", channel->name);
3807 reply("CSMSG_NO_MODES", channel->name);
3811 uData = GetChannelUser(channel->channel_info, user->handle_info);
3813 base_oplevel = MAXOPLEVEL;
3814 else if (uData->access >= UL_OWNER)
3817 base_oplevel = 1 + UL_OWNER - uData->access;
3818 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3821 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3825 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3826 && mode_lock_violated(&channel->channel_info->modes, change))
3829 mod_chanmode_format(&channel->channel_info->modes, modes);
3830 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3834 modcmd_chanmode_announce(change);
3835 mod_chanmode_free(change);
3836 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3840 static CHANSERV_FUNC(cmd_invite)
3842 struct userData *uData;
3843 struct userNode *invite;
3845 uData = GetChannelUser(channel->channel_info, user->handle_info);
3849 if(!(invite = GetUserH(argv[1])))
3851 reply("MSG_NICK_UNKNOWN", argv[1]);
3858 if(GetUserMode(channel, invite))
3860 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3868 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3869 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3872 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3874 irc_invite(chanserv, invite, channel);
3876 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3881 static CHANSERV_FUNC(cmd_inviteme)
3883 if(GetUserMode(channel, user))
3885 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3888 if(channel->channel_info
3889 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3891 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3894 irc_invite(cmd->parent->bot, user, channel);
3899 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3902 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3904 /* We display things based on two dimensions:
3905 * - Issue time: present or absent
3906 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3907 * (in order of precedence, so something both expired and revoked
3908 * only counts as revoked)
3910 combo = (suspended->issued ? 4 : 0)
3911 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3913 case 0: /* no issue time, indefinite expiration */
3914 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3916 case 1: /* no issue time, expires in future */
3917 intervalString(buf1, suspended->expires-now, user->handle_info);
3918 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3920 case 2: /* no issue time, expired */
3921 intervalString(buf1, now-suspended->expires, user->handle_info);
3922 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3924 case 3: /* no issue time, revoked */
3925 intervalString(buf1, now-suspended->revoked, user->handle_info);
3926 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3928 case 4: /* issue time set, indefinite expiration */
3929 intervalString(buf1, now-suspended->issued, user->handle_info);
3930 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3932 case 5: /* issue time set, expires in future */
3933 intervalString(buf1, now-suspended->issued, user->handle_info);
3934 intervalString(buf2, suspended->expires-now, user->handle_info);
3935 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3937 case 6: /* issue time set, expired */
3938 intervalString(buf1, now-suspended->issued, user->handle_info);
3939 intervalString(buf2, now-suspended->expires, user->handle_info);
3940 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3942 case 7: /* issue time set, revoked */
3943 intervalString(buf1, now-suspended->issued, user->handle_info);
3944 intervalString(buf2, now-suspended->revoked, user->handle_info);
3945 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3948 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3953 static CHANSERV_FUNC(cmd_info)
3955 char modes[MAXLEN], buffer[INTERVALLEN];
3956 struct userData *uData, *owner;
3957 struct chanData *cData;
3958 struct do_not_register *dnr;
3963 cData = channel->channel_info;
3964 reply("CSMSG_CHANNEL_INFO", channel->name);
3966 uData = GetChannelUser(cData, user->handle_info);
3967 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3969 mod_chanmode_format(&cData->modes, modes);
3970 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3971 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3974 for(it = dict_first(cData->notes); it; it = iter_next(it))
3978 note = iter_data(it);
3979 if(!note_type_visible_to_user(cData, note->type, user))
3982 padding = PADLEN - 1 - strlen(iter_key(it));
3983 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3986 reply("CSMSG_CHANNEL_MAX", cData->max);
3987 for(owner = cData->users; owner; owner = owner->next)
3988 if(owner->access == UL_OWNER)
3989 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3990 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3991 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3992 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3994 privileged = IsStaff(user);
3996 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3997 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3998 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4000 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4001 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4003 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4005 struct suspended *suspended;
4006 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4007 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4008 show_suspension_info(cmd, user, suspended);
4010 else if(IsSuspended(cData))
4012 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4013 show_suspension_info(cmd, user, cData->suspended);
4018 static CHANSERV_FUNC(cmd_netinfo)
4020 extern time_t boot_time;
4021 extern unsigned long burst_length;
4022 char interval[INTERVALLEN];
4024 reply("CSMSG_NETWORK_INFO");
4025 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4026 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4027 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4028 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4029 reply("CSMSG_NETWORK_BANS", banCount);
4030 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4031 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4032 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4037 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4039 struct helpfile_table table;
4041 struct userNode *user;
4046 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4047 table.contents = alloca(list->used*sizeof(*table.contents));
4048 for(nn=0; nn<list->used; nn++)
4050 user = list->list[nn];
4051 if(user->modes & skip_flags)
4055 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4058 nick = alloca(strlen(user->nick)+3);
4059 sprintf(nick, "(%s)", user->nick);
4063 table.contents[table.length][0] = nick;
4066 table_send(chanserv, to->nick, 0, NULL, table);
4069 static CHANSERV_FUNC(cmd_ircops)
4071 reply("CSMSG_STAFF_OPERS");
4072 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4076 static CHANSERV_FUNC(cmd_helpers)
4078 reply("CSMSG_STAFF_HELPERS");
4079 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4083 static CHANSERV_FUNC(cmd_staff)
4085 reply("CSMSG_NETWORK_STAFF");
4086 cmd_ircops(CSFUNC_ARGS);
4087 cmd_helpers(CSFUNC_ARGS);
4091 static CHANSERV_FUNC(cmd_peek)
4093 struct modeNode *mn;
4094 char modes[MODELEN];
4096 struct helpfile_table table;
4098 irc_make_chanmode(channel, modes);
4100 reply("CSMSG_PEEK_INFO", channel->name);
4101 reply("CSMSG_PEEK_TOPIC", channel->topic);
4102 reply("CSMSG_PEEK_MODES", modes);
4103 reply("CSMSG_PEEK_USERS", channel->members.used);
4107 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4108 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4109 for(n = 0; n < channel->members.used; n++)
4111 mn = channel->members.list[n];
4112 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4114 table.contents[table.length] = alloca(sizeof(**table.contents));
4115 table.contents[table.length][0] = mn->user->nick;
4120 reply("CSMSG_PEEK_OPS");
4121 table_send(chanserv, user->nick, 0, NULL, table);
4124 reply("CSMSG_PEEK_NO_OPS");
4128 static MODCMD_FUNC(cmd_wipeinfo)
4130 struct handle_info *victim;
4131 struct userData *ud, *actor;
4134 actor = GetChannelUser(channel->channel_info, user->handle_info);
4135 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4137 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4139 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4142 if((ud->access >= actor->access) && (ud != actor))
4144 reply("MSG_USER_OUTRANKED", victim->handle);
4150 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4154 static CHANSERV_FUNC(cmd_resync)
4156 struct mod_chanmode *changes;
4157 struct chanData *cData = channel->channel_info;
4158 unsigned int ii, used;
4160 changes = mod_chanmode_alloc(channel->members.used * 2);
4161 for(ii = used = 0; ii < channel->members.used; ++ii)
4163 struct modeNode *mn = channel->members.list[ii];
4164 struct userData *uData;
4166 if(IsService(mn->user))
4169 uData = GetChannelAccess(cData, mn->user->handle_info);
4170 if(!cData->lvlOpts[lvlGiveOps]
4171 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4173 if(!(mn->modes & MODE_CHANOP))
4175 changes->args[used].mode = MODE_CHANOP;
4176 changes->args[used++].u.member = mn;
4179 else if(!cData->lvlOpts[lvlGiveVoice]
4180 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4182 if(mn->modes & MODE_CHANOP)
4184 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4185 changes->args[used++].u.member = mn;
4187 if(!(mn->modes & MODE_VOICE))
4189 changes->args[used].mode = MODE_VOICE;
4190 changes->args[used++].u.member = mn;
4197 changes->args[used].mode = MODE_REMOVE | mn->modes;
4198 changes->args[used++].u.member = mn;
4202 changes->argc = used;
4203 modcmd_chanmode_announce(changes);
4204 mod_chanmode_free(changes);
4205 reply("CSMSG_RESYNCED_USERS", channel->name);
4209 static CHANSERV_FUNC(cmd_seen)
4211 struct userData *uData;
4212 struct handle_info *handle;
4213 char seen[INTERVALLEN];
4217 if(!irccasecmp(argv[1], chanserv->nick))
4219 reply("CSMSG_IS_CHANSERV");
4223 if(!(handle = get_handle_info(argv[1])))
4225 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4229 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4231 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4236 reply("CSMSG_USER_PRESENT", handle->handle);
4237 else if(uData->seen)
4238 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4240 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4242 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4243 reply("CSMSG_USER_VACATION", handle->handle);
4248 static MODCMD_FUNC(cmd_names)
4250 struct userNode *targ;
4251 struct userData *targData;
4252 unsigned int ii, pos;
4255 for(ii=pos=0; ii<channel->members.used; ++ii)
4257 targ = channel->members.list[ii]->user;
4258 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4261 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4264 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4268 if(IsUserSuspended(targData))
4270 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4273 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4274 reply("CSMSG_END_NAMES", channel->name);
4279 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4281 switch(ntype->visible_type)
4283 case NOTE_VIS_ALL: return 1;
4284 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4285 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4290 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4292 struct userData *uData;
4294 switch(ntype->set_access_type)
4296 case NOTE_SET_CHANNEL_ACCESS:
4297 if(!user->handle_info)
4299 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4301 return uData->access >= ntype->set_access.min_ulevel;
4302 case NOTE_SET_CHANNEL_SETTER:
4303 return check_user_level(channel, user, lvlSetters, 1, 0);
4304 case NOTE_SET_PRIVILEGED: default:
4305 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4309 static CHANSERV_FUNC(cmd_note)
4311 struct chanData *cData;
4313 struct note_type *ntype;
4315 cData = channel->channel_info;
4318 reply("CSMSG_NOT_REGISTERED", channel->name);
4322 /* If no arguments, show all visible notes for the channel. */
4328 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4330 note = iter_data(it);
4331 if(!note_type_visible_to_user(cData, note->type, user))
4334 reply("CSMSG_NOTELIST_HEADER", channel->name);
4335 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4338 reply("CSMSG_NOTELIST_END", channel->name);
4340 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4342 /* If one argument, show the named note. */
4345 if((note = dict_find(cData->notes, argv[1], NULL))
4346 && note_type_visible_to_user(cData, note->type, user))
4348 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4350 else if((ntype = dict_find(note_types, argv[1], NULL))
4351 && note_type_visible_to_user(NULL, ntype, user))
4353 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4358 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4362 /* Assume they're trying to set a note. */
4366 ntype = dict_find(note_types, argv[1], NULL);
4369 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4372 else if(note_type_settable_by_user(channel, ntype, user))
4374 note_text = unsplit_string(argv+2, argc-2, NULL);
4375 if((note = dict_find(cData->notes, argv[1], NULL)))
4376 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4377 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4378 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4380 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4382 /* The note is viewable to staff only, so return 0
4383 to keep the invocation from getting logged (or
4384 regular users can see it in !events). */
4390 reply("CSMSG_NO_ACCESS");
4397 static CHANSERV_FUNC(cmd_delnote)
4402 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4403 || !note_type_settable_by_user(channel, note->type, user))
4405 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4408 dict_remove(channel->channel_info->notes, note->type->name);
4409 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4413 static CHANSERV_FUNC(cmd_events)
4415 struct logSearch discrim;
4416 struct logReport report;
4417 unsigned int matches, limit;
4419 limit = (argc > 1) ? atoi(argv[1]) : 10;
4420 if(limit < 1 || limit > 200)
4423 memset(&discrim, 0, sizeof(discrim));
4424 discrim.masks.bot = chanserv;
4425 discrim.masks.channel_name = channel->name;
4427 discrim.masks.command = argv[2];
4428 discrim.limit = limit;
4429 discrim.max_time = INT_MAX;
4430 discrim.severities = 1 << LOG_COMMAND;
4431 report.reporter = chanserv;
4433 reply("CSMSG_EVENT_SEARCH_RESULTS");
4434 matches = log_entry_search(&discrim, log_report_entry, &report);
4436 reply("MSG_MATCH_COUNT", matches);
4438 reply("MSG_NO_MATCHES");
4442 static CHANSERV_FUNC(cmd_say)
4448 msg = unsplit_string(argv + 1, argc - 1, NULL);
4449 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4451 else if(GetUserH(argv[1]))
4454 msg = unsplit_string(argv + 2, argc - 2, NULL);
4455 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4459 reply("MSG_NOT_TARGET_NAME");
4465 static CHANSERV_FUNC(cmd_emote)
4471 /* CTCP is so annoying. */
4472 msg = unsplit_string(argv + 1, argc - 1, NULL);
4473 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4475 else if(GetUserH(argv[1]))
4477 msg = unsplit_string(argv + 2, argc - 2, NULL);
4478 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4482 reply("MSG_NOT_TARGET_NAME");
4488 struct channelList *
4489 chanserv_support_channels(void)
4491 return &chanserv_conf.support_channels;
4494 static CHANSERV_FUNC(cmd_expire)
4496 int channel_count = registered_channels;
4497 expire_channels(NULL);
4498 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4503 chanserv_expire_suspension(void *data)
4505 struct suspended *suspended = data;
4506 struct chanNode *channel;
4508 if(!suspended->expires || (now < suspended->expires))
4509 suspended->revoked = now;
4510 channel = suspended->cData->channel;
4511 suspended->cData->channel = channel;
4512 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4513 if(!IsOffChannel(suspended->cData))
4515 struct mod_chanmode change;
4516 mod_chanmode_init(&change);
4518 change.args[0].mode = MODE_CHANOP;
4519 change.args[0].u.member = AddChannelUser(chanserv, channel);
4520 mod_chanmode_announce(chanserv, channel, &change);
4524 static CHANSERV_FUNC(cmd_csuspend)
4526 struct suspended *suspended;
4527 char reason[MAXLEN];
4528 time_t expiry, duration;
4529 struct userData *uData;
4533 if(IsProtected(channel->channel_info))
4535 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4539 if(argv[1][0] == '!')
4541 else if(IsSuspended(channel->channel_info))
4543 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4544 show_suspension_info(cmd, user, channel->channel_info->suspended);
4548 if(!strcmp(argv[1], "0"))
4550 else if((duration = ParseInterval(argv[1])))
4551 expiry = now + duration;
4554 reply("MSG_INVALID_DURATION", argv[1]);
4558 unsplit_string(argv + 2, argc - 2, reason);
4560 suspended = calloc(1, sizeof(*suspended));
4561 suspended->revoked = 0;
4562 suspended->issued = now;
4563 suspended->suspender = strdup(user->handle_info->handle);
4564 suspended->expires = expiry;
4565 suspended->reason = strdup(reason);
4566 suspended->cData = channel->channel_info;
4567 suspended->previous = suspended->cData->suspended;
4568 suspended->cData->suspended = suspended;
4570 if(suspended->expires)
4571 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4573 if(IsSuspended(channel->channel_info))
4575 suspended->previous->revoked = now;
4576 if(suspended->previous->expires)
4577 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4578 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4579 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4583 /* Mark all users in channel as absent. */
4584 for(uData = channel->channel_info->users; uData; uData = uData->next)
4593 /* Mark the channel as suspended, then part. */
4594 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4595 DelChannelUser(chanserv, channel, suspended->reason, 0);
4596 reply("CSMSG_SUSPENDED", channel->name);
4597 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4598 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4603 static CHANSERV_FUNC(cmd_cunsuspend)
4605 struct suspended *suspended;
4606 char message[MAXLEN];
4608 if(!IsSuspended(channel->channel_info))
4610 reply("CSMSG_NOT_SUSPENDED", channel->name);
4614 suspended = channel->channel_info->suspended;
4616 /* Expire the suspension and join ChanServ to the channel. */
4617 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4618 chanserv_expire_suspension(suspended);
4619 reply("CSMSG_UNSUSPENDED", channel->name);
4620 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4621 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4625 typedef struct chanservSearch
4633 unsigned long flags;
4637 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4640 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4645 search = malloc(sizeof(struct chanservSearch));
4646 memset(search, 0, sizeof(*search));
4649 for(i = 0; i < argc; i++)
4651 /* Assume all criteria require arguments. */
4654 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4658 if(!irccasecmp(argv[i], "name"))
4659 search->name = argv[++i];
4660 else if(!irccasecmp(argv[i], "registrar"))
4661 search->registrar = argv[++i];
4662 else if(!irccasecmp(argv[i], "unvisited"))
4663 search->unvisited = ParseInterval(argv[++i]);
4664 else if(!irccasecmp(argv[i], "registered"))
4665 search->registered = ParseInterval(argv[++i]);
4666 else if(!irccasecmp(argv[i], "flags"))
4669 if(!irccasecmp(argv[i], "nodelete"))
4670 search->flags |= CHANNEL_NODELETE;
4671 else if(!irccasecmp(argv[i], "suspended"))
4672 search->flags |= CHANNEL_SUSPENDED;
4675 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4679 else if(!irccasecmp(argv[i], "limit"))
4680 search->limit = strtoul(argv[++i], NULL, 10);
4683 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4688 if(search->name && !strcmp(search->name, "*"))
4690 if(search->registrar && !strcmp(search->registrar, "*"))
4691 search->registrar = 0;
4700 chanserv_channel_match(struct chanData *channel, search_t search)
4702 const char *name = channel->channel->name;
4703 if((search->name && !match_ircglob(name, search->name)) ||
4704 (search->registrar && !channel->registrar) ||
4705 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4706 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4707 (search->registered && (now - channel->registered) > search->registered) ||
4708 (search->flags && ((search->flags & channel->flags) != search->flags)))
4715 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4717 struct chanData *channel;
4718 unsigned int matches = 0;
4720 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4722 if(!chanserv_channel_match(channel, search))
4732 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4737 search_print(struct chanData *channel, void *data)
4739 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4742 static CHANSERV_FUNC(cmd_search)
4745 unsigned int matches;
4746 channel_search_func action;
4750 if(!irccasecmp(argv[1], "count"))
4751 action = search_count;
4752 else if(!irccasecmp(argv[1], "print"))
4753 action = search_print;
4756 reply("CSMSG_ACTION_INVALID", argv[1]);
4760 search = chanserv_search_create(user, argc - 2, argv + 2);
4764 if(action == search_count)
4765 search->limit = INT_MAX;
4767 if(action == search_print)
4768 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4770 matches = chanserv_channel_search(search, action, user);
4773 reply("MSG_MATCH_COUNT", matches);
4775 reply("MSG_NO_MATCHES");
4781 static CHANSERV_FUNC(cmd_unvisited)
4783 struct chanData *cData;
4784 time_t interval = chanserv_conf.channel_expire_delay;
4785 char buffer[INTERVALLEN];
4786 unsigned int limit = 25, matches = 0;
4790 interval = ParseInterval(argv[1]);
4792 limit = atoi(argv[2]);
4795 intervalString(buffer, interval, user->handle_info);
4796 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4798 for(cData = channelList; cData && matches < limit; cData = cData->next)
4800 if((now - cData->visited) < interval)
4803 intervalString(buffer, now - cData->visited, user->handle_info);
4804 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4811 static MODCMD_FUNC(chan_opt_defaulttopic)
4817 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4819 reply("CSMSG_TOPIC_LOCKED", channel->name);
4823 topic = unsplit_string(argv+1, argc-1, NULL);
4825 free(channel->channel_info->topic);
4826 if(topic[0] == '*' && topic[1] == 0)
4828 topic = channel->channel_info->topic = NULL;
4832 topic = channel->channel_info->topic = strdup(topic);
4833 if(channel->channel_info->topic_mask
4834 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4835 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4837 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4840 if(channel->channel_info->topic)
4841 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4843 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4847 static MODCMD_FUNC(chan_opt_topicmask)
4851 struct chanData *cData = channel->channel_info;
4854 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4856 reply("CSMSG_TOPIC_LOCKED", channel->name);
4860 mask = unsplit_string(argv+1, argc-1, NULL);
4862 if(cData->topic_mask)
4863 free(cData->topic_mask);
4864 if(mask[0] == '*' && mask[1] == 0)
4866 cData->topic_mask = 0;
4870 cData->topic_mask = strdup(mask);
4872 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4873 else if(!match_ircglob(cData->topic, cData->topic_mask))
4874 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4878 if(channel->channel_info->topic_mask)
4879 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4881 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4885 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4889 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4893 if(greeting[0] == '*' && greeting[1] == 0)
4897 unsigned int length = strlen(greeting);
4898 if(length > chanserv_conf.greeting_length)
4900 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4903 *data = strdup(greeting);
4912 reply(name, user_find_message(user, "MSG_NONE"));
4916 static MODCMD_FUNC(chan_opt_greeting)
4918 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4921 static MODCMD_FUNC(chan_opt_usergreeting)
4923 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4926 static MODCMD_FUNC(chan_opt_modes)
4928 struct mod_chanmode *new_modes;
4929 char modes[MODELEN];
4933 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4935 reply("CSMSG_NO_ACCESS");
4938 if(argv[1][0] == '*' && argv[1][1] == 0)
4940 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4942 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4944 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4947 else if(new_modes->argc > 1)
4949 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4950 mod_chanmode_free(new_modes);
4955 channel->channel_info->modes = *new_modes;
4956 modcmd_chanmode_announce(new_modes);
4957 mod_chanmode_free(new_modes);
4961 mod_chanmode_format(&channel->channel_info->modes, modes);
4963 reply("CSMSG_SET_MODES", modes);
4965 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4969 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4971 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4973 struct chanData *cData = channel->channel_info;
4978 /* Set flag according to value. */
4979 if(enabled_string(argv[1]))
4981 cData->flags |= mask;
4984 else if(disabled_string(argv[1]))
4986 cData->flags &= ~mask;
4991 reply("MSG_INVALID_BINARY", argv[1]);
4997 /* Find current option value. */
4998 value = (cData->flags & mask) ? 1 : 0;
5002 reply(name, user_find_message(user, "MSG_ON"));
5004 reply(name, user_find_message(user, "MSG_OFF"));
5008 static MODCMD_FUNC(chan_opt_nodelete)
5010 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5012 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5016 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5019 static MODCMD_FUNC(chan_opt_dynlimit)
5021 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5024 static MODCMD_FUNC(chan_opt_offchannel)
5026 struct chanData *cData = channel->channel_info;
5031 /* Set flag according to value. */
5032 if(enabled_string(argv[1]))
5034 if(!IsOffChannel(cData))
5035 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5036 cData->flags |= CHANNEL_OFFCHANNEL;
5039 else if(disabled_string(argv[1]))
5041 if(IsOffChannel(cData))
5043 struct mod_chanmode change;
5044 mod_chanmode_init(&change);
5046 change.args[0].mode = MODE_CHANOP;
5047 change.args[0].u.member = AddChannelUser(chanserv, channel);
5048 mod_chanmode_announce(chanserv, channel, &change);
5050 cData->flags &= ~CHANNEL_OFFCHANNEL;
5055 reply("MSG_INVALID_BINARY", argv[1]);
5061 /* Find current option value. */
5062 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5066 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5068 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5072 static MODCMD_FUNC(chan_opt_defaults)
5074 struct userData *uData;
5075 struct chanData *cData;
5076 const char *confirm;
5077 enum levelOption lvlOpt;
5078 enum charOption chOpt;
5080 cData = channel->channel_info;
5081 uData = GetChannelUser(cData, user->handle_info);
5082 if(!uData || (uData->access < UL_OWNER))
5084 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5087 confirm = make_confirmation_string(uData);
5088 if((argc < 2) || strcmp(argv[1], confirm))
5090 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5093 cData->flags = CHANNEL_DEFAULT_FLAGS;
5094 cData->modes = chanserv_conf.default_modes;
5095 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5096 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5097 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5098 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5099 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5104 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5106 struct chanData *cData = channel->channel_info;
5107 struct userData *uData;
5108 unsigned short value;
5112 if(!check_user_level(channel, user, option, 1, 1))
5114 reply("CSMSG_CANNOT_SET");
5117 value = user_level_from_name(argv[1], UL_OWNER+1);
5118 if(!value && strcmp(argv[1], "0"))
5120 reply("CSMSG_INVALID_ACCESS", argv[1]);
5123 uData = GetChannelUser(cData, user->handle_info);
5124 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5126 reply("CSMSG_BAD_SETLEVEL");
5132 if(value > cData->lvlOpts[lvlGiveOps])
5134 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5139 if(value < cData->lvlOpts[lvlGiveVoice])
5141 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5146 /* This test only applies to owners, since non-owners
5147 * trying to set an option to above their level get caught
5148 * by the CSMSG_BAD_SETLEVEL test above.
5150 if(value > uData->access)
5152 reply("CSMSG_BAD_SETTERS");
5159 cData->lvlOpts[option] = value;
5161 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5165 static MODCMD_FUNC(chan_opt_enfops)
5167 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5170 static MODCMD_FUNC(chan_opt_giveops)
5172 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5175 static MODCMD_FUNC(chan_opt_enfmodes)
5177 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5180 static MODCMD_FUNC(chan_opt_enftopic)
5182 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5185 static MODCMD_FUNC(chan_opt_pubcmd)
5187 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5190 static MODCMD_FUNC(chan_opt_setters)
5192 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5195 static MODCMD_FUNC(chan_opt_ctcpusers)
5197 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5200 static MODCMD_FUNC(chan_opt_userinfo)
5202 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5205 static MODCMD_FUNC(chan_opt_givevoice)
5207 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5210 static MODCMD_FUNC(chan_opt_topicsnarf)
5212 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5215 static MODCMD_FUNC(chan_opt_inviteme)
5217 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5221 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5223 struct chanData *cData = channel->channel_info;
5224 int count = charOptions[option].count, index;
5228 index = atoi(argv[1]);
5230 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5232 reply("CSMSG_INVALID_NUMERIC", index);
5233 /* Show possible values. */
5234 for(index = 0; index < count; index++)
5235 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5239 cData->chOpts[option] = charOptions[option].values[index].value;
5243 /* Find current option value. */
5246 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5250 /* Somehow, the option value is corrupt; reset it to the default. */
5251 cData->chOpts[option] = charOptions[option].default_value;
5256 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5260 static MODCMD_FUNC(chan_opt_protect)
5262 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5265 static MODCMD_FUNC(chan_opt_toys)
5267 return channel_multiple_option(chToys, CSFUNC_ARGS);
5270 static MODCMD_FUNC(chan_opt_ctcpreaction)
5272 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5275 static MODCMD_FUNC(chan_opt_topicrefresh)
5277 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5280 static struct svccmd_list set_shows_list;
5283 handle_svccmd_unbind(struct svccmd *target) {
5285 for(ii=0; ii<set_shows_list.used; ++ii)
5286 if(target == set_shows_list.list[ii])
5287 set_shows_list.used = 0;
5290 static CHANSERV_FUNC(cmd_set)
5292 struct svccmd *subcmd;
5296 /* Check if we need to (re-)initialize set_shows_list. */
5297 if(!set_shows_list.used)
5299 if(!set_shows_list.size)
5301 set_shows_list.size = chanserv_conf.set_shows->used;
5302 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5304 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5306 const char *name = chanserv_conf.set_shows->list[ii];
5307 sprintf(buf, "%s %s", argv[0], name);
5308 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5311 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5314 svccmd_list_append(&set_shows_list, subcmd);
5320 reply("CSMSG_CHANNEL_OPTIONS");
5321 for(ii = 0; ii < set_shows_list.used; ii++)
5323 subcmd = set_shows_list.list[ii];
5324 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5329 sprintf(buf, "%s %s", argv[0], argv[1]);
5330 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5333 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5336 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5338 reply("CSMSG_NO_ACCESS");
5342 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5346 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5348 struct userData *uData;
5350 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5353 reply("CSMSG_NOT_USER", channel->name);
5359 /* Just show current option value. */
5361 else if(enabled_string(argv[1]))
5363 uData->flags |= mask;
5365 else if(disabled_string(argv[1]))
5367 uData->flags &= ~mask;
5371 reply("MSG_INVALID_BINARY", argv[1]);
5375 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5379 static MODCMD_FUNC(user_opt_noautoop)
5381 struct userData *uData;
5383 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5386 reply("CSMSG_NOT_USER", channel->name);
5389 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5390 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5392 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5395 static MODCMD_FUNC(user_opt_autoinvite)
5397 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5400 static MODCMD_FUNC(user_opt_info)
5402 struct userData *uData;
5405 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5409 /* If they got past the command restrictions (which require access)
5410 * but fail this test, we have some fool with security override on.
5412 reply("CSMSG_NOT_USER", channel->name);
5419 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5420 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5422 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5425 bp = strcspn(infoline, "\001");
5428 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5433 if(infoline[0] == '*' && infoline[1] == 0)
5436 uData->info = strdup(infoline);
5439 reply("CSMSG_USET_INFO", uData->info);
5441 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5445 struct svccmd_list uset_shows_list;
5447 static CHANSERV_FUNC(cmd_uset)
5449 struct svccmd *subcmd;
5453 /* Check if we need to (re-)initialize uset_shows_list. */
5454 if(!uset_shows_list.used)
5458 "NoAutoOp", "AutoInvite", "Info"
5461 if(!uset_shows_list.size)
5463 uset_shows_list.size = ArrayLength(options);
5464 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5466 for(ii = 0; ii < ArrayLength(options); ii++)
5468 const char *name = options[ii];
5469 sprintf(buf, "%s %s", argv[0], name);
5470 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5473 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5476 svccmd_list_append(&uset_shows_list, subcmd);
5482 /* Do this so options are presented in a consistent order. */
5483 reply("CSMSG_USER_OPTIONS");
5484 for(ii = 0; ii < uset_shows_list.used; ii++)
5485 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5489 sprintf(buf, "%s %s", argv[0], argv[1]);
5490 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5493 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5497 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5500 static CHANSERV_FUNC(cmd_giveownership)
5502 struct handle_info *new_owner_hi;
5503 struct userData *new_owner;
5504 struct userData *curr_user;
5505 struct userData *invoker;
5506 struct chanData *cData = channel->channel_info;
5507 struct do_not_register *dnr;
5508 const char *confirm;
5510 unsigned short co_access;
5511 char reason[MAXLEN];
5514 curr_user = GetChannelAccess(cData, user->handle_info);
5515 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5516 if(!curr_user || (curr_user->access != UL_OWNER))
5518 struct userData *owner = NULL;
5519 for(curr_user = channel->channel_info->users;
5521 curr_user = curr_user->next)
5523 if(curr_user->access != UL_OWNER)
5527 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5534 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5536 char delay[INTERVALLEN];
5537 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5538 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5541 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5543 if(new_owner_hi == user->handle_info)
5545 reply("CSMSG_NO_TRANSFER_SELF");
5548 new_owner = GetChannelAccess(cData, new_owner_hi);
5553 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5557 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5561 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5563 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5566 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5567 if(!IsHelping(user))
5568 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5570 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5573 invoker = GetChannelUser(cData, user->handle_info);
5574 if(invoker->access <= UL_OWNER)
5576 confirm = make_confirmation_string(curr_user);
5577 if((argc < 3) || strcmp(argv[2], confirm))
5579 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5583 if(new_owner->access >= UL_COOWNER)
5584 co_access = new_owner->access;
5586 co_access = UL_COOWNER;
5587 new_owner->access = UL_OWNER;
5589 curr_user->access = co_access;
5590 cData->ownerTransfer = now;
5591 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5592 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5593 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5597 static CHANSERV_FUNC(cmd_suspend)
5599 struct handle_info *hi;
5600 struct userData *self, *target;
5603 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5604 self = GetChannelUser(channel->channel_info, user->handle_info);
5605 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5607 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5610 if(target->access >= self->access)
5612 reply("MSG_USER_OUTRANKED", hi->handle);
5615 if(target->flags & USER_SUSPENDED)
5617 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5622 target->present = 0;
5625 target->flags |= USER_SUSPENDED;
5626 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5630 static CHANSERV_FUNC(cmd_unsuspend)
5632 struct handle_info *hi;
5633 struct userData *self, *target;
5636 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5637 self = GetChannelUser(channel->channel_info, user->handle_info);
5638 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5640 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5643 if(target->access >= self->access)
5645 reply("MSG_USER_OUTRANKED", hi->handle);
5648 if(!(target->flags & USER_SUSPENDED))
5650 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5653 target->flags &= ~USER_SUSPENDED;
5654 scan_user_presence(target, NULL);
5655 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5659 static MODCMD_FUNC(cmd_deleteme)
5661 struct handle_info *hi;
5662 struct userData *target;
5663 const char *confirm_string;
5664 unsigned short access;
5667 hi = user->handle_info;
5668 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5670 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5673 if(target->access == UL_OWNER)
5675 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5678 confirm_string = make_confirmation_string(target);
5679 if((argc < 2) || strcmp(argv[1], confirm_string))
5681 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5684 access = target->access;
5685 channel_name = strdup(channel->name);
5686 del_channel_user(target, 1);
5687 reply("CSMSG_DELETED_YOU", access, channel_name);
5693 chanserv_refresh_topics(UNUSED_ARG(void *data))
5695 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5696 struct chanData *cData;
5699 for(cData = channelList; cData; cData = cData->next)
5701 if(IsSuspended(cData))
5703 opt = cData->chOpts[chTopicRefresh];
5706 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5709 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5710 cData->last_refresh = refresh_num;
5712 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5715 static CHANSERV_FUNC(cmd_unf)
5719 char response[MAXLEN];
5720 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5721 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5722 irc_privmsg(cmd->parent->bot, channel->name, response);
5725 reply("CSMSG_UNF_RESPONSE");
5729 static CHANSERV_FUNC(cmd_ping)
5733 char response[MAXLEN];
5734 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5735 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5736 irc_privmsg(cmd->parent->bot, channel->name, response);
5739 reply("CSMSG_PING_RESPONSE");
5743 static CHANSERV_FUNC(cmd_wut)
5747 char response[MAXLEN];
5748 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5749 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5750 irc_privmsg(cmd->parent->bot, channel->name, response);
5753 reply("CSMSG_WUT_RESPONSE");
5757 static CHANSERV_FUNC(cmd_8ball)
5759 unsigned int i, j, accum;
5764 for(i=1; i<argc; i++)
5765 for(j=0; argv[i][j]; j++)
5766 accum = (accum << 5) - accum + toupper(argv[i][j]);
5767 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5770 char response[MAXLEN];
5771 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5772 irc_privmsg(cmd->parent->bot, channel->name, response);
5775 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5779 static CHANSERV_FUNC(cmd_d)
5781 unsigned long sides, count, modifier, ii, total;
5782 char response[MAXLEN], *sep;
5786 if((count = strtoul(argv[1], &sep, 10)) < 1)
5796 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5797 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5801 else if((sep[0] == '-') && isdigit(sep[1]))
5802 modifier = strtoul(sep, NULL, 10);
5803 else if((sep[0] == '+') && isdigit(sep[1]))
5804 modifier = strtoul(sep+1, NULL, 10);
5811 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5816 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5819 for(total = ii = 0; ii < count; ++ii)
5820 total += (rand() % sides) + 1;
5823 if((count > 1) || modifier)
5825 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5826 sprintf(response, fmt, total, count, sides, modifier);
5830 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5831 sprintf(response, fmt, total, sides);
5834 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5836 send_message_type(4, user, cmd->parent->bot, "%s", response);
5840 static CHANSERV_FUNC(cmd_huggle)
5842 /* CTCP must be via PRIVMSG, never notice */
5844 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5846 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5851 chanserv_adjust_limit(void *data)
5853 struct mod_chanmode change;
5854 struct chanData *cData = data;
5855 struct chanNode *channel = cData->channel;
5858 if(IsSuspended(cData))
5861 cData->limitAdjusted = now;
5862 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5863 if(cData->modes.modes_set & MODE_LIMIT)
5865 if(limit > cData->modes.new_limit)
5866 limit = cData->modes.new_limit;
5867 else if(limit == cData->modes.new_limit)
5871 mod_chanmode_init(&change);
5872 change.modes_set = MODE_LIMIT;
5873 change.new_limit = limit;
5874 mod_chanmode_announce(chanserv, channel, &change);
5878 handle_new_channel(struct chanNode *channel)
5880 struct chanData *cData;
5882 if(!(cData = channel->channel_info))
5885 if(cData->modes.modes_set || cData->modes.modes_clear)
5886 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5888 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5889 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5892 /* Welcome to my worst nightmare. Warning: Read (or modify)
5893 the code below at your own risk. */
5895 handle_join(struct modeNode *mNode)
5897 struct mod_chanmode change;
5898 struct userNode *user = mNode->user;
5899 struct chanNode *channel = mNode->channel;
5900 struct chanData *cData;
5901 struct userData *uData = NULL;
5902 struct banData *bData;
5903 struct handle_info *handle;
5904 unsigned int modes = 0, info = 0;
5907 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5910 cData = channel->channel_info;
5911 if(channel->members.used > cData->max)
5912 cData->max = channel->members.used;
5914 /* Check for bans. If they're joining through a ban, one of two
5916 * 1: Join during a netburst, by riding the break. Kick them
5917 * unless they have ops or voice in the channel.
5918 * 2: They're allowed to join through the ban (an invite in
5919 * ircu2.10, or a +e on Hybrid, or something).
5920 * If they're not joining through a ban, and the banlist is not
5921 * full, see if they're on the banlist for the channel. If so,
5924 if(user->uplink->burst && !mNode->modes)
5927 for(ii = 0; ii < channel->banlist.used; ii++)
5929 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5931 /* Riding a netburst. Naughty. */
5932 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5938 mod_chanmode_init(&change);
5940 if(channel->banlist.used < MAXBANS)
5942 /* Not joining through a ban. */
5943 for(bData = cData->bans;
5944 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5945 bData = bData->next);
5949 char kick_reason[MAXLEN];
5950 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5952 bData->triggered = now;
5953 if(bData != cData->bans)
5955 /* Shuffle the ban to the head of the list. */
5957 bData->next->prev = bData->prev;
5959 bData->prev->next = bData->next;
5962 bData->next = cData->bans;
5965 cData->bans->prev = bData;
5966 cData->bans = bData;
5969 change.args[0].mode = MODE_BAN;
5970 change.args[0].u.hostmask = bData->mask;
5971 mod_chanmode_announce(chanserv, channel, &change);
5972 KickChannelUser(user, channel, chanserv, kick_reason);
5977 /* ChanServ will not modify the limits in join-flooded channels.
5978 It will also skip DynLimit processing when the user (or srvx)
5979 is bursting in, because there are likely more incoming. */
5980 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5981 && !user->uplink->burst
5982 && !channel->join_flooded
5983 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5985 /* The user count has begun "bumping" into the channel limit,
5986 so set a timer to raise the limit a bit. Any previous
5987 timers are removed so three incoming users within the delay
5988 results in one limit change, not three. */
5990 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5991 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5994 if(channel->join_flooded)
5996 /* don't automatically give ops or voice during a join flood */
5998 else if(cData->lvlOpts[lvlGiveOps] == 0)
5999 modes |= MODE_CHANOP;
6000 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6001 modes |= MODE_VOICE;
6003 greeting = cData->greeting;
6004 if(user->handle_info)
6006 handle = user->handle_info;
6008 if(IsHelper(user) && !IsHelping(user))
6011 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6013 if(channel == chanserv_conf.support_channels.list[ii])
6015 HANDLE_SET_FLAG(user->handle_info, HELPING);
6021 uData = GetTrueChannelAccess(cData, handle);
6022 if(uData && !IsUserSuspended(uData))
6024 /* Ops and above were handled by the above case. */
6025 if(IsUserAutoOp(uData))
6027 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6028 modes |= MODE_CHANOP;
6029 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6030 modes |= MODE_VOICE;
6032 if(uData->access >= UL_PRESENT)
6033 cData->visited = now;
6034 if(cData->user_greeting)
6035 greeting = cData->user_greeting;
6037 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6038 && ((now - uData->seen) >= chanserv_conf.info_delay)
6046 /* If user joining normally (not during burst), apply op or voice,
6047 * and send greeting/userinfo as appropriate.
6049 if(!user->uplink->burst)
6053 if(modes & MODE_CHANOP)
6054 modes &= ~MODE_VOICE;
6055 change.args[0].mode = modes;
6056 change.args[0].u.member = mNode;
6057 mod_chanmode_announce(chanserv, channel, &change);
6060 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6062 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6068 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6070 struct mod_chanmode change;
6071 struct userData *channel;
6072 unsigned int ii, jj;
6074 if(!user->handle_info)
6077 mod_chanmode_init(&change);
6079 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6081 struct chanNode *cn;
6082 struct modeNode *mn;
6083 if(IsUserSuspended(channel)
6084 || IsSuspended(channel->channel)
6085 || !(cn = channel->channel->channel))
6088 mn = GetUserMode(cn, user);
6091 if(!IsUserSuspended(channel)
6092 && IsUserAutoInvite(channel)
6093 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6095 && !user->uplink->burst)
6096 irc_invite(chanserv, user, cn);
6100 if(channel->access >= UL_PRESENT)
6101 channel->channel->visited = now;
6103 if(IsUserAutoOp(channel))
6105 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6106 change.args[0].mode = MODE_CHANOP;
6107 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6108 change.args[0].mode = MODE_VOICE;
6110 change.args[0].mode = 0;
6111 change.args[0].u.member = mn;
6112 if(change.args[0].mode)
6113 mod_chanmode_announce(chanserv, cn, &change);
6116 channel->seen = now;
6117 channel->present = 1;
6120 for(ii = 0; ii < user->channels.used; ++ii)
6122 struct chanNode *channel = user->channels.list[ii]->channel;
6123 struct banData *ban;
6125 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6126 || !channel->channel_info
6127 || IsSuspended(channel->channel_info))
6129 for(jj = 0; jj < channel->banlist.used; ++jj)
6130 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6132 if(jj < channel->banlist.used)
6134 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6136 char kick_reason[MAXLEN];
6137 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6139 change.args[0].mode = MODE_BAN;
6140 change.args[0].u.hostmask = ban->mask;
6141 mod_chanmode_announce(chanserv, channel, &change);
6142 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6143 KickChannelUser(user, channel, chanserv, kick_reason);
6144 ban->triggered = now;
6149 if(IsSupportHelper(user))
6151 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6153 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6155 HANDLE_SET_FLAG(user->handle_info, HELPING);
6163 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6165 struct chanData *cData;
6166 struct userData *uData;
6168 cData = mn->channel->channel_info;
6169 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6172 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6174 /* Allow for a bit of padding so that the limit doesn't
6175 track the user count exactly, which could get annoying. */
6176 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6178 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6179 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6183 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6185 scan_user_presence(uData, mn->user);
6187 if (uData->access >= UL_PRESENT)
6188 cData->visited = now;
6191 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6193 unsigned int ii, jj;
6194 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6196 for(jj = 0; jj < mn->user->channels.used; ++jj)
6197 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6199 if(jj < mn->user->channels.used)
6202 if(ii == chanserv_conf.support_channels.used)
6203 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6208 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6210 struct userData *uData;
6212 if(!channel->channel_info || !kicker || IsService(kicker)
6213 || (kicker == victim) || IsSuspended(channel->channel_info)
6214 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6217 if(protect_user(victim, kicker, channel->channel_info))
6219 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6220 KickChannelUser(kicker, channel, chanserv, reason);
6223 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6228 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6230 struct chanData *cData;
6232 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6235 cData = channel->channel_info;
6236 if(bad_topic(channel, user, channel->topic))
6238 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6239 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6240 SetChannelTopic(channel, chanserv, old_topic, 1);
6241 else if(cData->topic)
6242 SetChannelTopic(channel, chanserv, cData->topic, 1);
6245 /* With topicsnarf, grab the topic and save it as the default topic. */
6246 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6249 cData->topic = strdup(channel->topic);
6255 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6257 struct mod_chanmode *bounce = NULL;
6258 unsigned int bnc, ii;
6261 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6264 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6265 && mode_lock_violated(&channel->channel_info->modes, change))
6267 char correct[MAXLEN];
6268 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6269 mod_chanmode_format(&channel->channel_info->modes, correct);
6270 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6272 for(ii = bnc = 0; ii < change->argc; ++ii)
6274 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6276 const struct userNode *victim = change->args[ii].u.member->user;
6277 if(!protect_user(victim, user, channel->channel_info))
6280 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6283 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6284 bounce->args[bnc].u.member = GetUserMode(channel, user);
6285 if(bounce->args[bnc].u.member)
6289 bounce->args[bnc].mode = MODE_CHANOP;
6290 bounce->args[bnc].u.member = change->args[ii].u.member;
6292 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6294 else if(change->args[ii].mode & MODE_CHANOP)
6296 const struct userNode *victim = change->args[ii].u.member->user;
6297 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6300 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6301 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6302 bounce->args[bnc].u.member = change->args[ii].u.member;
6305 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6307 const char *ban = change->args[ii].u.hostmask;
6308 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6311 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6312 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6313 bounce->args[bnc].u.hostmask = strdup(ban);
6315 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6320 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6321 mod_chanmode_announce(chanserv, channel, bounce);
6322 for(ii = 0; ii < change->argc; ++ii)
6323 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6324 free((char*)bounce->args[ii].u.hostmask);
6325 mod_chanmode_free(bounce);
6330 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6332 struct chanNode *channel;
6333 struct banData *bData;
6334 struct mod_chanmode change;
6335 unsigned int ii, jj;
6336 char kick_reason[MAXLEN];
6338 mod_chanmode_init(&change);
6340 change.args[0].mode = MODE_BAN;
6341 for(ii = 0; ii < user->channels.used; ++ii)
6343 channel = user->channels.list[ii]->channel;
6344 /* Need not check for bans if they're opped or voiced. */
6345 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6347 /* Need not check for bans unless channel registration is active. */
6348 if(!channel->channel_info || IsSuspended(channel->channel_info))
6350 /* Look for a matching ban already on the channel. */
6351 for(jj = 0; jj < channel->banlist.used; ++jj)
6352 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6354 /* Need not act if we found one. */
6355 if(jj < channel->banlist.used)
6357 /* Look for a matching ban in this channel. */
6358 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6360 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6362 change.args[0].u.hostmask = bData->mask;
6363 mod_chanmode_announce(chanserv, channel, &change);
6364 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6365 KickChannelUser(user, channel, chanserv, kick_reason);
6366 bData->triggered = now;
6367 break; /* we don't need to check any more bans in the channel */
6372 static void handle_rename(struct handle_info *handle, const char *old_handle)
6374 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6378 dict_remove2(handle_dnrs, old_handle, 1);
6379 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6380 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6385 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6387 struct userNode *h_user;
6389 if(handle->channels)
6391 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6392 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6394 while(handle->channels)
6395 del_channel_user(handle->channels, 1);
6400 handle_server_link(UNUSED_ARG(struct server *server))
6402 struct chanData *cData;
6404 for(cData = channelList; cData; cData = cData->next)
6406 if(!IsSuspended(cData))
6407 cData->may_opchan = 1;
6408 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6409 && !cData->channel->join_flooded
6410 && ((cData->channel->limit - cData->channel->members.used)
6411 < chanserv_conf.adjust_threshold))
6413 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6414 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6420 chanserv_conf_read(void)
6424 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6425 struct mod_chanmode *change;
6426 struct string_list *strlist;
6427 struct chanNode *chan;
6430 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6432 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6435 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6436 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6437 chanserv_conf.support_channels.used = 0;
6438 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6440 for(ii = 0; ii < strlist->used; ++ii)
6442 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6445 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6447 channelList_append(&chanserv_conf.support_channels, chan);
6450 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6453 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6456 chan = AddChannel(str, now, str2, NULL);
6458 channelList_append(&chanserv_conf.support_channels, chan);
6460 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6461 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6462 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6463 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6464 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6465 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6466 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6467 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6468 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6469 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6470 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6471 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6472 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6473 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6474 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6475 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6476 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6477 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6478 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6479 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6480 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6481 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6482 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6484 NickChange(chanserv, str, 0);
6485 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6486 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6487 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6488 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6489 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6490 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6491 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6492 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6493 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6494 chanserv_conf.max_owned = str ? atoi(str) : 5;
6495 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6496 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6497 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6498 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6499 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6500 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6501 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6504 safestrncpy(mode_line, str, sizeof(mode_line));
6505 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6506 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6507 && (change->argc < 2))
6509 chanserv_conf.default_modes = *change;
6510 mod_chanmode_free(change);
6512 free_string_list(chanserv_conf.set_shows);
6513 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6515 strlist = string_list_copy(strlist);
6518 static const char *list[] = {
6519 /* free form text */
6520 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6521 /* options based on user level */
6522 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6523 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6524 /* multiple choice options */
6525 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6526 /* binary options */
6527 "DynLimit", "NoDelete",
6532 strlist = alloc_string_list(ArrayLength(list)-1);
6533 for(ii=0; list[ii]; ii++)
6534 string_list_append(strlist, strdup(list[ii]));
6536 chanserv_conf.set_shows = strlist;
6537 /* We don't look things up now, in case the list refers to options
6538 * defined by modules initialized after this point. Just mark the
6539 * function list as invalid, so it will be initialized.
6541 set_shows_list.used = 0;
6542 free_string_list(chanserv_conf.eightball);
6543 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6546 strlist = string_list_copy(strlist);
6550 strlist = alloc_string_list(4);
6551 string_list_append(strlist, strdup("Yes."));
6552 string_list_append(strlist, strdup("No."));
6553 string_list_append(strlist, strdup("Maybe so."));
6555 chanserv_conf.eightball = strlist;
6556 free_string_list(chanserv_conf.old_ban_names);
6557 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6559 strlist = string_list_copy(strlist);
6561 strlist = alloc_string_list(2);
6562 chanserv_conf.old_ban_names = strlist;
6563 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6564 off_channel = str ? atoi(str) : 0;
6568 chanserv_note_type_read(const char *key, struct record_data *rd)
6571 struct note_type *ntype;
6574 if(!(obj = GET_RECORD_OBJECT(rd)))
6576 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6579 if(!(ntype = chanserv_create_note_type(key)))
6581 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6585 /* Figure out set access */
6586 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6588 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6589 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6591 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6593 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6594 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6596 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6598 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6602 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6603 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6604 ntype->set_access.min_opserv = 0;
6607 /* Figure out visibility */
6608 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6609 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6610 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6611 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6612 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6613 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6614 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6615 ntype->visible_type = NOTE_VIS_ALL;
6617 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6619 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6620 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6624 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6626 struct handle_info *handle;
6627 struct userData *uData;
6628 char *seen, *inf, *flags;
6630 unsigned short access;
6632 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6634 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6638 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6639 if(access > UL_OWNER)
6641 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6645 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6646 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6647 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6648 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6649 handle = get_handle_info(key);
6652 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6656 uData = add_channel_user(chan, handle, access, last_seen, inf);
6657 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6661 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6663 struct banData *bData;
6664 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6665 time_t set_time, triggered_time, expires_time;
6667 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6669 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6673 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6674 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6675 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6676 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6677 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6678 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6679 if (!reason || !owner)
6682 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6683 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6685 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6687 expires_time = set_time + atoi(s_duration);
6691 if(!reason || (expires_time && (expires_time < now)))
6694 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6697 static struct suspended *
6698 chanserv_read_suspended(dict_t obj)
6700 struct suspended *suspended = calloc(1, sizeof(*suspended));
6704 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6705 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6706 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6707 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6708 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6709 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6710 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6711 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6712 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6713 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6718 chanserv_channel_read(const char *key, struct record_data *hir)
6720 struct suspended *suspended;
6721 struct mod_chanmode *modes;
6722 struct chanNode *cNode;
6723 struct chanData *cData;
6724 struct dict *channel, *obj;
6725 char *str, *argv[10];
6729 channel = hir->d.object;
6731 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6734 cNode = AddChannel(key, now, NULL, NULL);
6737 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6740 cData = register_channel(cNode, str);
6743 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6747 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6749 enum levelOption lvlOpt;
6750 enum charOption chOpt;
6752 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6753 cData->flags = atoi(str);
6755 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6757 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6759 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6760 else if(levelOptions[lvlOpt].old_flag)
6762 if(cData->flags & levelOptions[lvlOpt].old_flag)
6763 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6765 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6769 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6771 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6773 cData->chOpts[chOpt] = str[0];
6776 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6778 enum levelOption lvlOpt;
6779 enum charOption chOpt;
6782 cData->flags = base64toint(str, 5);
6783 count = strlen(str += 5);
6784 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6787 if(levelOptions[lvlOpt].old_flag)
6789 if(cData->flags & levelOptions[lvlOpt].old_flag)
6790 lvl = levelOptions[lvlOpt].flag_value;
6792 lvl = levelOptions[lvlOpt].default_value;
6794 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6796 case 'c': lvl = UL_COOWNER; break;
6797 case 'm': lvl = UL_MASTER; break;
6798 case 'n': lvl = UL_OWNER+1; break;
6799 case 'o': lvl = UL_OP; break;
6800 case 'p': lvl = UL_PEON; break;
6801 case 'w': lvl = UL_OWNER; break;
6802 default: lvl = 0; break;
6804 cData->lvlOpts[lvlOpt] = lvl;
6806 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6807 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6810 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6812 suspended = chanserv_read_suspended(obj);
6813 cData->suspended = suspended;
6814 suspended->cData = cData;
6815 /* We could use suspended->expires and suspended->revoked to
6816 * set the CHANNEL_SUSPENDED flag, but we don't. */
6818 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6820 suspended = calloc(1, sizeof(*suspended));
6821 suspended->issued = 0;
6822 suspended->revoked = 0;
6823 suspended->suspender = strdup(str);
6824 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6825 suspended->expires = str ? atoi(str) : 0;
6826 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6827 suspended->reason = strdup(str ? str : "No reason");
6828 suspended->previous = NULL;
6829 cData->suspended = suspended;
6830 suspended->cData = cData;
6834 cData->flags &= ~CHANNEL_SUSPENDED;
6835 suspended = NULL; /* to squelch a warning */
6838 if(IsSuspended(cData)) {
6839 if(suspended->expires > now)
6840 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6841 else if(suspended->expires)
6842 cData->flags &= ~CHANNEL_SUSPENDED;
6845 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6846 struct mod_chanmode change;
6847 mod_chanmode_init(&change);
6849 change.args[0].mode = MODE_CHANOP;
6850 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6851 mod_chanmode_announce(chanserv, cNode, &change);
6854 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6855 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6856 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6857 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6858 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6859 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6860 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6861 cData->max = str ? atoi(str) : 0;
6862 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6863 cData->greeting = str ? strdup(str) : NULL;
6864 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6865 cData->user_greeting = str ? strdup(str) : NULL;
6866 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6867 cData->topic_mask = str ? strdup(str) : NULL;
6868 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6869 cData->topic = str ? strdup(str) : NULL;
6871 if(!IsSuspended(cData)
6872 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6873 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6874 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6875 cData->modes = *modes;
6877 cData->modes.modes_set |= MODE_REGISTERED;
6878 if(cData->modes.argc > 1)
6879 cData->modes.argc = 1;
6880 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6881 mod_chanmode_free(modes);
6884 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6885 for(it = dict_first(obj); it; it = iter_next(it))
6886 user_read_helper(iter_key(it), iter_data(it), cData);
6888 if(!cData->users && !IsProtected(cData))
6890 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6891 unregister_channel(cData, "has empty user list.");
6895 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6896 for(it = dict_first(obj); it; it = iter_next(it))
6897 ban_read_helper(iter_key(it), iter_data(it), cData);
6899 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6900 for(it = dict_first(obj); it; it = iter_next(it))
6902 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6903 struct record_data *rd = iter_data(it);
6904 const char *note, *setter;
6906 if(rd->type != RECDB_OBJECT)
6908 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6912 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6914 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6916 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6920 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6921 if(!setter) setter = "<unknown>";
6922 chanserv_add_channel_note(cData, ntype, setter, note);
6930 chanserv_dnr_read(const char *key, struct record_data *hir)
6932 const char *setter, *reason, *str;
6933 struct do_not_register *dnr;
6935 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6938 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6941 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6944 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6947 dnr = chanserv_add_dnr(key, setter, reason);
6950 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6952 dnr->set = atoi(str);
6958 chanserv_saxdb_read(struct dict *database)
6960 struct dict *section;
6963 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6964 for(it = dict_first(section); it; it = iter_next(it))
6965 chanserv_note_type_read(iter_key(it), iter_data(it));
6967 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6968 for(it = dict_first(section); it; it = iter_next(it))
6969 chanserv_channel_read(iter_key(it), iter_data(it));
6971 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6972 for(it = dict_first(section); it; it = iter_next(it))
6973 chanserv_dnr_read(iter_key(it), iter_data(it));
6979 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6981 int high_present = 0;
6982 saxdb_start_record(ctx, KEY_USERS, 1);
6983 for(; uData; uData = uData->next)
6985 if((uData->access >= UL_PRESENT) && uData->present)
6987 saxdb_start_record(ctx, uData->handle->handle, 0);
6988 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6989 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6991 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6993 saxdb_write_string(ctx, KEY_INFO, uData->info);
6994 saxdb_end_record(ctx);
6996 saxdb_end_record(ctx);
6997 return high_present;
7001 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7005 saxdb_start_record(ctx, KEY_BANS, 1);
7006 for(; bData; bData = bData->next)
7008 saxdb_start_record(ctx, bData->mask, 0);
7009 saxdb_write_int(ctx, KEY_SET, bData->set);
7010 if(bData->triggered)
7011 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7013 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7015 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7017 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7018 saxdb_end_record(ctx);
7020 saxdb_end_record(ctx);
7024 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7026 saxdb_start_record(ctx, name, 0);
7027 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7028 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7030 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7032 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7034 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7036 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7037 saxdb_end_record(ctx);
7041 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7045 enum levelOption lvlOpt;
7046 enum charOption chOpt;
7048 saxdb_start_record(ctx, channel->channel->name, 1);
7050 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7051 saxdb_write_int(ctx, KEY_MAX, channel->max);
7053 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7054 if(channel->registrar)
7055 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7056 if(channel->greeting)
7057 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7058 if(channel->user_greeting)
7059 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7060 if(channel->topic_mask)
7061 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7062 if(channel->suspended)
7063 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7065 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7066 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7067 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7068 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7069 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7071 buf[0] = channel->chOpts[chOpt];
7073 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7075 saxdb_end_record(ctx);
7077 if(channel->modes.modes_set || channel->modes.modes_clear)
7079 mod_chanmode_format(&channel->modes, buf);
7080 saxdb_write_string(ctx, KEY_MODES, buf);
7083 high_present = chanserv_write_users(ctx, channel->users);
7084 chanserv_write_bans(ctx, channel->bans);
7086 if(dict_size(channel->notes))
7090 saxdb_start_record(ctx, KEY_NOTES, 1);
7091 for(it = dict_first(channel->notes); it; it = iter_next(it))
7093 struct note *note = iter_data(it);
7094 saxdb_start_record(ctx, iter_key(it), 0);
7095 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7096 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7097 saxdb_end_record(ctx);
7099 saxdb_end_record(ctx);
7102 if(channel->ownerTransfer)
7103 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7104 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7105 saxdb_end_record(ctx);
7109 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7113 saxdb_start_record(ctx, ntype->name, 0);
7114 switch(ntype->set_access_type)
7116 case NOTE_SET_CHANNEL_ACCESS:
7117 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7119 case NOTE_SET_CHANNEL_SETTER:
7120 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7122 case NOTE_SET_PRIVILEGED: default:
7123 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7126 switch(ntype->visible_type)
7128 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7129 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7130 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7132 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7133 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7134 saxdb_end_record(ctx);
7138 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7140 struct do_not_register *dnr;
7143 for(it = dict_first(dnrs); it; it = iter_next(it))
7145 dnr = iter_data(it);
7146 saxdb_start_record(ctx, dnr->chan_name, 0);
7148 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7149 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7150 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7151 saxdb_end_record(ctx);
7156 chanserv_saxdb_write(struct saxdb_context *ctx)
7159 struct chanData *channel;
7162 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7163 for(it = dict_first(note_types); it; it = iter_next(it))
7164 chanserv_write_note_type(ctx, iter_data(it));
7165 saxdb_end_record(ctx);
7168 saxdb_start_record(ctx, KEY_DNR, 1);
7169 write_dnrs_helper(ctx, handle_dnrs);
7170 write_dnrs_helper(ctx, plain_dnrs);
7171 write_dnrs_helper(ctx, mask_dnrs);
7172 saxdb_end_record(ctx);
7175 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7176 for(channel = channelList; channel; channel = channel->next)
7177 chanserv_write_channel(ctx, channel);
7178 saxdb_end_record(ctx);
7184 chanserv_db_cleanup(void) {
7186 unreg_part_func(handle_part);
7188 unregister_channel(channelList, "terminating.");
7189 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7190 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7191 free(chanserv_conf.support_channels.list);
7192 dict_delete(handle_dnrs);
7193 dict_delete(plain_dnrs);
7194 dict_delete(mask_dnrs);
7195 dict_delete(note_types);
7196 free_string_list(chanserv_conf.eightball);
7197 free_string_list(chanserv_conf.old_ban_names);
7198 free_string_list(chanserv_conf.set_shows);
7199 free(set_shows_list.list);
7200 free(uset_shows_list.list);
7203 struct userData *helper = helperList;
7204 helperList = helperList->next;
7209 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7210 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7211 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7214 init_chanserv(const char *nick)
7216 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7217 conf_register_reload(chanserv_conf_read);
7221 reg_server_link_func(handle_server_link);
7222 reg_new_channel_func(handle_new_channel);
7223 reg_join_func(handle_join);
7224 reg_part_func(handle_part);
7225 reg_kick_func(handle_kick);
7226 reg_topic_func(handle_topic);
7227 reg_mode_change_func(handle_mode);
7228 reg_nick_change_func(handle_nick_change);
7229 reg_auth_func(handle_auth);
7232 reg_handle_rename_func(handle_rename);
7233 reg_unreg_func(handle_unreg);
7235 handle_dnrs = dict_new();
7236 dict_set_free_data(handle_dnrs, free);
7237 plain_dnrs = dict_new();
7238 dict_set_free_data(plain_dnrs, free);
7239 mask_dnrs = dict_new();
7240 dict_set_free_data(mask_dnrs, free);
7242 reg_svccmd_unbind_func(handle_svccmd_unbind);
7243 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7244 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7245 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7246 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7247 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7248 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7249 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7250 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7251 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7253 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7254 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7256 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7257 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7258 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7259 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7260 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7262 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7263 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7264 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7265 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7266 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7268 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7269 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7270 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7271 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7273 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7274 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7275 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7276 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7277 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7278 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7279 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7280 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7282 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7283 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7284 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7285 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7286 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7287 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7288 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7289 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7290 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7291 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7292 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7293 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7294 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7295 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7297 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7298 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7299 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7300 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7301 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7303 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7304 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7306 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7307 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7308 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7309 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7310 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7311 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7312 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7313 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7314 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7315 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7316 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7318 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7319 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7321 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7322 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7323 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7324 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7326 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7327 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7328 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7329 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7330 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7332 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7333 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7334 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7335 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7336 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7337 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7339 /* Channel options */
7340 DEFINE_CHANNEL_OPTION(defaulttopic);
7341 DEFINE_CHANNEL_OPTION(topicmask);
7342 DEFINE_CHANNEL_OPTION(greeting);
7343 DEFINE_CHANNEL_OPTION(usergreeting);
7344 DEFINE_CHANNEL_OPTION(modes);
7345 DEFINE_CHANNEL_OPTION(enfops);
7346 DEFINE_CHANNEL_OPTION(giveops);
7347 DEFINE_CHANNEL_OPTION(protect);
7348 DEFINE_CHANNEL_OPTION(enfmodes);
7349 DEFINE_CHANNEL_OPTION(enftopic);
7350 DEFINE_CHANNEL_OPTION(pubcmd);
7351 DEFINE_CHANNEL_OPTION(givevoice);
7352 DEFINE_CHANNEL_OPTION(userinfo);
7353 DEFINE_CHANNEL_OPTION(dynlimit);
7354 DEFINE_CHANNEL_OPTION(topicsnarf);
7355 DEFINE_CHANNEL_OPTION(nodelete);
7356 DEFINE_CHANNEL_OPTION(toys);
7357 DEFINE_CHANNEL_OPTION(setters);
7358 DEFINE_CHANNEL_OPTION(topicrefresh);
7359 DEFINE_CHANNEL_OPTION(ctcpusers);
7360 DEFINE_CHANNEL_OPTION(ctcpreaction);
7361 DEFINE_CHANNEL_OPTION(inviteme);
7363 DEFINE_CHANNEL_OPTION(offchannel);
7364 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7366 /* Alias set topic to set defaulttopic for compatibility. */
7367 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7370 DEFINE_USER_OPTION(noautoop);
7371 DEFINE_USER_OPTION(autoinvite);
7372 DEFINE_USER_OPTION(info);
7374 /* Alias uset autovoice to uset autoop. */
7375 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7377 note_types = dict_new();
7378 dict_set_free_data(note_types, chanserv_deref_note_type);
7381 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7382 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7383 service_register(chanserv)->trigger = '!';
7384 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7386 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7388 if(chanserv_conf.channel_expire_frequency)
7389 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7391 if(chanserv_conf.refresh_period)
7393 time_t next_refresh;
7394 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7395 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7398 reg_exit_func(chanserv_db_cleanup);
7399 message_register_table(msgtab);