1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_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_LAME_SMURF_TARGET", "%s is an IRC operator." },
360 /* Seen information */
361 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
362 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
363 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
364 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
366 /* Names information */
367 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
368 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
370 /* Channel information */
371 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
372 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
373 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
374 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
375 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
376 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
377 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
378 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
379 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
380 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
381 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
382 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
390 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
391 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
393 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
394 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
395 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
396 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
397 { "CSMSG_PEEK_OPS", "$bOps:$b" },
398 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
400 /* Network information */
401 { "CSMSG_NETWORK_INFO", "Network Information:" },
402 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
403 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
404 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
405 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
406 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
407 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
408 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
409 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
412 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
413 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
414 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
416 /* Channel searches */
417 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
418 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
419 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
420 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
422 /* Channel configuration */
423 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
424 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
425 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
426 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
429 { "CSMSG_USER_OPTIONS", "User Options:" },
430 { "CSMSG_USER_PROTECTED", "That user is protected." },
433 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
434 { "CSMSG_PING_RESPONSE", "Pong!" },
435 { "CSMSG_WUT_RESPONSE", "wut" },
436 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
437 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
438 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
439 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
440 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
441 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
442 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
445 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
449 /* eject_user and unban_user flags */
450 #define ACTION_KICK 0x0001
451 #define ACTION_BAN 0x0002
452 #define ACTION_ADD_BAN 0x0004
453 #define ACTION_ADD_TIMED_BAN 0x0008
454 #define ACTION_UNBAN 0x0010
455 #define ACTION_DEL_BAN 0x0020
457 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
458 #define MODELEN 40 + KEYLEN
462 #define CSFUNC_ARGS user, channel, argc, argv, cmd
464 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
465 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
466 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
467 reply("MSG_MISSING_PARAMS", argv[0]); \
471 DECLARE_LIST(dnrList, struct do_not_register *);
472 DEFINE_LIST(dnrList, struct do_not_register *);
474 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
476 struct userNode *chanserv;
479 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
480 static struct log_type *CS_LOG;
484 struct channelList support_channels;
485 struct mod_chanmode default_modes;
487 unsigned long db_backup_frequency;
488 unsigned long channel_expire_frequency;
491 unsigned int adjust_delay;
492 long channel_expire_delay;
493 unsigned int nodelete_level;
495 unsigned int adjust_threshold;
496 int join_flood_threshold;
498 unsigned int greeting_length;
499 unsigned int refresh_period;
500 unsigned int giveownership_period;
502 unsigned int max_owned;
503 unsigned int max_chan_users;
504 unsigned int max_chan_bans;
505 unsigned int max_userinfo_length;
507 struct string_list *set_shows;
508 struct string_list *eightball;
509 struct string_list *old_ban_names;
511 const char *ctcp_short_ban_duration;
512 const char *ctcp_long_ban_duration;
514 const char *irc_operator_epithet;
515 const char *network_helper_epithet;
516 const char *support_helper_epithet;
521 struct userNode *user;
522 struct userNode *bot;
523 struct chanNode *channel;
525 unsigned short lowest;
526 unsigned short highest;
527 struct userData **users;
528 struct helpfile_table table;
531 enum note_access_type
533 NOTE_SET_CHANNEL_ACCESS,
534 NOTE_SET_CHANNEL_SETTER,
538 enum note_visible_type
541 NOTE_VIS_CHANNEL_USERS,
547 enum note_access_type set_access_type;
549 unsigned int min_opserv;
550 unsigned short min_ulevel;
552 enum note_visible_type visible_type;
553 unsigned int max_length;
560 struct note_type *type;
561 char setter[NICKSERV_HANDLE_LEN+1];
565 static unsigned int registered_channels;
566 static unsigned int banCount;
568 static const struct {
571 unsigned short level;
574 { "peon", "Peon", UL_PEON, '+' },
575 { "op", "Op", UL_OP, '@' },
576 { "master", "Master", UL_MASTER, '%' },
577 { "coowner", "Coowner", UL_COOWNER, '*' },
578 { "owner", "Owner", UL_OWNER, '!' },
579 { "helper", "BUG:", UL_HELPER, 'X' }
582 static const struct {
585 unsigned short default_value;
586 unsigned int old_idx;
587 unsigned int old_flag;
588 unsigned short flag_value;
590 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
591 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
592 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
593 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
594 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
595 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
596 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
597 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
598 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
599 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
600 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
603 struct charOptionValues {
606 } protectValues[] = {
607 { 'a', "CSMSG_PROTECT_ALL" },
608 { 'e', "CSMSG_PROTECT_EQUAL" },
609 { 'l', "CSMSG_PROTECT_LOWER" },
610 { 'n', "CSMSG_PROTECT_NONE" }
612 { 'd', "CSMSG_TOYS_DISABLED" },
613 { 'n', "CSMSG_TOYS_PRIVATE" },
614 { 'p', "CSMSG_TOYS_PUBLIC" }
615 }, topicRefreshValues[] = {
616 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
617 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
618 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
619 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
620 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
621 }, ctcpReactionValues[] = {
622 { 'k', "CSMSG_CTCPREACTION_KICK" },
623 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
624 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
625 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
628 static const struct {
632 unsigned int old_idx;
634 struct charOptionValues *values;
636 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
637 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
638 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
639 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
642 struct userData *helperList;
643 struct chanData *channelList;
644 static struct module *chanserv_module;
645 static unsigned int userCount;
647 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
648 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
649 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
652 user_level_from_name(const char *name, unsigned short clamp_level)
654 unsigned int level = 0, ii;
656 level = strtoul(name, NULL, 10);
657 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
658 if(!irccasecmp(name, accessLevels[ii].name))
659 level = accessLevels[ii].level;
660 if(level > clamp_level)
666 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
669 *minl = strtoul(arg, &sep, 10);
677 *maxl = strtoul(sep+1, &sep, 10);
685 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
687 struct userData *uData, **head;
689 if(!channel || !handle)
692 if(override && HANDLE_FLAGGED(handle, HELPING)
693 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
695 for(uData = helperList;
696 uData && uData->handle != handle;
697 uData = uData->next);
701 uData = calloc(1, sizeof(struct userData));
702 uData->handle = handle;
704 uData->access = UL_HELPER;
710 uData->next = helperList;
712 helperList->prev = uData;
720 for(uData = channel->users; uData; uData = uData->next)
721 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
724 head = &(channel->users);
727 if(uData && (uData != *head))
729 /* Shuffle the user to the head of whatever list he was in. */
731 uData->next->prev = uData->prev;
733 uData->prev->next = uData->next;
739 (**head).prev = uData;
746 /* Returns non-zero if user has at least the minimum access.
747 * exempt_owner is set when handling !set, so the owner can set things
750 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
752 struct userData *uData;
753 struct chanData *cData = channel->channel_info;
754 unsigned short minimum = cData->lvlOpts[opt];
757 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
760 if(minimum <= uData->access)
762 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
767 /* Scan for other users authenticated to the same handle
768 still in the channel. If so, keep them listed as present.
770 user is optional, if not null, it skips checking that userNode
771 (for the handle_part function) */
773 scan_user_presence(struct userData *uData, struct userNode *user)
777 if(IsSuspended(uData->channel)
778 || IsUserSuspended(uData)
779 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
791 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
793 unsigned int eflags, argc;
795 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
797 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
798 if(!channel->channel_info
799 || IsSuspended(channel->channel_info)
801 || !ircncasecmp(text, "ACTION ", 7))
803 /* Figure out the minimum level needed to CTCP the channel */
804 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
806 /* We need to enforce against them; do so. */
809 argv[1] = user->nick;
811 if(GetUserMode(channel, user))
812 eflags |= ACTION_KICK;
813 switch(channel->channel_info->chOpts[chCTCPReaction]) {
814 default: case 'k': /* just do the kick */ break;
816 eflags |= ACTION_BAN;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
823 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
824 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
827 argv[argc++] = bad_ctcp_reason;
828 eject_user(chanserv, channel, argc, argv, NULL, eflags);
832 chanserv_create_note_type(const char *name)
834 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
835 strcpy(ntype->name, name);
837 dict_insert(note_types, ntype->name, ntype);
842 chanserv_deref_note_type(void *data)
844 struct note_type *ntype = data;
846 if(--ntype->refs > 0)
852 chanserv_flush_note_type(struct note_type *ntype)
854 struct chanData *cData;
855 for(cData = channelList; cData; cData = cData->next)
856 dict_remove(cData->notes, ntype->name);
860 chanserv_truncate_notes(struct note_type *ntype)
862 struct chanData *cData;
864 unsigned int size = sizeof(*note) + ntype->max_length;
866 for(cData = channelList; cData; cData = cData->next) {
867 note = dict_find(cData->notes, ntype->name, NULL);
870 if(strlen(note->note) <= ntype->max_length)
872 dict_remove2(cData->notes, ntype->name, 1);
873 note = realloc(note, size);
874 note->note[ntype->max_length] = 0;
875 dict_insert(cData->notes, ntype->name, note);
879 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
882 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
885 unsigned int len = strlen(text);
887 if(len > type->max_length) len = type->max_length;
888 note = calloc(1, sizeof(*note) + len);
890 strncpy(note->setter, setter, sizeof(note->setter)-1);
891 memcpy(note->note, text, len);
893 dict_insert(channel->notes, type->name, note);
899 chanserv_free_note(void *data)
901 struct note *note = data;
903 chanserv_deref_note_type(note->type);
904 assert(note->type->refs > 0); /* must use delnote to remove the type */
908 static MODCMD_FUNC(cmd_createnote) {
909 struct note_type *ntype;
910 unsigned int arg = 1, existed = 0, max_length;
912 if((ntype = dict_find(note_types, argv[1], NULL)))
915 ntype = chanserv_create_note_type(argv[arg]);
916 if(!irccasecmp(argv[++arg], "privileged"))
919 ntype->set_access_type = NOTE_SET_PRIVILEGED;
920 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
922 else if(!irccasecmp(argv[arg], "channel"))
924 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
927 reply("CSMSG_INVALID_ACCESS", argv[arg]);
930 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
931 ntype->set_access.min_ulevel = ulvl;
933 else if(!irccasecmp(argv[arg], "setter"))
935 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
939 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
943 if(!irccasecmp(argv[++arg], "privileged"))
944 ntype->visible_type = NOTE_VIS_PRIVILEGED;
945 else if(!irccasecmp(argv[arg], "channel_users"))
946 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
947 else if(!irccasecmp(argv[arg], "all"))
948 ntype->visible_type = NOTE_VIS_ALL;
950 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
954 if((arg+1) >= argc) {
955 reply("MSG_MISSING_PARAMS", argv[0]);
958 max_length = strtoul(argv[++arg], NULL, 0);
959 if(max_length < 20 || max_length > 450)
961 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
964 if(existed && (max_length < ntype->max_length))
966 ntype->max_length = max_length;
967 chanserv_truncate_notes(ntype);
969 ntype->max_length = max_length;
972 reply("CSMSG_NOTE_MODIFIED", ntype->name);
974 reply("CSMSG_NOTE_CREATED", ntype->name);
979 dict_remove(note_types, ntype->name);
983 static MODCMD_FUNC(cmd_removenote) {
984 struct note_type *ntype;
987 ntype = dict_find(note_types, argv[1], NULL);
988 force = (argc > 2) && !irccasecmp(argv[2], "force");
991 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
998 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1001 chanserv_flush_note_type(ntype);
1003 dict_remove(note_types, argv[1]);
1004 reply("CSMSG_NOTE_DELETED", argv[1]);
1009 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1013 if(orig->modes_set & change->modes_clear)
1015 if(orig->modes_clear & change->modes_set)
1017 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1018 && strcmp(orig->new_key, change->new_key))
1020 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1021 && (orig->new_limit != change->new_limit))
1026 static char max_length_text[MAXLEN+1][16];
1028 static struct helpfile_expansion
1029 chanserv_expand_variable(const char *variable)
1031 struct helpfile_expansion exp;
1033 if(!irccasecmp(variable, "notes"))
1036 exp.type = HF_TABLE;
1037 exp.value.table.length = 1;
1038 exp.value.table.width = 3;
1039 exp.value.table.flags = 0;
1040 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1041 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1042 exp.value.table.contents[0][0] = "Note Type";
1043 exp.value.table.contents[0][1] = "Visibility";
1044 exp.value.table.contents[0][2] = "Max Length";
1045 for(it=dict_first(note_types); it; it=iter_next(it))
1047 struct note_type *ntype = iter_data(it);
1050 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1051 row = exp.value.table.length++;
1052 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1053 exp.value.table.contents[row][0] = ntype->name;
1054 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1055 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1057 if(!max_length_text[ntype->max_length][0])
1058 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1059 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1064 exp.type = HF_STRING;
1065 exp.value.str = NULL;
1069 static struct chanData*
1070 register_channel(struct chanNode *cNode, char *registrar)
1072 struct chanData *channel;
1073 enum levelOption lvlOpt;
1074 enum charOption chOpt;
1076 channel = calloc(1, sizeof(struct chanData));
1078 channel->notes = dict_new();
1079 dict_set_free_data(channel->notes, chanserv_free_note);
1081 channel->registrar = strdup(registrar);
1082 channel->registered = now;
1083 channel->visited = now;
1084 channel->limitAdjusted = now;
1085 channel->ownerTransfer = now;
1086 channel->flags = CHANNEL_DEFAULT_FLAGS;
1087 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1088 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1089 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1090 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1092 channel->prev = NULL;
1093 channel->next = channelList;
1096 channelList->prev = channel;
1097 channelList = channel;
1098 registered_channels++;
1100 channel->channel = cNode;
1102 cNode->channel_info = channel;
1107 static struct userData*
1108 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1110 struct userData *ud;
1112 if(access > UL_OWNER)
1115 ud = calloc(1, sizeof(*ud));
1116 ud->channel = channel;
1117 ud->handle = handle;
1119 ud->access = access;
1120 ud->info = info ? strdup(info) : NULL;
1123 ud->next = channel->users;
1125 channel->users->prev = ud;
1126 channel->users = ud;
1128 channel->userCount++;
1132 ud->u_next = ud->handle->channels;
1134 ud->u_next->u_prev = ud;
1135 ud->handle->channels = ud;
1140 static void unregister_channel(struct chanData *channel, const char *reason);
1143 del_channel_user(struct userData *user, int do_gc)
1145 struct chanData *channel = user->channel;
1147 channel->userCount--;
1151 user->prev->next = user->next;
1153 channel->users = user->next;
1155 user->next->prev = user->prev;
1158 user->u_prev->u_next = user->u_next;
1160 user->handle->channels = user->u_next;
1162 user->u_next->u_prev = user->u_prev;
1166 if(do_gc && !channel->users && !IsProtected(channel))
1167 unregister_channel(channel, "lost all users.");
1170 static void expire_ban(void *data);
1172 static struct banData*
1173 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1176 unsigned int ii, l1, l2;
1181 bd = malloc(sizeof(struct banData));
1183 bd->channel = channel;
1185 bd->triggered = triggered;
1186 bd->expires = expires;
1188 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1190 extern const char *hidden_host_suffix;
1191 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1195 l2 = strlen(old_name);
1198 if(irccasecmp(mask + l1 - l2, old_name))
1200 new_mask = alloca(MAXLEN);
1201 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1204 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1206 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1207 bd->reason = strdup(reason);
1210 timeq_add(expires, expire_ban, bd);
1213 bd->next = channel->bans;
1215 channel->bans->prev = bd;
1217 channel->banCount++;
1224 del_channel_ban(struct banData *ban)
1226 ban->channel->banCount--;
1230 ban->prev->next = ban->next;
1232 ban->channel->bans = ban->next;
1235 ban->next->prev = ban->prev;
1238 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1247 expire_ban(void *data)
1249 struct banData *bd = data;
1250 if(!IsSuspended(bd->channel))
1252 struct banList bans;
1253 struct mod_chanmode change;
1255 bans = bd->channel->channel->banlist;
1256 mod_chanmode_init(&change);
1257 for(ii=0; ii<bans.used; ii++)
1259 if(!strcmp(bans.list[ii]->ban, bd->mask))
1262 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1263 change.args[0].u.hostmask = bd->mask;
1264 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1270 del_channel_ban(bd);
1273 static void chanserv_expire_suspension(void *data);
1276 unregister_channel(struct chanData *channel, const char *reason)
1278 struct mod_chanmode change;
1279 char msgbuf[MAXLEN];
1281 /* After channel unregistration, the following must be cleaned
1283 - Channel information.
1286 - Channel suspension data.
1287 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1293 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1297 mod_chanmode_init(&change);
1298 change.modes_clear |= MODE_REGISTERED;
1299 mod_chanmode_announce(chanserv, channel->channel, &change);
1302 while(channel->users)
1303 del_channel_user(channel->users, 0);
1305 while(channel->bans)
1306 del_channel_ban(channel->bans);
1308 free(channel->topic);
1309 free(channel->registrar);
1310 free(channel->greeting);
1311 free(channel->user_greeting);
1312 free(channel->topic_mask);
1315 channel->prev->next = channel->next;
1317 channelList = channel->next;
1320 channel->next->prev = channel->prev;
1322 if(channel->suspended)
1324 struct chanNode *cNode = channel->channel;
1325 struct suspended *suspended, *next_suspended;
1327 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1329 next_suspended = suspended->previous;
1330 free(suspended->suspender);
1331 free(suspended->reason);
1332 if(suspended->expires)
1333 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1338 cNode->channel_info = NULL;
1340 channel->channel->channel_info = NULL;
1342 dict_delete(channel->notes);
1343 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1344 if(!IsSuspended(channel))
1345 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1346 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1347 UnlockChannel(channel->channel);
1349 registered_channels--;
1353 expire_channels(UNUSED_ARG(void *data))
1355 struct chanData *channel, *next;
1356 struct userData *user;
1357 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1359 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1360 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1362 for(channel = channelList; channel; channel = next)
1364 next = channel->next;
1366 /* See if the channel can be expired. */
1367 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1368 || IsProtected(channel))
1371 /* Make sure there are no high-ranking users still in the channel. */
1372 for(user=channel->users; user; user=user->next)
1373 if(user->present && (user->access >= UL_PRESENT))
1378 /* Unregister the channel */
1379 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1380 unregister_channel(channel, "registration expired.");
1383 if(chanserv_conf.channel_expire_frequency)
1384 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1388 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1390 char protect = channel->chOpts[chProtect];
1391 struct userData *cs_victim, *cs_aggressor;
1393 /* Don't protect if no one is to be protected, someone is attacking
1394 himself, or if the aggressor is an IRC Operator. */
1395 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1398 /* Don't protect if the victim isn't authenticated (because they
1399 can't be a channel user), unless we are to protect non-users
1401 cs_victim = GetChannelAccess(channel, victim->handle_info);
1402 if(protect != 'a' && !cs_victim)
1405 /* Protect if the aggressor isn't a user because at this point,
1406 the aggressor can only be less than or equal to the victim. */
1407 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1411 /* If the aggressor was a user, then the victim can't be helped. */
1418 if(cs_victim->access > cs_aggressor->access)
1423 if(cs_victim->access >= cs_aggressor->access)
1432 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1434 struct chanData *cData = channel->channel_info;
1435 struct userData *cs_victim;
1437 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1438 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1439 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1441 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1449 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1451 if(IsService(victim))
1453 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1457 if(protect_user(victim, user, channel->channel_info))
1459 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1466 static struct do_not_register *
1467 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1469 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1470 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1471 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1472 strcpy(dnr->reason, reason);
1474 if(dnr->chan_name[0] == '*')
1475 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1476 else if(strpbrk(dnr->chan_name, "*?"))
1477 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1479 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1483 static struct dnrList
1484 chanserv_find_dnrs(const char *chan_name, const char *handle)
1486 struct dnrList list;
1488 struct do_not_register *dnr;
1490 dnrList_init(&list);
1491 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1492 dnrList_append(&list, dnr);
1493 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1494 dnrList_append(&list, dnr);
1496 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1497 if(match_ircglob(chan_name, iter_key(it)))
1498 dnrList_append(&list, iter_data(it));
1503 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1505 struct dnrList list;
1506 struct do_not_register *dnr;
1508 char buf[INTERVALLEN];
1510 list = chanserv_find_dnrs(chan_name, handle);
1511 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1513 dnr = list.list[ii];
1516 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1517 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1520 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1523 reply("CSMSG_MORE_DNRS", list.used - ii);
1528 struct do_not_register *
1529 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1531 struct do_not_register *dnr;
1534 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1538 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1540 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1541 if(match_ircglob(chan_name, iter_key(it)))
1542 return iter_data(it);
1547 static CHANSERV_FUNC(cmd_noregister)
1550 struct do_not_register *dnr;
1551 char buf[INTERVALLEN];
1552 unsigned int matches;
1558 reply("CSMSG_DNR_SEARCH_RESULTS");
1560 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1562 dnr = iter_data(it);
1564 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1566 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1569 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1571 dnr = iter_data(it);
1573 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1575 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1578 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1580 dnr = iter_data(it);
1582 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1584 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1589 reply("MSG_MATCH_COUNT", matches);
1591 reply("MSG_NO_MATCHES");
1597 if(!IsChannelName(target) && (*target != '*'))
1599 reply("CSMSG_NOT_DNR", target);
1605 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1606 if((*target == '*') && !get_handle_info(target + 1))
1608 reply("MSG_HANDLE_UNKNOWN", target + 1);
1611 chanserv_add_dnr(target, user->handle_info->handle, reason);
1612 reply("CSMSG_NOREGISTER_CHANNEL", target);
1616 reply("CSMSG_DNR_SEARCH_RESULTS");
1618 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1620 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1622 reply("MSG_NO_MATCHES");
1626 static CHANSERV_FUNC(cmd_allowregister)
1628 const char *chan_name = argv[1];
1630 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1632 dict_remove(handle_dnrs, chan_name+1);
1633 reply("CSMSG_DNR_REMOVED", chan_name);
1635 else if(dict_find(plain_dnrs, chan_name, NULL))
1637 dict_remove(plain_dnrs, chan_name);
1638 reply("CSMSG_DNR_REMOVED", chan_name);
1640 else if(dict_find(mask_dnrs, chan_name, NULL))
1642 dict_remove(mask_dnrs, chan_name);
1643 reply("CSMSG_DNR_REMOVED", chan_name);
1647 reply("CSMSG_NO_SUCH_DNR", chan_name);
1654 chanserv_get_owned_count(struct handle_info *hi)
1656 struct userData *cList;
1659 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1660 if(cList->access == UL_OWNER)
1665 static CHANSERV_FUNC(cmd_register)
1667 struct handle_info *handle;
1668 struct chanData *cData;
1669 struct modeNode *mn;
1670 char reason[MAXLEN];
1672 unsigned int new_channel, force=0;
1673 struct do_not_register *dnr;
1677 if(channel->channel_info)
1679 reply("CSMSG_ALREADY_REGGED", channel->name);
1683 if(channel->bad_channel)
1685 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1690 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1692 reply("CSMSG_MUST_BE_OPPED", channel->name);
1697 chan_name = channel->name;
1701 if((argc < 2) || !IsChannelName(argv[1]))
1703 reply("MSG_NOT_CHANNEL_NAME");
1707 if(opserv_bad_channel(argv[1]))
1709 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1714 chan_name = argv[1];
1717 if(argc >= (new_channel+2))
1719 if(!IsHelping(user))
1721 reply("CSMSG_PROXY_FORBIDDEN");
1725 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1727 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1728 dnr = chanserv_is_dnr(chan_name, handle);
1732 handle = user->handle_info;
1733 dnr = chanserv_is_dnr(chan_name, handle);
1737 if(!IsHelping(user))
1738 reply("CSMSG_DNR_CHANNEL", chan_name);
1740 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1744 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1746 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1751 channel = AddChannel(argv[1], now, NULL, NULL);
1753 cData = register_channel(channel, user->handle_info->handle);
1754 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1755 cData->modes = chanserv_conf.default_modes;
1757 cData->modes.modes_set |= MODE_REGISTERED;
1758 if (IsOffChannel(cData))
1760 mod_chanmode_announce(chanserv, channel, &cData->modes);
1764 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1765 change->args[change->argc].mode = MODE_CHANOP;
1766 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1768 mod_chanmode_announce(chanserv, channel, change);
1769 mod_chanmode_free(change);
1772 /* Initialize the channel's max user record. */
1773 cData->max = channel->members.used;
1775 if(handle != user->handle_info)
1776 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1778 reply("CSMSG_REG_SUCCESS", channel->name);
1780 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1781 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1786 make_confirmation_string(struct userData *uData)
1788 static char strbuf[16];
1793 for(src = uData->handle->handle; *src; )
1794 accum = accum * 31 + toupper(*src++);
1796 for(src = uData->channel->channel->name; *src; )
1797 accum = accum * 31 + toupper(*src++);
1798 sprintf(strbuf, "%08x", accum);
1802 static CHANSERV_FUNC(cmd_unregister)
1805 char reason[MAXLEN];
1806 struct chanData *cData;
1807 struct userData *uData;
1809 cData = channel->channel_info;
1812 reply("CSMSG_NOT_REGISTERED", channel->name);
1816 uData = GetChannelUser(cData, user->handle_info);
1817 if(!uData || (uData->access < UL_OWNER))
1819 reply("CSMSG_NO_ACCESS");
1823 if(IsProtected(cData))
1825 reply("CSMSG_UNREG_NODELETE", channel->name);
1829 if(!IsHelping(user))
1831 const char *confirm_string;
1832 if(IsSuspended(cData))
1834 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1837 confirm_string = make_confirmation_string(uData);
1838 if((argc < 2) || strcmp(argv[1], confirm_string))
1840 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1845 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1846 name = strdup(channel->name);
1847 unregister_channel(cData, reason);
1848 reply("CSMSG_UNREG_SUCCESS", name);
1853 static CHANSERV_FUNC(cmd_move)
1855 struct mod_chanmode change;
1856 struct chanNode *target;
1857 struct modeNode *mn;
1858 struct userData *uData;
1859 char reason[MAXLEN];
1860 struct do_not_register *dnr;
1864 if(IsProtected(channel->channel_info))
1866 reply("CSMSG_MOVE_NODELETE", channel->name);
1870 if(!IsChannelName(argv[1]))
1872 reply("MSG_NOT_CHANNEL_NAME");
1876 if(opserv_bad_channel(argv[1]))
1878 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1882 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1884 for(uData = channel->channel_info->users; uData; uData = uData->next)
1886 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1888 if(!IsHelping(user))
1889 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1891 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1897 mod_chanmode_init(&change);
1898 if(!(target = GetChannel(argv[1])))
1900 target = AddChannel(argv[1], now, NULL, NULL);
1901 if(!IsSuspended(channel->channel_info))
1902 AddChannelUser(chanserv, target);
1904 else if(target->channel_info)
1906 reply("CSMSG_ALREADY_REGGED", target->name);
1909 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1910 && !IsHelping(user))
1912 reply("CSMSG_MUST_BE_OPPED", target->name);
1915 else if(!IsSuspended(channel->channel_info))
1918 change.args[0].mode = MODE_CHANOP;
1919 change.args[0].u.member = AddChannelUser(chanserv, target);
1920 mod_chanmode_announce(chanserv, target, &change);
1925 /* Clear MODE_REGISTERED from old channel, add it to new. */
1927 change.modes_clear = MODE_REGISTERED;
1928 mod_chanmode_announce(chanserv, channel, &change);
1929 change.modes_clear = 0;
1930 change.modes_set = MODE_REGISTERED;
1931 mod_chanmode_announce(chanserv, target, &change);
1934 /* Move the channel_info to the target channel; it
1935 shouldn't be necessary to clear timeq callbacks
1936 for the old channel. */
1937 target->channel_info = channel->channel_info;
1938 target->channel_info->channel = target;
1939 channel->channel_info = NULL;
1941 reply("CSMSG_MOVE_SUCCESS", target->name);
1943 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1944 if(!IsSuspended(target->channel_info))
1946 char reason2[MAXLEN];
1947 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1948 DelChannelUser(chanserv, channel, reason2, 0);
1950 UnlockChannel(channel);
1951 LockChannel(target);
1952 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1957 merge_users(struct chanData *source, struct chanData *target)
1959 struct userData *suData, *tuData, *next;
1965 /* Insert the source's users into the scratch area. */
1966 for(suData = source->users; suData; suData = suData->next)
1967 dict_insert(merge, suData->handle->handle, suData);
1969 /* Iterate through the target's users, looking for
1970 users common to both channels. The lower access is
1971 removed from either the scratch area or target user
1973 for(tuData = target->users; tuData; tuData = next)
1975 struct userData *choice;
1977 next = tuData->next;
1979 /* If a source user exists with the same handle as a target
1980 channel's user, resolve the conflict by removing one. */
1981 suData = dict_find(merge, tuData->handle->handle, NULL);
1985 /* Pick the data we want to keep. */
1986 /* If the access is the same, use the later seen time. */
1987 if(suData->access == tuData->access)
1988 choice = (suData->seen > tuData->seen) ? suData : tuData;
1989 else /* Otherwise, keep the higher access level. */
1990 choice = (suData->access > tuData->access) ? suData : tuData;
1992 /* Remove the user that wasn't picked. */
1993 if(choice == tuData)
1995 dict_remove(merge, suData->handle->handle);
1996 del_channel_user(suData, 0);
1999 del_channel_user(tuData, 0);
2002 /* Move the remaining users to the target channel. */
2003 for(it = dict_first(merge); it; it = iter_next(it))
2005 suData = iter_data(it);
2007 /* Insert the user into the target channel's linked list. */
2008 suData->prev = NULL;
2009 suData->next = target->users;
2010 suData->channel = target;
2013 target->users->prev = suData;
2014 target->users = suData;
2016 /* Update the user counts for the target channel; the
2017 source counts are left alone. */
2018 target->userCount++;
2021 /* Possible to assert (source->users == NULL) here. */
2022 source->users = NULL;
2027 merge_bans(struct chanData *source, struct chanData *target)
2029 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2031 /* Hold on to the original head of the target ban list
2032 to avoid comparing source bans with source bans. */
2033 tFront = target->bans;
2035 /* Perform a totally expensive O(n*m) merge, ick. */
2036 for(sbData = source->bans; sbData; sbData = sNext)
2038 /* Flag to track whether the ban's been moved
2039 to the destination yet. */
2042 /* Possible to assert (sbData->prev == NULL) here. */
2043 sNext = sbData->next;
2045 for(tbData = tFront; tbData; tbData = tNext)
2047 tNext = tbData->next;
2049 /* Perform two comparisons between each source
2050 and target ban, conflicts are resolved by
2051 keeping the broader ban and copying the later
2052 expiration and triggered time. */
2053 if(match_ircglobs(tbData->mask, sbData->mask))
2055 /* There is a broader ban in the target channel that
2056 overrides one in the source channel; remove the
2057 source ban and break. */
2058 if(sbData->expires > tbData->expires)
2059 tbData->expires = sbData->expires;
2060 if(sbData->triggered > tbData->triggered)
2061 tbData->triggered = sbData->triggered;
2062 del_channel_ban(sbData);
2065 else if(match_ircglobs(sbData->mask, tbData->mask))
2067 /* There is a broader ban in the source channel that
2068 overrides one in the target channel; remove the
2069 target ban, fall through and move the source over. */
2070 if(tbData->expires > sbData->expires)
2071 sbData->expires = tbData->expires;
2072 if(tbData->triggered > sbData->triggered)
2073 sbData->triggered = tbData->triggered;
2074 if(tbData == tFront)
2076 del_channel_ban(tbData);
2079 /* Source bans can override multiple target bans, so
2080 we allow a source to run through this loop multiple
2081 times, but we can only move it once. */
2086 /* Remove the source ban from the source ban list. */
2088 sbData->next->prev = sbData->prev;
2090 /* Modify the source ban's associated channel. */
2091 sbData->channel = target;
2093 /* Insert the ban into the target channel's linked list. */
2094 sbData->prev = NULL;
2095 sbData->next = target->bans;
2098 target->bans->prev = sbData;
2099 target->bans = sbData;
2101 /* Update the user counts for the target channel. */
2106 /* Possible to assert (source->bans == NULL) here. */
2107 source->bans = NULL;
2111 merge_data(struct chanData *source, struct chanData *target)
2113 /* Use more recent visited and owner-transfer time; use older
2114 * registered time. Bitwise or may_opchan. Use higher max.
2115 * Do not touch last_refresh, ban count or user counts.
2117 if(source->visited > target->visited)
2118 target->visited = source->visited;
2119 if(source->registered < target->registered)
2120 target->registered = source->registered;
2121 if(source->ownerTransfer > target->ownerTransfer)
2122 target->ownerTransfer = source->ownerTransfer;
2123 if(source->may_opchan)
2124 target->may_opchan = 1;
2125 if(source->max > target->max)
2126 target->max = source->max;
2130 merge_channel(struct chanData *source, struct chanData *target)
2132 merge_users(source, target);
2133 merge_bans(source, target);
2134 merge_data(source, target);
2137 static CHANSERV_FUNC(cmd_merge)
2139 struct userData *target_user;
2140 struct chanNode *target;
2141 char reason[MAXLEN];
2145 /* Make sure the target channel exists and is registered to the user
2146 performing the command. */
2147 if(!(target = GetChannel(argv[1])))
2149 reply("MSG_INVALID_CHANNEL");
2153 if(!target->channel_info)
2155 reply("CSMSG_NOT_REGISTERED", target->name);
2159 if(IsProtected(channel->channel_info))
2161 reply("CSMSG_MERGE_NODELETE");
2165 if(IsSuspended(target->channel_info))
2167 reply("CSMSG_MERGE_SUSPENDED");
2171 if(channel == target)
2173 reply("CSMSG_MERGE_SELF");
2177 target_user = GetChannelUser(target->channel_info, user->handle_info);
2178 if(!target_user || (target_user->access < UL_OWNER))
2180 reply("CSMSG_MERGE_NOT_OWNER");
2184 /* Merge the channel structures and associated data. */
2185 merge_channel(channel->channel_info, target->channel_info);
2186 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2187 unregister_channel(channel->channel_info, reason);
2188 reply("CSMSG_MERGE_SUCCESS", target->name);
2192 static CHANSERV_FUNC(cmd_opchan)
2194 struct mod_chanmode change;
2195 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2197 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2200 channel->channel_info->may_opchan = 0;
2201 mod_chanmode_init(&change);
2203 change.args[0].mode = MODE_CHANOP;
2204 change.args[0].u.member = GetUserMode(channel, chanserv);
2205 mod_chanmode_announce(chanserv, channel, &change);
2206 reply("CSMSG_OPCHAN_DONE", channel->name);
2210 static CHANSERV_FUNC(cmd_adduser)
2212 struct userData *actee;
2213 struct userData *actor;
2214 struct handle_info *handle;
2215 unsigned short access;
2219 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2221 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2225 access = user_level_from_name(argv[2], UL_OWNER);
2228 reply("CSMSG_INVALID_ACCESS", argv[2]);
2232 actor = GetChannelUser(channel->channel_info, user->handle_info);
2233 if(actor->access <= access)
2235 reply("CSMSG_NO_BUMP_ACCESS");
2239 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2242 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2244 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2248 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2249 scan_user_presence(actee, NULL);
2250 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2254 static CHANSERV_FUNC(cmd_clvl)
2256 struct handle_info *handle;
2257 struct userData *victim;
2258 struct userData *actor;
2259 unsigned short new_access;
2260 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2264 actor = GetChannelUser(channel->channel_info, user->handle_info);
2266 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2269 if(handle == user->handle_info && !privileged)
2271 reply("CSMSG_NO_SELF_CLVL");
2275 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2277 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2281 if(actor->access <= victim->access && !privileged)
2283 reply("MSG_USER_OUTRANKED", handle->handle);
2287 new_access = user_level_from_name(argv[2], UL_OWNER);
2291 reply("CSMSG_INVALID_ACCESS", argv[2]);
2295 if(new_access >= actor->access && !privileged)
2297 reply("CSMSG_NO_BUMP_ACCESS");
2301 victim->access = new_access;
2302 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2306 static CHANSERV_FUNC(cmd_deluser)
2308 struct handle_info *handle;
2309 struct userData *victim;
2310 struct userData *actor;
2311 unsigned short access;
2316 actor = GetChannelUser(channel->channel_info, user->handle_info);
2318 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2321 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2323 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2329 access = user_level_from_name(argv[1], UL_OWNER);
2332 reply("CSMSG_INVALID_ACCESS", argv[1]);
2335 if(access != victim->access)
2337 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2343 access = victim->access;
2346 if((actor->access <= victim->access) && !IsHelping(user))
2348 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2352 chan_name = strdup(channel->name);
2353 del_channel_user(victim, 1);
2354 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2360 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2362 struct userData *actor, *uData, *next;
2364 actor = GetChannelUser(channel->channel_info, user->handle_info);
2366 if(min_access > max_access)
2368 reply("CSMSG_BAD_RANGE", min_access, max_access);
2372 if((actor->access <= max_access) && !IsHelping(user))
2374 reply("CSMSG_NO_ACCESS");
2378 for(uData = channel->channel_info->users; uData; uData = next)
2382 if((uData->access >= min_access)
2383 && (uData->access <= max_access)
2384 && match_ircglob(uData->handle->handle, mask))
2385 del_channel_user(uData, 1);
2388 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2392 static CHANSERV_FUNC(cmd_mdelowner)
2394 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2397 static CHANSERV_FUNC(cmd_mdelcoowner)
2399 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2402 static CHANSERV_FUNC(cmd_mdelmaster)
2404 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2407 static CHANSERV_FUNC(cmd_mdelop)
2409 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2412 static CHANSERV_FUNC(cmd_mdelpeon)
2414 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2418 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2420 struct banData *bData, *next;
2421 char interval[INTERVALLEN];
2426 limit = now - duration;
2427 for(bData = channel->channel_info->bans; bData; bData = next)
2431 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2434 del_channel_ban(bData);
2438 intervalString(interval, duration, user->handle_info);
2439 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2444 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2446 struct userData *actor, *uData, *next;
2447 char interval[INTERVALLEN];
2451 actor = GetChannelUser(channel->channel_info, user->handle_info);
2452 if(min_access > max_access)
2454 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2458 if((actor->access <= max_access) && !IsHelping(user))
2460 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2465 limit = now - duration;
2466 for(uData = channel->channel_info->users; uData; uData = next)
2470 if((uData->seen > limit)
2472 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2475 if(((uData->access >= min_access) && (uData->access <= max_access))
2476 || (!max_access && (uData->access < actor->access)))
2478 del_channel_user(uData, 1);
2486 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2488 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2492 static CHANSERV_FUNC(cmd_trim)
2494 unsigned long duration;
2495 unsigned short min_level, max_level;
2500 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2501 duration = ParseInterval(argv[2]);
2504 reply("CSMSG_CANNOT_TRIM");
2508 if(!irccasecmp(argv[1], "bans"))
2510 cmd_trim_bans(user, channel, duration);
2513 else if(!irccasecmp(argv[1], "users"))
2515 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2518 else if(parse_level_range(&min_level, &max_level, argv[1]))
2520 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2523 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2525 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2530 reply("CSMSG_INVALID_TRIM", argv[1]);
2535 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2536 to the user. cmd_all takes advantage of this. */
2537 static CHANSERV_FUNC(cmd_up)
2539 struct mod_chanmode change;
2540 struct userData *uData;
2543 mod_chanmode_init(&change);
2545 change.args[0].u.member = GetUserMode(channel, user);
2546 if(!change.args[0].u.member)
2549 reply("MSG_CHANNEL_ABSENT", channel->name);
2553 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2557 reply("CSMSG_GODMODE_UP", argv[0]);
2560 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2562 change.args[0].mode = MODE_CHANOP;
2563 errmsg = "CSMSG_ALREADY_OPPED";
2565 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2567 change.args[0].mode = MODE_VOICE;
2568 errmsg = "CSMSG_ALREADY_VOICED";
2573 reply("CSMSG_NO_ACCESS");
2576 change.args[0].mode &= ~change.args[0].u.member->modes;
2577 if(!change.args[0].mode)
2580 reply(errmsg, channel->name);
2583 modcmd_chanmode_announce(&change);
2587 static CHANSERV_FUNC(cmd_down)
2589 struct mod_chanmode change;
2591 mod_chanmode_init(&change);
2593 change.args[0].u.member = GetUserMode(channel, user);
2594 if(!change.args[0].u.member)
2597 reply("MSG_CHANNEL_ABSENT", channel->name);
2601 if(!change.args[0].u.member->modes)
2604 reply("CSMSG_ALREADY_DOWN", channel->name);
2608 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2609 modcmd_chanmode_announce(&change);
2613 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)
2615 struct userData *cList;
2617 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2619 if(IsSuspended(cList->channel)
2620 || IsUserSuspended(cList)
2621 || !GetUserMode(cList->channel->channel, user))
2624 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2630 static CHANSERV_FUNC(cmd_upall)
2632 return cmd_all(CSFUNC_ARGS, cmd_up);
2635 static CHANSERV_FUNC(cmd_downall)
2637 return cmd_all(CSFUNC_ARGS, cmd_down);
2640 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2641 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2644 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)
2646 unsigned int ii, valid;
2647 struct userNode *victim;
2648 struct mod_chanmode *change;
2650 change = mod_chanmode_alloc(argc - 1);
2652 for(ii=valid=0; ++ii < argc; )
2654 if(!(victim = GetUserH(argv[ii])))
2656 change->args[valid].mode = mode;
2657 change->args[valid].u.member = GetUserMode(channel, victim);
2658 if(!change->args[valid].u.member)
2660 if(validate && !validate(user, channel, victim))
2665 change->argc = valid;
2666 if(valid < (argc-1))
2667 reply("CSMSG_PROCESS_FAILED");
2670 modcmd_chanmode_announce(change);
2671 reply(action, channel->name);
2673 mod_chanmode_free(change);
2677 static CHANSERV_FUNC(cmd_op)
2679 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2682 static CHANSERV_FUNC(cmd_deop)
2684 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2687 static CHANSERV_FUNC(cmd_voice)
2689 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2692 static CHANSERV_FUNC(cmd_devoice)
2694 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2698 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2704 for(ii=0; ii<channel->members.used; ii++)
2706 struct modeNode *mn = channel->members.list[ii];
2708 if(IsService(mn->user))
2711 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2714 if(protect_user(mn->user, user, channel->channel_info))
2718 victims[(*victimCount)++] = mn;
2724 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2726 struct userNode *victim;
2727 struct modeNode **victims;
2728 unsigned int offset, n, victimCount, duration = 0;
2729 char *reason = "Bye.", *ban, *name;
2730 char interval[INTERVALLEN];
2732 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2733 REQUIRE_PARAMS(offset);
2736 reason = unsplit_string(argv + offset, argc - offset, NULL);
2737 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2739 /* Truncate the reason to a length of TOPICLEN, as
2740 the ircd does; however, leave room for an ellipsis
2741 and the kicker's nick. */
2742 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2746 if((victim = GetUserH(argv[1])))
2748 victims = alloca(sizeof(victims[0]));
2749 victims[0] = GetUserMode(channel, victim);
2750 /* XXX: The comparison with ACTION_KICK is just because all
2751 * other actions can work on users outside the channel, and we
2752 * want to allow those (e.g. unbans) in that case. If we add
2753 * some other ejection action for in-channel users, change
2755 victimCount = victims[0] ? 1 : 0;
2757 if(IsService(victim))
2759 reply("MSG_SERVICE_IMMUNE", victim->nick);
2763 if((action == ACTION_KICK) && !victimCount)
2765 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2769 if(protect_user(victim, user, channel->channel_info))
2771 reply("CSMSG_USER_PROTECTED", victim->nick);
2775 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2776 name = victim->nick;
2780 if(!is_ircmask(argv[1]))
2782 reply("MSG_NICK_UNKNOWN", argv[1]);
2786 victims = alloca(sizeof(victims[0]) * channel->members.used);
2788 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2790 reply("CSMSG_MASK_PROTECTED", argv[1]);
2794 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2796 reply("CSMSG_LAME_MASK", argv[1]);
2800 if((action == ACTION_KICK) && (victimCount == 0))
2802 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2806 name = ban = strdup(argv[1]);
2809 /* Truncate the ban in place if necessary; we must ensure
2810 that 'ban' is a valid ban mask before sanitizing it. */
2811 sanitize_ircmask(ban);
2813 if(action & ACTION_ADD_BAN)
2815 struct banData *bData, *next;
2817 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2819 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2824 if(action & ACTION_ADD_TIMED_BAN)
2826 duration = ParseInterval(argv[2]);
2830 reply("CSMSG_DURATION_TOO_LOW");
2834 else if(duration > (86400 * 365 * 2))
2836 reply("CSMSG_DURATION_TOO_HIGH");
2842 for(bData = channel->channel_info->bans; bData; bData = next)
2844 if(match_ircglobs(bData->mask, ban))
2846 int exact = !irccasecmp(bData->mask, ban);
2848 /* The ban is redundant; there is already a ban
2849 with the same effect in place. */
2853 free(bData->reason);
2854 bData->reason = strdup(reason);
2855 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2857 reply("CSMSG_REASON_CHANGE", ban);
2861 if(exact && bData->expires)
2865 /* If the ban matches an existing one exactly,
2866 extend the expiration time if the provided
2867 duration is longer. */
2868 if(duration && ((time_t)(now + duration) > bData->expires))
2870 bData->expires = now + duration;
2881 /* Delete the expiration timeq entry and
2882 requeue if necessary. */
2883 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2886 timeq_add(bData->expires, expire_ban, bData);
2890 /* automated kickban */
2893 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2895 reply("CSMSG_BAN_ADDED", name, channel->name);
2901 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2908 if(match_ircglobs(ban, bData->mask))
2910 /* The ban we are adding makes previously existing
2911 bans redundant; silently remove them. */
2912 del_channel_ban(bData);
2916 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);
2918 name = ban = strdup(bData->mask);
2922 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2924 extern const char *hidden_host_suffix;
2925 const char *old_name = chanserv_conf.old_ban_names->list[n];
2927 unsigned int l1, l2;
2930 l2 = strlen(old_name);
2933 if(irccasecmp(ban + l1 - l2, old_name))
2935 new_mask = malloc(MAXLEN);
2936 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2938 name = ban = new_mask;
2943 if(action & ACTION_BAN)
2945 unsigned int exists;
2946 struct mod_chanmode *change;
2948 if(channel->banlist.used >= MAXBANS)
2951 reply("CSMSG_BANLIST_FULL", channel->name);
2956 exists = ChannelBanExists(channel, ban);
2957 change = mod_chanmode_alloc(victimCount + 1);
2958 for(n = 0; n < victimCount; ++n)
2960 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2961 change->args[n].u.member = victims[n];
2965 change->args[n].mode = MODE_BAN;
2966 change->args[n++].u.hostmask = ban;
2970 modcmd_chanmode_announce(change);
2972 mod_chanmode_announce(chanserv, channel, change);
2973 mod_chanmode_free(change);
2975 if(exists && (action == ACTION_BAN))
2978 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2984 if(action & ACTION_KICK)
2986 char kick_reason[MAXLEN];
2987 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2989 for(n = 0; n < victimCount; n++)
2990 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2995 /* No response, since it was automated. */
2997 else if(action & ACTION_ADD_BAN)
3000 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3002 reply("CSMSG_BAN_ADDED", name, channel->name);
3004 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3005 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3006 else if(action & ACTION_BAN)
3007 reply("CSMSG_BAN_DONE", name, channel->name);
3008 else if(action & ACTION_KICK && victimCount)
3009 reply("CSMSG_KICK_DONE", name, channel->name);
3015 static CHANSERV_FUNC(cmd_kickban)
3017 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3020 static CHANSERV_FUNC(cmd_kick)
3022 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3025 static CHANSERV_FUNC(cmd_ban)
3027 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3030 static CHANSERV_FUNC(cmd_addban)
3032 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3035 static CHANSERV_FUNC(cmd_addtimedban)
3037 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3040 static struct mod_chanmode *
3041 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3043 struct mod_chanmode *change;
3044 unsigned char *match;
3045 unsigned int ii, count;
3047 match = alloca(bans->used);
3050 for(ii = count = 0; ii < bans->used; ++ii)
3052 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3053 MATCH_USENICK | MATCH_VISIBLE);
3060 for(ii = count = 0; ii < bans->used; ++ii)
3062 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3069 change = mod_chanmode_alloc(count);
3070 for(ii = count = 0; ii < bans->used; ++ii)
3074 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3075 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3077 assert(count == change->argc);
3082 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3084 struct userNode *actee;
3090 /* may want to allow a comma delimited list of users... */
3091 if(!(actee = GetUserH(argv[1])))
3093 if(!is_ircmask(argv[1]))
3095 reply("MSG_NICK_UNKNOWN", argv[1]);
3099 mask = strdup(argv[1]);
3102 /* We don't sanitize the mask here because ircu
3104 if(action & ACTION_UNBAN)
3106 struct mod_chanmode *change;
3107 change = find_matching_bans(&channel->banlist, actee, mask);
3112 modcmd_chanmode_announce(change);
3113 for(ii = 0; ii < change->argc; ++ii)
3114 free((char*)change->args[ii].u.hostmask);
3115 mod_chanmode_free(change);
3120 if(action & ACTION_DEL_BAN)
3122 struct banData *ban, *next;
3124 ban = channel->channel_info->bans;
3128 for( ; ban && !user_matches_glob(actee, ban->mask,
3129 MATCH_USENICK | MATCH_VISIBLE);
3132 for( ; ban && !match_ircglobs(mask, ban->mask);
3137 del_channel_ban(ban);
3144 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3146 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3152 static CHANSERV_FUNC(cmd_unban)
3154 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3157 static CHANSERV_FUNC(cmd_delban)
3159 /* it doesn't necessarily have to remove the channel ban - may want
3160 to make that an option. */
3161 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3164 static CHANSERV_FUNC(cmd_unbanme)
3166 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3167 long flags = ACTION_UNBAN;
3169 /* remove permanent bans if the user has the proper access. */
3170 if(uData->access >= UL_MASTER)
3171 flags |= ACTION_DEL_BAN;
3173 argv[1] = user->nick;
3174 return unban_user(user, channel, 2, argv, cmd, flags);
3177 static CHANSERV_FUNC(cmd_unbanall)
3179 struct mod_chanmode *change;
3182 if(!channel->banlist.used)
3184 reply("CSMSG_NO_BANS", channel->name);
3188 change = mod_chanmode_alloc(channel->banlist.used);
3189 for(ii=0; ii<channel->banlist.used; ii++)
3191 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3192 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3194 modcmd_chanmode_announce(change);
3195 for(ii = 0; ii < change->argc; ++ii)
3196 free((char*)change->args[ii].u.hostmask);
3197 mod_chanmode_free(change);
3198 reply("CSMSG_BANS_REMOVED", channel->name);
3202 static CHANSERV_FUNC(cmd_open)
3204 struct mod_chanmode *change;
3207 change = find_matching_bans(&channel->banlist, user, NULL);
3209 change = mod_chanmode_alloc(0);
3210 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3211 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3212 && channel->channel_info->modes.modes_set)
3213 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3214 modcmd_chanmode_announce(change);
3215 reply("CSMSG_CHANNEL_OPENED", channel->name);
3216 for(ii = 0; ii < change->argc; ++ii)
3217 free((char*)change->args[ii].u.hostmask);
3218 mod_chanmode_free(change);
3222 static CHANSERV_FUNC(cmd_myaccess)
3224 static struct string_buffer sbuf;
3225 struct handle_info *target_handle;
3226 struct userData *uData;
3229 target_handle = user->handle_info;
3230 else if(!IsHelping(user))
3232 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3235 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3238 if(!target_handle->channels)
3240 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3244 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3245 for(uData = target_handle->channels; uData; uData = uData->u_next)
3247 struct chanData *cData = uData->channel;
3249 if(uData->access > UL_OWNER)
3251 if(IsProtected(cData)
3252 && (target_handle != user->handle_info)
3253 && !GetTrueChannelAccess(cData, user->handle_info))
3256 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3257 if(uData->flags != USER_AUTO_OP)
3258 string_buffer_append(&sbuf, ',');
3259 if(IsUserSuspended(uData))
3260 string_buffer_append(&sbuf, 's');
3261 if(IsUserAutoOp(uData))
3263 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3264 string_buffer_append(&sbuf, 'o');
3265 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3266 string_buffer_append(&sbuf, 'v');
3268 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3269 string_buffer_append(&sbuf, 'i');
3271 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3273 string_buffer_append_string(&sbuf, ")]");
3274 string_buffer_append(&sbuf, '\0');
3275 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3281 static CHANSERV_FUNC(cmd_access)
3283 struct userNode *target;
3284 struct handle_info *target_handle;
3285 struct userData *uData;
3287 char prefix[MAXLEN];
3292 target_handle = target->handle_info;
3294 else if((target = GetUserH(argv[1])))
3296 target_handle = target->handle_info;
3298 else if(argv[1][0] == '*')
3300 if(!(target_handle = get_handle_info(argv[1]+1)))
3302 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3308 reply("MSG_NICK_UNKNOWN", argv[1]);
3312 assert(target || target_handle);
3314 if(target == chanserv)
3316 reply("CSMSG_IS_CHANSERV");
3324 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3329 reply("MSG_USER_AUTHENTICATE", target->nick);
3332 reply("MSG_AUTHENTICATE");
3338 const char *epithet = NULL, *type = NULL;
3341 epithet = chanserv_conf.irc_operator_epithet;
3344 else if(IsNetworkHelper(target))
3346 epithet = chanserv_conf.network_helper_epithet;
3347 type = "network helper";
3349 else if(IsSupportHelper(target))
3351 epithet = chanserv_conf.support_helper_epithet;
3352 type = "support helper";
3356 if(target_handle->epithet)
3357 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3359 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3361 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3365 sprintf(prefix, "%s", target_handle->handle);
3368 if(!channel->channel_info)
3370 reply("CSMSG_NOT_REGISTERED", channel->name);
3374 helping = HANDLE_FLAGGED(target_handle, HELPING)
3375 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3376 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3378 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3379 /* To prevent possible information leaks, only show infolines
3380 * if the requestor is in the channel or it's their own
3382 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3384 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3386 /* Likewise, only say it's suspended if the user has active
3387 * access in that channel or it's their own entry. */
3388 if(IsUserSuspended(uData)
3389 && (GetChannelUser(channel->channel_info, user->handle_info)
3390 || (user->handle_info == uData->handle)))
3392 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3397 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3404 zoot_list(struct listData *list)
3406 struct userData *uData;
3407 unsigned int start, curr, highest, lowest;
3408 struct helpfile_table tmp_table;
3409 const char **temp, *msg;
3411 if(list->table.length == 1)
3414 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3416 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3417 msg = user_find_message(list->user, "MSG_NONE");
3418 send_message_type(4, list->user, list->bot, " %s", msg);
3420 tmp_table.width = list->table.width;
3421 tmp_table.flags = list->table.flags;
3422 list->table.contents[0][0] = " ";
3423 highest = list->highest;
3424 if(list->lowest != 0)
3425 lowest = list->lowest;
3426 else if(highest < 100)
3429 lowest = highest - 100;
3430 for(start = curr = 1; curr < list->table.length; )
3432 uData = list->users[curr-1];
3433 list->table.contents[curr++][0] = " ";
3434 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3437 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3439 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3440 temp = list->table.contents[--start];
3441 list->table.contents[start] = list->table.contents[0];
3442 tmp_table.contents = list->table.contents + start;
3443 tmp_table.length = curr - start;
3444 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3445 list->table.contents[start] = temp;
3447 highest = lowest - 1;
3448 lowest = (highest < 100) ? 0 : (highest - 99);
3454 def_list(struct listData *list)
3458 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3460 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3461 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3462 if(list->table.length == 1)
3464 msg = user_find_message(list->user, "MSG_NONE");
3465 send_message_type(4, list->user, list->bot, " %s", msg);
3470 userData_access_comp(const void *arg_a, const void *arg_b)
3472 const struct userData *a = *(struct userData**)arg_a;
3473 const struct userData *b = *(struct userData**)arg_b;
3475 if(a->access != b->access)
3476 res = b->access - a->access;
3478 res = irccasecmp(a->handle->handle, b->handle->handle);
3483 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3485 void (*send_list)(struct listData *);
3486 struct userData *uData;
3487 struct listData lData;
3488 unsigned int matches;
3492 lData.bot = cmd->parent->bot;
3493 lData.channel = channel;
3494 lData.lowest = lowest;
3495 lData.highest = highest;
3496 lData.search = (argc > 1) ? argv[1] : NULL;
3497 send_list = def_list;
3498 (void)zoot_list; /* since it doesn't show user levels */
3500 if(user->handle_info)
3502 switch(user->handle_info->userlist_style)
3504 case HI_STYLE_DEF: send_list = def_list; break;
3505 case HI_STYLE_ZOOT: send_list = def_list; break;
3509 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3511 for(uData = channel->channel_info->users; uData; uData = uData->next)
3513 if((uData->access < lowest)
3514 || (uData->access > highest)
3515 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3517 lData.users[matches++] = uData;
3519 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3521 lData.table.length = matches+1;
3522 lData.table.width = 4;
3523 lData.table.flags = TABLE_NO_FREE;
3524 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3525 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3526 lData.table.contents[0] = ary;
3529 ary[2] = "Last Seen";
3531 for(matches = 1; matches < lData.table.length; ++matches)
3533 struct userData *uData = lData.users[matches-1];
3534 char seen[INTERVALLEN];
3536 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3537 lData.table.contents[matches] = ary;
3538 ary[0] = strtab(uData->access);
3539 ary[1] = uData->handle->handle;
3542 else if(!uData->seen)
3545 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3546 ary[2] = strdup(ary[2]);
3547 if(IsUserSuspended(uData))
3548 ary[3] = "Suspended";
3549 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3550 ary[3] = "Vacation";
3555 for(matches = 1; matches < lData.table.length; ++matches)
3557 free((char*)lData.table.contents[matches][2]);
3558 free(lData.table.contents[matches]);
3560 free(lData.table.contents[0]);
3561 free(lData.table.contents);
3565 static CHANSERV_FUNC(cmd_users)
3567 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3570 static CHANSERV_FUNC(cmd_wlist)
3572 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3575 static CHANSERV_FUNC(cmd_clist)
3577 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3580 static CHANSERV_FUNC(cmd_mlist)
3582 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3585 static CHANSERV_FUNC(cmd_olist)
3587 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3590 static CHANSERV_FUNC(cmd_plist)
3592 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3595 static CHANSERV_FUNC(cmd_bans)
3597 struct userNode *search_u = NULL;
3598 struct helpfile_table tbl;
3599 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3600 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3601 const char *msg_never, *triggered, *expires;
3602 struct banData *ban, **bans;
3606 else if(strchr(search = argv[1], '!'))
3609 search_wilds = search[strcspn(search, "?*")];
3611 else if(!(search_u = GetUserH(search)))
3612 reply("MSG_NICK_UNKNOWN", search);
3614 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3616 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3620 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3625 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3628 bans[matches++] = ban;
3633 tbl.length = matches + 1;
3634 tbl.width = 4 + timed;
3636 tbl.flags = TABLE_NO_FREE;
3637 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3638 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3639 tbl.contents[0][0] = "Mask";
3640 tbl.contents[0][1] = "Set By";
3641 tbl.contents[0][2] = "Triggered";
3644 tbl.contents[0][3] = "Expires";
3645 tbl.contents[0][4] = "Reason";
3648 tbl.contents[0][3] = "Reason";
3651 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3653 free(tbl.contents[0]);
3658 msg_never = user_find_message(user, "MSG_NEVER");
3659 for(ii = 0; ii < matches; )
3665 else if(ban->expires)
3666 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3668 expires = msg_never;
3671 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3673 triggered = msg_never;
3675 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3676 tbl.contents[ii][0] = ban->mask;
3677 tbl.contents[ii][1] = ban->owner;
3678 tbl.contents[ii][2] = strdup(triggered);
3681 tbl.contents[ii][3] = strdup(expires);
3682 tbl.contents[ii][4] = ban->reason;
3685 tbl.contents[ii][3] = ban->reason;
3687 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3688 reply("MSG_MATCH_COUNT", matches);
3689 for(ii = 1; ii < tbl.length; ++ii)
3691 free((char*)tbl.contents[ii][2]);
3693 free((char*)tbl.contents[ii][3]);
3694 free(tbl.contents[ii]);
3696 free(tbl.contents[0]);
3702 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3704 struct chanData *cData = channel->channel_info;
3705 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3707 if(cData->topic_mask)
3708 return !match_ircglob(new_topic, cData->topic_mask);
3709 else if(cData->topic)
3710 return irccasecmp(new_topic, cData->topic);
3715 static CHANSERV_FUNC(cmd_topic)
3717 struct chanData *cData;
3720 cData = channel->channel_info;
3725 SetChannelTopic(channel, chanserv, cData->topic, 1);
3726 reply("CSMSG_TOPIC_SET", cData->topic);
3730 reply("CSMSG_NO_TOPIC", channel->name);
3734 topic = unsplit_string(argv + 1, argc - 1, NULL);
3735 /* If they say "!topic *", use an empty topic. */
3736 if((topic[0] == '*') && (topic[1] == 0))
3738 if(bad_topic(channel, user, topic))
3740 char *topic_mask = cData->topic_mask;
3743 char new_topic[TOPICLEN+1], tchar;
3744 int pos=0, starpos=-1, dpos=0, len;
3746 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3753 len = strlen(topic);
3754 if((dpos + len) > TOPICLEN)
3755 len = TOPICLEN + 1 - dpos;
3756 memcpy(new_topic+dpos, topic, len);
3760 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3761 default: new_topic[dpos++] = tchar; break;
3764 if((dpos > TOPICLEN) || tchar)
3767 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3768 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3771 new_topic[dpos] = 0;
3772 SetChannelTopic(channel, chanserv, new_topic, 1);
3774 reply("CSMSG_TOPIC_LOCKED", channel->name);
3779 SetChannelTopic(channel, chanserv, topic, 1);
3781 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3783 /* Grab the topic and save it as the default topic. */
3785 cData->topic = strdup(channel->topic);
3791 static CHANSERV_FUNC(cmd_mode)
3793 struct userData *uData;
3794 struct mod_chanmode *change;
3799 change = &channel->channel_info->modes;
3800 if(change->modes_set || change->modes_clear) {
3801 modcmd_chanmode_announce(change);
3802 reply("CSMSG_DEFAULTED_MODES", channel->name);
3804 reply("CSMSG_NO_MODES", channel->name);
3808 uData = GetChannelUser(channel->channel_info, user->handle_info);
3810 base_oplevel = MAXOPLEVEL;
3811 else if (uData->access >= UL_OWNER)
3814 base_oplevel = 1 + UL_OWNER - uData->access;
3815 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3818 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3822 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3823 && mode_lock_violated(&channel->channel_info->modes, change))
3826 mod_chanmode_format(&channel->channel_info->modes, modes);
3827 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3831 modcmd_chanmode_announce(change);
3832 mod_chanmode_free(change);
3833 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3837 static CHANSERV_FUNC(cmd_invite)
3839 struct userData *uData;
3840 struct userNode *invite;
3842 uData = GetChannelUser(channel->channel_info, user->handle_info);
3846 if(!(invite = GetUserH(argv[1])))
3848 reply("MSG_NICK_UNKNOWN", argv[1]);
3855 if(GetUserMode(channel, invite))
3857 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3865 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3866 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3869 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3871 irc_invite(chanserv, invite, channel);
3873 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3878 static CHANSERV_FUNC(cmd_inviteme)
3880 if(GetUserMode(channel, user))
3882 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3885 if(channel->channel_info
3886 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3888 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3891 irc_invite(cmd->parent->bot, user, channel);
3896 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3899 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3901 /* We display things based on two dimensions:
3902 * - Issue time: present or absent
3903 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3904 * (in order of precedence, so something both expired and revoked
3905 * only counts as revoked)
3907 combo = (suspended->issued ? 4 : 0)
3908 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3910 case 0: /* no issue time, indefinite expiration */
3911 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3913 case 1: /* no issue time, expires in future */
3914 intervalString(buf1, suspended->expires-now, user->handle_info);
3915 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3917 case 2: /* no issue time, expired */
3918 intervalString(buf1, now-suspended->expires, user->handle_info);
3919 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3921 case 3: /* no issue time, revoked */
3922 intervalString(buf1, now-suspended->revoked, user->handle_info);
3923 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3925 case 4: /* issue time set, indefinite expiration */
3926 intervalString(buf1, now-suspended->issued, user->handle_info);
3927 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3929 case 5: /* issue time set, expires in future */
3930 intervalString(buf1, now-suspended->issued, user->handle_info);
3931 intervalString(buf2, suspended->expires-now, user->handle_info);
3932 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3934 case 6: /* issue time set, expired */
3935 intervalString(buf1, now-suspended->issued, user->handle_info);
3936 intervalString(buf2, now-suspended->expires, user->handle_info);
3937 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3939 case 7: /* issue time set, revoked */
3940 intervalString(buf1, now-suspended->issued, user->handle_info);
3941 intervalString(buf2, now-suspended->revoked, user->handle_info);
3942 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3945 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3950 static CHANSERV_FUNC(cmd_info)
3952 char modes[MAXLEN], buffer[INTERVALLEN];
3953 struct userData *uData, *owner;
3954 struct chanData *cData;
3955 struct do_not_register *dnr;
3960 cData = channel->channel_info;
3961 reply("CSMSG_CHANNEL_INFO", channel->name);
3963 uData = GetChannelUser(cData, user->handle_info);
3964 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3966 mod_chanmode_format(&cData->modes, modes);
3967 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3968 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3971 for(it = dict_first(cData->notes); it; it = iter_next(it))
3975 note = iter_data(it);
3976 if(!note_type_visible_to_user(cData, note->type, user))
3979 padding = PADLEN - 1 - strlen(iter_key(it));
3980 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3983 reply("CSMSG_CHANNEL_MAX", cData->max);
3984 for(owner = cData->users; owner; owner = owner->next)
3985 if(owner->access == UL_OWNER)
3986 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3987 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3988 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3989 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3991 privileged = IsStaff(user);
3993 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3994 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3995 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3997 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3998 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4000 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4002 struct suspended *suspended;
4003 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4004 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4005 show_suspension_info(cmd, user, suspended);
4007 else if(IsSuspended(cData))
4009 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4010 show_suspension_info(cmd, user, cData->suspended);
4015 static CHANSERV_FUNC(cmd_netinfo)
4017 extern time_t boot_time;
4018 extern unsigned long burst_length;
4019 char interval[INTERVALLEN];
4021 reply("CSMSG_NETWORK_INFO");
4022 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4023 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4024 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4025 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4026 reply("CSMSG_NETWORK_BANS", banCount);
4027 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4028 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4029 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4034 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4036 struct helpfile_table table;
4038 struct userNode *user;
4043 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4044 table.contents = alloca(list->used*sizeof(*table.contents));
4045 for(nn=0; nn<list->used; nn++)
4047 user = list->list[nn];
4048 if(user->modes & skip_flags)
4052 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4055 nick = alloca(strlen(user->nick)+3);
4056 sprintf(nick, "(%s)", user->nick);
4060 table.contents[table.length][0] = nick;
4063 table_send(chanserv, to->nick, 0, NULL, table);
4066 static CHANSERV_FUNC(cmd_ircops)
4068 reply("CSMSG_STAFF_OPERS");
4069 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4073 static CHANSERV_FUNC(cmd_helpers)
4075 reply("CSMSG_STAFF_HELPERS");
4076 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4080 static CHANSERV_FUNC(cmd_staff)
4082 reply("CSMSG_NETWORK_STAFF");
4083 cmd_ircops(CSFUNC_ARGS);
4084 cmd_helpers(CSFUNC_ARGS);
4088 static CHANSERV_FUNC(cmd_peek)
4090 struct modeNode *mn;
4091 char modes[MODELEN];
4093 struct helpfile_table table;
4095 irc_make_chanmode(channel, modes);
4097 reply("CSMSG_PEEK_INFO", channel->name);
4098 reply("CSMSG_PEEK_TOPIC", channel->topic);
4099 reply("CSMSG_PEEK_MODES", modes);
4100 reply("CSMSG_PEEK_USERS", channel->members.used);
4104 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4105 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4106 for(n = 0; n < channel->members.used; n++)
4108 mn = channel->members.list[n];
4109 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4111 table.contents[table.length] = alloca(sizeof(**table.contents));
4112 table.contents[table.length][0] = mn->user->nick;
4117 reply("CSMSG_PEEK_OPS");
4118 table_send(chanserv, user->nick, 0, NULL, table);
4121 reply("CSMSG_PEEK_NO_OPS");
4125 static MODCMD_FUNC(cmd_wipeinfo)
4127 struct handle_info *victim;
4128 struct userData *ud, *actor;
4131 actor = GetChannelUser(channel->channel_info, user->handle_info);
4132 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4134 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4136 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4139 if((ud->access >= actor->access) && (ud != actor))
4141 reply("MSG_USER_OUTRANKED", victim->handle);
4147 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4151 static CHANSERV_FUNC(cmd_resync)
4153 struct mod_chanmode *changes;
4154 struct chanData *cData = channel->channel_info;
4155 unsigned int ii, used;
4157 changes = mod_chanmode_alloc(channel->members.used * 2);
4158 for(ii = used = 0; ii < channel->members.used; ++ii)
4160 struct modeNode *mn = channel->members.list[ii];
4161 struct userData *uData;
4163 if(IsService(mn->user))
4166 uData = GetChannelAccess(cData, mn->user->handle_info);
4167 if(!cData->lvlOpts[lvlGiveOps]
4168 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4170 if(!(mn->modes & MODE_CHANOP))
4172 changes->args[used].mode = MODE_CHANOP;
4173 changes->args[used++].u.member = mn;
4176 else if(!cData->lvlOpts[lvlGiveVoice]
4177 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4179 if(mn->modes & MODE_CHANOP)
4181 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4182 changes->args[used++].u.member = mn;
4184 if(!(mn->modes & MODE_VOICE))
4186 changes->args[used].mode = MODE_VOICE;
4187 changes->args[used++].u.member = mn;
4194 changes->args[used].mode = MODE_REMOVE | mn->modes;
4195 changes->args[used++].u.member = mn;
4199 changes->argc = used;
4200 modcmd_chanmode_announce(changes);
4201 mod_chanmode_free(changes);
4202 reply("CSMSG_RESYNCED_USERS", channel->name);
4206 static CHANSERV_FUNC(cmd_seen)
4208 struct userData *uData;
4209 struct handle_info *handle;
4210 char seen[INTERVALLEN];
4214 if(!irccasecmp(argv[1], chanserv->nick))
4216 reply("CSMSG_IS_CHANSERV");
4220 if(!(handle = get_handle_info(argv[1])))
4222 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4226 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4228 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4233 reply("CSMSG_USER_PRESENT", handle->handle);
4234 else if(uData->seen)
4235 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4237 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4239 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4240 reply("CSMSG_USER_VACATION", handle->handle);
4245 static MODCMD_FUNC(cmd_names)
4247 struct userNode *targ;
4248 struct userData *targData;
4249 unsigned int ii, pos;
4252 for(ii=pos=0; ii<channel->members.used; ++ii)
4254 targ = channel->members.list[ii]->user;
4255 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4258 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4261 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4265 if(IsUserSuspended(targData))
4267 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4270 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4271 reply("CSMSG_END_NAMES", channel->name);
4276 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4278 switch(ntype->visible_type)
4280 case NOTE_VIS_ALL: return 1;
4281 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4282 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4287 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4289 struct userData *uData;
4291 switch(ntype->set_access_type)
4293 case NOTE_SET_CHANNEL_ACCESS:
4294 if(!user->handle_info)
4296 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4298 return uData->access >= ntype->set_access.min_ulevel;
4299 case NOTE_SET_CHANNEL_SETTER:
4300 return check_user_level(channel, user, lvlSetters, 1, 0);
4301 case NOTE_SET_PRIVILEGED: default:
4302 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4306 static CHANSERV_FUNC(cmd_note)
4308 struct chanData *cData;
4310 struct note_type *ntype;
4312 cData = channel->channel_info;
4315 reply("CSMSG_NOT_REGISTERED", channel->name);
4319 /* If no arguments, show all visible notes for the channel. */
4325 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4327 note = iter_data(it);
4328 if(!note_type_visible_to_user(cData, note->type, user))
4331 reply("CSMSG_NOTELIST_HEADER", channel->name);
4332 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4335 reply("CSMSG_NOTELIST_END", channel->name);
4337 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4339 /* If one argument, show the named note. */
4342 if((note = dict_find(cData->notes, argv[1], NULL))
4343 && note_type_visible_to_user(cData, note->type, user))
4345 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4347 else if((ntype = dict_find(note_types, argv[1], NULL))
4348 && note_type_visible_to_user(NULL, ntype, user))
4350 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4355 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4359 /* Assume they're trying to set a note. */
4363 ntype = dict_find(note_types, argv[1], NULL);
4366 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4369 else if(note_type_settable_by_user(channel, ntype, user))
4371 note_text = unsplit_string(argv+2, argc-2, NULL);
4372 if((note = dict_find(cData->notes, argv[1], NULL)))
4373 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4374 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4375 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4377 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4379 /* The note is viewable to staff only, so return 0
4380 to keep the invocation from getting logged (or
4381 regular users can see it in !events). */
4387 reply("CSMSG_NO_ACCESS");
4394 static CHANSERV_FUNC(cmd_delnote)
4399 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4400 || !note_type_settable_by_user(channel, note->type, user))
4402 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4405 dict_remove(channel->channel_info->notes, note->type->name);
4406 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4410 static CHANSERV_FUNC(cmd_events)
4412 struct logSearch discrim;
4413 struct logReport report;
4414 unsigned int matches, limit;
4416 limit = (argc > 1) ? atoi(argv[1]) : 10;
4417 if(limit < 1 || limit > 200)
4420 memset(&discrim, 0, sizeof(discrim));
4421 discrim.masks.bot = chanserv;
4422 discrim.masks.channel_name = channel->name;
4424 discrim.masks.command = argv[2];
4425 discrim.limit = limit;
4426 discrim.max_time = INT_MAX;
4427 discrim.severities = 1 << LOG_COMMAND;
4428 report.reporter = chanserv;
4430 reply("CSMSG_EVENT_SEARCH_RESULTS");
4431 matches = log_entry_search(&discrim, log_report_entry, &report);
4433 reply("MSG_MATCH_COUNT", matches);
4435 reply("MSG_NO_MATCHES");
4439 static CHANSERV_FUNC(cmd_say)
4445 msg = unsplit_string(argv + 1, argc - 1, NULL);
4446 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4448 else if(GetUserH(argv[1]))
4451 msg = unsplit_string(argv + 2, argc - 2, NULL);
4452 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4456 reply("MSG_NOT_TARGET_NAME");
4462 static CHANSERV_FUNC(cmd_emote)
4468 /* CTCP is so annoying. */
4469 msg = unsplit_string(argv + 1, argc - 1, NULL);
4470 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4472 else if(GetUserH(argv[1]))
4474 msg = unsplit_string(argv + 2, argc - 2, NULL);
4475 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4479 reply("MSG_NOT_TARGET_NAME");
4485 struct channelList *
4486 chanserv_support_channels(void)
4488 return &chanserv_conf.support_channels;
4491 static CHANSERV_FUNC(cmd_expire)
4493 int channel_count = registered_channels;
4494 expire_channels(NULL);
4495 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4500 chanserv_expire_suspension(void *data)
4502 struct suspended *suspended = data;
4503 struct chanNode *channel;
4505 if(!suspended->expires || (now < suspended->expires))
4506 suspended->revoked = now;
4507 channel = suspended->cData->channel;
4508 suspended->cData->channel = channel;
4509 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4510 if(!IsOffChannel(suspended->cData))
4512 struct mod_chanmode change;
4513 mod_chanmode_init(&change);
4515 change.args[0].mode = MODE_CHANOP;
4516 change.args[0].u.member = AddChannelUser(chanserv, channel);
4517 mod_chanmode_announce(chanserv, channel, &change);
4521 static CHANSERV_FUNC(cmd_csuspend)
4523 struct suspended *suspended;
4524 char reason[MAXLEN];
4525 time_t expiry, duration;
4526 struct userData *uData;
4530 if(IsProtected(channel->channel_info))
4532 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4536 if(argv[1][0] == '!')
4538 else if(IsSuspended(channel->channel_info))
4540 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4541 show_suspension_info(cmd, user, channel->channel_info->suspended);
4545 if(!strcmp(argv[1], "0"))
4547 else if((duration = ParseInterval(argv[1])))
4548 expiry = now + duration;
4551 reply("MSG_INVALID_DURATION", argv[1]);
4555 unsplit_string(argv + 2, argc - 2, reason);
4557 suspended = calloc(1, sizeof(*suspended));
4558 suspended->revoked = 0;
4559 suspended->issued = now;
4560 suspended->suspender = strdup(user->handle_info->handle);
4561 suspended->expires = expiry;
4562 suspended->reason = strdup(reason);
4563 suspended->cData = channel->channel_info;
4564 suspended->previous = suspended->cData->suspended;
4565 suspended->cData->suspended = suspended;
4567 if(suspended->expires)
4568 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4570 if(IsSuspended(channel->channel_info))
4572 suspended->previous->revoked = now;
4573 if(suspended->previous->expires)
4574 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4575 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4576 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4580 /* Mark all users in channel as absent. */
4581 for(uData = channel->channel_info->users; uData; uData = uData->next)
4590 /* Mark the channel as suspended, then part. */
4591 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4592 DelChannelUser(chanserv, channel, suspended->reason, 0);
4593 reply("CSMSG_SUSPENDED", channel->name);
4594 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4595 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4600 static CHANSERV_FUNC(cmd_cunsuspend)
4602 struct suspended *suspended;
4603 char message[MAXLEN];
4605 if(!IsSuspended(channel->channel_info))
4607 reply("CSMSG_NOT_SUSPENDED", channel->name);
4611 suspended = channel->channel_info->suspended;
4613 /* Expire the suspension and join ChanServ to the channel. */
4614 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4615 chanserv_expire_suspension(suspended);
4616 reply("CSMSG_UNSUSPENDED", channel->name);
4617 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4618 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4622 typedef struct chanservSearch
4630 unsigned long flags;
4634 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4637 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4642 search = malloc(sizeof(struct chanservSearch));
4643 memset(search, 0, sizeof(*search));
4646 for(i = 0; i < argc; i++)
4648 /* Assume all criteria require arguments. */
4651 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4655 if(!irccasecmp(argv[i], "name"))
4656 search->name = argv[++i];
4657 else if(!irccasecmp(argv[i], "registrar"))
4658 search->registrar = argv[++i];
4659 else if(!irccasecmp(argv[i], "unvisited"))
4660 search->unvisited = ParseInterval(argv[++i]);
4661 else if(!irccasecmp(argv[i], "registered"))
4662 search->registered = ParseInterval(argv[++i]);
4663 else if(!irccasecmp(argv[i], "flags"))
4666 if(!irccasecmp(argv[i], "nodelete"))
4667 search->flags |= CHANNEL_NODELETE;
4668 else if(!irccasecmp(argv[i], "suspended"))
4669 search->flags |= CHANNEL_SUSPENDED;
4672 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4676 else if(!irccasecmp(argv[i], "limit"))
4677 search->limit = strtoul(argv[++i], NULL, 10);
4680 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4685 if(search->name && !strcmp(search->name, "*"))
4687 if(search->registrar && !strcmp(search->registrar, "*"))
4688 search->registrar = 0;
4697 chanserv_channel_match(struct chanData *channel, search_t search)
4699 const char *name = channel->channel->name;
4700 if((search->name && !match_ircglob(name, search->name)) ||
4701 (search->registrar && !channel->registrar) ||
4702 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4703 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4704 (search->registered && (now - channel->registered) > search->registered) ||
4705 (search->flags && ((search->flags & channel->flags) != search->flags)))
4712 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4714 struct chanData *channel;
4715 unsigned int matches = 0;
4717 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4719 if(!chanserv_channel_match(channel, search))
4729 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4734 search_print(struct chanData *channel, void *data)
4736 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4739 static CHANSERV_FUNC(cmd_search)
4742 unsigned int matches;
4743 channel_search_func action;
4747 if(!irccasecmp(argv[1], "count"))
4748 action = search_count;
4749 else if(!irccasecmp(argv[1], "print"))
4750 action = search_print;
4753 reply("CSMSG_ACTION_INVALID", argv[1]);
4757 search = chanserv_search_create(user, argc - 2, argv + 2);
4761 if(action == search_count)
4762 search->limit = INT_MAX;
4764 if(action == search_print)
4765 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4767 matches = chanserv_channel_search(search, action, user);
4770 reply("MSG_MATCH_COUNT", matches);
4772 reply("MSG_NO_MATCHES");
4778 static CHANSERV_FUNC(cmd_unvisited)
4780 struct chanData *cData;
4781 time_t interval = chanserv_conf.channel_expire_delay;
4782 char buffer[INTERVALLEN];
4783 unsigned int limit = 25, matches = 0;
4787 interval = ParseInterval(argv[1]);
4789 limit = atoi(argv[2]);
4792 intervalString(buffer, interval, user->handle_info);
4793 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4795 for(cData = channelList; cData && matches < limit; cData = cData->next)
4797 if((now - cData->visited) < interval)
4800 intervalString(buffer, now - cData->visited, user->handle_info);
4801 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4808 static MODCMD_FUNC(chan_opt_defaulttopic)
4814 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4816 reply("CSMSG_TOPIC_LOCKED", channel->name);
4820 topic = unsplit_string(argv+1, argc-1, NULL);
4822 free(channel->channel_info->topic);
4823 if(topic[0] == '*' && topic[1] == 0)
4825 topic = channel->channel_info->topic = NULL;
4829 topic = channel->channel_info->topic = strdup(topic);
4830 if(channel->channel_info->topic_mask
4831 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4832 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4834 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4837 if(channel->channel_info->topic)
4838 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4840 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4844 static MODCMD_FUNC(chan_opt_topicmask)
4848 struct chanData *cData = channel->channel_info;
4851 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4853 reply("CSMSG_TOPIC_LOCKED", channel->name);
4857 mask = unsplit_string(argv+1, argc-1, NULL);
4859 if(cData->topic_mask)
4860 free(cData->topic_mask);
4861 if(mask[0] == '*' && mask[1] == 0)
4863 cData->topic_mask = 0;
4867 cData->topic_mask = strdup(mask);
4869 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4870 else if(!match_ircglob(cData->topic, cData->topic_mask))
4871 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4875 if(channel->channel_info->topic_mask)
4876 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4878 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4882 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4886 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4890 if(greeting[0] == '*' && greeting[1] == 0)
4894 unsigned int length = strlen(greeting);
4895 if(length > chanserv_conf.greeting_length)
4897 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4900 *data = strdup(greeting);
4909 reply(name, user_find_message(user, "MSG_NONE"));
4913 static MODCMD_FUNC(chan_opt_greeting)
4915 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4918 static MODCMD_FUNC(chan_opt_usergreeting)
4920 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4923 static MODCMD_FUNC(chan_opt_modes)
4925 struct mod_chanmode *new_modes;
4926 char modes[MODELEN];
4930 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4932 reply("CSMSG_NO_ACCESS");
4935 if(argv[1][0] == '*' && argv[1][1] == 0)
4937 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4939 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4941 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4944 else if(new_modes->argc > 1)
4946 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4947 mod_chanmode_free(new_modes);
4952 channel->channel_info->modes = *new_modes;
4953 modcmd_chanmode_announce(new_modes);
4954 mod_chanmode_free(new_modes);
4958 mod_chanmode_format(&channel->channel_info->modes, modes);
4960 reply("CSMSG_SET_MODES", modes);
4962 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4966 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4968 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4970 struct chanData *cData = channel->channel_info;
4975 /* Set flag according to value. */
4976 if(enabled_string(argv[1]))
4978 cData->flags |= mask;
4981 else if(disabled_string(argv[1]))
4983 cData->flags &= ~mask;
4988 reply("MSG_INVALID_BINARY", argv[1]);
4994 /* Find current option value. */
4995 value = (cData->flags & mask) ? 1 : 0;
4999 reply(name, user_find_message(user, "MSG_ON"));
5001 reply(name, user_find_message(user, "MSG_OFF"));
5005 static MODCMD_FUNC(chan_opt_nodelete)
5007 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5009 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5013 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5016 static MODCMD_FUNC(chan_opt_dynlimit)
5018 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5021 static MODCMD_FUNC(chan_opt_offchannel)
5023 struct chanData *cData = channel->channel_info;
5028 /* Set flag according to value. */
5029 if(enabled_string(argv[1]))
5031 if(!IsOffChannel(cData))
5032 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5033 cData->flags |= CHANNEL_OFFCHANNEL;
5036 else if(disabled_string(argv[1]))
5038 if(IsOffChannel(cData))
5040 struct mod_chanmode change;
5041 mod_chanmode_init(&change);
5043 change.args[0].mode = MODE_CHANOP;
5044 change.args[0].u.member = AddChannelUser(chanserv, channel);
5045 mod_chanmode_announce(chanserv, channel, &change);
5047 cData->flags &= ~CHANNEL_OFFCHANNEL;
5052 reply("MSG_INVALID_BINARY", argv[1]);
5058 /* Find current option value. */
5059 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5063 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5065 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5069 static MODCMD_FUNC(chan_opt_defaults)
5071 struct userData *uData;
5072 struct chanData *cData;
5073 const char *confirm;
5074 enum levelOption lvlOpt;
5075 enum charOption chOpt;
5077 cData = channel->channel_info;
5078 uData = GetChannelUser(cData, user->handle_info);
5079 if(!uData || (uData->access < UL_OWNER))
5081 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5084 confirm = make_confirmation_string(uData);
5085 if((argc < 2) || strcmp(argv[1], confirm))
5087 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5090 cData->flags = CHANNEL_DEFAULT_FLAGS;
5091 cData->modes = chanserv_conf.default_modes;
5092 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5093 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5094 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5095 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5096 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5101 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5103 struct chanData *cData = channel->channel_info;
5104 struct userData *uData;
5105 unsigned short value;
5109 if(!check_user_level(channel, user, option, 1, 1))
5111 reply("CSMSG_CANNOT_SET");
5114 value = user_level_from_name(argv[1], UL_OWNER+1);
5115 if(!value && strcmp(argv[1], "0"))
5117 reply("CSMSG_INVALID_ACCESS", argv[1]);
5120 uData = GetChannelUser(cData, user->handle_info);
5121 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5123 reply("CSMSG_BAD_SETLEVEL");
5129 if(value > cData->lvlOpts[lvlGiveOps])
5131 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5136 if(value < cData->lvlOpts[lvlGiveVoice])
5138 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5143 /* This test only applies to owners, since non-owners
5144 * trying to set an option to above their level get caught
5145 * by the CSMSG_BAD_SETLEVEL test above.
5147 if(value > uData->access)
5149 reply("CSMSG_BAD_SETTERS");
5156 cData->lvlOpts[option] = value;
5158 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5162 static MODCMD_FUNC(chan_opt_enfops)
5164 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5167 static MODCMD_FUNC(chan_opt_giveops)
5169 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5172 static MODCMD_FUNC(chan_opt_enfmodes)
5174 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5177 static MODCMD_FUNC(chan_opt_enftopic)
5179 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5182 static MODCMD_FUNC(chan_opt_pubcmd)
5184 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5187 static MODCMD_FUNC(chan_opt_setters)
5189 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5192 static MODCMD_FUNC(chan_opt_ctcpusers)
5194 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5197 static MODCMD_FUNC(chan_opt_userinfo)
5199 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5202 static MODCMD_FUNC(chan_opt_givevoice)
5204 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5207 static MODCMD_FUNC(chan_opt_topicsnarf)
5209 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5212 static MODCMD_FUNC(chan_opt_inviteme)
5214 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5218 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5220 struct chanData *cData = channel->channel_info;
5221 int count = charOptions[option].count, index;
5225 index = atoi(argv[1]);
5227 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5229 reply("CSMSG_INVALID_NUMERIC", index);
5230 /* Show possible values. */
5231 for(index = 0; index < count; index++)
5232 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5236 cData->chOpts[option] = charOptions[option].values[index].value;
5240 /* Find current option value. */
5243 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5247 /* Somehow, the option value is corrupt; reset it to the default. */
5248 cData->chOpts[option] = charOptions[option].default_value;
5253 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5257 static MODCMD_FUNC(chan_opt_protect)
5259 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5262 static MODCMD_FUNC(chan_opt_toys)
5264 return channel_multiple_option(chToys, CSFUNC_ARGS);
5267 static MODCMD_FUNC(chan_opt_ctcpreaction)
5269 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5272 static MODCMD_FUNC(chan_opt_topicrefresh)
5274 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5277 static struct svccmd_list set_shows_list;
5280 handle_svccmd_unbind(struct svccmd *target) {
5282 for(ii=0; ii<set_shows_list.used; ++ii)
5283 if(target == set_shows_list.list[ii])
5284 set_shows_list.used = 0;
5287 static CHANSERV_FUNC(cmd_set)
5289 struct svccmd *subcmd;
5293 /* Check if we need to (re-)initialize set_shows_list. */
5294 if(!set_shows_list.used)
5296 if(!set_shows_list.size)
5298 set_shows_list.size = chanserv_conf.set_shows->used;
5299 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5301 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5303 const char *name = chanserv_conf.set_shows->list[ii];
5304 sprintf(buf, "%s %s", argv[0], name);
5305 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5308 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5311 svccmd_list_append(&set_shows_list, subcmd);
5317 reply("CSMSG_CHANNEL_OPTIONS");
5318 for(ii = 0; ii < set_shows_list.used; ii++)
5320 subcmd = set_shows_list.list[ii];
5321 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5326 sprintf(buf, "%s %s", argv[0], argv[1]);
5327 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5330 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5333 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5335 reply("CSMSG_NO_ACCESS");
5339 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5343 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5345 struct userData *uData;
5347 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5350 reply("CSMSG_NOT_USER", channel->name);
5356 /* Just show current option value. */
5358 else if(enabled_string(argv[1]))
5360 uData->flags |= mask;
5362 else if(disabled_string(argv[1]))
5364 uData->flags &= ~mask;
5368 reply("MSG_INVALID_BINARY", argv[1]);
5372 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5376 static MODCMD_FUNC(user_opt_noautoop)
5378 struct userData *uData;
5380 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5383 reply("CSMSG_NOT_USER", channel->name);
5386 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5387 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5389 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5392 static MODCMD_FUNC(user_opt_autoinvite)
5394 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5397 static MODCMD_FUNC(user_opt_info)
5399 struct userData *uData;
5402 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5406 /* If they got past the command restrictions (which require access)
5407 * but fail this test, we have some fool with security override on.
5409 reply("CSMSG_NOT_USER", channel->name);
5416 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5417 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5419 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5422 bp = strcspn(infoline, "\001");
5425 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5430 if(infoline[0] == '*' && infoline[1] == 0)
5433 uData->info = strdup(infoline);
5436 reply("CSMSG_USET_INFO", uData->info);
5438 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5442 struct svccmd_list uset_shows_list;
5444 static CHANSERV_FUNC(cmd_uset)
5446 struct svccmd *subcmd;
5450 /* Check if we need to (re-)initialize uset_shows_list. */
5451 if(!uset_shows_list.used)
5455 "NoAutoOp", "AutoInvite", "Info"
5458 if(!uset_shows_list.size)
5460 uset_shows_list.size = ArrayLength(options);
5461 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5463 for(ii = 0; ii < ArrayLength(options); ii++)
5465 const char *name = options[ii];
5466 sprintf(buf, "%s %s", argv[0], name);
5467 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5470 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5473 svccmd_list_append(&uset_shows_list, subcmd);
5479 /* Do this so options are presented in a consistent order. */
5480 reply("CSMSG_USER_OPTIONS");
5481 for(ii = 0; ii < uset_shows_list.used; ii++)
5482 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5486 sprintf(buf, "%s %s", argv[0], argv[1]);
5487 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5490 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5494 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5497 static CHANSERV_FUNC(cmd_giveownership)
5499 struct handle_info *new_owner_hi;
5500 struct userData *new_owner, *curr_user;
5501 struct chanData *cData = channel->channel_info;
5502 struct do_not_register *dnr;
5503 const char *confirm;
5505 unsigned short co_access;
5506 char reason[MAXLEN];
5509 curr_user = GetChannelAccess(cData, user->handle_info);
5510 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5511 if(!curr_user || (curr_user->access != UL_OWNER))
5513 struct userData *owner = NULL;
5514 for(curr_user = channel->channel_info->users;
5516 curr_user = curr_user->next)
5518 if(curr_user->access != UL_OWNER)
5522 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5529 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5531 char delay[INTERVALLEN];
5532 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5533 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5536 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5538 if(new_owner_hi == user->handle_info)
5540 reply("CSMSG_NO_TRANSFER_SELF");
5543 new_owner = GetChannelAccess(cData, new_owner_hi);
5548 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5552 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5556 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5558 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5561 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5562 if(!IsHelping(user))
5563 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5565 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5568 if(curr_user && !force && curr_user->access <= UL_OWNER)
5570 confirm = make_confirmation_string(curr_user);
5571 if(!force && ((argc < 3) || strcmp(argv[2], confirm)))
5573 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5577 if(new_owner->access >= UL_COOWNER)
5578 co_access = new_owner->access;
5580 co_access = UL_COOWNER;
5581 new_owner->access = UL_OWNER;
5583 curr_user->access = co_access;
5584 cData->ownerTransfer = now;
5585 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5586 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5587 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5591 static CHANSERV_FUNC(cmd_suspend)
5593 struct handle_info *hi;
5594 struct userData *self, *target;
5597 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5598 self = GetChannelUser(channel->channel_info, user->handle_info);
5599 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5601 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5604 if(target->access >= self->access)
5606 reply("MSG_USER_OUTRANKED", hi->handle);
5609 if(target->flags & USER_SUSPENDED)
5611 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5616 target->present = 0;
5619 target->flags |= USER_SUSPENDED;
5620 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5624 static CHANSERV_FUNC(cmd_unsuspend)
5626 struct handle_info *hi;
5627 struct userData *self, *target;
5630 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5631 self = GetChannelUser(channel->channel_info, user->handle_info);
5632 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5634 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5637 if(target->access >= self->access)
5639 reply("MSG_USER_OUTRANKED", hi->handle);
5642 if(!(target->flags & USER_SUSPENDED))
5644 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5647 target->flags &= ~USER_SUSPENDED;
5648 scan_user_presence(target, NULL);
5649 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5653 static MODCMD_FUNC(cmd_deleteme)
5655 struct handle_info *hi;
5656 struct userData *target;
5657 const char *confirm_string;
5658 unsigned short access;
5661 hi = user->handle_info;
5662 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5664 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5667 if(target->access == UL_OWNER)
5669 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5672 confirm_string = make_confirmation_string(target);
5673 if((argc < 2) || strcmp(argv[1], confirm_string))
5675 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5678 access = target->access;
5679 channel_name = strdup(channel->name);
5680 del_channel_user(target, 1);
5681 reply("CSMSG_DELETED_YOU", access, channel_name);
5687 chanserv_refresh_topics(UNUSED_ARG(void *data))
5689 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5690 struct chanData *cData;
5693 for(cData = channelList; cData; cData = cData->next)
5695 if(IsSuspended(cData))
5697 opt = cData->chOpts[chTopicRefresh];
5700 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5703 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5704 cData->last_refresh = refresh_num;
5706 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5709 static CHANSERV_FUNC(cmd_unf)
5713 char response[MAXLEN];
5714 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5715 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5716 irc_privmsg(cmd->parent->bot, channel->name, response);
5719 reply("CSMSG_UNF_RESPONSE");
5723 static CHANSERV_FUNC(cmd_ping)
5727 char response[MAXLEN];
5728 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5729 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5730 irc_privmsg(cmd->parent->bot, channel->name, response);
5733 reply("CSMSG_PING_RESPONSE");
5737 static CHANSERV_FUNC(cmd_wut)
5741 char response[MAXLEN];
5742 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5743 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5744 irc_privmsg(cmd->parent->bot, channel->name, response);
5747 reply("CSMSG_WUT_RESPONSE");
5751 static CHANSERV_FUNC(cmd_8ball)
5753 unsigned int i, j, accum;
5758 for(i=1; i<argc; i++)
5759 for(j=0; argv[i][j]; j++)
5760 accum = (accum << 5) - accum + toupper(argv[i][j]);
5761 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5764 char response[MAXLEN];
5765 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5766 irc_privmsg(cmd->parent->bot, channel->name, response);
5769 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5773 static CHANSERV_FUNC(cmd_d)
5775 unsigned long sides, count, modifier, ii, total;
5776 char response[MAXLEN], *sep;
5780 if((count = strtoul(argv[1], &sep, 10)) < 1)
5790 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5791 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5795 else if((sep[0] == '-') && isdigit(sep[1]))
5796 modifier = strtoul(sep, NULL, 10);
5797 else if((sep[0] == '+') && isdigit(sep[1]))
5798 modifier = strtoul(sep+1, NULL, 10);
5805 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5810 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5813 for(total = ii = 0; ii < count; ++ii)
5814 total += (rand() % sides) + 1;
5817 if((count > 1) || modifier)
5819 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5820 sprintf(response, fmt, total, count, sides, modifier);
5824 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5825 sprintf(response, fmt, total, sides);
5828 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5830 send_message_type(4, user, cmd->parent->bot, "%s", response);
5834 static CHANSERV_FUNC(cmd_huggle)
5836 /* CTCP must be via PRIVMSG, never notice */
5838 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5840 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5845 chanserv_adjust_limit(void *data)
5847 struct mod_chanmode change;
5848 struct chanData *cData = data;
5849 struct chanNode *channel = cData->channel;
5852 if(IsSuspended(cData))
5855 cData->limitAdjusted = now;
5856 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5857 if(cData->modes.modes_set & MODE_LIMIT)
5859 if(limit > cData->modes.new_limit)
5860 limit = cData->modes.new_limit;
5861 else if(limit == cData->modes.new_limit)
5865 mod_chanmode_init(&change);
5866 change.modes_set = MODE_LIMIT;
5867 change.new_limit = limit;
5868 mod_chanmode_announce(chanserv, channel, &change);
5872 handle_new_channel(struct chanNode *channel)
5874 struct chanData *cData;
5876 if(!(cData = channel->channel_info))
5879 if(cData->modes.modes_set || cData->modes.modes_clear)
5880 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5882 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5883 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5886 /* Welcome to my worst nightmare. Warning: Read (or modify)
5887 the code below at your own risk. */
5889 handle_join(struct modeNode *mNode)
5891 struct mod_chanmode change;
5892 struct userNode *user = mNode->user;
5893 struct chanNode *channel = mNode->channel;
5894 struct chanData *cData;
5895 struct userData *uData = NULL;
5896 struct banData *bData;
5897 struct handle_info *handle;
5898 unsigned int modes = 0, info = 0;
5901 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5904 cData = channel->channel_info;
5905 if(channel->members.used > cData->max)
5906 cData->max = channel->members.used;
5908 /* Check for bans. If they're joining through a ban, one of two
5910 * 1: Join during a netburst, by riding the break. Kick them
5911 * unless they have ops or voice in the channel.
5912 * 2: They're allowed to join through the ban (an invite in
5913 * ircu2.10, or a +e on Hybrid, or something).
5914 * If they're not joining through a ban, and the banlist is not
5915 * full, see if they're on the banlist for the channel. If so,
5918 if(user->uplink->burst && !mNode->modes)
5921 for(ii = 0; ii < channel->banlist.used; ii++)
5923 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5925 /* Riding a netburst. Naughty. */
5926 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5932 mod_chanmode_init(&change);
5934 if(channel->banlist.used < MAXBANS)
5936 /* Not joining through a ban. */
5937 for(bData = cData->bans;
5938 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5939 bData = bData->next);
5943 char kick_reason[MAXLEN];
5944 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5946 bData->triggered = now;
5947 if(bData != cData->bans)
5949 /* Shuffle the ban to the head of the list. */
5951 bData->next->prev = bData->prev;
5953 bData->prev->next = bData->next;
5956 bData->next = cData->bans;
5959 cData->bans->prev = bData;
5960 cData->bans = bData;
5963 change.args[0].mode = MODE_BAN;
5964 change.args[0].u.hostmask = bData->mask;
5965 mod_chanmode_announce(chanserv, channel, &change);
5966 KickChannelUser(user, channel, chanserv, kick_reason);
5971 /* ChanServ will not modify the limits in join-flooded channels.
5972 It will also skip DynLimit processing when the user (or srvx)
5973 is bursting in, because there are likely more incoming. */
5974 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5975 && !user->uplink->burst
5976 && !channel->join_flooded
5977 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5979 /* The user count has begun "bumping" into the channel limit,
5980 so set a timer to raise the limit a bit. Any previous
5981 timers are removed so three incoming users within the delay
5982 results in one limit change, not three. */
5984 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5985 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5988 if(channel->join_flooded)
5990 /* don't automatically give ops or voice during a join flood */
5992 else if(cData->lvlOpts[lvlGiveOps] == 0)
5993 modes |= MODE_CHANOP;
5994 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5995 modes |= MODE_VOICE;
5997 greeting = cData->greeting;
5998 if(user->handle_info)
6000 handle = user->handle_info;
6002 if(IsHelper(user) && !IsHelping(user))
6005 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6007 if(channel == chanserv_conf.support_channels.list[ii])
6009 HANDLE_SET_FLAG(user->handle_info, HELPING);
6015 uData = GetTrueChannelAccess(cData, handle);
6016 if(uData && !IsUserSuspended(uData))
6018 /* Ops and above were handled by the above case. */
6019 if(IsUserAutoOp(uData))
6021 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6022 modes |= MODE_CHANOP;
6023 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6024 modes |= MODE_VOICE;
6026 if(uData->access >= UL_PRESENT)
6027 cData->visited = now;
6028 if(cData->user_greeting)
6029 greeting = cData->user_greeting;
6031 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6032 && ((now - uData->seen) >= chanserv_conf.info_delay)
6040 /* If user joining normally (not during burst), apply op or voice,
6041 * and send greeting/userinfo as appropriate.
6043 if(!user->uplink->burst)
6047 if(modes & MODE_CHANOP)
6048 modes &= ~MODE_VOICE;
6049 change.args[0].mode = modes;
6050 change.args[0].u.member = mNode;
6051 mod_chanmode_announce(chanserv, channel, &change);
6054 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6056 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6062 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6064 struct mod_chanmode change;
6065 struct userData *channel;
6066 unsigned int ii, jj;
6068 if(!user->handle_info)
6071 mod_chanmode_init(&change);
6073 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6075 struct chanNode *cn;
6076 struct modeNode *mn;
6077 if(IsUserSuspended(channel)
6078 || IsSuspended(channel->channel)
6079 || !(cn = channel->channel->channel))
6082 mn = GetUserMode(cn, user);
6085 if(!IsUserSuspended(channel)
6086 && IsUserAutoInvite(channel)
6087 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6089 && !user->uplink->burst)
6090 irc_invite(chanserv, user, cn);
6094 if(channel->access >= UL_PRESENT)
6095 channel->channel->visited = now;
6097 if(IsUserAutoOp(channel))
6099 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6100 change.args[0].mode = MODE_CHANOP;
6101 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6102 change.args[0].mode = MODE_VOICE;
6104 change.args[0].mode = 0;
6105 change.args[0].u.member = mn;
6106 if(change.args[0].mode)
6107 mod_chanmode_announce(chanserv, cn, &change);
6110 channel->seen = now;
6111 channel->present = 1;
6114 for(ii = 0; ii < user->channels.used; ++ii)
6116 struct chanNode *channel = user->channels.list[ii]->channel;
6117 struct banData *ban;
6119 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6120 || !channel->channel_info
6121 || IsSuspended(channel->channel_info))
6123 for(jj = 0; jj < channel->banlist.used; ++jj)
6124 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6126 if(jj < channel->banlist.used)
6128 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6130 char kick_reason[MAXLEN];
6131 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6133 change.args[0].mode = MODE_BAN;
6134 change.args[0].u.hostmask = ban->mask;
6135 mod_chanmode_announce(chanserv, channel, &change);
6136 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6137 KickChannelUser(user, channel, chanserv, kick_reason);
6138 ban->triggered = now;
6143 if(IsSupportHelper(user))
6145 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6147 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6149 HANDLE_SET_FLAG(user->handle_info, HELPING);
6157 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6159 struct chanData *cData;
6160 struct userData *uData;
6162 cData = mn->channel->channel_info;
6163 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6166 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6168 /* Allow for a bit of padding so that the limit doesn't
6169 track the user count exactly, which could get annoying. */
6170 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6172 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6173 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6177 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6179 scan_user_presence(uData, mn->user);
6181 if (uData->access >= UL_PRESENT)
6182 cData->visited = now;
6185 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6187 unsigned int ii, jj;
6188 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6190 for(jj = 0; jj < mn->user->channels.used; ++jj)
6191 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6193 if(jj < mn->user->channels.used)
6196 if(ii == chanserv_conf.support_channels.used)
6197 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6202 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6204 struct userData *uData;
6206 if(!channel->channel_info || !kicker || IsService(kicker)
6207 || (kicker == victim) || IsSuspended(channel->channel_info)
6208 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6211 if(protect_user(victim, kicker, channel->channel_info))
6213 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6214 KickChannelUser(kicker, channel, chanserv, reason);
6217 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6222 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6224 struct chanData *cData;
6226 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6229 cData = channel->channel_info;
6230 if(bad_topic(channel, user, channel->topic))
6232 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6233 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6234 SetChannelTopic(channel, chanserv, old_topic, 1);
6235 else if(cData->topic)
6236 SetChannelTopic(channel, chanserv, cData->topic, 1);
6239 /* With topicsnarf, grab the topic and save it as the default topic. */
6240 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6243 cData->topic = strdup(channel->topic);
6249 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6251 struct mod_chanmode *bounce = NULL;
6252 unsigned int bnc, ii;
6255 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6258 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6259 && mode_lock_violated(&channel->channel_info->modes, change))
6261 char correct[MAXLEN];
6262 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6263 mod_chanmode_format(&channel->channel_info->modes, correct);
6264 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6266 for(ii = bnc = 0; ii < change->argc; ++ii)
6268 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6270 const struct userNode *victim = change->args[ii].u.member->user;
6271 if(!protect_user(victim, user, channel->channel_info))
6274 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6277 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6278 bounce->args[bnc].u.member = GetUserMode(channel, user);
6279 if(bounce->args[bnc].u.member)
6283 bounce->args[bnc].mode = MODE_CHANOP;
6284 bounce->args[bnc].u.member = change->args[ii].u.member;
6286 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6288 else if(change->args[ii].mode & MODE_CHANOP)
6290 const struct userNode *victim = change->args[ii].u.member->user;
6291 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6294 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6295 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6296 bounce->args[bnc].u.member = change->args[ii].u.member;
6299 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6301 const char *ban = change->args[ii].u.hostmask;
6302 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6305 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6306 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6307 bounce->args[bnc].u.hostmask = strdup(ban);
6309 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6314 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6315 mod_chanmode_announce(chanserv, channel, bounce);
6316 for(ii = 0; ii < change->argc; ++ii)
6317 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6318 free((char*)bounce->args[ii].u.hostmask);
6319 mod_chanmode_free(bounce);
6324 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6326 struct chanNode *channel;
6327 struct banData *bData;
6328 struct mod_chanmode change;
6329 unsigned int ii, jj;
6330 char kick_reason[MAXLEN];
6332 mod_chanmode_init(&change);
6334 change.args[0].mode = MODE_BAN;
6335 for(ii = 0; ii < user->channels.used; ++ii)
6337 channel = user->channels.list[ii]->channel;
6338 /* Need not check for bans if they're opped or voiced. */
6339 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6341 /* Need not check for bans unless channel registration is active. */
6342 if(!channel->channel_info || IsSuspended(channel->channel_info))
6344 /* Look for a matching ban already on the channel. */
6345 for(jj = 0; jj < channel->banlist.used; ++jj)
6346 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6348 /* Need not act if we found one. */
6349 if(jj < channel->banlist.used)
6351 /* Look for a matching ban in this channel. */
6352 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6354 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6356 change.args[0].u.hostmask = bData->mask;
6357 mod_chanmode_announce(chanserv, channel, &change);
6358 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6359 KickChannelUser(user, channel, chanserv, kick_reason);
6360 bData->triggered = now;
6361 break; /* we don't need to check any more bans in the channel */
6366 static void handle_rename(struct handle_info *handle, const char *old_handle)
6368 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6372 dict_remove2(handle_dnrs, old_handle, 1);
6373 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6374 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6379 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6381 struct userNode *h_user;
6383 if(handle->channels)
6385 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6386 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6388 while(handle->channels)
6389 del_channel_user(handle->channels, 1);
6394 handle_server_link(UNUSED_ARG(struct server *server))
6396 struct chanData *cData;
6398 for(cData = channelList; cData; cData = cData->next)
6400 if(!IsSuspended(cData))
6401 cData->may_opchan = 1;
6402 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6403 && !cData->channel->join_flooded
6404 && ((cData->channel->limit - cData->channel->members.used)
6405 < chanserv_conf.adjust_threshold))
6407 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6408 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6414 chanserv_conf_read(void)
6418 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6419 struct mod_chanmode *change;
6420 struct string_list *strlist;
6421 struct chanNode *chan;
6424 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6426 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6429 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6430 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6431 chanserv_conf.support_channels.used = 0;
6432 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6434 for(ii = 0; ii < strlist->used; ++ii)
6436 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6439 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6441 channelList_append(&chanserv_conf.support_channels, chan);
6444 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6447 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6450 chan = AddChannel(str, now, str2, NULL);
6452 channelList_append(&chanserv_conf.support_channels, chan);
6454 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6455 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6456 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6457 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6458 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6459 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6460 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6461 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6462 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6463 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6464 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6465 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6466 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6467 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6468 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6469 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6470 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6471 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6472 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6473 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6474 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6475 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6476 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6478 NickChange(chanserv, str, 0);
6479 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6480 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6481 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6482 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6483 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6484 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6485 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6486 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6487 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6488 chanserv_conf.max_owned = str ? atoi(str) : 5;
6489 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6490 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6491 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6492 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6493 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6494 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6495 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6498 safestrncpy(mode_line, str, sizeof(mode_line));
6499 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6500 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6501 && (change->argc < 2))
6503 chanserv_conf.default_modes = *change;
6504 mod_chanmode_free(change);
6506 free_string_list(chanserv_conf.set_shows);
6507 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6509 strlist = string_list_copy(strlist);
6512 static const char *list[] = {
6513 /* free form text */
6514 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6515 /* options based on user level */
6516 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6517 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6518 /* multiple choice options */
6519 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6520 /* binary options */
6521 "DynLimit", "NoDelete",
6526 strlist = alloc_string_list(ArrayLength(list)-1);
6527 for(ii=0; list[ii]; ii++)
6528 string_list_append(strlist, strdup(list[ii]));
6530 chanserv_conf.set_shows = strlist;
6531 /* We don't look things up now, in case the list refers to options
6532 * defined by modules initialized after this point. Just mark the
6533 * function list as invalid, so it will be initialized.
6535 set_shows_list.used = 0;
6536 free_string_list(chanserv_conf.eightball);
6537 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6540 strlist = string_list_copy(strlist);
6544 strlist = alloc_string_list(4);
6545 string_list_append(strlist, strdup("Yes."));
6546 string_list_append(strlist, strdup("No."));
6547 string_list_append(strlist, strdup("Maybe so."));
6549 chanserv_conf.eightball = strlist;
6550 free_string_list(chanserv_conf.old_ban_names);
6551 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6553 strlist = string_list_copy(strlist);
6555 strlist = alloc_string_list(2);
6556 chanserv_conf.old_ban_names = strlist;
6557 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6558 off_channel = str ? atoi(str) : 0;
6562 chanserv_note_type_read(const char *key, struct record_data *rd)
6565 struct note_type *ntype;
6568 if(!(obj = GET_RECORD_OBJECT(rd)))
6570 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6573 if(!(ntype = chanserv_create_note_type(key)))
6575 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6579 /* Figure out set access */
6580 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6582 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6583 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6585 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6587 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6588 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6590 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6592 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6596 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6597 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6598 ntype->set_access.min_opserv = 0;
6601 /* Figure out visibility */
6602 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6603 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6604 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6605 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6606 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6607 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6608 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6609 ntype->visible_type = NOTE_VIS_ALL;
6611 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6613 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6614 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6618 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6620 struct handle_info *handle;
6621 struct userData *uData;
6622 char *seen, *inf, *flags;
6624 unsigned short access;
6626 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6628 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6632 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6633 if(access > UL_OWNER)
6635 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6639 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6640 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6641 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6642 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6643 handle = get_handle_info(key);
6646 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6650 uData = add_channel_user(chan, handle, access, last_seen, inf);
6651 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6655 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6657 struct banData *bData;
6658 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6659 time_t set_time, triggered_time, expires_time;
6661 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6663 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6667 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6668 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6669 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6670 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6671 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6672 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6673 if (!reason || !owner)
6676 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6677 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6679 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6681 expires_time = set_time + atoi(s_duration);
6685 if(!reason || (expires_time && (expires_time < now)))
6688 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6691 static struct suspended *
6692 chanserv_read_suspended(dict_t obj)
6694 struct suspended *suspended = calloc(1, sizeof(*suspended));
6698 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6699 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6700 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6701 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6702 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6703 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6704 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6705 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6706 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6707 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6712 chanserv_channel_read(const char *key, struct record_data *hir)
6714 struct suspended *suspended;
6715 struct mod_chanmode *modes;
6716 struct chanNode *cNode;
6717 struct chanData *cData;
6718 struct dict *channel, *obj;
6719 char *str, *argv[10];
6723 channel = hir->d.object;
6725 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6728 cNode = AddChannel(key, now, NULL, NULL);
6731 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6734 cData = register_channel(cNode, str);
6737 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6741 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6743 enum levelOption lvlOpt;
6744 enum charOption chOpt;
6746 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6747 cData->flags = atoi(str);
6749 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6751 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6753 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6754 else if(levelOptions[lvlOpt].old_flag)
6756 if(cData->flags & levelOptions[lvlOpt].old_flag)
6757 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6759 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6763 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6765 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6767 cData->chOpts[chOpt] = str[0];
6770 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6772 enum levelOption lvlOpt;
6773 enum charOption chOpt;
6776 cData->flags = base64toint(str, 5);
6777 count = strlen(str += 5);
6778 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6781 if(levelOptions[lvlOpt].old_flag)
6783 if(cData->flags & levelOptions[lvlOpt].old_flag)
6784 lvl = levelOptions[lvlOpt].flag_value;
6786 lvl = levelOptions[lvlOpt].default_value;
6788 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6790 case 'c': lvl = UL_COOWNER; break;
6791 case 'm': lvl = UL_MASTER; break;
6792 case 'n': lvl = UL_OWNER+1; break;
6793 case 'o': lvl = UL_OP; break;
6794 case 'p': lvl = UL_PEON; break;
6795 case 'w': lvl = UL_OWNER; break;
6796 default: lvl = 0; break;
6798 cData->lvlOpts[lvlOpt] = lvl;
6800 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6801 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6804 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6806 suspended = chanserv_read_suspended(obj);
6807 cData->suspended = suspended;
6808 suspended->cData = cData;
6809 /* We could use suspended->expires and suspended->revoked to
6810 * set the CHANNEL_SUSPENDED flag, but we don't. */
6812 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6814 suspended = calloc(1, sizeof(*suspended));
6815 suspended->issued = 0;
6816 suspended->revoked = 0;
6817 suspended->suspender = strdup(str);
6818 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6819 suspended->expires = str ? atoi(str) : 0;
6820 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6821 suspended->reason = strdup(str ? str : "No reason");
6822 suspended->previous = NULL;
6823 cData->suspended = suspended;
6824 suspended->cData = cData;
6828 cData->flags &= ~CHANNEL_SUSPENDED;
6829 suspended = NULL; /* to squelch a warning */
6832 if(IsSuspended(cData)) {
6833 if(suspended->expires > now)
6834 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6835 else if(suspended->expires)
6836 cData->flags &= ~CHANNEL_SUSPENDED;
6839 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6840 struct mod_chanmode change;
6841 mod_chanmode_init(&change);
6843 change.args[0].mode = MODE_CHANOP;
6844 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6845 mod_chanmode_announce(chanserv, cNode, &change);
6848 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6849 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6850 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6851 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6852 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6853 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6854 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6855 cData->max = str ? atoi(str) : 0;
6856 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6857 cData->greeting = str ? strdup(str) : NULL;
6858 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6859 cData->user_greeting = str ? strdup(str) : NULL;
6860 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6861 cData->topic_mask = str ? strdup(str) : NULL;
6862 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6863 cData->topic = str ? strdup(str) : NULL;
6865 if(!IsSuspended(cData)
6866 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6867 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6868 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6869 cData->modes = *modes;
6871 cData->modes.modes_set |= MODE_REGISTERED;
6872 if(cData->modes.argc > 1)
6873 cData->modes.argc = 1;
6874 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6875 mod_chanmode_free(modes);
6878 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6879 for(it = dict_first(obj); it; it = iter_next(it))
6880 user_read_helper(iter_key(it), iter_data(it), cData);
6882 if(!cData->users && !IsProtected(cData))
6884 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6885 unregister_channel(cData, "has empty user list.");
6889 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6890 for(it = dict_first(obj); it; it = iter_next(it))
6891 ban_read_helper(iter_key(it), iter_data(it), cData);
6893 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6894 for(it = dict_first(obj); it; it = iter_next(it))
6896 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6897 struct record_data *rd = iter_data(it);
6898 const char *note, *setter;
6900 if(rd->type != RECDB_OBJECT)
6902 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6906 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6908 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6910 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6914 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6915 if(!setter) setter = "<unknown>";
6916 chanserv_add_channel_note(cData, ntype, setter, note);
6924 chanserv_dnr_read(const char *key, struct record_data *hir)
6926 const char *setter, *reason, *str;
6927 struct do_not_register *dnr;
6929 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6932 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6935 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6938 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6941 dnr = chanserv_add_dnr(key, setter, reason);
6944 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6946 dnr->set = atoi(str);
6952 chanserv_saxdb_read(struct dict *database)
6954 struct dict *section;
6957 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6958 for(it = dict_first(section); it; it = iter_next(it))
6959 chanserv_note_type_read(iter_key(it), iter_data(it));
6961 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6962 for(it = dict_first(section); it; it = iter_next(it))
6963 chanserv_channel_read(iter_key(it), iter_data(it));
6965 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6966 for(it = dict_first(section); it; it = iter_next(it))
6967 chanserv_dnr_read(iter_key(it), iter_data(it));
6973 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6975 int high_present = 0;
6976 saxdb_start_record(ctx, KEY_USERS, 1);
6977 for(; uData; uData = uData->next)
6979 if((uData->access >= UL_PRESENT) && uData->present)
6981 saxdb_start_record(ctx, uData->handle->handle, 0);
6982 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6983 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6985 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6987 saxdb_write_string(ctx, KEY_INFO, uData->info);
6988 saxdb_end_record(ctx);
6990 saxdb_end_record(ctx);
6991 return high_present;
6995 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6999 saxdb_start_record(ctx, KEY_BANS, 1);
7000 for(; bData; bData = bData->next)
7002 saxdb_start_record(ctx, bData->mask, 0);
7003 saxdb_write_int(ctx, KEY_SET, bData->set);
7004 if(bData->triggered)
7005 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7007 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7009 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7011 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7012 saxdb_end_record(ctx);
7014 saxdb_end_record(ctx);
7018 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7020 saxdb_start_record(ctx, name, 0);
7021 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7022 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7024 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7026 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7028 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7030 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7031 saxdb_end_record(ctx);
7035 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7039 enum levelOption lvlOpt;
7040 enum charOption chOpt;
7042 saxdb_start_record(ctx, channel->channel->name, 1);
7044 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7045 saxdb_write_int(ctx, KEY_MAX, channel->max);
7047 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7048 if(channel->registrar)
7049 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7050 if(channel->greeting)
7051 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7052 if(channel->user_greeting)
7053 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7054 if(channel->topic_mask)
7055 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7056 if(channel->suspended)
7057 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7059 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7060 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7061 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7062 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7063 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7065 buf[0] = channel->chOpts[chOpt];
7067 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7069 saxdb_end_record(ctx);
7071 if(channel->modes.modes_set || channel->modes.modes_clear)
7073 mod_chanmode_format(&channel->modes, buf);
7074 saxdb_write_string(ctx, KEY_MODES, buf);
7077 high_present = chanserv_write_users(ctx, channel->users);
7078 chanserv_write_bans(ctx, channel->bans);
7080 if(dict_size(channel->notes))
7084 saxdb_start_record(ctx, KEY_NOTES, 1);
7085 for(it = dict_first(channel->notes); it; it = iter_next(it))
7087 struct note *note = iter_data(it);
7088 saxdb_start_record(ctx, iter_key(it), 0);
7089 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7090 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7091 saxdb_end_record(ctx);
7093 saxdb_end_record(ctx);
7096 if(channel->ownerTransfer)
7097 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7098 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7099 saxdb_end_record(ctx);
7103 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7107 saxdb_start_record(ctx, ntype->name, 0);
7108 switch(ntype->set_access_type)
7110 case NOTE_SET_CHANNEL_ACCESS:
7111 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7113 case NOTE_SET_CHANNEL_SETTER:
7114 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7116 case NOTE_SET_PRIVILEGED: default:
7117 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7120 switch(ntype->visible_type)
7122 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7123 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7124 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7126 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7127 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7128 saxdb_end_record(ctx);
7132 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7134 struct do_not_register *dnr;
7137 for(it = dict_first(dnrs); it; it = iter_next(it))
7139 dnr = iter_data(it);
7140 saxdb_start_record(ctx, dnr->chan_name, 0);
7142 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7143 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7144 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7145 saxdb_end_record(ctx);
7150 chanserv_saxdb_write(struct saxdb_context *ctx)
7153 struct chanData *channel;
7156 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7157 for(it = dict_first(note_types); it; it = iter_next(it))
7158 chanserv_write_note_type(ctx, iter_data(it));
7159 saxdb_end_record(ctx);
7162 saxdb_start_record(ctx, KEY_DNR, 1);
7163 write_dnrs_helper(ctx, handle_dnrs);
7164 write_dnrs_helper(ctx, plain_dnrs);
7165 write_dnrs_helper(ctx, mask_dnrs);
7166 saxdb_end_record(ctx);
7169 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7170 for(channel = channelList; channel; channel = channel->next)
7171 chanserv_write_channel(ctx, channel);
7172 saxdb_end_record(ctx);
7178 chanserv_db_cleanup(void) {
7180 unreg_part_func(handle_part);
7182 unregister_channel(channelList, "terminating.");
7183 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7184 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7185 free(chanserv_conf.support_channels.list);
7186 dict_delete(handle_dnrs);
7187 dict_delete(plain_dnrs);
7188 dict_delete(mask_dnrs);
7189 dict_delete(note_types);
7190 free_string_list(chanserv_conf.eightball);
7191 free_string_list(chanserv_conf.old_ban_names);
7192 free_string_list(chanserv_conf.set_shows);
7193 free(set_shows_list.list);
7194 free(uset_shows_list.list);
7197 struct userData *helper = helperList;
7198 helperList = helperList->next;
7203 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7204 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7205 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7208 init_chanserv(const char *nick)
7210 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7211 conf_register_reload(chanserv_conf_read);
7215 reg_server_link_func(handle_server_link);
7216 reg_new_channel_func(handle_new_channel);
7217 reg_join_func(handle_join);
7218 reg_part_func(handle_part);
7219 reg_kick_func(handle_kick);
7220 reg_topic_func(handle_topic);
7221 reg_mode_change_func(handle_mode);
7222 reg_nick_change_func(handle_nick_change);
7223 reg_auth_func(handle_auth);
7226 reg_handle_rename_func(handle_rename);
7227 reg_unreg_func(handle_unreg);
7229 handle_dnrs = dict_new();
7230 dict_set_free_data(handle_dnrs, free);
7231 plain_dnrs = dict_new();
7232 dict_set_free_data(plain_dnrs, free);
7233 mask_dnrs = dict_new();
7234 dict_set_free_data(mask_dnrs, free);
7236 reg_svccmd_unbind_func(handle_svccmd_unbind);
7237 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7238 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7239 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7240 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7241 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7242 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7243 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7244 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7245 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7247 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7248 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7250 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7251 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7252 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7253 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7254 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7256 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7257 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7258 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7259 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7260 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7262 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7263 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7264 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7265 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7267 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7268 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7269 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7270 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7271 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7272 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7273 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7274 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7276 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7277 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7278 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7279 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7280 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7281 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7282 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7283 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7284 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7285 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7286 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7287 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7288 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7289 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7291 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7292 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7293 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7294 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7295 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7297 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7298 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7300 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7301 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7302 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7303 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7304 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7305 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7306 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7307 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7308 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7309 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7310 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7312 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7313 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7315 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7316 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7317 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7318 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7320 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7321 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7322 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7323 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7324 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7326 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7327 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7328 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7329 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7330 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7331 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7333 /* Channel options */
7334 DEFINE_CHANNEL_OPTION(defaulttopic);
7335 DEFINE_CHANNEL_OPTION(topicmask);
7336 DEFINE_CHANNEL_OPTION(greeting);
7337 DEFINE_CHANNEL_OPTION(usergreeting);
7338 DEFINE_CHANNEL_OPTION(modes);
7339 DEFINE_CHANNEL_OPTION(enfops);
7340 DEFINE_CHANNEL_OPTION(giveops);
7341 DEFINE_CHANNEL_OPTION(protect);
7342 DEFINE_CHANNEL_OPTION(enfmodes);
7343 DEFINE_CHANNEL_OPTION(enftopic);
7344 DEFINE_CHANNEL_OPTION(pubcmd);
7345 DEFINE_CHANNEL_OPTION(givevoice);
7346 DEFINE_CHANNEL_OPTION(userinfo);
7347 DEFINE_CHANNEL_OPTION(dynlimit);
7348 DEFINE_CHANNEL_OPTION(topicsnarf);
7349 DEFINE_CHANNEL_OPTION(nodelete);
7350 DEFINE_CHANNEL_OPTION(toys);
7351 DEFINE_CHANNEL_OPTION(setters);
7352 DEFINE_CHANNEL_OPTION(topicrefresh);
7353 DEFINE_CHANNEL_OPTION(ctcpusers);
7354 DEFINE_CHANNEL_OPTION(ctcpreaction);
7355 DEFINE_CHANNEL_OPTION(inviteme);
7357 DEFINE_CHANNEL_OPTION(offchannel);
7358 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7360 /* Alias set topic to set defaulttopic for compatibility. */
7361 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7364 DEFINE_USER_OPTION(noautoop);
7365 DEFINE_USER_OPTION(autoinvite);
7366 DEFINE_USER_OPTION(info);
7368 /* Alias uset autovoice to uset autoop. */
7369 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7371 note_types = dict_new();
7372 dict_set_free_data(note_types, chanserv_deref_note_type);
7375 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7376 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7377 service_register(chanserv)->trigger = '!';
7378 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7380 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7382 if(chanserv_conf.channel_expire_frequency)
7383 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7385 if(chanserv_conf.refresh_period)
7387 time_t next_refresh;
7388 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7389 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7392 reg_exit_func(chanserv_db_cleanup);
7393 message_register_table(msgtab);