1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
424 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
425 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
428 { "CSMSG_USER_OPTIONS", "User Options:" },
429 { "CSMSG_USER_PROTECTED", "That user is protected." },
432 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
433 { "CSMSG_PING_RESPONSE", "Pong!" },
434 { "CSMSG_WUT_RESPONSE", "wut" },
435 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
436 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
437 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
438 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
439 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
440 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
441 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
444 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
448 /* eject_user and unban_user flags */
449 #define ACTION_KICK 0x0001
450 #define ACTION_BAN 0x0002
451 #define ACTION_ADD_BAN 0x0004
452 #define ACTION_ADD_TIMED_BAN 0x0008
453 #define ACTION_UNBAN 0x0010
454 #define ACTION_DEL_BAN 0x0020
456 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
457 #define MODELEN 40 + KEYLEN
461 #define CSFUNC_ARGS user, channel, argc, argv, cmd
463 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
464 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
465 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
466 reply("MSG_MISSING_PARAMS", argv[0]); \
470 DECLARE_LIST(dnrList, struct do_not_register *);
471 DEFINE_LIST(dnrList, struct do_not_register *);
473 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
475 struct userNode *chanserv;
478 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
479 static struct log_type *CS_LOG;
483 struct channelList support_channels;
484 struct mod_chanmode default_modes;
486 unsigned long db_backup_frequency;
487 unsigned long channel_expire_frequency;
490 unsigned int adjust_delay;
491 long channel_expire_delay;
492 unsigned int nodelete_level;
494 unsigned int adjust_threshold;
495 int join_flood_threshold;
497 unsigned int greeting_length;
498 unsigned int refresh_period;
499 unsigned int giveownership_period;
501 unsigned int max_owned;
502 unsigned int max_chan_users;
503 unsigned int max_chan_bans;
504 unsigned int max_userinfo_length;
506 struct string_list *set_shows;
507 struct string_list *eightball;
508 struct string_list *old_ban_names;
510 const char *ctcp_short_ban_duration;
511 const char *ctcp_long_ban_duration;
513 const char *irc_operator_epithet;
514 const char *network_helper_epithet;
515 const char *support_helper_epithet;
520 struct userNode *user;
521 struct userNode *bot;
522 struct chanNode *channel;
524 unsigned short lowest;
525 unsigned short highest;
526 struct userData **users;
527 struct helpfile_table table;
530 enum note_access_type
532 NOTE_SET_CHANNEL_ACCESS,
533 NOTE_SET_CHANNEL_SETTER,
537 enum note_visible_type
540 NOTE_VIS_CHANNEL_USERS,
546 enum note_access_type set_access_type;
548 unsigned int min_opserv;
549 unsigned short min_ulevel;
551 enum note_visible_type visible_type;
552 unsigned int max_length;
559 struct note_type *type;
560 char setter[NICKSERV_HANDLE_LEN+1];
564 static unsigned int registered_channels;
565 static unsigned int banCount;
567 static const struct {
570 unsigned short level;
573 { "peon", "Peon", UL_PEON, '+' },
574 { "op", "Op", UL_OP, '@' },
575 { "master", "Master", UL_MASTER, '%' },
576 { "coowner", "Coowner", UL_COOWNER, '*' },
577 { "owner", "Owner", UL_OWNER, '!' },
578 { "helper", "BUG:", UL_HELPER, 'X' }
581 static const struct {
584 unsigned short default_value;
585 unsigned int old_idx;
586 unsigned int old_flag;
587 unsigned short flag_value;
589 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
590 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
591 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
592 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
593 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
594 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
595 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
596 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
597 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
598 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
599 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
602 struct charOptionValues {
605 } protectValues[] = {
606 { 'a', "CSMSG_PROTECT_ALL" },
607 { 'e', "CSMSG_PROTECT_EQUAL" },
608 { 'l', "CSMSG_PROTECT_LOWER" },
609 { 'n', "CSMSG_PROTECT_NONE" }
611 { 'd', "CSMSG_TOYS_DISABLED" },
612 { 'n', "CSMSG_TOYS_PRIVATE" },
613 { 'p', "CSMSG_TOYS_PUBLIC" }
614 }, topicRefreshValues[] = {
615 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
616 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
617 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
618 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
619 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
620 }, ctcpReactionValues[] = {
621 { 'k', "CSMSG_CTCPREACTION_KICK" },
622 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
623 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
624 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
627 static const struct {
631 unsigned int old_idx;
633 struct charOptionValues *values;
635 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
636 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
637 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
638 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
641 struct userData *helperList;
642 struct chanData *channelList;
643 static struct module *chanserv_module;
644 static unsigned int userCount;
646 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
647 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
648 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
651 user_level_from_name(const char *name, unsigned short clamp_level)
653 unsigned int level = 0, ii;
655 level = strtoul(name, NULL, 10);
656 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
657 if(!irccasecmp(name, accessLevels[ii].name))
658 level = accessLevels[ii].level;
659 if(level > clamp_level)
665 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
668 *minl = strtoul(arg, &sep, 10);
676 *maxl = strtoul(sep+1, &sep, 10);
684 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
686 struct userData *uData, **head;
688 if(!channel || !handle)
691 if(override && HANDLE_FLAGGED(handle, HELPING)
692 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
694 for(uData = helperList;
695 uData && uData->handle != handle;
696 uData = uData->next);
700 uData = calloc(1, sizeof(struct userData));
701 uData->handle = handle;
703 uData->access = UL_HELPER;
709 uData->next = helperList;
711 helperList->prev = uData;
719 for(uData = channel->users; uData; uData = uData->next)
720 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
723 head = &(channel->users);
726 if(uData && (uData != *head))
728 /* Shuffle the user to the head of whatever list he was in. */
730 uData->next->prev = uData->prev;
732 uData->prev->next = uData->next;
738 (**head).prev = uData;
745 /* Returns non-zero if user has at least the minimum access.
746 * exempt_owner is set when handling !set, so the owner can set things
749 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
751 struct userData *uData;
752 struct chanData *cData = channel->channel_info;
753 unsigned short minimum = cData->lvlOpts[opt];
756 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
759 if(minimum <= uData->access)
761 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
766 /* Scan for other users authenticated to the same handle
767 still in the channel. If so, keep them listed as present.
769 user is optional, if not null, it skips checking that userNode
770 (for the handle_part function) */
772 scan_user_presence(struct userData *uData, struct userNode *user)
776 if(IsSuspended(uData->channel)
777 || IsUserSuspended(uData)
778 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
790 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
792 unsigned int eflags, argc;
794 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
796 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
797 if(!channel->channel_info
798 || IsSuspended(channel->channel_info)
800 || !ircncasecmp(text, "ACTION ", 7))
802 /* Figure out the minimum level needed to CTCP the channel */
803 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
805 /* We need to enforce against them; do so. */
808 argv[1] = user->nick;
810 if(GetUserMode(channel, user))
811 eflags |= ACTION_KICK;
812 switch(channel->channel_info->chOpts[chCTCPReaction]) {
813 default: case 'k': /* just do the kick */ break;
815 eflags |= ACTION_BAN;
818 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
819 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
822 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
823 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
826 argv[argc++] = bad_ctcp_reason;
827 eject_user(chanserv, channel, argc, argv, NULL, eflags);
831 chanserv_create_note_type(const char *name)
833 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
834 strcpy(ntype->name, name);
836 dict_insert(note_types, ntype->name, ntype);
841 chanserv_deref_note_type(void *data)
843 struct note_type *ntype = data;
845 if(--ntype->refs > 0)
851 chanserv_flush_note_type(struct note_type *ntype)
853 struct chanData *cData;
854 for(cData = channelList; cData; cData = cData->next)
855 dict_remove(cData->notes, ntype->name);
859 chanserv_truncate_notes(struct note_type *ntype)
861 struct chanData *cData;
863 unsigned int size = sizeof(*note) + ntype->max_length;
865 for(cData = channelList; cData; cData = cData->next) {
866 note = dict_find(cData->notes, ntype->name, NULL);
869 if(strlen(note->note) <= ntype->max_length)
871 dict_remove2(cData->notes, ntype->name, 1);
872 note = realloc(note, size);
873 note->note[ntype->max_length] = 0;
874 dict_insert(cData->notes, ntype->name, note);
878 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
881 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
884 unsigned int len = strlen(text);
886 if(len > type->max_length) len = type->max_length;
887 note = calloc(1, sizeof(*note) + len);
889 strncpy(note->setter, setter, sizeof(note->setter)-1);
890 memcpy(note->note, text, len);
892 dict_insert(channel->notes, type->name, note);
898 chanserv_free_note(void *data)
900 struct note *note = data;
902 chanserv_deref_note_type(note->type);
903 assert(note->type->refs > 0); /* must use delnote to remove the type */
907 static MODCMD_FUNC(cmd_createnote) {
908 struct note_type *ntype;
909 unsigned int arg = 1, existed = 0, max_length;
911 if((ntype = dict_find(note_types, argv[1], NULL)))
914 ntype = chanserv_create_note_type(argv[arg]);
915 if(!irccasecmp(argv[++arg], "privileged"))
918 ntype->set_access_type = NOTE_SET_PRIVILEGED;
919 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
921 else if(!irccasecmp(argv[arg], "channel"))
923 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
926 reply("CSMSG_INVALID_ACCESS", argv[arg]);
929 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
930 ntype->set_access.min_ulevel = ulvl;
932 else if(!irccasecmp(argv[arg], "setter"))
934 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
938 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
942 if(!irccasecmp(argv[++arg], "privileged"))
943 ntype->visible_type = NOTE_VIS_PRIVILEGED;
944 else if(!irccasecmp(argv[arg], "channel_users"))
945 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
946 else if(!irccasecmp(argv[arg], "all"))
947 ntype->visible_type = NOTE_VIS_ALL;
949 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
953 if((arg+1) >= argc) {
954 reply("MSG_MISSING_PARAMS", argv[0]);
957 max_length = strtoul(argv[++arg], NULL, 0);
958 if(max_length < 20 || max_length > 450)
960 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
963 if(existed && (max_length < ntype->max_length))
965 ntype->max_length = max_length;
966 chanserv_truncate_notes(ntype);
968 ntype->max_length = max_length;
971 reply("CSMSG_NOTE_MODIFIED", ntype->name);
973 reply("CSMSG_NOTE_CREATED", ntype->name);
978 dict_remove(note_types, ntype->name);
982 static MODCMD_FUNC(cmd_removenote) {
983 struct note_type *ntype;
986 ntype = dict_find(note_types, argv[1], NULL);
987 force = (argc > 2) && !irccasecmp(argv[2], "force");
990 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
997 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1000 chanserv_flush_note_type(ntype);
1002 dict_remove(note_types, argv[1]);
1003 reply("CSMSG_NOTE_DELETED", argv[1]);
1008 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1012 if(orig->modes_set & change->modes_clear)
1014 if(orig->modes_clear & change->modes_set)
1016 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1017 && strcmp(orig->new_key, change->new_key))
1019 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1020 && (orig->new_limit != change->new_limit))
1025 static char max_length_text[MAXLEN+1][16];
1027 static struct helpfile_expansion
1028 chanserv_expand_variable(const char *variable)
1030 struct helpfile_expansion exp;
1032 if(!irccasecmp(variable, "notes"))
1035 exp.type = HF_TABLE;
1036 exp.value.table.length = 1;
1037 exp.value.table.width = 3;
1038 exp.value.table.flags = 0;
1039 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1040 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1041 exp.value.table.contents[0][0] = "Note Type";
1042 exp.value.table.contents[0][1] = "Visibility";
1043 exp.value.table.contents[0][2] = "Max Length";
1044 for(it=dict_first(note_types); it; it=iter_next(it))
1046 struct note_type *ntype = iter_data(it);
1049 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1050 row = exp.value.table.length++;
1051 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1052 exp.value.table.contents[row][0] = ntype->name;
1053 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1054 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1056 if(!max_length_text[ntype->max_length][0])
1057 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1058 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1063 exp.type = HF_STRING;
1064 exp.value.str = NULL;
1068 static struct chanData*
1069 register_channel(struct chanNode *cNode, char *registrar)
1071 struct chanData *channel;
1072 enum levelOption lvlOpt;
1073 enum charOption chOpt;
1075 channel = calloc(1, sizeof(struct chanData));
1077 channel->notes = dict_new();
1078 dict_set_free_data(channel->notes, chanserv_free_note);
1080 channel->registrar = strdup(registrar);
1081 channel->registered = now;
1082 channel->visited = now;
1083 channel->limitAdjusted = now;
1084 channel->ownerTransfer = now;
1085 channel->flags = CHANNEL_DEFAULT_FLAGS;
1086 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1087 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1088 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1089 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1091 channel->prev = NULL;
1092 channel->next = channelList;
1095 channelList->prev = channel;
1096 channelList = channel;
1097 registered_channels++;
1099 channel->channel = cNode;
1101 cNode->channel_info = channel;
1106 static struct userData*
1107 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1109 struct userData *ud;
1111 if(access > UL_OWNER)
1114 ud = calloc(1, sizeof(*ud));
1115 ud->channel = channel;
1116 ud->handle = handle;
1118 ud->access = access;
1119 ud->info = info ? strdup(info) : NULL;
1122 ud->next = channel->users;
1124 channel->users->prev = ud;
1125 channel->users = ud;
1127 channel->userCount++;
1131 ud->u_next = ud->handle->channels;
1133 ud->u_next->u_prev = ud;
1134 ud->handle->channels = ud;
1139 static void unregister_channel(struct chanData *channel, const char *reason);
1142 del_channel_user(struct userData *user, int do_gc)
1144 struct chanData *channel = user->channel;
1146 channel->userCount--;
1150 user->prev->next = user->next;
1152 channel->users = user->next;
1154 user->next->prev = user->prev;
1157 user->u_prev->u_next = user->u_next;
1159 user->handle->channels = user->u_next;
1161 user->u_next->u_prev = user->u_prev;
1165 if(do_gc && !channel->users && !IsProtected(channel))
1166 unregister_channel(channel, "lost all users.");
1169 static void expire_ban(void *data);
1171 static struct banData*
1172 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1175 unsigned int ii, l1, l2;
1180 bd = malloc(sizeof(struct banData));
1182 bd->channel = channel;
1184 bd->triggered = triggered;
1185 bd->expires = expires;
1187 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1189 extern const char *hidden_host_suffix;
1190 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1194 l2 = strlen(old_name);
1197 if(irccasecmp(mask + l1 - l2, old_name))
1199 new_mask = alloca(MAXLEN);
1200 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1203 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1205 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1206 bd->reason = strdup(reason);
1209 timeq_add(expires, expire_ban, bd);
1212 bd->next = channel->bans;
1214 channel->bans->prev = bd;
1216 channel->banCount++;
1223 del_channel_ban(struct banData *ban)
1225 ban->channel->banCount--;
1229 ban->prev->next = ban->next;
1231 ban->channel->bans = ban->next;
1234 ban->next->prev = ban->prev;
1237 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1246 expire_ban(void *data)
1248 struct banData *bd = data;
1249 if(!IsSuspended(bd->channel))
1251 struct banList bans;
1252 struct mod_chanmode change;
1254 bans = bd->channel->channel->banlist;
1255 mod_chanmode_init(&change);
1256 for(ii=0; ii<bans.used; ii++)
1258 if(!strcmp(bans.list[ii]->ban, bd->mask))
1261 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1262 change.args[0].u.hostmask = bd->mask;
1263 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1269 del_channel_ban(bd);
1272 static void chanserv_expire_suspension(void *data);
1275 unregister_channel(struct chanData *channel, const char *reason)
1277 struct mod_chanmode change;
1278 char msgbuf[MAXLEN];
1280 /* After channel unregistration, the following must be cleaned
1282 - Channel information.
1285 - Channel suspension data.
1286 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1292 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1296 mod_chanmode_init(&change);
1297 change.modes_clear |= MODE_REGISTERED;
1298 mod_chanmode_announce(chanserv, channel->channel, &change);
1301 while(channel->users)
1302 del_channel_user(channel->users, 0);
1304 while(channel->bans)
1305 del_channel_ban(channel->bans);
1307 free(channel->topic);
1308 free(channel->registrar);
1309 free(channel->greeting);
1310 free(channel->user_greeting);
1311 free(channel->topic_mask);
1314 channel->prev->next = channel->next;
1316 channelList = channel->next;
1319 channel->next->prev = channel->prev;
1321 if(channel->suspended)
1323 struct chanNode *cNode = channel->channel;
1324 struct suspended *suspended, *next_suspended;
1326 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1328 next_suspended = suspended->previous;
1329 free(suspended->suspender);
1330 free(suspended->reason);
1331 if(suspended->expires)
1332 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1337 cNode->channel_info = NULL;
1339 channel->channel->channel_info = NULL;
1341 dict_delete(channel->notes);
1342 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1343 if(!IsSuspended(channel))
1344 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1345 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1346 UnlockChannel(channel->channel);
1348 registered_channels--;
1352 expire_channels(UNUSED_ARG(void *data))
1354 struct chanData *channel, *next;
1355 struct userData *user;
1356 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1358 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1359 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1361 for(channel = channelList; channel; channel = next)
1363 next = channel->next;
1365 /* See if the channel can be expired. */
1366 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1367 || IsProtected(channel))
1370 /* Make sure there are no high-ranking users still in the channel. */
1371 for(user=channel->users; user; user=user->next)
1372 if(user->present && (user->access >= UL_PRESENT))
1377 /* Unregister the channel */
1378 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1379 unregister_channel(channel, "registration expired.");
1382 if(chanserv_conf.channel_expire_frequency)
1383 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1387 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1389 char protect = channel->chOpts[chProtect];
1390 struct userData *cs_victim, *cs_aggressor;
1392 /* Don't protect if no one is to be protected, someone is attacking
1393 himself, or if the aggressor is an IRC Operator. */
1394 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1397 /* Don't protect if the victim isn't authenticated (because they
1398 can't be a channel user), unless we are to protect non-users
1400 cs_victim = GetChannelAccess(channel, victim->handle_info);
1401 if(protect != 'a' && !cs_victim)
1404 /* Protect if the aggressor isn't a user because at this point,
1405 the aggressor can only be less than or equal to the victim. */
1406 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1410 /* If the aggressor was a user, then the victim can't be helped. */
1417 if(cs_victim->access > cs_aggressor->access)
1422 if(cs_victim->access >= cs_aggressor->access)
1431 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1433 struct chanData *cData = channel->channel_info;
1434 struct userData *cs_victim;
1436 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1437 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1438 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1440 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1448 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1450 if(IsService(victim))
1452 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1456 if(protect_user(victim, user, channel->channel_info))
1458 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1465 static struct do_not_register *
1466 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1468 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1469 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1470 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1471 strcpy(dnr->reason, reason);
1473 if(dnr->chan_name[0] == '*')
1474 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1475 else if(strpbrk(dnr->chan_name, "*?"))
1476 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1478 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1482 static struct dnrList
1483 chanserv_find_dnrs(const char *chan_name, const char *handle)
1485 struct dnrList list;
1487 struct do_not_register *dnr;
1489 dnrList_init(&list);
1490 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1491 dnrList_append(&list, dnr);
1492 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1493 dnrList_append(&list, dnr);
1495 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1496 if(match_ircglob(chan_name, iter_key(it)))
1497 dnrList_append(&list, iter_data(it));
1502 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1504 struct dnrList list;
1505 struct do_not_register *dnr;
1507 char buf[INTERVALLEN];
1509 list = chanserv_find_dnrs(chan_name, handle);
1510 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1512 dnr = list.list[ii];
1515 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1516 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1519 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1522 reply("CSMSG_MORE_DNRS", list.used - ii);
1527 struct do_not_register *
1528 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1530 struct do_not_register *dnr;
1533 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1537 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1539 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1540 if(match_ircglob(chan_name, iter_key(it)))
1541 return iter_data(it);
1546 static CHANSERV_FUNC(cmd_noregister)
1549 struct do_not_register *dnr;
1550 char buf[INTERVALLEN];
1551 unsigned int matches;
1557 reply("CSMSG_DNR_SEARCH_RESULTS");
1559 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1561 dnr = iter_data(it);
1563 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1565 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1568 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1570 dnr = iter_data(it);
1572 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1574 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1577 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1579 dnr = iter_data(it);
1581 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1583 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1588 reply("MSG_MATCH_COUNT", matches);
1590 reply("MSG_NO_MATCHES");
1596 if(!IsChannelName(target) && (*target != '*'))
1598 reply("CSMSG_NOT_DNR", target);
1604 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1605 if((*target == '*') && !get_handle_info(target + 1))
1607 reply("MSG_HANDLE_UNKNOWN", target + 1);
1610 chanserv_add_dnr(target, user->handle_info->handle, reason);
1611 reply("CSMSG_NOREGISTER_CHANNEL", target);
1615 reply("CSMSG_DNR_SEARCH_RESULTS");
1617 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1619 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1621 reply("MSG_NO_MATCHES");
1625 static CHANSERV_FUNC(cmd_allowregister)
1627 const char *chan_name = argv[1];
1629 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1631 dict_remove(handle_dnrs, chan_name+1);
1632 reply("CSMSG_DNR_REMOVED", chan_name);
1634 else if(dict_find(plain_dnrs, chan_name, NULL))
1636 dict_remove(plain_dnrs, chan_name);
1637 reply("CSMSG_DNR_REMOVED", chan_name);
1639 else if(dict_find(mask_dnrs, chan_name, NULL))
1641 dict_remove(mask_dnrs, chan_name);
1642 reply("CSMSG_DNR_REMOVED", chan_name);
1646 reply("CSMSG_NO_SUCH_DNR", chan_name);
1653 chanserv_get_owned_count(struct handle_info *hi)
1655 struct userData *cList;
1658 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1659 if(cList->access == UL_OWNER)
1664 static CHANSERV_FUNC(cmd_register)
1666 struct handle_info *handle;
1667 struct chanData *cData;
1668 struct modeNode *mn;
1669 char reason[MAXLEN];
1671 unsigned int new_channel, force=0;
1672 struct do_not_register *dnr;
1676 if(channel->channel_info)
1678 reply("CSMSG_ALREADY_REGGED", channel->name);
1682 if(channel->bad_channel)
1684 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1689 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1691 reply("CSMSG_MUST_BE_OPPED", channel->name);
1696 chan_name = channel->name;
1700 if((argc < 2) || !IsChannelName(argv[1]))
1702 reply("MSG_NOT_CHANNEL_NAME");
1706 if(opserv_bad_channel(argv[1]))
1708 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1713 chan_name = argv[1];
1716 if(argc >= (new_channel+2))
1718 if(!IsHelping(user))
1720 reply("CSMSG_PROXY_FORBIDDEN");
1724 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1726 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1727 dnr = chanserv_is_dnr(chan_name, handle);
1731 handle = user->handle_info;
1732 dnr = chanserv_is_dnr(chan_name, handle);
1736 if(!IsHelping(user))
1737 reply("CSMSG_DNR_CHANNEL", chan_name);
1739 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1743 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1745 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1750 channel = AddChannel(argv[1], now, NULL, NULL);
1752 cData = register_channel(channel, user->handle_info->handle);
1753 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1754 cData->modes = chanserv_conf.default_modes;
1756 cData->modes.modes_set |= MODE_REGISTERED;
1757 if (IsOffChannel(cData))
1759 mod_chanmode_announce(chanserv, channel, &cData->modes);
1763 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1764 change->args[change->argc].mode = MODE_CHANOP;
1765 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1767 mod_chanmode_announce(chanserv, channel, change);
1768 mod_chanmode_free(change);
1771 /* Initialize the channel's max user record. */
1772 cData->max = channel->members.used;
1774 if(handle != user->handle_info)
1775 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1777 reply("CSMSG_REG_SUCCESS", channel->name);
1779 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1780 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1785 make_confirmation_string(struct userData *uData)
1787 static char strbuf[16];
1792 for(src = uData->handle->handle; *src; )
1793 accum = accum * 31 + toupper(*src++);
1795 for(src = uData->channel->channel->name; *src; )
1796 accum = accum * 31 + toupper(*src++);
1797 sprintf(strbuf, "%08x", accum);
1801 static CHANSERV_FUNC(cmd_unregister)
1804 char reason[MAXLEN];
1805 struct chanData *cData;
1806 struct userData *uData;
1808 cData = channel->channel_info;
1811 reply("CSMSG_NOT_REGISTERED", channel->name);
1815 uData = GetChannelUser(cData, user->handle_info);
1816 if(!uData || (uData->access < UL_OWNER))
1818 reply("CSMSG_NO_ACCESS");
1822 if(IsProtected(cData))
1824 reply("CSMSG_UNREG_NODELETE", channel->name);
1828 if(!IsHelping(user))
1830 const char *confirm_string;
1831 if(IsSuspended(cData))
1833 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1836 confirm_string = make_confirmation_string(uData);
1837 if((argc < 2) || strcmp(argv[1], confirm_string))
1839 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1844 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1845 name = strdup(channel->name);
1846 unregister_channel(cData, reason);
1847 reply("CSMSG_UNREG_SUCCESS", name);
1852 static CHANSERV_FUNC(cmd_move)
1854 struct mod_chanmode change;
1855 struct chanNode *target;
1856 struct modeNode *mn;
1857 struct userData *uData;
1858 char reason[MAXLEN];
1859 struct do_not_register *dnr;
1863 if(IsProtected(channel->channel_info))
1865 reply("CSMSG_MOVE_NODELETE", channel->name);
1869 if(!IsChannelName(argv[1]))
1871 reply("MSG_NOT_CHANNEL_NAME");
1875 if(opserv_bad_channel(argv[1]))
1877 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1881 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1883 for(uData = channel->channel_info->users; uData; uData = uData->next)
1885 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1887 if(!IsHelping(user))
1888 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1890 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1896 mod_chanmode_init(&change);
1897 if(!(target = GetChannel(argv[1])))
1899 target = AddChannel(argv[1], now, NULL, NULL);
1900 if(!IsSuspended(channel->channel_info))
1901 AddChannelUser(chanserv, target);
1903 else if(target->channel_info)
1905 reply("CSMSG_ALREADY_REGGED", target->name);
1908 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1909 && !IsHelping(user))
1911 reply("CSMSG_MUST_BE_OPPED", target->name);
1914 else if(!IsSuspended(channel->channel_info))
1917 change.args[0].mode = MODE_CHANOP;
1918 change.args[0].u.member = AddChannelUser(chanserv, target);
1919 mod_chanmode_announce(chanserv, target, &change);
1924 /* Clear MODE_REGISTERED from old channel, add it to new. */
1926 change.modes_clear = MODE_REGISTERED;
1927 mod_chanmode_announce(chanserv, channel, &change);
1928 change.modes_clear = 0;
1929 change.modes_set = MODE_REGISTERED;
1930 mod_chanmode_announce(chanserv, target, &change);
1933 /* Move the channel_info to the target channel; it
1934 shouldn't be necessary to clear timeq callbacks
1935 for the old channel. */
1936 target->channel_info = channel->channel_info;
1937 target->channel_info->channel = target;
1938 channel->channel_info = NULL;
1940 reply("CSMSG_MOVE_SUCCESS", target->name);
1942 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1943 if(!IsSuspended(target->channel_info))
1945 char reason2[MAXLEN];
1946 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1947 DelChannelUser(chanserv, channel, reason2, 0);
1949 UnlockChannel(channel);
1950 LockChannel(target);
1951 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1956 merge_users(struct chanData *source, struct chanData *target)
1958 struct userData *suData, *tuData, *next;
1964 /* Insert the source's users into the scratch area. */
1965 for(suData = source->users; suData; suData = suData->next)
1966 dict_insert(merge, suData->handle->handle, suData);
1968 /* Iterate through the target's users, looking for
1969 users common to both channels. The lower access is
1970 removed from either the scratch area or target user
1972 for(tuData = target->users; tuData; tuData = next)
1974 struct userData *choice;
1976 next = tuData->next;
1978 /* If a source user exists with the same handle as a target
1979 channel's user, resolve the conflict by removing one. */
1980 suData = dict_find(merge, tuData->handle->handle, NULL);
1984 /* Pick the data we want to keep. */
1985 /* If the access is the same, use the later seen time. */
1986 if(suData->access == tuData->access)
1987 choice = (suData->seen > tuData->seen) ? suData : tuData;
1988 else /* Otherwise, keep the higher access level. */
1989 choice = (suData->access > tuData->access) ? suData : tuData;
1991 /* Remove the user that wasn't picked. */
1992 if(choice == tuData)
1994 dict_remove(merge, suData->handle->handle);
1995 del_channel_user(suData, 0);
1998 del_channel_user(tuData, 0);
2001 /* Move the remaining users to the target channel. */
2002 for(it = dict_first(merge); it; it = iter_next(it))
2004 suData = iter_data(it);
2006 /* Insert the user into the target channel's linked list. */
2007 suData->prev = NULL;
2008 suData->next = target->users;
2009 suData->channel = target;
2012 target->users->prev = suData;
2013 target->users = suData;
2015 /* Update the user counts for the target channel; the
2016 source counts are left alone. */
2017 target->userCount++;
2020 /* Possible to assert (source->users == NULL) here. */
2021 source->users = NULL;
2026 merge_bans(struct chanData *source, struct chanData *target)
2028 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2030 /* Hold on to the original head of the target ban list
2031 to avoid comparing source bans with source bans. */
2032 tFront = target->bans;
2034 /* Perform a totally expensive O(n*m) merge, ick. */
2035 for(sbData = source->bans; sbData; sbData = sNext)
2037 /* Flag to track whether the ban's been moved
2038 to the destination yet. */
2041 /* Possible to assert (sbData->prev == NULL) here. */
2042 sNext = sbData->next;
2044 for(tbData = tFront; tbData; tbData = tNext)
2046 tNext = tbData->next;
2048 /* Perform two comparisons between each source
2049 and target ban, conflicts are resolved by
2050 keeping the broader ban and copying the later
2051 expiration and triggered time. */
2052 if(match_ircglobs(tbData->mask, sbData->mask))
2054 /* There is a broader ban in the target channel that
2055 overrides one in the source channel; remove the
2056 source ban and break. */
2057 if(sbData->expires > tbData->expires)
2058 tbData->expires = sbData->expires;
2059 if(sbData->triggered > tbData->triggered)
2060 tbData->triggered = sbData->triggered;
2061 del_channel_ban(sbData);
2064 else if(match_ircglobs(sbData->mask, tbData->mask))
2066 /* There is a broader ban in the source channel that
2067 overrides one in the target channel; remove the
2068 target ban, fall through and move the source over. */
2069 if(tbData->expires > sbData->expires)
2070 sbData->expires = tbData->expires;
2071 if(tbData->triggered > sbData->triggered)
2072 sbData->triggered = tbData->triggered;
2073 if(tbData == tFront)
2075 del_channel_ban(tbData);
2078 /* Source bans can override multiple target bans, so
2079 we allow a source to run through this loop multiple
2080 times, but we can only move it once. */
2085 /* Remove the source ban from the source ban list. */
2087 sbData->next->prev = sbData->prev;
2089 /* Modify the source ban's associated channel. */
2090 sbData->channel = target;
2092 /* Insert the ban into the target channel's linked list. */
2093 sbData->prev = NULL;
2094 sbData->next = target->bans;
2097 target->bans->prev = sbData;
2098 target->bans = sbData;
2100 /* Update the user counts for the target channel. */
2105 /* Possible to assert (source->bans == NULL) here. */
2106 source->bans = NULL;
2110 merge_data(struct chanData *source, struct chanData *target)
2112 /* Use more recent visited and owner-transfer time; use older
2113 * registered time. Bitwise or may_opchan. Use higher max.
2114 * Do not touch last_refresh, ban count or user counts.
2116 if(source->visited > target->visited)
2117 target->visited = source->visited;
2118 if(source->registered < target->registered)
2119 target->registered = source->registered;
2120 if(source->ownerTransfer > target->ownerTransfer)
2121 target->ownerTransfer = source->ownerTransfer;
2122 if(source->may_opchan)
2123 target->may_opchan = 1;
2124 if(source->max > target->max)
2125 target->max = source->max;
2129 merge_channel(struct chanData *source, struct chanData *target)
2131 merge_users(source, target);
2132 merge_bans(source, target);
2133 merge_data(source, target);
2136 static CHANSERV_FUNC(cmd_merge)
2138 struct userData *target_user;
2139 struct chanNode *target;
2140 char reason[MAXLEN];
2144 /* Make sure the target channel exists and is registered to the user
2145 performing the command. */
2146 if(!(target = GetChannel(argv[1])))
2148 reply("MSG_INVALID_CHANNEL");
2152 if(!target->channel_info)
2154 reply("CSMSG_NOT_REGISTERED", target->name);
2158 if(IsProtected(channel->channel_info))
2160 reply("CSMSG_MERGE_NODELETE");
2164 if(IsSuspended(target->channel_info))
2166 reply("CSMSG_MERGE_SUSPENDED");
2170 if(channel == target)
2172 reply("CSMSG_MERGE_SELF");
2176 target_user = GetChannelUser(target->channel_info, user->handle_info);
2177 if(!target_user || (target_user->access < UL_OWNER))
2179 reply("CSMSG_MERGE_NOT_OWNER");
2183 /* Merge the channel structures and associated data. */
2184 merge_channel(channel->channel_info, target->channel_info);
2185 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2186 unregister_channel(channel->channel_info, reason);
2187 reply("CSMSG_MERGE_SUCCESS", target->name);
2191 static CHANSERV_FUNC(cmd_opchan)
2193 struct mod_chanmode change;
2194 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2196 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2199 channel->channel_info->may_opchan = 0;
2200 mod_chanmode_init(&change);
2202 change.args[0].mode = MODE_CHANOP;
2203 change.args[0].u.member = GetUserMode(channel, chanserv);
2204 mod_chanmode_announce(chanserv, channel, &change);
2205 reply("CSMSG_OPCHAN_DONE", channel->name);
2209 static CHANSERV_FUNC(cmd_adduser)
2211 struct userData *actee;
2212 struct userData *actor;
2213 struct handle_info *handle;
2214 unsigned short access;
2218 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2220 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2224 access = user_level_from_name(argv[2], UL_OWNER);
2227 reply("CSMSG_INVALID_ACCESS", argv[2]);
2231 actor = GetChannelUser(channel->channel_info, user->handle_info);
2232 if(actor->access <= access)
2234 reply("CSMSG_NO_BUMP_ACCESS");
2238 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2241 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2243 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2247 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2248 scan_user_presence(actee, NULL);
2249 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2253 static CHANSERV_FUNC(cmd_clvl)
2255 struct handle_info *handle;
2256 struct userData *victim;
2257 struct userData *actor;
2258 unsigned short new_access;
2259 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2263 actor = GetChannelUser(channel->channel_info, user->handle_info);
2265 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2268 if(handle == user->handle_info && !privileged)
2270 reply("CSMSG_NO_SELF_CLVL");
2274 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2276 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2280 if(actor->access <= victim->access && !privileged)
2282 reply("MSG_USER_OUTRANKED", handle->handle);
2286 new_access = user_level_from_name(argv[2], UL_OWNER);
2290 reply("CSMSG_INVALID_ACCESS", argv[2]);
2294 if(new_access >= actor->access && !privileged)
2296 reply("CSMSG_NO_BUMP_ACCESS");
2300 victim->access = new_access;
2301 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2305 static CHANSERV_FUNC(cmd_deluser)
2307 struct handle_info *handle;
2308 struct userData *victim;
2309 struct userData *actor;
2310 unsigned short access;
2315 actor = GetChannelUser(channel->channel_info, user->handle_info);
2317 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2320 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2322 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2328 access = user_level_from_name(argv[1], UL_OWNER);
2331 reply("CSMSG_INVALID_ACCESS", argv[1]);
2334 if(access != victim->access)
2336 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2342 access = victim->access;
2345 if((actor->access <= victim->access) && !IsHelping(user))
2347 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2351 chan_name = strdup(channel->name);
2352 del_channel_user(victim, 1);
2353 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2359 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2361 struct userData *actor, *uData, *next;
2363 actor = GetChannelUser(channel->channel_info, user->handle_info);
2365 if(min_access > max_access)
2367 reply("CSMSG_BAD_RANGE", min_access, max_access);
2371 if((actor->access <= max_access) && !IsHelping(user))
2373 reply("CSMSG_NO_ACCESS");
2377 for(uData = channel->channel_info->users; uData; uData = next)
2381 if((uData->access >= min_access)
2382 && (uData->access <= max_access)
2383 && match_ircglob(uData->handle->handle, mask))
2384 del_channel_user(uData, 1);
2387 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2391 static CHANSERV_FUNC(cmd_mdelowner)
2393 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2396 static CHANSERV_FUNC(cmd_mdelcoowner)
2398 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2401 static CHANSERV_FUNC(cmd_mdelmaster)
2403 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2406 static CHANSERV_FUNC(cmd_mdelop)
2408 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2411 static CHANSERV_FUNC(cmd_mdelpeon)
2413 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2417 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2419 struct banData *bData, *next;
2420 char interval[INTERVALLEN];
2425 limit = now - duration;
2426 for(bData = channel->channel_info->bans; bData; bData = next)
2430 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2433 del_channel_ban(bData);
2437 intervalString(interval, duration, user->handle_info);
2438 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2443 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2445 struct userData *actor, *uData, *next;
2446 char interval[INTERVALLEN];
2450 actor = GetChannelUser(channel->channel_info, user->handle_info);
2451 if(min_access > max_access)
2453 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2457 if((actor->access <= max_access) && !IsHelping(user))
2459 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2464 limit = now - duration;
2465 for(uData = channel->channel_info->users; uData; uData = next)
2469 if((uData->seen > limit)
2471 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2474 if(((uData->access >= min_access) && (uData->access <= max_access))
2475 || (!max_access && (uData->access < actor->access)))
2477 del_channel_user(uData, 1);
2485 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2487 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2491 static CHANSERV_FUNC(cmd_trim)
2493 unsigned long duration;
2494 unsigned short min_level, max_level;
2499 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2500 duration = ParseInterval(argv[2]);
2503 reply("CSMSG_CANNOT_TRIM");
2507 if(!irccasecmp(argv[1], "bans"))
2509 cmd_trim_bans(user, channel, duration);
2512 else if(!irccasecmp(argv[1], "users"))
2514 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2517 else if(parse_level_range(&min_level, &max_level, argv[1]))
2519 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2522 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2524 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2529 reply("CSMSG_INVALID_TRIM", argv[1]);
2534 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2535 to the user. cmd_all takes advantage of this. */
2536 static CHANSERV_FUNC(cmd_up)
2538 struct mod_chanmode change;
2539 struct userData *uData;
2542 mod_chanmode_init(&change);
2544 change.args[0].u.member = GetUserMode(channel, user);
2545 if(!change.args[0].u.member)
2548 reply("MSG_CHANNEL_ABSENT", channel->name);
2552 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2556 reply("CSMSG_GODMODE_UP", argv[0]);
2559 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2561 change.args[0].mode = MODE_CHANOP;
2562 errmsg = "CSMSG_ALREADY_OPPED";
2564 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2566 change.args[0].mode = MODE_VOICE;
2567 errmsg = "CSMSG_ALREADY_VOICED";
2572 reply("CSMSG_NO_ACCESS");
2575 change.args[0].mode &= ~change.args[0].u.member->modes;
2576 if(!change.args[0].mode)
2579 reply(errmsg, channel->name);
2582 modcmd_chanmode_announce(&change);
2586 static CHANSERV_FUNC(cmd_down)
2588 struct mod_chanmode change;
2590 mod_chanmode_init(&change);
2592 change.args[0].u.member = GetUserMode(channel, user);
2593 if(!change.args[0].u.member)
2596 reply("MSG_CHANNEL_ABSENT", channel->name);
2600 if(!change.args[0].u.member->modes)
2603 reply("CSMSG_ALREADY_DOWN", channel->name);
2607 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2608 modcmd_chanmode_announce(&change);
2612 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)
2614 struct userData *cList;
2616 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2618 if(IsSuspended(cList->channel)
2619 || IsUserSuspended(cList)
2620 || !GetUserMode(cList->channel->channel, user))
2623 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2629 static CHANSERV_FUNC(cmd_upall)
2631 return cmd_all(CSFUNC_ARGS, cmd_up);
2634 static CHANSERV_FUNC(cmd_downall)
2636 return cmd_all(CSFUNC_ARGS, cmd_down);
2639 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2640 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2643 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)
2645 unsigned int ii, valid;
2646 struct userNode *victim;
2647 struct mod_chanmode *change;
2649 change = mod_chanmode_alloc(argc - 1);
2651 for(ii=valid=0; ++ii < argc; )
2653 if(!(victim = GetUserH(argv[ii])))
2655 change->args[valid].mode = mode;
2656 change->args[valid].u.member = GetUserMode(channel, victim);
2657 if(!change->args[valid].u.member)
2659 if(validate && !validate(user, channel, victim))
2664 change->argc = valid;
2665 if(valid < (argc-1))
2666 reply("CSMSG_PROCESS_FAILED");
2669 modcmd_chanmode_announce(change);
2670 reply(action, channel->name);
2672 mod_chanmode_free(change);
2676 static CHANSERV_FUNC(cmd_op)
2678 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2681 static CHANSERV_FUNC(cmd_deop)
2683 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2686 static CHANSERV_FUNC(cmd_voice)
2688 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2691 static CHANSERV_FUNC(cmd_devoice)
2693 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2697 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2703 for(ii=0; ii<channel->members.used; ii++)
2705 struct modeNode *mn = channel->members.list[ii];
2707 if(IsService(mn->user))
2710 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2713 if(protect_user(mn->user, user, channel->channel_info))
2717 victims[(*victimCount)++] = mn;
2723 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2725 struct userNode *victim;
2726 struct modeNode **victims;
2727 unsigned int offset, n, victimCount, duration = 0;
2728 char *reason = "Bye.", *ban, *name;
2729 char interval[INTERVALLEN];
2731 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2732 REQUIRE_PARAMS(offset);
2735 reason = unsplit_string(argv + offset, argc - offset, NULL);
2736 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2738 /* Truncate the reason to a length of TOPICLEN, as
2739 the ircd does; however, leave room for an ellipsis
2740 and the kicker's nick. */
2741 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2745 if((victim = GetUserH(argv[1])))
2747 victims = alloca(sizeof(victims[0]));
2748 victims[0] = GetUserMode(channel, victim);
2749 /* XXX: The comparison with ACTION_KICK is just because all
2750 * other actions can work on users outside the channel, and we
2751 * want to allow those (e.g. unbans) in that case. If we add
2752 * some other ejection action for in-channel users, change
2754 victimCount = victims[0] ? 1 : 0;
2756 if(IsService(victim))
2758 reply("MSG_SERVICE_IMMUNE", victim->nick);
2762 if((action == ACTION_KICK) && !victimCount)
2764 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2768 if(protect_user(victim, user, channel->channel_info))
2770 reply("CSMSG_USER_PROTECTED", victim->nick);
2774 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2775 name = victim->nick;
2779 if(!is_ircmask(argv[1]))
2781 reply("MSG_NICK_UNKNOWN", argv[1]);
2785 victims = alloca(sizeof(victims[0]) * channel->members.used);
2787 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2789 reply("CSMSG_MASK_PROTECTED", argv[1]);
2793 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2795 reply("CSMSG_LAME_MASK", argv[1]);
2799 if((action == ACTION_KICK) && (victimCount == 0))
2801 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2805 name = ban = strdup(argv[1]);
2808 /* Truncate the ban in place if necessary; we must ensure
2809 that 'ban' is a valid ban mask before sanitizing it. */
2810 sanitize_ircmask(ban);
2812 if(action & ACTION_ADD_BAN)
2814 struct banData *bData, *next;
2816 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2818 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2823 if(action & ACTION_ADD_TIMED_BAN)
2825 duration = ParseInterval(argv[2]);
2829 reply("CSMSG_DURATION_TOO_LOW");
2833 else if(duration > (86400 * 365 * 2))
2835 reply("CSMSG_DURATION_TOO_HIGH");
2841 for(bData = channel->channel_info->bans; bData; bData = next)
2843 if(match_ircglobs(bData->mask, ban))
2845 int exact = !irccasecmp(bData->mask, ban);
2847 /* The ban is redundant; there is already a ban
2848 with the same effect in place. */
2852 free(bData->reason);
2853 bData->reason = strdup(reason);
2854 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2856 reply("CSMSG_REASON_CHANGE", ban);
2860 if(exact && bData->expires)
2864 /* If the ban matches an existing one exactly,
2865 extend the expiration time if the provided
2866 duration is longer. */
2867 if(duration && ((time_t)(now + duration) > bData->expires))
2869 bData->expires = now + duration;
2880 /* Delete the expiration timeq entry and
2881 requeue if necessary. */
2882 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2885 timeq_add(bData->expires, expire_ban, bData);
2889 /* automated kickban */
2892 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2894 reply("CSMSG_BAN_ADDED", name, channel->name);
2900 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2907 if(match_ircglobs(ban, bData->mask))
2909 /* The ban we are adding makes previously existing
2910 bans redundant; silently remove them. */
2911 del_channel_ban(bData);
2915 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);
2917 name = ban = strdup(bData->mask);
2921 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2923 extern const char *hidden_host_suffix;
2924 const char *old_name = chanserv_conf.old_ban_names->list[n];
2926 unsigned int l1, l2;
2929 l2 = strlen(old_name);
2932 if(irccasecmp(ban + l1 - l2, old_name))
2934 new_mask = malloc(MAXLEN);
2935 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2937 name = ban = new_mask;
2942 if(action & ACTION_BAN)
2944 unsigned int exists;
2945 struct mod_chanmode *change;
2947 if(channel->banlist.used >= MAXBANS)
2950 reply("CSMSG_BANLIST_FULL", channel->name);
2955 exists = ChannelBanExists(channel, ban);
2956 change = mod_chanmode_alloc(victimCount + 1);
2957 for(n = 0; n < victimCount; ++n)
2959 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2960 change->args[n].u.member = victims[n];
2964 change->args[n].mode = MODE_BAN;
2965 change->args[n++].u.hostmask = ban;
2969 modcmd_chanmode_announce(change);
2971 mod_chanmode_announce(chanserv, channel, change);
2972 mod_chanmode_free(change);
2974 if(exists && (action == ACTION_BAN))
2977 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2983 if(action & ACTION_KICK)
2985 char kick_reason[MAXLEN];
2986 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2988 for(n = 0; n < victimCount; n++)
2989 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2994 /* No response, since it was automated. */
2996 else if(action & ACTION_ADD_BAN)
2999 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3001 reply("CSMSG_BAN_ADDED", name, channel->name);
3003 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3004 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3005 else if(action & ACTION_BAN)
3006 reply("CSMSG_BAN_DONE", name, channel->name);
3007 else if(action & ACTION_KICK && victimCount)
3008 reply("CSMSG_KICK_DONE", name, channel->name);
3014 static CHANSERV_FUNC(cmd_kickban)
3016 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3019 static CHANSERV_FUNC(cmd_kick)
3021 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3024 static CHANSERV_FUNC(cmd_ban)
3026 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3029 static CHANSERV_FUNC(cmd_addban)
3031 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3034 static CHANSERV_FUNC(cmd_addtimedban)
3036 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3039 static struct mod_chanmode *
3040 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3042 struct mod_chanmode *change;
3043 unsigned char *match;
3044 unsigned int ii, count;
3046 match = alloca(bans->used);
3049 for(ii = count = 0; ii < bans->used; ++ii)
3051 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3052 MATCH_USENICK | MATCH_VISIBLE);
3059 for(ii = count = 0; ii < bans->used; ++ii)
3061 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3068 change = mod_chanmode_alloc(count);
3069 for(ii = count = 0; ii < bans->used; ++ii)
3073 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3074 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3076 assert(count == change->argc);
3081 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3083 struct userNode *actee;
3089 /* may want to allow a comma delimited list of users... */
3090 if(!(actee = GetUserH(argv[1])))
3092 if(!is_ircmask(argv[1]))
3094 reply("MSG_NICK_UNKNOWN", argv[1]);
3098 mask = strdup(argv[1]);
3101 /* We don't sanitize the mask here because ircu
3103 if(action & ACTION_UNBAN)
3105 struct mod_chanmode *change;
3106 change = find_matching_bans(&channel->banlist, actee, mask);
3111 modcmd_chanmode_announce(change);
3112 for(ii = 0; ii < change->argc; ++ii)
3113 free((char*)change->args[ii].u.hostmask);
3114 mod_chanmode_free(change);
3119 if(action & ACTION_DEL_BAN)
3121 struct banData *ban, *next;
3123 ban = channel->channel_info->bans;
3127 for( ; ban && !user_matches_glob(actee, ban->mask,
3128 MATCH_USENICK | MATCH_VISIBLE);
3131 for( ; ban && !match_ircglobs(mask, ban->mask);
3136 del_channel_ban(ban);
3143 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3145 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3151 static CHANSERV_FUNC(cmd_unban)
3153 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3156 static CHANSERV_FUNC(cmd_delban)
3158 /* it doesn't necessarily have to remove the channel ban - may want
3159 to make that an option. */
3160 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3163 static CHANSERV_FUNC(cmd_unbanme)
3165 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3166 long flags = ACTION_UNBAN;
3168 /* remove permanent bans if the user has the proper access. */
3169 if(uData->access >= UL_MASTER)
3170 flags |= ACTION_DEL_BAN;
3172 argv[1] = user->nick;
3173 return unban_user(user, channel, 2, argv, cmd, flags);
3176 static CHANSERV_FUNC(cmd_unbanall)
3178 struct mod_chanmode *change;
3181 if(!channel->banlist.used)
3183 reply("CSMSG_NO_BANS", channel->name);
3187 change = mod_chanmode_alloc(channel->banlist.used);
3188 for(ii=0; ii<channel->banlist.used; ii++)
3190 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3191 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3193 modcmd_chanmode_announce(change);
3194 for(ii = 0; ii < change->argc; ++ii)
3195 free((char*)change->args[ii].u.hostmask);
3196 mod_chanmode_free(change);
3197 reply("CSMSG_BANS_REMOVED", channel->name);
3201 static CHANSERV_FUNC(cmd_open)
3203 struct mod_chanmode *change;
3206 change = find_matching_bans(&channel->banlist, user, NULL);
3208 change = mod_chanmode_alloc(0);
3209 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3210 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3211 && channel->channel_info->modes.modes_set)
3212 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3213 modcmd_chanmode_announce(change);
3214 reply("CSMSG_CHANNEL_OPENED", channel->name);
3215 for(ii = 0; ii < change->argc; ++ii)
3216 free((char*)change->args[ii].u.hostmask);
3217 mod_chanmode_free(change);
3221 static CHANSERV_FUNC(cmd_myaccess)
3223 static struct string_buffer sbuf;
3224 struct handle_info *target_handle;
3225 struct userData *uData;
3228 target_handle = user->handle_info;
3229 else if(!IsHelping(user))
3231 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3234 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3237 if(!target_handle->channels)
3239 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3243 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3244 for(uData = target_handle->channels; uData; uData = uData->u_next)
3246 struct chanData *cData = uData->channel;
3248 if(uData->access > UL_OWNER)
3250 if(IsProtected(cData)
3251 && (target_handle != user->handle_info)
3252 && !GetTrueChannelAccess(cData, user->handle_info))
3255 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3256 if(uData->flags != USER_AUTO_OP)
3257 string_buffer_append(&sbuf, ',');
3258 if(IsUserSuspended(uData))
3259 string_buffer_append(&sbuf, 's');
3260 if(IsUserAutoOp(uData))
3262 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3263 string_buffer_append(&sbuf, 'o');
3264 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3265 string_buffer_append(&sbuf, 'v');
3267 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3268 string_buffer_append(&sbuf, 'i');
3270 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3272 string_buffer_append_string(&sbuf, ")]");
3273 string_buffer_append(&sbuf, '\0');
3274 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3280 static CHANSERV_FUNC(cmd_access)
3282 struct userNode *target;
3283 struct handle_info *target_handle;
3284 struct userData *uData;
3286 char prefix[MAXLEN];
3291 target_handle = target->handle_info;
3293 else if((target = GetUserH(argv[1])))
3295 target_handle = target->handle_info;
3297 else if(argv[1][0] == '*')
3299 if(!(target_handle = get_handle_info(argv[1]+1)))
3301 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3307 reply("MSG_NICK_UNKNOWN", argv[1]);
3311 assert(target || target_handle);
3313 if(target == chanserv)
3315 reply("CSMSG_IS_CHANSERV");
3323 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3328 reply("MSG_USER_AUTHENTICATE", target->nick);
3331 reply("MSG_AUTHENTICATE");
3337 const char *epithet = NULL, *type = NULL;
3340 epithet = chanserv_conf.irc_operator_epithet;
3343 else if(IsNetworkHelper(target))
3345 epithet = chanserv_conf.network_helper_epithet;
3346 type = "network helper";
3348 else if(IsSupportHelper(target))
3350 epithet = chanserv_conf.support_helper_epithet;
3351 type = "support helper";
3355 if(target_handle->epithet)
3356 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3358 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3360 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3364 sprintf(prefix, "%s", target_handle->handle);
3367 if(!channel->channel_info)
3369 reply("CSMSG_NOT_REGISTERED", channel->name);
3373 helping = HANDLE_FLAGGED(target_handle, HELPING)
3374 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3375 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3377 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3378 /* To prevent possible information leaks, only show infolines
3379 * if the requestor is in the channel or it's their own
3381 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3383 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3385 /* Likewise, only say it's suspended if the user has active
3386 * access in that channel or it's their own entry. */
3387 if(IsUserSuspended(uData)
3388 && (GetChannelUser(channel->channel_info, user->handle_info)
3389 || (user->handle_info == uData->handle)))
3391 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3396 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3403 zoot_list(struct listData *list)
3405 struct userData *uData;
3406 unsigned int start, curr, highest, lowest;
3407 struct helpfile_table tmp_table;
3408 const char **temp, *msg;
3410 if(list->table.length == 1)
3413 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3415 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3416 msg = user_find_message(list->user, "MSG_NONE");
3417 send_message_type(4, list->user, list->bot, " %s", msg);
3419 tmp_table.width = list->table.width;
3420 tmp_table.flags = list->table.flags;
3421 list->table.contents[0][0] = " ";
3422 highest = list->highest;
3423 if(list->lowest != 0)
3424 lowest = list->lowest;
3425 else if(highest < 100)
3428 lowest = highest - 100;
3429 for(start = curr = 1; curr < list->table.length; )
3431 uData = list->users[curr-1];
3432 list->table.contents[curr++][0] = " ";
3433 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3436 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3438 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3439 temp = list->table.contents[--start];
3440 list->table.contents[start] = list->table.contents[0];
3441 tmp_table.contents = list->table.contents + start;
3442 tmp_table.length = curr - start;
3443 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3444 list->table.contents[start] = temp;
3446 highest = lowest - 1;
3447 lowest = (highest < 100) ? 0 : (highest - 99);
3453 def_list(struct listData *list)
3457 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3459 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3460 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3461 if(list->table.length == 1)
3463 msg = user_find_message(list->user, "MSG_NONE");
3464 send_message_type(4, list->user, list->bot, " %s", msg);
3469 userData_access_comp(const void *arg_a, const void *arg_b)
3471 const struct userData *a = *(struct userData**)arg_a;
3472 const struct userData *b = *(struct userData**)arg_b;
3474 if(a->access != b->access)
3475 res = b->access - a->access;
3477 res = irccasecmp(a->handle->handle, b->handle->handle);
3482 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3484 void (*send_list)(struct listData *);
3485 struct userData *uData;
3486 struct listData lData;
3487 unsigned int matches;
3491 lData.bot = cmd->parent->bot;
3492 lData.channel = channel;
3493 lData.lowest = lowest;
3494 lData.highest = highest;
3495 lData.search = (argc > 1) ? argv[1] : NULL;
3496 send_list = def_list;
3497 (void)zoot_list; /* since it doesn't show user levels */
3499 if(user->handle_info)
3501 switch(user->handle_info->userlist_style)
3503 case HI_STYLE_DEF: send_list = def_list; break;
3504 case HI_STYLE_ZOOT: send_list = def_list; break;
3508 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3510 for(uData = channel->channel_info->users; uData; uData = uData->next)
3512 if((uData->access < lowest)
3513 || (uData->access > highest)
3514 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3516 lData.users[matches++] = uData;
3518 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3520 lData.table.length = matches+1;
3521 lData.table.width = 4;
3522 lData.table.flags = TABLE_NO_FREE;
3523 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3524 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3525 lData.table.contents[0] = ary;
3528 ary[2] = "Last Seen";
3530 for(matches = 1; matches < lData.table.length; ++matches)
3532 struct userData *uData = lData.users[matches-1];
3533 char seen[INTERVALLEN];
3535 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3536 lData.table.contents[matches] = ary;
3537 ary[0] = strtab(uData->access);
3538 ary[1] = uData->handle->handle;
3541 else if(!uData->seen)
3544 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3545 ary[2] = strdup(ary[2]);
3546 if(IsUserSuspended(uData))
3547 ary[3] = "Suspended";
3548 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3549 ary[3] = "Vacation";
3554 for(matches = 1; matches < lData.table.length; ++matches)
3556 free((char*)lData.table.contents[matches][2]);
3557 free(lData.table.contents[matches]);
3559 free(lData.table.contents[0]);
3560 free(lData.table.contents);
3564 static CHANSERV_FUNC(cmd_users)
3566 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3569 static CHANSERV_FUNC(cmd_wlist)
3571 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3574 static CHANSERV_FUNC(cmd_clist)
3576 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3579 static CHANSERV_FUNC(cmd_mlist)
3581 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3584 static CHANSERV_FUNC(cmd_olist)
3586 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3589 static CHANSERV_FUNC(cmd_plist)
3591 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3594 static CHANSERV_FUNC(cmd_bans)
3596 struct userNode *search_u = NULL;
3597 struct helpfile_table tbl;
3598 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3599 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3600 const char *msg_never, *triggered, *expires;
3601 struct banData *ban, **bans;
3605 else if(strchr(search = argv[1], '!'))
3608 search_wilds = search[strcspn(search, "?*")];
3610 else if(!(search_u = GetUserH(search)))
3611 reply("MSG_NICK_UNKNOWN", search);
3613 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3615 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3619 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3624 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3627 bans[matches++] = ban;
3632 tbl.length = matches + 1;
3633 tbl.width = 4 + timed;
3635 tbl.flags = TABLE_NO_FREE;
3636 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3637 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3638 tbl.contents[0][0] = "Mask";
3639 tbl.contents[0][1] = "Set By";
3640 tbl.contents[0][2] = "Triggered";
3643 tbl.contents[0][3] = "Expires";
3644 tbl.contents[0][4] = "Reason";
3647 tbl.contents[0][3] = "Reason";
3650 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3652 free(tbl.contents[0]);
3657 msg_never = user_find_message(user, "MSG_NEVER");
3658 for(ii = 0; ii < matches; )
3664 else if(ban->expires)
3665 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3667 expires = msg_never;
3670 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3672 triggered = msg_never;
3674 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3675 tbl.contents[ii][0] = ban->mask;
3676 tbl.contents[ii][1] = ban->owner;
3677 tbl.contents[ii][2] = strdup(triggered);
3680 tbl.contents[ii][3] = strdup(expires);
3681 tbl.contents[ii][4] = ban->reason;
3684 tbl.contents[ii][3] = ban->reason;
3686 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3687 reply("MSG_MATCH_COUNT", matches);
3688 for(ii = 1; ii < tbl.length; ++ii)
3690 free((char*)tbl.contents[ii][2]);
3692 free((char*)tbl.contents[ii][3]);
3693 free(tbl.contents[ii]);
3695 free(tbl.contents[0]);
3701 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3703 struct chanData *cData = channel->channel_info;
3704 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3706 if(cData->topic_mask)
3707 return !match_ircglob(new_topic, cData->topic_mask);
3708 else if(cData->topic)
3709 return irccasecmp(new_topic, cData->topic);
3714 static CHANSERV_FUNC(cmd_topic)
3716 struct chanData *cData;
3719 cData = channel->channel_info;
3724 SetChannelTopic(channel, chanserv, cData->topic, 1);
3725 reply("CSMSG_TOPIC_SET", cData->topic);
3729 reply("CSMSG_NO_TOPIC", channel->name);
3733 topic = unsplit_string(argv + 1, argc - 1, NULL);
3734 /* If they say "!topic *", use an empty topic. */
3735 if((topic[0] == '*') && (topic[1] == 0))
3737 if(bad_topic(channel, user, topic))
3739 char *topic_mask = cData->topic_mask;
3742 char new_topic[TOPICLEN+1], tchar;
3743 int pos=0, starpos=-1, dpos=0, len;
3745 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3752 len = strlen(topic);
3753 if((dpos + len) > TOPICLEN)
3754 len = TOPICLEN + 1 - dpos;
3755 memcpy(new_topic+dpos, topic, len);
3759 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3760 default: new_topic[dpos++] = tchar; break;
3763 if((dpos > TOPICLEN) || tchar)
3766 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3767 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3770 new_topic[dpos] = 0;
3771 SetChannelTopic(channel, chanserv, new_topic, 1);
3773 reply("CSMSG_TOPIC_LOCKED", channel->name);
3778 SetChannelTopic(channel, chanserv, topic, 1);
3780 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3782 /* Grab the topic and save it as the default topic. */
3784 cData->topic = strdup(channel->topic);
3790 static CHANSERV_FUNC(cmd_mode)
3792 struct userData *uData;
3793 struct mod_chanmode *change;
3798 change = &channel->channel_info->modes;
3799 if(change->modes_set || change->modes_clear) {
3800 modcmd_chanmode_announce(change);
3801 reply("CSMSG_DEFAULTED_MODES", channel->name);
3803 reply("CSMSG_NO_MODES", channel->name);
3807 uData = GetChannelUser(channel->channel_info, user->handle_info);
3809 base_oplevel = MAXOPLEVEL;
3810 else if (uData->access >= UL_OWNER)
3813 base_oplevel = 1 + UL_OWNER - uData->access;
3814 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3817 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3821 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3822 && mode_lock_violated(&channel->channel_info->modes, change))
3825 mod_chanmode_format(&channel->channel_info->modes, modes);
3826 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3830 modcmd_chanmode_announce(change);
3831 mod_chanmode_free(change);
3832 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3836 static CHANSERV_FUNC(cmd_invite)
3838 struct userData *uData;
3839 struct userNode *invite;
3841 uData = GetChannelUser(channel->channel_info, user->handle_info);
3845 if(!(invite = GetUserH(argv[1])))
3847 reply("MSG_NICK_UNKNOWN", argv[1]);
3854 if(GetUserMode(channel, invite))
3856 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3864 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3865 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3868 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3870 irc_invite(chanserv, invite, channel);
3872 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3877 static CHANSERV_FUNC(cmd_inviteme)
3879 if(GetUserMode(channel, user))
3881 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3884 if(channel->channel_info
3885 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3887 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3890 irc_invite(cmd->parent->bot, user, channel);
3895 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3898 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3900 /* We display things based on two dimensions:
3901 * - Issue time: present or absent
3902 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3903 * (in order of precedence, so something both expired and revoked
3904 * only counts as revoked)
3906 combo = (suspended->issued ? 4 : 0)
3907 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3909 case 0: /* no issue time, indefinite expiration */
3910 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3912 case 1: /* no issue time, expires in future */
3913 intervalString(buf1, suspended->expires-now, user->handle_info);
3914 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3916 case 2: /* no issue time, expired */
3917 intervalString(buf1, now-suspended->expires, user->handle_info);
3918 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3920 case 3: /* no issue time, revoked */
3921 intervalString(buf1, now-suspended->revoked, user->handle_info);
3922 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3924 case 4: /* issue time set, indefinite expiration */
3925 intervalString(buf1, now-suspended->issued, user->handle_info);
3926 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3928 case 5: /* issue time set, expires in future */
3929 intervalString(buf1, now-suspended->issued, user->handle_info);
3930 intervalString(buf2, suspended->expires-now, user->handle_info);
3931 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3933 case 6: /* issue time set, expired */
3934 intervalString(buf1, now-suspended->issued, user->handle_info);
3935 intervalString(buf2, now-suspended->expires, user->handle_info);
3936 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3938 case 7: /* issue time set, revoked */
3939 intervalString(buf1, now-suspended->issued, user->handle_info);
3940 intervalString(buf2, now-suspended->revoked, user->handle_info);
3941 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3944 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3949 static CHANSERV_FUNC(cmd_info)
3951 char modes[MAXLEN], buffer[INTERVALLEN];
3952 struct userData *uData, *owner;
3953 struct chanData *cData;
3954 struct do_not_register *dnr;
3959 cData = channel->channel_info;
3960 reply("CSMSG_CHANNEL_INFO", channel->name);
3962 uData = GetChannelUser(cData, user->handle_info);
3963 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3965 mod_chanmode_format(&cData->modes, modes);
3966 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3967 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3970 for(it = dict_first(cData->notes); it; it = iter_next(it))
3974 note = iter_data(it);
3975 if(!note_type_visible_to_user(cData, note->type, user))
3978 padding = PADLEN - 1 - strlen(iter_key(it));
3979 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3982 reply("CSMSG_CHANNEL_MAX", cData->max);
3983 for(owner = cData->users; owner; owner = owner->next)
3984 if(owner->access == UL_OWNER)
3985 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3986 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3987 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3988 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3990 privileged = IsStaff(user);
3992 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3993 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3994 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3996 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3997 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3999 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4001 struct suspended *suspended;
4002 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4003 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4004 show_suspension_info(cmd, user, suspended);
4006 else if(IsSuspended(cData))
4008 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4009 show_suspension_info(cmd, user, cData->suspended);
4014 static CHANSERV_FUNC(cmd_netinfo)
4016 extern time_t boot_time;
4017 extern unsigned long burst_length;
4018 char interval[INTERVALLEN];
4020 reply("CSMSG_NETWORK_INFO");
4021 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4022 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4023 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4024 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4025 reply("CSMSG_NETWORK_BANS", banCount);
4026 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4027 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4028 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4033 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4035 struct helpfile_table table;
4037 struct userNode *user;
4042 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4043 table.contents = alloca(list->used*sizeof(*table.contents));
4044 for(nn=0; nn<list->used; nn++)
4046 user = list->list[nn];
4047 if(user->modes & skip_flags)
4051 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4054 nick = alloca(strlen(user->nick)+3);
4055 sprintf(nick, "(%s)", user->nick);
4059 table.contents[table.length][0] = nick;
4062 table_send(chanserv, to->nick, 0, NULL, table);
4065 static CHANSERV_FUNC(cmd_ircops)
4067 reply("CSMSG_STAFF_OPERS");
4068 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4072 static CHANSERV_FUNC(cmd_helpers)
4074 reply("CSMSG_STAFF_HELPERS");
4075 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4079 static CHANSERV_FUNC(cmd_staff)
4081 reply("CSMSG_NETWORK_STAFF");
4082 cmd_ircops(CSFUNC_ARGS);
4083 cmd_helpers(CSFUNC_ARGS);
4087 static CHANSERV_FUNC(cmd_peek)
4089 struct modeNode *mn;
4090 char modes[MODELEN];
4092 struct helpfile_table table;
4094 irc_make_chanmode(channel, modes);
4096 reply("CSMSG_PEEK_INFO", channel->name);
4097 reply("CSMSG_PEEK_TOPIC", channel->topic);
4098 reply("CSMSG_PEEK_MODES", modes);
4099 reply("CSMSG_PEEK_USERS", channel->members.used);
4103 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4104 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4105 for(n = 0; n < channel->members.used; n++)
4107 mn = channel->members.list[n];
4108 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4110 table.contents[table.length] = alloca(sizeof(**table.contents));
4111 table.contents[table.length][0] = mn->user->nick;
4116 reply("CSMSG_PEEK_OPS");
4117 table_send(chanserv, user->nick, 0, NULL, table);
4120 reply("CSMSG_PEEK_NO_OPS");
4124 static MODCMD_FUNC(cmd_wipeinfo)
4126 struct handle_info *victim;
4127 struct userData *ud, *actor;
4130 actor = GetChannelUser(channel->channel_info, user->handle_info);
4131 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4133 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4135 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4138 if((ud->access >= actor->access) && (ud != actor))
4140 reply("MSG_USER_OUTRANKED", victim->handle);
4146 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4150 static CHANSERV_FUNC(cmd_resync)
4152 struct mod_chanmode *changes;
4153 struct chanData *cData = channel->channel_info;
4154 unsigned int ii, used;
4156 changes = mod_chanmode_alloc(channel->members.used * 2);
4157 for(ii = used = 0; ii < channel->members.used; ++ii)
4159 struct modeNode *mn = channel->members.list[ii];
4160 struct userData *uData;
4162 if(IsService(mn->user))
4165 uData = GetChannelAccess(cData, mn->user->handle_info);
4166 if(!cData->lvlOpts[lvlGiveOps]
4167 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4169 if(!(mn->modes & MODE_CHANOP))
4171 changes->args[used].mode = MODE_CHANOP;
4172 changes->args[used++].u.member = mn;
4175 else if(!cData->lvlOpts[lvlGiveVoice]
4176 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4178 if(mn->modes & MODE_CHANOP)
4180 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4181 changes->args[used++].u.member = mn;
4183 if(!(mn->modes & MODE_VOICE))
4185 changes->args[used].mode = MODE_VOICE;
4186 changes->args[used++].u.member = mn;
4193 changes->args[used].mode = MODE_REMOVE | mn->modes;
4194 changes->args[used++].u.member = mn;
4198 changes->argc = used;
4199 modcmd_chanmode_announce(changes);
4200 mod_chanmode_free(changes);
4201 reply("CSMSG_RESYNCED_USERS", channel->name);
4205 static CHANSERV_FUNC(cmd_seen)
4207 struct userData *uData;
4208 struct handle_info *handle;
4209 char seen[INTERVALLEN];
4213 if(!irccasecmp(argv[1], chanserv->nick))
4215 reply("CSMSG_IS_CHANSERV");
4219 if(!(handle = get_handle_info(argv[1])))
4221 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4225 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4227 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4232 reply("CSMSG_USER_PRESENT", handle->handle);
4233 else if(uData->seen)
4234 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4236 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4238 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4239 reply("CSMSG_USER_VACATION", handle->handle);
4244 static MODCMD_FUNC(cmd_names)
4246 struct userNode *targ;
4247 struct userData *targData;
4248 unsigned int ii, pos;
4251 for(ii=pos=0; ii<channel->members.used; ++ii)
4253 targ = channel->members.list[ii]->user;
4254 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4257 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4260 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4264 if(IsUserSuspended(targData))
4266 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4269 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4270 reply("CSMSG_END_NAMES", channel->name);
4275 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4277 switch(ntype->visible_type)
4279 case NOTE_VIS_ALL: return 1;
4280 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4281 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4286 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4288 struct userData *uData;
4290 switch(ntype->set_access_type)
4292 case NOTE_SET_CHANNEL_ACCESS:
4293 if(!user->handle_info)
4295 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4297 return uData->access >= ntype->set_access.min_ulevel;
4298 case NOTE_SET_CHANNEL_SETTER:
4299 return check_user_level(channel, user, lvlSetters, 1, 0);
4300 case NOTE_SET_PRIVILEGED: default:
4301 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4305 static CHANSERV_FUNC(cmd_note)
4307 struct chanData *cData;
4309 struct note_type *ntype;
4311 cData = channel->channel_info;
4314 reply("CSMSG_NOT_REGISTERED", channel->name);
4318 /* If no arguments, show all visible notes for the channel. */
4324 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4326 note = iter_data(it);
4327 if(!note_type_visible_to_user(cData, note->type, user))
4330 reply("CSMSG_NOTELIST_HEADER", channel->name);
4331 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4334 reply("CSMSG_NOTELIST_END", channel->name);
4336 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4338 /* If one argument, show the named note. */
4341 if((note = dict_find(cData->notes, argv[1], NULL))
4342 && note_type_visible_to_user(cData, note->type, user))
4344 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4346 else if((ntype = dict_find(note_types, argv[1], NULL))
4347 && note_type_visible_to_user(NULL, ntype, user))
4349 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4354 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4358 /* Assume they're trying to set a note. */
4362 ntype = dict_find(note_types, argv[1], NULL);
4365 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4368 else if(note_type_settable_by_user(channel, ntype, user))
4370 note_text = unsplit_string(argv+2, argc-2, NULL);
4371 if((note = dict_find(cData->notes, argv[1], NULL)))
4372 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4373 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4374 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4376 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4378 /* The note is viewable to staff only, so return 0
4379 to keep the invocation from getting logged (or
4380 regular users can see it in !events). */
4386 reply("CSMSG_NO_ACCESS");
4393 static CHANSERV_FUNC(cmd_delnote)
4398 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4399 || !note_type_settable_by_user(channel, note->type, user))
4401 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4404 dict_remove(channel->channel_info->notes, note->type->name);
4405 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4409 static CHANSERV_FUNC(cmd_events)
4411 struct logSearch discrim;
4412 struct logReport report;
4413 unsigned int matches, limit;
4415 limit = (argc > 1) ? atoi(argv[1]) : 10;
4416 if(limit < 1 || limit > 200)
4419 memset(&discrim, 0, sizeof(discrim));
4420 discrim.masks.bot = chanserv;
4421 discrim.masks.channel_name = channel->name;
4423 discrim.masks.command = argv[2];
4424 discrim.limit = limit;
4425 discrim.max_time = INT_MAX;
4426 discrim.severities = 1 << LOG_COMMAND;
4427 report.reporter = chanserv;
4429 reply("CSMSG_EVENT_SEARCH_RESULTS");
4430 matches = log_entry_search(&discrim, log_report_entry, &report);
4432 reply("MSG_MATCH_COUNT", matches);
4434 reply("MSG_NO_MATCHES");
4438 static CHANSERV_FUNC(cmd_say)
4444 msg = unsplit_string(argv + 1, argc - 1, NULL);
4445 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4447 else if(GetUserH(argv[1]))
4450 msg = unsplit_string(argv + 2, argc - 2, NULL);
4451 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4455 reply("MSG_NOT_TARGET_NAME");
4461 static CHANSERV_FUNC(cmd_emote)
4467 /* CTCP is so annoying. */
4468 msg = unsplit_string(argv + 1, argc - 1, NULL);
4469 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4471 else if(GetUserH(argv[1]))
4473 msg = unsplit_string(argv + 2, argc - 2, NULL);
4474 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4478 reply("MSG_NOT_TARGET_NAME");
4484 struct channelList *
4485 chanserv_support_channels(void)
4487 return &chanserv_conf.support_channels;
4490 static CHANSERV_FUNC(cmd_expire)
4492 int channel_count = registered_channels;
4493 expire_channels(NULL);
4494 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4499 chanserv_expire_suspension(void *data)
4501 struct suspended *suspended = data;
4502 struct chanNode *channel;
4504 if(!suspended->expires || (now < suspended->expires))
4505 suspended->revoked = now;
4506 channel = suspended->cData->channel;
4507 suspended->cData->channel = channel;
4508 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4509 if(!IsOffChannel(suspended->cData))
4511 struct mod_chanmode change;
4512 mod_chanmode_init(&change);
4514 change.args[0].mode = MODE_CHANOP;
4515 change.args[0].u.member = AddChannelUser(chanserv, channel);
4516 mod_chanmode_announce(chanserv, channel, &change);
4520 static CHANSERV_FUNC(cmd_csuspend)
4522 struct suspended *suspended;
4523 char reason[MAXLEN];
4524 time_t expiry, duration;
4525 struct userData *uData;
4529 if(IsProtected(channel->channel_info))
4531 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4535 if(argv[1][0] == '!')
4537 else if(IsSuspended(channel->channel_info))
4539 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4540 show_suspension_info(cmd, user, channel->channel_info->suspended);
4544 if(!strcmp(argv[1], "0"))
4546 else if((duration = ParseInterval(argv[1])))
4547 expiry = now + duration;
4550 reply("MSG_INVALID_DURATION", argv[1]);
4554 unsplit_string(argv + 2, argc - 2, reason);
4556 suspended = calloc(1, sizeof(*suspended));
4557 suspended->revoked = 0;
4558 suspended->issued = now;
4559 suspended->suspender = strdup(user->handle_info->handle);
4560 suspended->expires = expiry;
4561 suspended->reason = strdup(reason);
4562 suspended->cData = channel->channel_info;
4563 suspended->previous = suspended->cData->suspended;
4564 suspended->cData->suspended = suspended;
4566 if(suspended->expires)
4567 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4569 if(IsSuspended(channel->channel_info))
4571 suspended->previous->revoked = now;
4572 if(suspended->previous->expires)
4573 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4574 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4575 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4579 /* Mark all users in channel as absent. */
4580 for(uData = channel->channel_info->users; uData; uData = uData->next)
4589 /* Mark the channel as suspended, then part. */
4590 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4591 DelChannelUser(chanserv, channel, suspended->reason, 0);
4592 reply("CSMSG_SUSPENDED", channel->name);
4593 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4594 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4599 static CHANSERV_FUNC(cmd_cunsuspend)
4601 struct suspended *suspended;
4602 char message[MAXLEN];
4604 if(!IsSuspended(channel->channel_info))
4606 reply("CSMSG_NOT_SUSPENDED", channel->name);
4610 suspended = channel->channel_info->suspended;
4612 /* Expire the suspension and join ChanServ to the channel. */
4613 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4614 chanserv_expire_suspension(suspended);
4615 reply("CSMSG_UNSUSPENDED", channel->name);
4616 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4617 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4621 typedef struct chanservSearch
4629 unsigned long flags;
4633 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4636 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4641 search = malloc(sizeof(struct chanservSearch));
4642 memset(search, 0, sizeof(*search));
4645 for(i = 0; i < argc; i++)
4647 /* Assume all criteria require arguments. */
4650 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4654 if(!irccasecmp(argv[i], "name"))
4655 search->name = argv[++i];
4656 else if(!irccasecmp(argv[i], "registrar"))
4657 search->registrar = argv[++i];
4658 else if(!irccasecmp(argv[i], "unvisited"))
4659 search->unvisited = ParseInterval(argv[++i]);
4660 else if(!irccasecmp(argv[i], "registered"))
4661 search->registered = ParseInterval(argv[++i]);
4662 else if(!irccasecmp(argv[i], "flags"))
4665 if(!irccasecmp(argv[i], "nodelete"))
4666 search->flags |= CHANNEL_NODELETE;
4667 else if(!irccasecmp(argv[i], "suspended"))
4668 search->flags |= CHANNEL_SUSPENDED;
4671 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4675 else if(!irccasecmp(argv[i], "limit"))
4676 search->limit = strtoul(argv[++i], NULL, 10);
4679 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4684 if(search->name && !strcmp(search->name, "*"))
4686 if(search->registrar && !strcmp(search->registrar, "*"))
4687 search->registrar = 0;
4696 chanserv_channel_match(struct chanData *channel, search_t search)
4698 const char *name = channel->channel->name;
4699 if((search->name && !match_ircglob(name, search->name)) ||
4700 (search->registrar && !channel->registrar) ||
4701 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4702 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4703 (search->registered && (now - channel->registered) > search->registered) ||
4704 (search->flags && ((search->flags & channel->flags) != search->flags)))
4711 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4713 struct chanData *channel;
4714 unsigned int matches = 0;
4716 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4718 if(!chanserv_channel_match(channel, search))
4728 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4733 search_print(struct chanData *channel, void *data)
4735 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4738 static CHANSERV_FUNC(cmd_search)
4741 unsigned int matches;
4742 channel_search_func action;
4746 if(!irccasecmp(argv[1], "count"))
4747 action = search_count;
4748 else if(!irccasecmp(argv[1], "print"))
4749 action = search_print;
4752 reply("CSMSG_ACTION_INVALID", argv[1]);
4756 search = chanserv_search_create(user, argc - 2, argv + 2);
4760 if(action == search_count)
4761 search->limit = INT_MAX;
4763 if(action == search_print)
4764 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4766 matches = chanserv_channel_search(search, action, user);
4769 reply("MSG_MATCH_COUNT", matches);
4771 reply("MSG_NO_MATCHES");
4777 static CHANSERV_FUNC(cmd_unvisited)
4779 struct chanData *cData;
4780 time_t interval = chanserv_conf.channel_expire_delay;
4781 char buffer[INTERVALLEN];
4782 unsigned int limit = 25, matches = 0;
4786 interval = ParseInterval(argv[1]);
4788 limit = atoi(argv[2]);
4791 intervalString(buffer, interval, user->handle_info);
4792 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4794 for(cData = channelList; cData && matches < limit; cData = cData->next)
4796 if((now - cData->visited) < interval)
4799 intervalString(buffer, now - cData->visited, user->handle_info);
4800 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4807 static MODCMD_FUNC(chan_opt_defaulttopic)
4813 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4815 reply("CSMSG_TOPIC_LOCKED", channel->name);
4819 topic = unsplit_string(argv+1, argc-1, NULL);
4821 free(channel->channel_info->topic);
4822 if(topic[0] == '*' && topic[1] == 0)
4824 topic = channel->channel_info->topic = NULL;
4828 topic = channel->channel_info->topic = strdup(topic);
4829 if(channel->channel_info->topic_mask
4830 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4831 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4833 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4836 if(channel->channel_info->topic)
4837 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4839 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4843 static MODCMD_FUNC(chan_opt_topicmask)
4847 struct chanData *cData = channel->channel_info;
4850 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4852 reply("CSMSG_TOPIC_LOCKED", channel->name);
4856 mask = unsplit_string(argv+1, argc-1, NULL);
4858 if(cData->topic_mask)
4859 free(cData->topic_mask);
4860 if(mask[0] == '*' && mask[1] == 0)
4862 cData->topic_mask = 0;
4866 cData->topic_mask = strdup(mask);
4868 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4869 else if(!match_ircglob(cData->topic, cData->topic_mask))
4870 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4874 if(channel->channel_info->topic_mask)
4875 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4877 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4881 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4885 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4889 if(greeting[0] == '*' && greeting[1] == 0)
4893 unsigned int length = strlen(greeting);
4894 if(length > chanserv_conf.greeting_length)
4896 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4899 *data = strdup(greeting);
4908 reply(name, user_find_message(user, "MSG_NONE"));
4912 static MODCMD_FUNC(chan_opt_greeting)
4914 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4917 static MODCMD_FUNC(chan_opt_usergreeting)
4919 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4922 static MODCMD_FUNC(chan_opt_modes)
4924 struct mod_chanmode *new_modes;
4925 char modes[MODELEN];
4929 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4931 reply("CSMSG_NO_ACCESS");
4934 if(argv[1][0] == '*' && argv[1][1] == 0)
4936 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4938 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4940 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4943 else if(new_modes->argc > 1)
4945 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4946 mod_chanmode_free(new_modes);
4951 channel->channel_info->modes = *new_modes;
4952 modcmd_chanmode_announce(new_modes);
4953 mod_chanmode_free(new_modes);
4957 mod_chanmode_format(&channel->channel_info->modes, modes);
4959 reply("CSMSG_SET_MODES", modes);
4961 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4965 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4967 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4969 struct chanData *cData = channel->channel_info;
4974 /* Set flag according to value. */
4975 if(enabled_string(argv[1]))
4977 cData->flags |= mask;
4980 else if(disabled_string(argv[1]))
4982 cData->flags &= ~mask;
4987 reply("MSG_INVALID_BINARY", argv[1]);
4993 /* Find current option value. */
4994 value = (cData->flags & mask) ? 1 : 0;
4998 reply(name, user_find_message(user, "MSG_ON"));
5000 reply(name, user_find_message(user, "MSG_OFF"));
5004 static MODCMD_FUNC(chan_opt_nodelete)
5006 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5008 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5012 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5015 static MODCMD_FUNC(chan_opt_dynlimit)
5017 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5020 static MODCMD_FUNC(chan_opt_offchannel)
5022 struct chanData *cData = channel->channel_info;
5027 /* Set flag according to value. */
5028 if(enabled_string(argv[1]))
5030 if(!IsOffChannel(cData))
5031 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5032 cData->flags |= CHANNEL_OFFCHANNEL;
5035 else if(disabled_string(argv[1]))
5037 if(IsOffChannel(cData))
5039 struct mod_chanmode change;
5040 mod_chanmode_init(&change);
5042 change.args[0].mode = MODE_CHANOP;
5043 change.args[0].u.member = AddChannelUser(chanserv, channel);
5044 mod_chanmode_announce(chanserv, channel, &change);
5046 cData->flags &= ~CHANNEL_OFFCHANNEL;
5051 reply("MSG_INVALID_BINARY", argv[1]);
5057 /* Find current option value. */
5058 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5062 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5064 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5068 static MODCMD_FUNC(chan_opt_defaults)
5070 struct userData *uData;
5071 struct chanData *cData;
5072 const char *confirm;
5073 enum levelOption lvlOpt;
5074 enum charOption chOpt;
5076 cData = channel->channel_info;
5077 uData = GetChannelUser(cData, user->handle_info);
5078 if(!uData || (uData->access < UL_OWNER))
5080 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5083 confirm = make_confirmation_string(uData);
5084 if((argc < 2) || strcmp(argv[1], confirm))
5086 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5089 cData->flags = CHANNEL_DEFAULT_FLAGS;
5090 cData->modes = chanserv_conf.default_modes;
5091 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5092 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5093 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5094 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5095 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5100 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5102 struct chanData *cData = channel->channel_info;
5103 struct userData *uData;
5104 unsigned short value;
5108 if(!check_user_level(channel, user, option, 1, 1))
5110 reply("CSMSG_CANNOT_SET");
5113 value = user_level_from_name(argv[1], UL_OWNER+1);
5114 if(!value && strcmp(argv[1], "0"))
5116 reply("CSMSG_INVALID_ACCESS", argv[1]);
5119 uData = GetChannelUser(cData, user->handle_info);
5120 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5122 reply("CSMSG_BAD_SETLEVEL");
5128 if(value > cData->lvlOpts[lvlGiveOps])
5130 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5135 if(value < cData->lvlOpts[lvlGiveVoice])
5137 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5142 /* This test only applies to owners, since non-owners
5143 * trying to set an option to above their level get caught
5144 * by the CSMSG_BAD_SETLEVEL test above.
5146 if(value > uData->access)
5148 reply("CSMSG_BAD_SETTERS");
5155 cData->lvlOpts[option] = value;
5157 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5161 static MODCMD_FUNC(chan_opt_enfops)
5163 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5166 static MODCMD_FUNC(chan_opt_giveops)
5168 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5171 static MODCMD_FUNC(chan_opt_enfmodes)
5173 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5176 static MODCMD_FUNC(chan_opt_enftopic)
5178 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5181 static MODCMD_FUNC(chan_opt_pubcmd)
5183 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5186 static MODCMD_FUNC(chan_opt_setters)
5188 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5191 static MODCMD_FUNC(chan_opt_ctcpusers)
5193 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5196 static MODCMD_FUNC(chan_opt_userinfo)
5198 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5201 static MODCMD_FUNC(chan_opt_givevoice)
5203 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5206 static MODCMD_FUNC(chan_opt_topicsnarf)
5208 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5211 static MODCMD_FUNC(chan_opt_inviteme)
5213 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5217 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5219 struct chanData *cData = channel->channel_info;
5220 int count = charOptions[option].count, index;
5224 index = atoi(argv[1]);
5226 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5228 reply("CSMSG_INVALID_NUMERIC", index);
5229 /* Show possible values. */
5230 for(index = 0; index < count; index++)
5231 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5235 cData->chOpts[option] = charOptions[option].values[index].value;
5239 /* Find current option value. */
5242 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5246 /* Somehow, the option value is corrupt; reset it to the default. */
5247 cData->chOpts[option] = charOptions[option].default_value;
5252 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5256 static MODCMD_FUNC(chan_opt_protect)
5258 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5261 static MODCMD_FUNC(chan_opt_toys)
5263 return channel_multiple_option(chToys, CSFUNC_ARGS);
5266 static MODCMD_FUNC(chan_opt_ctcpreaction)
5268 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5271 static MODCMD_FUNC(chan_opt_topicrefresh)
5273 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5276 static struct svccmd_list set_shows_list;
5279 handle_svccmd_unbind(struct svccmd *target) {
5281 for(ii=0; ii<set_shows_list.used; ++ii)
5282 if(target == set_shows_list.list[ii])
5283 set_shows_list.used = 0;
5286 static CHANSERV_FUNC(cmd_set)
5288 struct svccmd *subcmd;
5292 /* Check if we need to (re-)initialize set_shows_list. */
5293 if(!set_shows_list.used)
5295 if(!set_shows_list.size)
5297 set_shows_list.size = chanserv_conf.set_shows->used;
5298 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5300 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5302 const char *name = chanserv_conf.set_shows->list[ii];
5303 sprintf(buf, "%s %s", argv[0], name);
5304 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5307 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5310 svccmd_list_append(&set_shows_list, subcmd);
5316 reply("CSMSG_CHANNEL_OPTIONS");
5317 for(ii = 0; ii < set_shows_list.used; ii++)
5319 subcmd = set_shows_list.list[ii];
5320 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5325 sprintf(buf, "%s %s", argv[0], argv[1]);
5326 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5329 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5332 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5334 reply("CSMSG_NO_ACCESS");
5338 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5342 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5344 struct userData *uData;
5346 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5349 reply("CSMSG_NOT_USER", channel->name);
5355 /* Just show current option value. */
5357 else if(enabled_string(argv[1]))
5359 uData->flags |= mask;
5361 else if(disabled_string(argv[1]))
5363 uData->flags &= ~mask;
5367 reply("MSG_INVALID_BINARY", argv[1]);
5371 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5375 static MODCMD_FUNC(user_opt_noautoop)
5377 struct userData *uData;
5379 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5382 reply("CSMSG_NOT_USER", channel->name);
5385 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5386 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5388 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5391 static MODCMD_FUNC(user_opt_autoinvite)
5393 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5396 static MODCMD_FUNC(user_opt_info)
5398 struct userData *uData;
5401 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5405 /* If they got past the command restrictions (which require access)
5406 * but fail this test, we have some fool with security override on.
5408 reply("CSMSG_NOT_USER", channel->name);
5415 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5416 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5418 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5421 bp = strcspn(infoline, "\001");
5424 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5429 if(infoline[0] == '*' && infoline[1] == 0)
5432 uData->info = strdup(infoline);
5435 reply("CSMSG_USET_INFO", uData->info);
5437 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5441 struct svccmd_list uset_shows_list;
5443 static CHANSERV_FUNC(cmd_uset)
5445 struct svccmd *subcmd;
5449 /* Check if we need to (re-)initialize uset_shows_list. */
5450 if(!uset_shows_list.used)
5454 "NoAutoOp", "AutoInvite", "Info"
5457 if(!uset_shows_list.size)
5459 uset_shows_list.size = ArrayLength(options);
5460 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5462 for(ii = 0; ii < ArrayLength(options); ii++)
5464 const char *name = options[ii];
5465 sprintf(buf, "%s %s", argv[0], name);
5466 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5469 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5472 svccmd_list_append(&uset_shows_list, subcmd);
5478 /* Do this so options are presented in a consistent order. */
5479 reply("CSMSG_USER_OPTIONS");
5480 for(ii = 0; ii < uset_shows_list.used; ii++)
5481 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5485 sprintf(buf, "%s %s", argv[0], argv[1]);
5486 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5489 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5493 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5496 static CHANSERV_FUNC(cmd_giveownership)
5498 struct handle_info *new_owner_hi;
5499 struct userData *new_owner, *curr_user;
5500 struct chanData *cData = channel->channel_info;
5501 struct do_not_register *dnr;
5503 unsigned short co_access;
5504 char reason[MAXLEN];
5507 curr_user = GetChannelAccess(cData, user->handle_info);
5508 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5509 if(!curr_user || (curr_user->access != UL_OWNER))
5511 struct userData *owner = NULL;
5512 for(curr_user = channel->channel_info->users;
5514 curr_user = curr_user->next)
5516 if(curr_user->access != UL_OWNER)
5520 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5527 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5529 char delay[INTERVALLEN];
5530 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5531 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5534 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5536 if(new_owner_hi == user->handle_info)
5538 reply("CSMSG_NO_TRANSFER_SELF");
5541 new_owner = GetChannelAccess(cData, new_owner_hi);
5546 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5550 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5554 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5556 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5559 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5560 if(!IsHelping(user))
5561 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5563 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5566 if(new_owner->access >= UL_COOWNER)
5567 co_access = new_owner->access;
5569 co_access = UL_COOWNER;
5570 new_owner->access = UL_OWNER;
5572 curr_user->access = co_access;
5573 cData->ownerTransfer = now;
5574 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5575 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5576 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5580 static CHANSERV_FUNC(cmd_suspend)
5582 struct handle_info *hi;
5583 struct userData *self, *target;
5586 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5587 self = GetChannelUser(channel->channel_info, user->handle_info);
5588 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5590 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5593 if(target->access >= self->access)
5595 reply("MSG_USER_OUTRANKED", hi->handle);
5598 if(target->flags & USER_SUSPENDED)
5600 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5605 target->present = 0;
5608 target->flags |= USER_SUSPENDED;
5609 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5613 static CHANSERV_FUNC(cmd_unsuspend)
5615 struct handle_info *hi;
5616 struct userData *self, *target;
5619 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5620 self = GetChannelUser(channel->channel_info, user->handle_info);
5621 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5623 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5626 if(target->access >= self->access)
5628 reply("MSG_USER_OUTRANKED", hi->handle);
5631 if(!(target->flags & USER_SUSPENDED))
5633 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5636 target->flags &= ~USER_SUSPENDED;
5637 scan_user_presence(target, NULL);
5638 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5642 static MODCMD_FUNC(cmd_deleteme)
5644 struct handle_info *hi;
5645 struct userData *target;
5646 const char *confirm_string;
5647 unsigned short access;
5650 hi = user->handle_info;
5651 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5653 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5656 if(target->access == UL_OWNER)
5658 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5661 confirm_string = make_confirmation_string(target);
5662 if((argc < 2) || strcmp(argv[1], confirm_string))
5664 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5667 access = target->access;
5668 channel_name = strdup(channel->name);
5669 del_channel_user(target, 1);
5670 reply("CSMSG_DELETED_YOU", access, channel_name);
5676 chanserv_refresh_topics(UNUSED_ARG(void *data))
5678 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5679 struct chanData *cData;
5682 for(cData = channelList; cData; cData = cData->next)
5684 if(IsSuspended(cData))
5686 opt = cData->chOpts[chTopicRefresh];
5689 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5692 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5693 cData->last_refresh = refresh_num;
5695 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5698 static CHANSERV_FUNC(cmd_unf)
5702 char response[MAXLEN];
5703 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5704 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5705 irc_privmsg(cmd->parent->bot, channel->name, response);
5708 reply("CSMSG_UNF_RESPONSE");
5712 static CHANSERV_FUNC(cmd_ping)
5716 char response[MAXLEN];
5717 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5718 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5719 irc_privmsg(cmd->parent->bot, channel->name, response);
5722 reply("CSMSG_PING_RESPONSE");
5726 static CHANSERV_FUNC(cmd_wut)
5730 char response[MAXLEN];
5731 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5732 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5733 irc_privmsg(cmd->parent->bot, channel->name, response);
5736 reply("CSMSG_WUT_RESPONSE");
5740 static CHANSERV_FUNC(cmd_8ball)
5742 unsigned int i, j, accum;
5747 for(i=1; i<argc; i++)
5748 for(j=0; argv[i][j]; j++)
5749 accum = (accum << 5) - accum + toupper(argv[i][j]);
5750 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5753 char response[MAXLEN];
5754 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5755 irc_privmsg(cmd->parent->bot, channel->name, response);
5758 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5762 static CHANSERV_FUNC(cmd_d)
5764 unsigned long sides, count, modifier, ii, total;
5765 char response[MAXLEN], *sep;
5769 if((count = strtoul(argv[1], &sep, 10)) < 1)
5779 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5780 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5784 else if((sep[0] == '-') && isdigit(sep[1]))
5785 modifier = strtoul(sep, NULL, 10);
5786 else if((sep[0] == '+') && isdigit(sep[1]))
5787 modifier = strtoul(sep+1, NULL, 10);
5794 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5799 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5802 for(total = ii = 0; ii < count; ++ii)
5803 total += (rand() % sides) + 1;
5806 if((count > 1) || modifier)
5808 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5809 sprintf(response, fmt, total, count, sides, modifier);
5813 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5814 sprintf(response, fmt, total, sides);
5817 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5819 send_message_type(4, user, cmd->parent->bot, "%s", response);
5823 static CHANSERV_FUNC(cmd_huggle)
5825 /* CTCP must be via PRIVMSG, never notice */
5827 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5829 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5834 chanserv_adjust_limit(void *data)
5836 struct mod_chanmode change;
5837 struct chanData *cData = data;
5838 struct chanNode *channel = cData->channel;
5841 if(IsSuspended(cData))
5844 cData->limitAdjusted = now;
5845 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5846 if(cData->modes.modes_set & MODE_LIMIT)
5848 if(limit > cData->modes.new_limit)
5849 limit = cData->modes.new_limit;
5850 else if(limit == cData->modes.new_limit)
5854 mod_chanmode_init(&change);
5855 change.modes_set = MODE_LIMIT;
5856 change.new_limit = limit;
5857 mod_chanmode_announce(chanserv, channel, &change);
5861 handle_new_channel(struct chanNode *channel)
5863 struct chanData *cData;
5865 if(!(cData = channel->channel_info))
5868 if(cData->modes.modes_set || cData->modes.modes_clear)
5869 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5871 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5872 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5875 /* Welcome to my worst nightmare. Warning: Read (or modify)
5876 the code below at your own risk. */
5878 handle_join(struct modeNode *mNode)
5880 struct mod_chanmode change;
5881 struct userNode *user = mNode->user;
5882 struct chanNode *channel = mNode->channel;
5883 struct chanData *cData;
5884 struct userData *uData = NULL;
5885 struct banData *bData;
5886 struct handle_info *handle;
5887 unsigned int modes = 0, info = 0;
5890 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5893 cData = channel->channel_info;
5894 if(channel->members.used > cData->max)
5895 cData->max = channel->members.used;
5897 /* Check for bans. If they're joining through a ban, one of two
5899 * 1: Join during a netburst, by riding the break. Kick them
5900 * unless they have ops or voice in the channel.
5901 * 2: They're allowed to join through the ban (an invite in
5902 * ircu2.10, or a +e on Hybrid, or something).
5903 * If they're not joining through a ban, and the banlist is not
5904 * full, see if they're on the banlist for the channel. If so,
5907 if(user->uplink->burst && !mNode->modes)
5910 for(ii = 0; ii < channel->banlist.used; ii++)
5912 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5914 /* Riding a netburst. Naughty. */
5915 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5921 mod_chanmode_init(&change);
5923 if(channel->banlist.used < MAXBANS)
5925 /* Not joining through a ban. */
5926 for(bData = cData->bans;
5927 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5928 bData = bData->next);
5932 char kick_reason[MAXLEN];
5933 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5935 bData->triggered = now;
5936 if(bData != cData->bans)
5938 /* Shuffle the ban to the head of the list. */
5940 bData->next->prev = bData->prev;
5942 bData->prev->next = bData->next;
5945 bData->next = cData->bans;
5948 cData->bans->prev = bData;
5949 cData->bans = bData;
5952 change.args[0].mode = MODE_BAN;
5953 change.args[0].u.hostmask = bData->mask;
5954 mod_chanmode_announce(chanserv, channel, &change);
5955 KickChannelUser(user, channel, chanserv, kick_reason);
5960 /* ChanServ will not modify the limits in join-flooded channels.
5961 It will also skip DynLimit processing when the user (or srvx)
5962 is bursting in, because there are likely more incoming. */
5963 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5964 && !user->uplink->burst
5965 && !channel->join_flooded
5966 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5968 /* The user count has begun "bumping" into the channel limit,
5969 so set a timer to raise the limit a bit. Any previous
5970 timers are removed so three incoming users within the delay
5971 results in one limit change, not three. */
5973 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5974 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5977 if(channel->join_flooded)
5979 /* don't automatically give ops or voice during a join flood */
5981 else if(cData->lvlOpts[lvlGiveOps] == 0)
5982 modes |= MODE_CHANOP;
5983 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5984 modes |= MODE_VOICE;
5986 greeting = cData->greeting;
5987 if(user->handle_info)
5989 handle = user->handle_info;
5991 if(IsHelper(user) && !IsHelping(user))
5994 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5996 if(channel == chanserv_conf.support_channels.list[ii])
5998 HANDLE_SET_FLAG(user->handle_info, HELPING);
6004 uData = GetTrueChannelAccess(cData, handle);
6005 if(uData && !IsUserSuspended(uData))
6007 /* Ops and above were handled by the above case. */
6008 if(IsUserAutoOp(uData))
6010 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6011 modes |= MODE_CHANOP;
6012 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6013 modes |= MODE_VOICE;
6015 if(uData->access >= UL_PRESENT)
6016 cData->visited = now;
6017 if(cData->user_greeting)
6018 greeting = cData->user_greeting;
6020 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6021 && ((now - uData->seen) >= chanserv_conf.info_delay)
6029 /* If user joining normally (not during burst), apply op or voice,
6030 * and send greeting/userinfo as appropriate.
6032 if(!user->uplink->burst)
6036 if(modes & MODE_CHANOP)
6037 modes &= ~MODE_VOICE;
6038 change.args[0].mode = modes;
6039 change.args[0].u.member = mNode;
6040 mod_chanmode_announce(chanserv, channel, &change);
6043 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6045 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6051 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6053 struct mod_chanmode change;
6054 struct userData *channel;
6055 unsigned int ii, jj;
6057 if(!user->handle_info)
6060 mod_chanmode_init(&change);
6062 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6064 struct chanNode *cn;
6065 struct modeNode *mn;
6066 if(IsUserSuspended(channel)
6067 || IsSuspended(channel->channel)
6068 || !(cn = channel->channel->channel))
6071 mn = GetUserMode(cn, user);
6074 if(!IsUserSuspended(channel)
6075 && IsUserAutoInvite(channel)
6076 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6078 && !user->uplink->burst)
6079 irc_invite(chanserv, user, cn);
6083 if(channel->access >= UL_PRESENT)
6084 channel->channel->visited = now;
6086 if(IsUserAutoOp(channel))
6088 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6089 change.args[0].mode = MODE_CHANOP;
6090 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6091 change.args[0].mode = MODE_VOICE;
6093 change.args[0].mode = 0;
6094 change.args[0].u.member = mn;
6095 if(change.args[0].mode)
6096 mod_chanmode_announce(chanserv, cn, &change);
6099 channel->seen = now;
6100 channel->present = 1;
6103 for(ii = 0; ii < user->channels.used; ++ii)
6105 struct chanNode *channel = user->channels.list[ii]->channel;
6106 struct banData *ban;
6108 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6109 || !channel->channel_info
6110 || IsSuspended(channel->channel_info))
6112 for(jj = 0; jj < channel->banlist.used; ++jj)
6113 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6115 if(jj < channel->banlist.used)
6117 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6119 char kick_reason[MAXLEN];
6120 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6122 change.args[0].mode = MODE_BAN;
6123 change.args[0].u.hostmask = ban->mask;
6124 mod_chanmode_announce(chanserv, channel, &change);
6125 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6126 KickChannelUser(user, channel, chanserv, kick_reason);
6127 ban->triggered = now;
6132 if(IsSupportHelper(user))
6134 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6136 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6138 HANDLE_SET_FLAG(user->handle_info, HELPING);
6146 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6148 struct chanData *cData;
6149 struct userData *uData;
6151 cData = mn->channel->channel_info;
6152 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6155 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6157 /* Allow for a bit of padding so that the limit doesn't
6158 track the user count exactly, which could get annoying. */
6159 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6161 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6162 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6166 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6168 scan_user_presence(uData, mn->user);
6172 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6174 unsigned int ii, jj;
6175 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6177 for(jj = 0; jj < mn->user->channels.used; ++jj)
6178 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6180 if(jj < mn->user->channels.used)
6183 if(ii == chanserv_conf.support_channels.used)
6184 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6189 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6191 struct userData *uData;
6193 if(!channel->channel_info || !kicker || IsService(kicker)
6194 || (kicker == victim) || IsSuspended(channel->channel_info)
6195 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6198 if(protect_user(victim, kicker, channel->channel_info))
6200 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6201 KickChannelUser(kicker, channel, chanserv, reason);
6204 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6209 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6211 struct chanData *cData;
6213 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6216 cData = channel->channel_info;
6217 if(bad_topic(channel, user, channel->topic))
6219 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6220 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6221 SetChannelTopic(channel, chanserv, old_topic, 1);
6222 else if(cData->topic)
6223 SetChannelTopic(channel, chanserv, cData->topic, 1);
6226 /* With topicsnarf, grab the topic and save it as the default topic. */
6227 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6230 cData->topic = strdup(channel->topic);
6236 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6238 struct mod_chanmode *bounce = NULL;
6239 unsigned int bnc, ii;
6242 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6245 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6246 && mode_lock_violated(&channel->channel_info->modes, change))
6248 char correct[MAXLEN];
6249 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6250 mod_chanmode_format(&channel->channel_info->modes, correct);
6251 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6253 for(ii = bnc = 0; ii < change->argc; ++ii)
6255 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6257 const struct userNode *victim = change->args[ii].u.member->user;
6258 if(!protect_user(victim, user, channel->channel_info))
6261 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6264 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6265 bounce->args[bnc].u.member = GetUserMode(channel, user);
6266 if(bounce->args[bnc].u.member)
6270 bounce->args[bnc].mode = MODE_CHANOP;
6271 bounce->args[bnc].u.member = change->args[ii].u.member;
6273 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6275 else if(change->args[ii].mode & MODE_CHANOP)
6277 const struct userNode *victim = change->args[ii].u.member->user;
6278 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6281 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6282 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6283 bounce->args[bnc].u.member = change->args[ii].u.member;
6286 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6288 const char *ban = change->args[ii].u.hostmask;
6289 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6292 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6293 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6294 bounce->args[bnc].u.hostmask = strdup(ban);
6296 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6301 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6302 mod_chanmode_announce(chanserv, channel, bounce);
6303 for(ii = 0; ii < change->argc; ++ii)
6304 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6305 free((char*)bounce->args[ii].u.hostmask);
6306 mod_chanmode_free(bounce);
6311 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6313 struct chanNode *channel;
6314 struct banData *bData;
6315 struct mod_chanmode change;
6316 unsigned int ii, jj;
6317 char kick_reason[MAXLEN];
6319 mod_chanmode_init(&change);
6321 change.args[0].mode = MODE_BAN;
6322 for(ii = 0; ii < user->channels.used; ++ii)
6324 channel = user->channels.list[ii]->channel;
6325 /* Need not check for bans if they're opped or voiced. */
6326 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6328 /* Need not check for bans unless channel registration is active. */
6329 if(!channel->channel_info || IsSuspended(channel->channel_info))
6331 /* Look for a matching ban already on the channel. */
6332 for(jj = 0; jj < channel->banlist.used; ++jj)
6333 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6335 /* Need not act if we found one. */
6336 if(jj < channel->banlist.used)
6338 /* Look for a matching ban in this channel. */
6339 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6341 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6343 change.args[0].u.hostmask = bData->mask;
6344 mod_chanmode_announce(chanserv, channel, &change);
6345 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6346 KickChannelUser(user, channel, chanserv, kick_reason);
6347 bData->triggered = now;
6348 break; /* we don't need to check any more bans in the channel */
6353 static void handle_rename(struct handle_info *handle, const char *old_handle)
6355 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6359 dict_remove2(handle_dnrs, old_handle, 1);
6360 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6361 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6366 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6368 struct userNode *h_user;
6370 if(handle->channels)
6372 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6373 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6375 while(handle->channels)
6376 del_channel_user(handle->channels, 1);
6381 handle_server_link(UNUSED_ARG(struct server *server))
6383 struct chanData *cData;
6385 for(cData = channelList; cData; cData = cData->next)
6387 if(!IsSuspended(cData))
6388 cData->may_opchan = 1;
6389 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6390 && !cData->channel->join_flooded
6391 && ((cData->channel->limit - cData->channel->members.used)
6392 < chanserv_conf.adjust_threshold))
6394 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6395 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6401 chanserv_conf_read(void)
6405 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6406 struct mod_chanmode *change;
6407 struct string_list *strlist;
6408 struct chanNode *chan;
6411 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6413 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6416 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6417 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6418 chanserv_conf.support_channels.used = 0;
6419 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6421 for(ii = 0; ii < strlist->used; ++ii)
6423 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6426 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6428 channelList_append(&chanserv_conf.support_channels, chan);
6431 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6434 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6437 chan = AddChannel(str, now, str2, NULL);
6439 channelList_append(&chanserv_conf.support_channels, chan);
6441 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6442 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6443 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6444 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6445 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6446 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6447 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6448 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6449 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6450 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6451 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6452 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6453 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6454 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6455 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6456 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6457 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6458 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6459 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6460 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6461 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6462 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6463 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6465 NickChange(chanserv, str, 0);
6466 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6467 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6468 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6469 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6470 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6471 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6472 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6473 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6474 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6475 chanserv_conf.max_owned = str ? atoi(str) : 5;
6476 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6477 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6478 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6479 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6480 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6481 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6482 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6485 safestrncpy(mode_line, str, sizeof(mode_line));
6486 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6487 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6488 && (change->argc < 2))
6490 chanserv_conf.default_modes = *change;
6491 mod_chanmode_free(change);
6493 free_string_list(chanserv_conf.set_shows);
6494 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6496 strlist = string_list_copy(strlist);
6499 static const char *list[] = {
6500 /* free form text */
6501 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6502 /* options based on user level */
6503 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6504 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6505 /* multiple choice options */
6506 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6507 /* binary options */
6508 "DynLimit", "NoDelete",
6513 strlist = alloc_string_list(ArrayLength(list)-1);
6514 for(ii=0; list[ii]; ii++)
6515 string_list_append(strlist, strdup(list[ii]));
6517 chanserv_conf.set_shows = strlist;
6518 /* We don't look things up now, in case the list refers to options
6519 * defined by modules initialized after this point. Just mark the
6520 * function list as invalid, so it will be initialized.
6522 set_shows_list.used = 0;
6523 free_string_list(chanserv_conf.eightball);
6524 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6527 strlist = string_list_copy(strlist);
6531 strlist = alloc_string_list(4);
6532 string_list_append(strlist, strdup("Yes."));
6533 string_list_append(strlist, strdup("No."));
6534 string_list_append(strlist, strdup("Maybe so."));
6536 chanserv_conf.eightball = strlist;
6537 free_string_list(chanserv_conf.old_ban_names);
6538 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6540 strlist = string_list_copy(strlist);
6542 strlist = alloc_string_list(2);
6543 chanserv_conf.old_ban_names = strlist;
6544 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6545 off_channel = str ? atoi(str) : 0;
6549 chanserv_note_type_read(const char *key, struct record_data *rd)
6552 struct note_type *ntype;
6555 if(!(obj = GET_RECORD_OBJECT(rd)))
6557 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6560 if(!(ntype = chanserv_create_note_type(key)))
6562 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6566 /* Figure out set access */
6567 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6569 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6570 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6572 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6574 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6575 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6577 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6579 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6583 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6584 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6585 ntype->set_access.min_opserv = 0;
6588 /* Figure out visibility */
6589 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6590 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6591 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6592 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6593 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6594 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6595 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6596 ntype->visible_type = NOTE_VIS_ALL;
6598 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6600 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6601 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6605 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6607 struct handle_info *handle;
6608 struct userData *uData;
6609 char *seen, *inf, *flags;
6611 unsigned short access;
6613 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6615 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6619 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6620 if(access > UL_OWNER)
6622 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6626 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6627 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6628 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6629 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6630 handle = get_handle_info(key);
6633 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6637 uData = add_channel_user(chan, handle, access, last_seen, inf);
6638 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6642 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6644 struct banData *bData;
6645 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6646 time_t set_time, triggered_time, expires_time;
6648 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6650 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6654 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6655 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6656 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6657 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6658 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6659 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6660 if (!reason || !owner)
6663 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6664 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6666 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6668 expires_time = set_time + atoi(s_duration);
6672 if(!reason || (expires_time && (expires_time < now)))
6675 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6678 static struct suspended *
6679 chanserv_read_suspended(dict_t obj)
6681 struct suspended *suspended = calloc(1, sizeof(*suspended));
6685 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6686 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6687 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6688 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6689 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6690 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6691 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6692 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6693 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6694 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6699 chanserv_channel_read(const char *key, struct record_data *hir)
6701 struct suspended *suspended;
6702 struct mod_chanmode *modes;
6703 struct chanNode *cNode;
6704 struct chanData *cData;
6705 struct dict *channel, *obj;
6706 char *str, *argv[10];
6710 channel = hir->d.object;
6712 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6715 cNode = AddChannel(key, now, NULL, NULL);
6718 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6721 cData = register_channel(cNode, str);
6724 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6728 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6730 enum levelOption lvlOpt;
6731 enum charOption chOpt;
6733 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6734 cData->flags = atoi(str);
6736 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6738 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6740 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6741 else if(levelOptions[lvlOpt].old_flag)
6743 if(cData->flags & levelOptions[lvlOpt].old_flag)
6744 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6746 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6750 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6752 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6754 cData->chOpts[chOpt] = str[0];
6757 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6759 enum levelOption lvlOpt;
6760 enum charOption chOpt;
6763 cData->flags = base64toint(str, 5);
6764 count = strlen(str += 5);
6765 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6768 if(levelOptions[lvlOpt].old_flag)
6770 if(cData->flags & levelOptions[lvlOpt].old_flag)
6771 lvl = levelOptions[lvlOpt].flag_value;
6773 lvl = levelOptions[lvlOpt].default_value;
6775 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6777 case 'c': lvl = UL_COOWNER; break;
6778 case 'm': lvl = UL_MASTER; break;
6779 case 'n': lvl = UL_OWNER+1; break;
6780 case 'o': lvl = UL_OP; break;
6781 case 'p': lvl = UL_PEON; break;
6782 case 'w': lvl = UL_OWNER; break;
6783 default: lvl = 0; break;
6785 cData->lvlOpts[lvlOpt] = lvl;
6787 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6788 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6791 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6793 suspended = chanserv_read_suspended(obj);
6794 cData->suspended = suspended;
6795 suspended->cData = cData;
6796 /* We could use suspended->expires and suspended->revoked to
6797 * set the CHANNEL_SUSPENDED flag, but we don't. */
6799 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6801 suspended = calloc(1, sizeof(*suspended));
6802 suspended->issued = 0;
6803 suspended->revoked = 0;
6804 suspended->suspender = strdup(str);
6805 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6806 suspended->expires = str ? atoi(str) : 0;
6807 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6808 suspended->reason = strdup(str ? str : "No reason");
6809 suspended->previous = NULL;
6810 cData->suspended = suspended;
6811 suspended->cData = cData;
6815 cData->flags &= ~CHANNEL_SUSPENDED;
6816 suspended = NULL; /* to squelch a warning */
6819 if(IsSuspended(cData)) {
6820 if(suspended->expires > now)
6821 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6822 else if(suspended->expires)
6823 cData->flags &= ~CHANNEL_SUSPENDED;
6826 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6827 struct mod_chanmode change;
6828 mod_chanmode_init(&change);
6830 change.args[0].mode = MODE_CHANOP;
6831 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6832 mod_chanmode_announce(chanserv, cNode, &change);
6835 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6836 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6837 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6838 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6839 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6840 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6841 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6842 cData->max = str ? atoi(str) : 0;
6843 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6844 cData->greeting = str ? strdup(str) : NULL;
6845 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6846 cData->user_greeting = str ? strdup(str) : NULL;
6847 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6848 cData->topic_mask = str ? strdup(str) : NULL;
6849 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6850 cData->topic = str ? strdup(str) : NULL;
6852 if(!IsSuspended(cData)
6853 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6854 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6855 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6856 cData->modes = *modes;
6858 cData->modes.modes_set |= MODE_REGISTERED;
6859 if(cData->modes.argc > 1)
6860 cData->modes.argc = 1;
6861 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6862 mod_chanmode_free(modes);
6865 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6866 for(it = dict_first(obj); it; it = iter_next(it))
6867 user_read_helper(iter_key(it), iter_data(it), cData);
6869 if(!cData->users && !IsProtected(cData))
6871 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6872 unregister_channel(cData, "has empty user list.");
6876 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6877 for(it = dict_first(obj); it; it = iter_next(it))
6878 ban_read_helper(iter_key(it), iter_data(it), cData);
6880 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6881 for(it = dict_first(obj); it; it = iter_next(it))
6883 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6884 struct record_data *rd = iter_data(it);
6885 const char *note, *setter;
6887 if(rd->type != RECDB_OBJECT)
6889 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6893 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6895 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6897 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6901 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6902 if(!setter) setter = "<unknown>";
6903 chanserv_add_channel_note(cData, ntype, setter, note);
6911 chanserv_dnr_read(const char *key, struct record_data *hir)
6913 const char *setter, *reason, *str;
6914 struct do_not_register *dnr;
6916 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6919 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6922 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6925 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6928 dnr = chanserv_add_dnr(key, setter, reason);
6931 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6933 dnr->set = atoi(str);
6939 chanserv_saxdb_read(struct dict *database)
6941 struct dict *section;
6944 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6945 for(it = dict_first(section); it; it = iter_next(it))
6946 chanserv_note_type_read(iter_key(it), iter_data(it));
6948 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6949 for(it = dict_first(section); it; it = iter_next(it))
6950 chanserv_channel_read(iter_key(it), iter_data(it));
6952 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6953 for(it = dict_first(section); it; it = iter_next(it))
6954 chanserv_dnr_read(iter_key(it), iter_data(it));
6960 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6962 int high_present = 0;
6963 saxdb_start_record(ctx, KEY_USERS, 1);
6964 for(; uData; uData = uData->next)
6966 if((uData->access >= UL_PRESENT) && uData->present)
6968 saxdb_start_record(ctx, uData->handle->handle, 0);
6969 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6970 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6972 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6974 saxdb_write_string(ctx, KEY_INFO, uData->info);
6975 saxdb_end_record(ctx);
6977 saxdb_end_record(ctx);
6978 return high_present;
6982 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6986 saxdb_start_record(ctx, KEY_BANS, 1);
6987 for(; bData; bData = bData->next)
6989 saxdb_start_record(ctx, bData->mask, 0);
6990 saxdb_write_int(ctx, KEY_SET, bData->set);
6991 if(bData->triggered)
6992 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6994 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6996 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6998 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6999 saxdb_end_record(ctx);
7001 saxdb_end_record(ctx);
7005 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7007 saxdb_start_record(ctx, name, 0);
7008 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7009 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7011 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7013 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7015 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7017 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7018 saxdb_end_record(ctx);
7022 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7026 enum levelOption lvlOpt;
7027 enum charOption chOpt;
7029 saxdb_start_record(ctx, channel->channel->name, 1);
7031 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7032 saxdb_write_int(ctx, KEY_MAX, channel->max);
7034 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7035 if(channel->registrar)
7036 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7037 if(channel->greeting)
7038 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7039 if(channel->user_greeting)
7040 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7041 if(channel->topic_mask)
7042 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7043 if(channel->suspended)
7044 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7046 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7047 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7048 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7049 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7050 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7052 buf[0] = channel->chOpts[chOpt];
7054 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7056 saxdb_end_record(ctx);
7058 if(channel->modes.modes_set || channel->modes.modes_clear)
7060 mod_chanmode_format(&channel->modes, buf);
7061 saxdb_write_string(ctx, KEY_MODES, buf);
7064 high_present = chanserv_write_users(ctx, channel->users);
7065 chanserv_write_bans(ctx, channel->bans);
7067 if(dict_size(channel->notes))
7071 saxdb_start_record(ctx, KEY_NOTES, 1);
7072 for(it = dict_first(channel->notes); it; it = iter_next(it))
7074 struct note *note = iter_data(it);
7075 saxdb_start_record(ctx, iter_key(it), 0);
7076 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7077 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7078 saxdb_end_record(ctx);
7080 saxdb_end_record(ctx);
7083 if(channel->ownerTransfer)
7084 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7085 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7086 saxdb_end_record(ctx);
7090 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7094 saxdb_start_record(ctx, ntype->name, 0);
7095 switch(ntype->set_access_type)
7097 case NOTE_SET_CHANNEL_ACCESS:
7098 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7100 case NOTE_SET_CHANNEL_SETTER:
7101 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7103 case NOTE_SET_PRIVILEGED: default:
7104 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7107 switch(ntype->visible_type)
7109 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7110 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7111 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7113 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7114 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7115 saxdb_end_record(ctx);
7119 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7121 struct do_not_register *dnr;
7124 for(it = dict_first(dnrs); it; it = iter_next(it))
7126 dnr = iter_data(it);
7127 saxdb_start_record(ctx, dnr->chan_name, 0);
7129 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7130 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7131 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7132 saxdb_end_record(ctx);
7137 chanserv_saxdb_write(struct saxdb_context *ctx)
7140 struct chanData *channel;
7143 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7144 for(it = dict_first(note_types); it; it = iter_next(it))
7145 chanserv_write_note_type(ctx, iter_data(it));
7146 saxdb_end_record(ctx);
7149 saxdb_start_record(ctx, KEY_DNR, 1);
7150 write_dnrs_helper(ctx, handle_dnrs);
7151 write_dnrs_helper(ctx, plain_dnrs);
7152 write_dnrs_helper(ctx, mask_dnrs);
7153 saxdb_end_record(ctx);
7156 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7157 for(channel = channelList; channel; channel = channel->next)
7158 chanserv_write_channel(ctx, channel);
7159 saxdb_end_record(ctx);
7165 chanserv_db_cleanup(void) {
7167 unreg_part_func(handle_part);
7169 unregister_channel(channelList, "terminating.");
7170 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7171 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7172 free(chanserv_conf.support_channels.list);
7173 dict_delete(handle_dnrs);
7174 dict_delete(plain_dnrs);
7175 dict_delete(mask_dnrs);
7176 dict_delete(note_types);
7177 free_string_list(chanserv_conf.eightball);
7178 free_string_list(chanserv_conf.old_ban_names);
7179 free_string_list(chanserv_conf.set_shows);
7180 free(set_shows_list.list);
7181 free(uset_shows_list.list);
7184 struct userData *helper = helperList;
7185 helperList = helperList->next;
7190 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7191 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7192 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7195 init_chanserv(const char *nick)
7197 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7198 conf_register_reload(chanserv_conf_read);
7200 reg_server_link_func(handle_server_link);
7202 reg_new_channel_func(handle_new_channel);
7203 reg_join_func(handle_join);
7204 reg_part_func(handle_part);
7205 reg_kick_func(handle_kick);
7206 reg_topic_func(handle_topic);
7207 reg_mode_change_func(handle_mode);
7208 reg_nick_change_func(handle_nick_change);
7210 reg_auth_func(handle_auth);
7211 reg_handle_rename_func(handle_rename);
7212 reg_unreg_func(handle_unreg);
7214 handle_dnrs = dict_new();
7215 dict_set_free_data(handle_dnrs, free);
7216 plain_dnrs = dict_new();
7217 dict_set_free_data(plain_dnrs, free);
7218 mask_dnrs = dict_new();
7219 dict_set_free_data(mask_dnrs, free);
7221 reg_svccmd_unbind_func(handle_svccmd_unbind);
7222 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7223 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7224 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7225 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7226 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7227 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7228 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7229 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7230 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7232 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7233 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7235 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7236 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7237 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7238 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7239 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7241 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7242 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7243 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7244 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7245 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7247 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7248 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7249 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7250 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7252 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7253 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7254 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7255 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7256 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7257 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7258 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7259 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7261 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7262 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7263 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7264 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7265 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7266 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7267 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7268 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7269 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7270 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7271 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7272 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7273 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7274 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7276 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7277 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7278 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7279 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7280 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7282 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7283 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7285 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7286 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7287 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7288 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7289 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7290 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7291 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7292 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7293 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7294 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7295 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7297 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7298 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7300 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7301 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7302 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7303 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7305 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7306 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7307 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7308 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7309 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7311 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7312 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7313 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7314 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7315 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7316 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7318 /* Channel options */
7319 DEFINE_CHANNEL_OPTION(defaulttopic);
7320 DEFINE_CHANNEL_OPTION(topicmask);
7321 DEFINE_CHANNEL_OPTION(greeting);
7322 DEFINE_CHANNEL_OPTION(usergreeting);
7323 DEFINE_CHANNEL_OPTION(modes);
7324 DEFINE_CHANNEL_OPTION(enfops);
7325 DEFINE_CHANNEL_OPTION(giveops);
7326 DEFINE_CHANNEL_OPTION(protect);
7327 DEFINE_CHANNEL_OPTION(enfmodes);
7328 DEFINE_CHANNEL_OPTION(enftopic);
7329 DEFINE_CHANNEL_OPTION(pubcmd);
7330 DEFINE_CHANNEL_OPTION(givevoice);
7331 DEFINE_CHANNEL_OPTION(userinfo);
7332 DEFINE_CHANNEL_OPTION(dynlimit);
7333 DEFINE_CHANNEL_OPTION(topicsnarf);
7334 DEFINE_CHANNEL_OPTION(nodelete);
7335 DEFINE_CHANNEL_OPTION(toys);
7336 DEFINE_CHANNEL_OPTION(setters);
7337 DEFINE_CHANNEL_OPTION(topicrefresh);
7338 DEFINE_CHANNEL_OPTION(ctcpusers);
7339 DEFINE_CHANNEL_OPTION(ctcpreaction);
7340 DEFINE_CHANNEL_OPTION(inviteme);
7342 DEFINE_CHANNEL_OPTION(offchannel);
7343 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7345 /* Alias set topic to set defaulttopic for compatibility. */
7346 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7349 DEFINE_USER_OPTION(noautoop);
7350 DEFINE_USER_OPTION(autoinvite);
7351 DEFINE_USER_OPTION(info);
7353 /* Alias uset autovoice to uset autoop. */
7354 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7356 note_types = dict_new();
7357 dict_set_free_data(note_types, chanserv_deref_note_type);
7360 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7361 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7362 service_register(chanserv)->trigger = '!';
7363 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7365 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7367 if(chanserv_conf.channel_expire_frequency)
7368 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7370 if(chanserv_conf.refresh_period)
7372 time_t next_refresh;
7373 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7374 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7377 reg_exit_func(chanserv_db_cleanup);
7378 message_register_table(msgtab);