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 confirm = make_confirmation_string(curr_user);
5569 if(!force && ((argc < 3) || strcmp(argv[2], confirm)))
5571 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5574 if(new_owner->access >= UL_COOWNER)
5575 co_access = new_owner->access;
5577 co_access = UL_COOWNER;
5578 new_owner->access = UL_OWNER;
5580 curr_user->access = co_access;
5581 cData->ownerTransfer = now;
5582 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5583 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5584 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5588 static CHANSERV_FUNC(cmd_suspend)
5590 struct handle_info *hi;
5591 struct userData *self, *target;
5594 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5595 self = GetChannelUser(channel->channel_info, user->handle_info);
5596 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5598 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5601 if(target->access >= self->access)
5603 reply("MSG_USER_OUTRANKED", hi->handle);
5606 if(target->flags & USER_SUSPENDED)
5608 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5613 target->present = 0;
5616 target->flags |= USER_SUSPENDED;
5617 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5621 static CHANSERV_FUNC(cmd_unsuspend)
5623 struct handle_info *hi;
5624 struct userData *self, *target;
5627 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5628 self = GetChannelUser(channel->channel_info, user->handle_info);
5629 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5631 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5634 if(target->access >= self->access)
5636 reply("MSG_USER_OUTRANKED", hi->handle);
5639 if(!(target->flags & USER_SUSPENDED))
5641 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5644 target->flags &= ~USER_SUSPENDED;
5645 scan_user_presence(target, NULL);
5646 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5650 static MODCMD_FUNC(cmd_deleteme)
5652 struct handle_info *hi;
5653 struct userData *target;
5654 const char *confirm_string;
5655 unsigned short access;
5658 hi = user->handle_info;
5659 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5661 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5664 if(target->access == UL_OWNER)
5666 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5669 confirm_string = make_confirmation_string(target);
5670 if((argc < 2) || strcmp(argv[1], confirm_string))
5672 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5675 access = target->access;
5676 channel_name = strdup(channel->name);
5677 del_channel_user(target, 1);
5678 reply("CSMSG_DELETED_YOU", access, channel_name);
5684 chanserv_refresh_topics(UNUSED_ARG(void *data))
5686 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5687 struct chanData *cData;
5690 for(cData = channelList; cData; cData = cData->next)
5692 if(IsSuspended(cData))
5694 opt = cData->chOpts[chTopicRefresh];
5697 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5700 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5701 cData->last_refresh = refresh_num;
5703 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5706 static CHANSERV_FUNC(cmd_unf)
5710 char response[MAXLEN];
5711 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5712 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5713 irc_privmsg(cmd->parent->bot, channel->name, response);
5716 reply("CSMSG_UNF_RESPONSE");
5720 static CHANSERV_FUNC(cmd_ping)
5724 char response[MAXLEN];
5725 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5726 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5727 irc_privmsg(cmd->parent->bot, channel->name, response);
5730 reply("CSMSG_PING_RESPONSE");
5734 static CHANSERV_FUNC(cmd_wut)
5738 char response[MAXLEN];
5739 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5740 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5741 irc_privmsg(cmd->parent->bot, channel->name, response);
5744 reply("CSMSG_WUT_RESPONSE");
5748 static CHANSERV_FUNC(cmd_8ball)
5750 unsigned int i, j, accum;
5755 for(i=1; i<argc; i++)
5756 for(j=0; argv[i][j]; j++)
5757 accum = (accum << 5) - accum + toupper(argv[i][j]);
5758 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5761 char response[MAXLEN];
5762 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5763 irc_privmsg(cmd->parent->bot, channel->name, response);
5766 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5770 static CHANSERV_FUNC(cmd_d)
5772 unsigned long sides, count, modifier, ii, total;
5773 char response[MAXLEN], *sep;
5777 if((count = strtoul(argv[1], &sep, 10)) < 1)
5787 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5788 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5792 else if((sep[0] == '-') && isdigit(sep[1]))
5793 modifier = strtoul(sep, NULL, 10);
5794 else if((sep[0] == '+') && isdigit(sep[1]))
5795 modifier = strtoul(sep+1, NULL, 10);
5802 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5807 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5810 for(total = ii = 0; ii < count; ++ii)
5811 total += (rand() % sides) + 1;
5814 if((count > 1) || modifier)
5816 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5817 sprintf(response, fmt, total, count, sides, modifier);
5821 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5822 sprintf(response, fmt, total, sides);
5825 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5827 send_message_type(4, user, cmd->parent->bot, "%s", response);
5831 static CHANSERV_FUNC(cmd_huggle)
5833 /* CTCP must be via PRIVMSG, never notice */
5835 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5837 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5842 chanserv_adjust_limit(void *data)
5844 struct mod_chanmode change;
5845 struct chanData *cData = data;
5846 struct chanNode *channel = cData->channel;
5849 if(IsSuspended(cData))
5852 cData->limitAdjusted = now;
5853 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5854 if(cData->modes.modes_set & MODE_LIMIT)
5856 if(limit > cData->modes.new_limit)
5857 limit = cData->modes.new_limit;
5858 else if(limit == cData->modes.new_limit)
5862 mod_chanmode_init(&change);
5863 change.modes_set = MODE_LIMIT;
5864 change.new_limit = limit;
5865 mod_chanmode_announce(chanserv, channel, &change);
5869 handle_new_channel(struct chanNode *channel)
5871 struct chanData *cData;
5873 if(!(cData = channel->channel_info))
5876 if(cData->modes.modes_set || cData->modes.modes_clear)
5877 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5879 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5880 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5883 /* Welcome to my worst nightmare. Warning: Read (or modify)
5884 the code below at your own risk. */
5886 handle_join(struct modeNode *mNode)
5888 struct mod_chanmode change;
5889 struct userNode *user = mNode->user;
5890 struct chanNode *channel = mNode->channel;
5891 struct chanData *cData;
5892 struct userData *uData = NULL;
5893 struct banData *bData;
5894 struct handle_info *handle;
5895 unsigned int modes = 0, info = 0;
5898 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5901 cData = channel->channel_info;
5902 if(channel->members.used > cData->max)
5903 cData->max = channel->members.used;
5905 /* Check for bans. If they're joining through a ban, one of two
5907 * 1: Join during a netburst, by riding the break. Kick them
5908 * unless they have ops or voice in the channel.
5909 * 2: They're allowed to join through the ban (an invite in
5910 * ircu2.10, or a +e on Hybrid, or something).
5911 * If they're not joining through a ban, and the banlist is not
5912 * full, see if they're on the banlist for the channel. If so,
5915 if(user->uplink->burst && !mNode->modes)
5918 for(ii = 0; ii < channel->banlist.used; ii++)
5920 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5922 /* Riding a netburst. Naughty. */
5923 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5929 mod_chanmode_init(&change);
5931 if(channel->banlist.used < MAXBANS)
5933 /* Not joining through a ban. */
5934 for(bData = cData->bans;
5935 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5936 bData = bData->next);
5940 char kick_reason[MAXLEN];
5941 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5943 bData->triggered = now;
5944 if(bData != cData->bans)
5946 /* Shuffle the ban to the head of the list. */
5948 bData->next->prev = bData->prev;
5950 bData->prev->next = bData->next;
5953 bData->next = cData->bans;
5956 cData->bans->prev = bData;
5957 cData->bans = bData;
5960 change.args[0].mode = MODE_BAN;
5961 change.args[0].u.hostmask = bData->mask;
5962 mod_chanmode_announce(chanserv, channel, &change);
5963 KickChannelUser(user, channel, chanserv, kick_reason);
5968 /* ChanServ will not modify the limits in join-flooded channels.
5969 It will also skip DynLimit processing when the user (or srvx)
5970 is bursting in, because there are likely more incoming. */
5971 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5972 && !user->uplink->burst
5973 && !channel->join_flooded
5974 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5976 /* The user count has begun "bumping" into the channel limit,
5977 so set a timer to raise the limit a bit. Any previous
5978 timers are removed so three incoming users within the delay
5979 results in one limit change, not three. */
5981 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5982 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5985 if(channel->join_flooded)
5987 /* don't automatically give ops or voice during a join flood */
5989 else if(cData->lvlOpts[lvlGiveOps] == 0)
5990 modes |= MODE_CHANOP;
5991 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5992 modes |= MODE_VOICE;
5994 greeting = cData->greeting;
5995 if(user->handle_info)
5997 handle = user->handle_info;
5999 if(IsHelper(user) && !IsHelping(user))
6002 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6004 if(channel == chanserv_conf.support_channels.list[ii])
6006 HANDLE_SET_FLAG(user->handle_info, HELPING);
6012 uData = GetTrueChannelAccess(cData, handle);
6013 if(uData && !IsUserSuspended(uData))
6015 /* Ops and above were handled by the above case. */
6016 if(IsUserAutoOp(uData))
6018 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6019 modes |= MODE_CHANOP;
6020 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6021 modes |= MODE_VOICE;
6023 if(uData->access >= UL_PRESENT)
6024 cData->visited = now;
6025 if(cData->user_greeting)
6026 greeting = cData->user_greeting;
6028 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6029 && ((now - uData->seen) >= chanserv_conf.info_delay)
6037 /* If user joining normally (not during burst), apply op or voice,
6038 * and send greeting/userinfo as appropriate.
6040 if(!user->uplink->burst)
6044 if(modes & MODE_CHANOP)
6045 modes &= ~MODE_VOICE;
6046 change.args[0].mode = modes;
6047 change.args[0].u.member = mNode;
6048 mod_chanmode_announce(chanserv, channel, &change);
6051 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6053 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6059 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6061 struct mod_chanmode change;
6062 struct userData *channel;
6063 unsigned int ii, jj;
6065 if(!user->handle_info)
6068 mod_chanmode_init(&change);
6070 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6072 struct chanNode *cn;
6073 struct modeNode *mn;
6074 if(IsUserSuspended(channel)
6075 || IsSuspended(channel->channel)
6076 || !(cn = channel->channel->channel))
6079 mn = GetUserMode(cn, user);
6082 if(!IsUserSuspended(channel)
6083 && IsUserAutoInvite(channel)
6084 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6086 && !user->uplink->burst)
6087 irc_invite(chanserv, user, cn);
6091 if(channel->access >= UL_PRESENT)
6092 channel->channel->visited = now;
6094 if(IsUserAutoOp(channel))
6096 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6097 change.args[0].mode = MODE_CHANOP;
6098 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6099 change.args[0].mode = MODE_VOICE;
6101 change.args[0].mode = 0;
6102 change.args[0].u.member = mn;
6103 if(change.args[0].mode)
6104 mod_chanmode_announce(chanserv, cn, &change);
6107 channel->seen = now;
6108 channel->present = 1;
6111 for(ii = 0; ii < user->channels.used; ++ii)
6113 struct chanNode *channel = user->channels.list[ii]->channel;
6114 struct banData *ban;
6116 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6117 || !channel->channel_info
6118 || IsSuspended(channel->channel_info))
6120 for(jj = 0; jj < channel->banlist.used; ++jj)
6121 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6123 if(jj < channel->banlist.used)
6125 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6127 char kick_reason[MAXLEN];
6128 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6130 change.args[0].mode = MODE_BAN;
6131 change.args[0].u.hostmask = ban->mask;
6132 mod_chanmode_announce(chanserv, channel, &change);
6133 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6134 KickChannelUser(user, channel, chanserv, kick_reason);
6135 ban->triggered = now;
6140 if(IsSupportHelper(user))
6142 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6144 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6146 HANDLE_SET_FLAG(user->handle_info, HELPING);
6154 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6156 struct chanData *cData;
6157 struct userData *uData;
6159 cData = mn->channel->channel_info;
6160 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6163 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6165 /* Allow for a bit of padding so that the limit doesn't
6166 track the user count exactly, which could get annoying. */
6167 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6169 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6170 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6174 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6176 scan_user_presence(uData, mn->user);
6178 if (uData->access >= UL_PRESENT)
6179 cData->visited = now;
6182 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6184 unsigned int ii, jj;
6185 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6187 for(jj = 0; jj < mn->user->channels.used; ++jj)
6188 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6190 if(jj < mn->user->channels.used)
6193 if(ii == chanserv_conf.support_channels.used)
6194 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6199 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6201 struct userData *uData;
6203 if(!channel->channel_info || !kicker || IsService(kicker)
6204 || (kicker == victim) || IsSuspended(channel->channel_info)
6205 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6208 if(protect_user(victim, kicker, channel->channel_info))
6210 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6211 KickChannelUser(kicker, channel, chanserv, reason);
6214 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6219 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6221 struct chanData *cData;
6223 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6226 cData = channel->channel_info;
6227 if(bad_topic(channel, user, channel->topic))
6229 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6230 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6231 SetChannelTopic(channel, chanserv, old_topic, 1);
6232 else if(cData->topic)
6233 SetChannelTopic(channel, chanserv, cData->topic, 1);
6236 /* With topicsnarf, grab the topic and save it as the default topic. */
6237 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6240 cData->topic = strdup(channel->topic);
6246 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6248 struct mod_chanmode *bounce = NULL;
6249 unsigned int bnc, ii;
6252 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6255 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6256 && mode_lock_violated(&channel->channel_info->modes, change))
6258 char correct[MAXLEN];
6259 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6260 mod_chanmode_format(&channel->channel_info->modes, correct);
6261 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6263 for(ii = bnc = 0; ii < change->argc; ++ii)
6265 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6267 const struct userNode *victim = change->args[ii].u.member->user;
6268 if(!protect_user(victim, user, channel->channel_info))
6271 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6274 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6275 bounce->args[bnc].u.member = GetUserMode(channel, user);
6276 if(bounce->args[bnc].u.member)
6280 bounce->args[bnc].mode = MODE_CHANOP;
6281 bounce->args[bnc].u.member = change->args[ii].u.member;
6283 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6285 else if(change->args[ii].mode & MODE_CHANOP)
6287 const struct userNode *victim = change->args[ii].u.member->user;
6288 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6291 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6292 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6293 bounce->args[bnc].u.member = change->args[ii].u.member;
6296 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6298 const char *ban = change->args[ii].u.hostmask;
6299 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6302 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6303 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6304 bounce->args[bnc].u.hostmask = strdup(ban);
6306 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6311 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6312 mod_chanmode_announce(chanserv, channel, bounce);
6313 for(ii = 0; ii < change->argc; ++ii)
6314 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6315 free((char*)bounce->args[ii].u.hostmask);
6316 mod_chanmode_free(bounce);
6321 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6323 struct chanNode *channel;
6324 struct banData *bData;
6325 struct mod_chanmode change;
6326 unsigned int ii, jj;
6327 char kick_reason[MAXLEN];
6329 mod_chanmode_init(&change);
6331 change.args[0].mode = MODE_BAN;
6332 for(ii = 0; ii < user->channels.used; ++ii)
6334 channel = user->channels.list[ii]->channel;
6335 /* Need not check for bans if they're opped or voiced. */
6336 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6338 /* Need not check for bans unless channel registration is active. */
6339 if(!channel->channel_info || IsSuspended(channel->channel_info))
6341 /* Look for a matching ban already on the channel. */
6342 for(jj = 0; jj < channel->banlist.used; ++jj)
6343 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6345 /* Need not act if we found one. */
6346 if(jj < channel->banlist.used)
6348 /* Look for a matching ban in this channel. */
6349 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6351 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6353 change.args[0].u.hostmask = bData->mask;
6354 mod_chanmode_announce(chanserv, channel, &change);
6355 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6356 KickChannelUser(user, channel, chanserv, kick_reason);
6357 bData->triggered = now;
6358 break; /* we don't need to check any more bans in the channel */
6363 static void handle_rename(struct handle_info *handle, const char *old_handle)
6365 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6369 dict_remove2(handle_dnrs, old_handle, 1);
6370 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6371 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6376 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6378 struct userNode *h_user;
6380 if(handle->channels)
6382 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6383 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6385 while(handle->channels)
6386 del_channel_user(handle->channels, 1);
6391 handle_server_link(UNUSED_ARG(struct server *server))
6393 struct chanData *cData;
6395 for(cData = channelList; cData; cData = cData->next)
6397 if(!IsSuspended(cData))
6398 cData->may_opchan = 1;
6399 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6400 && !cData->channel->join_flooded
6401 && ((cData->channel->limit - cData->channel->members.used)
6402 < chanserv_conf.adjust_threshold))
6404 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6405 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6411 chanserv_conf_read(void)
6415 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6416 struct mod_chanmode *change;
6417 struct string_list *strlist;
6418 struct chanNode *chan;
6421 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6423 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6426 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6427 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6428 chanserv_conf.support_channels.used = 0;
6429 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6431 for(ii = 0; ii < strlist->used; ++ii)
6433 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6436 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6438 channelList_append(&chanserv_conf.support_channels, chan);
6441 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6444 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6447 chan = AddChannel(str, now, str2, NULL);
6449 channelList_append(&chanserv_conf.support_channels, chan);
6451 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6452 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6453 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6454 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6455 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6456 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6457 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6458 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6459 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6460 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6461 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6462 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6463 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6464 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6465 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6466 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6467 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6468 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6469 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6470 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6471 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6472 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6473 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6475 NickChange(chanserv, str, 0);
6476 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6477 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6478 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6479 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6480 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6481 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6482 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6483 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6484 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6485 chanserv_conf.max_owned = str ? atoi(str) : 5;
6486 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6487 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6488 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6489 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6490 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6491 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6492 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6495 safestrncpy(mode_line, str, sizeof(mode_line));
6496 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6497 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6498 && (change->argc < 2))
6500 chanserv_conf.default_modes = *change;
6501 mod_chanmode_free(change);
6503 free_string_list(chanserv_conf.set_shows);
6504 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6506 strlist = string_list_copy(strlist);
6509 static const char *list[] = {
6510 /* free form text */
6511 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6512 /* options based on user level */
6513 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6514 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6515 /* multiple choice options */
6516 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6517 /* binary options */
6518 "DynLimit", "NoDelete",
6523 strlist = alloc_string_list(ArrayLength(list)-1);
6524 for(ii=0; list[ii]; ii++)
6525 string_list_append(strlist, strdup(list[ii]));
6527 chanserv_conf.set_shows = strlist;
6528 /* We don't look things up now, in case the list refers to options
6529 * defined by modules initialized after this point. Just mark the
6530 * function list as invalid, so it will be initialized.
6532 set_shows_list.used = 0;
6533 free_string_list(chanserv_conf.eightball);
6534 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6537 strlist = string_list_copy(strlist);
6541 strlist = alloc_string_list(4);
6542 string_list_append(strlist, strdup("Yes."));
6543 string_list_append(strlist, strdup("No."));
6544 string_list_append(strlist, strdup("Maybe so."));
6546 chanserv_conf.eightball = strlist;
6547 free_string_list(chanserv_conf.old_ban_names);
6548 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6550 strlist = string_list_copy(strlist);
6552 strlist = alloc_string_list(2);
6553 chanserv_conf.old_ban_names = strlist;
6554 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6555 off_channel = str ? atoi(str) : 0;
6559 chanserv_note_type_read(const char *key, struct record_data *rd)
6562 struct note_type *ntype;
6565 if(!(obj = GET_RECORD_OBJECT(rd)))
6567 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6570 if(!(ntype = chanserv_create_note_type(key)))
6572 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6576 /* Figure out set access */
6577 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6579 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6580 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6582 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6584 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6585 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6587 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6589 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6593 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6594 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6595 ntype->set_access.min_opserv = 0;
6598 /* Figure out visibility */
6599 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6600 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6601 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6602 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6603 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6604 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6605 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6606 ntype->visible_type = NOTE_VIS_ALL;
6608 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6610 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6611 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6615 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6617 struct handle_info *handle;
6618 struct userData *uData;
6619 char *seen, *inf, *flags;
6621 unsigned short access;
6623 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6625 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6629 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6630 if(access > UL_OWNER)
6632 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6636 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6637 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6638 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6639 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6640 handle = get_handle_info(key);
6643 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6647 uData = add_channel_user(chan, handle, access, last_seen, inf);
6648 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6652 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6654 struct banData *bData;
6655 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6656 time_t set_time, triggered_time, expires_time;
6658 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6660 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6664 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6665 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6666 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6667 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6668 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6669 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6670 if (!reason || !owner)
6673 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6674 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6676 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6678 expires_time = set_time + atoi(s_duration);
6682 if(!reason || (expires_time && (expires_time < now)))
6685 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6688 static struct suspended *
6689 chanserv_read_suspended(dict_t obj)
6691 struct suspended *suspended = calloc(1, sizeof(*suspended));
6695 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6696 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6697 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6698 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6699 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6700 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6701 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6702 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6703 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6704 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6709 chanserv_channel_read(const char *key, struct record_data *hir)
6711 struct suspended *suspended;
6712 struct mod_chanmode *modes;
6713 struct chanNode *cNode;
6714 struct chanData *cData;
6715 struct dict *channel, *obj;
6716 char *str, *argv[10];
6720 channel = hir->d.object;
6722 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6725 cNode = AddChannel(key, now, NULL, NULL);
6728 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6731 cData = register_channel(cNode, str);
6734 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6738 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6740 enum levelOption lvlOpt;
6741 enum charOption chOpt;
6743 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6744 cData->flags = atoi(str);
6746 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6748 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6750 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6751 else if(levelOptions[lvlOpt].old_flag)
6753 if(cData->flags & levelOptions[lvlOpt].old_flag)
6754 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6756 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6760 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6762 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6764 cData->chOpts[chOpt] = str[0];
6767 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6769 enum levelOption lvlOpt;
6770 enum charOption chOpt;
6773 cData->flags = base64toint(str, 5);
6774 count = strlen(str += 5);
6775 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6778 if(levelOptions[lvlOpt].old_flag)
6780 if(cData->flags & levelOptions[lvlOpt].old_flag)
6781 lvl = levelOptions[lvlOpt].flag_value;
6783 lvl = levelOptions[lvlOpt].default_value;
6785 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6787 case 'c': lvl = UL_COOWNER; break;
6788 case 'm': lvl = UL_MASTER; break;
6789 case 'n': lvl = UL_OWNER+1; break;
6790 case 'o': lvl = UL_OP; break;
6791 case 'p': lvl = UL_PEON; break;
6792 case 'w': lvl = UL_OWNER; break;
6793 default: lvl = 0; break;
6795 cData->lvlOpts[lvlOpt] = lvl;
6797 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6798 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6801 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6803 suspended = chanserv_read_suspended(obj);
6804 cData->suspended = suspended;
6805 suspended->cData = cData;
6806 /* We could use suspended->expires and suspended->revoked to
6807 * set the CHANNEL_SUSPENDED flag, but we don't. */
6809 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6811 suspended = calloc(1, sizeof(*suspended));
6812 suspended->issued = 0;
6813 suspended->revoked = 0;
6814 suspended->suspender = strdup(str);
6815 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6816 suspended->expires = str ? atoi(str) : 0;
6817 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6818 suspended->reason = strdup(str ? str : "No reason");
6819 suspended->previous = NULL;
6820 cData->suspended = suspended;
6821 suspended->cData = cData;
6825 cData->flags &= ~CHANNEL_SUSPENDED;
6826 suspended = NULL; /* to squelch a warning */
6829 if(IsSuspended(cData)) {
6830 if(suspended->expires > now)
6831 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6832 else if(suspended->expires)
6833 cData->flags &= ~CHANNEL_SUSPENDED;
6836 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6837 struct mod_chanmode change;
6838 mod_chanmode_init(&change);
6840 change.args[0].mode = MODE_CHANOP;
6841 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6842 mod_chanmode_announce(chanserv, cNode, &change);
6845 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6846 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6847 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6848 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6849 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6850 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6851 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6852 cData->max = str ? atoi(str) : 0;
6853 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6854 cData->greeting = str ? strdup(str) : NULL;
6855 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6856 cData->user_greeting = str ? strdup(str) : NULL;
6857 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6858 cData->topic_mask = str ? strdup(str) : NULL;
6859 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6860 cData->topic = str ? strdup(str) : NULL;
6862 if(!IsSuspended(cData)
6863 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6864 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6865 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6866 cData->modes = *modes;
6868 cData->modes.modes_set |= MODE_REGISTERED;
6869 if(cData->modes.argc > 1)
6870 cData->modes.argc = 1;
6871 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6872 mod_chanmode_free(modes);
6875 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6876 for(it = dict_first(obj); it; it = iter_next(it))
6877 user_read_helper(iter_key(it), iter_data(it), cData);
6879 if(!cData->users && !IsProtected(cData))
6881 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6882 unregister_channel(cData, "has empty user list.");
6886 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6887 for(it = dict_first(obj); it; it = iter_next(it))
6888 ban_read_helper(iter_key(it), iter_data(it), cData);
6890 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6891 for(it = dict_first(obj); it; it = iter_next(it))
6893 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6894 struct record_data *rd = iter_data(it);
6895 const char *note, *setter;
6897 if(rd->type != RECDB_OBJECT)
6899 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6903 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6905 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6907 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6911 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6912 if(!setter) setter = "<unknown>";
6913 chanserv_add_channel_note(cData, ntype, setter, note);
6921 chanserv_dnr_read(const char *key, struct record_data *hir)
6923 const char *setter, *reason, *str;
6924 struct do_not_register *dnr;
6926 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6929 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6932 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6935 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6938 dnr = chanserv_add_dnr(key, setter, reason);
6941 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6943 dnr->set = atoi(str);
6949 chanserv_saxdb_read(struct dict *database)
6951 struct dict *section;
6954 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6955 for(it = dict_first(section); it; it = iter_next(it))
6956 chanserv_note_type_read(iter_key(it), iter_data(it));
6958 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6959 for(it = dict_first(section); it; it = iter_next(it))
6960 chanserv_channel_read(iter_key(it), iter_data(it));
6962 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6963 for(it = dict_first(section); it; it = iter_next(it))
6964 chanserv_dnr_read(iter_key(it), iter_data(it));
6970 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6972 int high_present = 0;
6973 saxdb_start_record(ctx, KEY_USERS, 1);
6974 for(; uData; uData = uData->next)
6976 if((uData->access >= UL_PRESENT) && uData->present)
6978 saxdb_start_record(ctx, uData->handle->handle, 0);
6979 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6980 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6982 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6984 saxdb_write_string(ctx, KEY_INFO, uData->info);
6985 saxdb_end_record(ctx);
6987 saxdb_end_record(ctx);
6988 return high_present;
6992 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6996 saxdb_start_record(ctx, KEY_BANS, 1);
6997 for(; bData; bData = bData->next)
6999 saxdb_start_record(ctx, bData->mask, 0);
7000 saxdb_write_int(ctx, KEY_SET, bData->set);
7001 if(bData->triggered)
7002 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7004 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7006 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7008 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7009 saxdb_end_record(ctx);
7011 saxdb_end_record(ctx);
7015 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7017 saxdb_start_record(ctx, name, 0);
7018 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7019 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7021 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7023 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7025 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7027 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7028 saxdb_end_record(ctx);
7032 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7036 enum levelOption lvlOpt;
7037 enum charOption chOpt;
7039 saxdb_start_record(ctx, channel->channel->name, 1);
7041 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7042 saxdb_write_int(ctx, KEY_MAX, channel->max);
7044 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7045 if(channel->registrar)
7046 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7047 if(channel->greeting)
7048 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7049 if(channel->user_greeting)
7050 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7051 if(channel->topic_mask)
7052 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7053 if(channel->suspended)
7054 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7056 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7057 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7058 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7059 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7060 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7062 buf[0] = channel->chOpts[chOpt];
7064 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7066 saxdb_end_record(ctx);
7068 if(channel->modes.modes_set || channel->modes.modes_clear)
7070 mod_chanmode_format(&channel->modes, buf);
7071 saxdb_write_string(ctx, KEY_MODES, buf);
7074 high_present = chanserv_write_users(ctx, channel->users);
7075 chanserv_write_bans(ctx, channel->bans);
7077 if(dict_size(channel->notes))
7081 saxdb_start_record(ctx, KEY_NOTES, 1);
7082 for(it = dict_first(channel->notes); it; it = iter_next(it))
7084 struct note *note = iter_data(it);
7085 saxdb_start_record(ctx, iter_key(it), 0);
7086 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7087 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7088 saxdb_end_record(ctx);
7090 saxdb_end_record(ctx);
7093 if(channel->ownerTransfer)
7094 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7095 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7096 saxdb_end_record(ctx);
7100 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7104 saxdb_start_record(ctx, ntype->name, 0);
7105 switch(ntype->set_access_type)
7107 case NOTE_SET_CHANNEL_ACCESS:
7108 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7110 case NOTE_SET_CHANNEL_SETTER:
7111 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7113 case NOTE_SET_PRIVILEGED: default:
7114 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7117 switch(ntype->visible_type)
7119 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7120 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7121 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7123 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7124 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7125 saxdb_end_record(ctx);
7129 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7131 struct do_not_register *dnr;
7134 for(it = dict_first(dnrs); it; it = iter_next(it))
7136 dnr = iter_data(it);
7137 saxdb_start_record(ctx, dnr->chan_name, 0);
7139 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7140 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7141 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7142 saxdb_end_record(ctx);
7147 chanserv_saxdb_write(struct saxdb_context *ctx)
7150 struct chanData *channel;
7153 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7154 for(it = dict_first(note_types); it; it = iter_next(it))
7155 chanserv_write_note_type(ctx, iter_data(it));
7156 saxdb_end_record(ctx);
7159 saxdb_start_record(ctx, KEY_DNR, 1);
7160 write_dnrs_helper(ctx, handle_dnrs);
7161 write_dnrs_helper(ctx, plain_dnrs);
7162 write_dnrs_helper(ctx, mask_dnrs);
7163 saxdb_end_record(ctx);
7166 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7167 for(channel = channelList; channel; channel = channel->next)
7168 chanserv_write_channel(ctx, channel);
7169 saxdb_end_record(ctx);
7175 chanserv_db_cleanup(void) {
7177 unreg_part_func(handle_part);
7179 unregister_channel(channelList, "terminating.");
7180 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7181 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7182 free(chanserv_conf.support_channels.list);
7183 dict_delete(handle_dnrs);
7184 dict_delete(plain_dnrs);
7185 dict_delete(mask_dnrs);
7186 dict_delete(note_types);
7187 free_string_list(chanserv_conf.eightball);
7188 free_string_list(chanserv_conf.old_ban_names);
7189 free_string_list(chanserv_conf.set_shows);
7190 free(set_shows_list.list);
7191 free(uset_shows_list.list);
7194 struct userData *helper = helperList;
7195 helperList = helperList->next;
7200 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7201 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7202 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7205 init_chanserv(const char *nick)
7207 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7208 conf_register_reload(chanserv_conf_read);
7210 reg_server_link_func(handle_server_link);
7212 reg_new_channel_func(handle_new_channel);
7213 reg_join_func(handle_join);
7214 reg_part_func(handle_part);
7215 reg_kick_func(handle_kick);
7216 reg_topic_func(handle_topic);
7217 reg_mode_change_func(handle_mode);
7218 reg_nick_change_func(handle_nick_change);
7220 reg_auth_func(handle_auth);
7221 reg_handle_rename_func(handle_rename);
7222 reg_unreg_func(handle_unreg);
7224 handle_dnrs = dict_new();
7225 dict_set_free_data(handle_dnrs, free);
7226 plain_dnrs = dict_new();
7227 dict_set_free_data(plain_dnrs, free);
7228 mask_dnrs = dict_new();
7229 dict_set_free_data(mask_dnrs, free);
7231 reg_svccmd_unbind_func(handle_svccmd_unbind);
7232 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7233 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7234 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7235 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7236 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7237 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7238 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7239 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7240 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7242 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7243 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7245 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7246 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7247 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7248 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7249 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7251 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7252 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7253 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7254 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7255 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7257 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7258 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7259 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7260 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7262 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7263 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7264 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7265 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7266 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7267 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7268 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7269 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7271 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7272 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7273 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7274 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7275 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7276 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7277 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7278 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7279 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7280 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7281 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7282 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7283 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7284 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7286 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7287 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7288 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7289 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7290 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7292 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7293 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7295 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7296 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7297 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7298 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7299 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7300 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7301 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7302 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7303 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7304 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7305 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7307 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7308 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7310 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7311 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7312 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7313 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7315 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7316 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7317 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7318 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7319 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7321 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7322 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7323 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7324 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7325 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7326 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7328 /* Channel options */
7329 DEFINE_CHANNEL_OPTION(defaulttopic);
7330 DEFINE_CHANNEL_OPTION(topicmask);
7331 DEFINE_CHANNEL_OPTION(greeting);
7332 DEFINE_CHANNEL_OPTION(usergreeting);
7333 DEFINE_CHANNEL_OPTION(modes);
7334 DEFINE_CHANNEL_OPTION(enfops);
7335 DEFINE_CHANNEL_OPTION(giveops);
7336 DEFINE_CHANNEL_OPTION(protect);
7337 DEFINE_CHANNEL_OPTION(enfmodes);
7338 DEFINE_CHANNEL_OPTION(enftopic);
7339 DEFINE_CHANNEL_OPTION(pubcmd);
7340 DEFINE_CHANNEL_OPTION(givevoice);
7341 DEFINE_CHANNEL_OPTION(userinfo);
7342 DEFINE_CHANNEL_OPTION(dynlimit);
7343 DEFINE_CHANNEL_OPTION(topicsnarf);
7344 DEFINE_CHANNEL_OPTION(nodelete);
7345 DEFINE_CHANNEL_OPTION(toys);
7346 DEFINE_CHANNEL_OPTION(setters);
7347 DEFINE_CHANNEL_OPTION(topicrefresh);
7348 DEFINE_CHANNEL_OPTION(ctcpusers);
7349 DEFINE_CHANNEL_OPTION(ctcpreaction);
7350 DEFINE_CHANNEL_OPTION(inviteme);
7352 DEFINE_CHANNEL_OPTION(offchannel);
7353 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7355 /* Alias set topic to set defaulttopic for compatibility. */
7356 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7359 DEFINE_USER_OPTION(noautoop);
7360 DEFINE_USER_OPTION(autoinvite);
7361 DEFINE_USER_OPTION(info);
7363 /* Alias uset autovoice to uset autoop. */
7364 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7366 note_types = dict_new();
7367 dict_set_free_data(note_types, chanserv_deref_note_type);
7370 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7371 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7372 service_register(chanserv)->trigger = '!';
7373 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7375 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7377 if(chanserv_conf.channel_expire_frequency)
7378 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7380 if(chanserv_conf.refresh_period)
7382 time_t next_refresh;
7383 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7384 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7387 reg_exit_func(chanserv_db_cleanup);
7388 message_register_table(msgtab);