1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
201 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
204 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
205 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
206 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
207 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
208 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
209 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
210 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
211 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
212 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
213 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
214 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
215 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
216 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
217 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
218 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
219 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
221 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
223 /* Channel management */
224 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
225 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
226 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
228 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
229 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
230 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
231 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
232 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
233 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
234 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
236 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
237 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
238 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
239 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
240 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
241 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
242 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
243 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
244 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
245 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
246 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
247 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
248 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
249 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
250 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
251 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
252 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
253 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
254 { "CSMSG_SET_MODES", "$bModes $b %s" },
255 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
256 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
257 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
258 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
259 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
260 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
261 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
262 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
263 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
264 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
265 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
266 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
267 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
268 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
269 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
270 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
271 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
272 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
273 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
274 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
275 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
276 { "CSMSG_USET_INFO", "$bInfo $b %s" },
278 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
279 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
280 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
281 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
282 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
283 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
284 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
285 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
287 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
288 { "CSMSG_PROTECT_NONE", "No users will be protected." },
289 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
290 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
291 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
292 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
293 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
294 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
295 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
296 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
297 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
300 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
302 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
303 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
304 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
305 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
306 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
307 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
308 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
309 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
311 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
312 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
313 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
315 /* Channel userlist */
316 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
317 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
318 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
319 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
321 /* Channel note list */
322 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
323 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
324 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
325 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
326 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
327 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
328 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
329 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
330 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
331 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
332 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
333 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
334 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
335 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
336 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
338 /* Channel [un]suspension */
339 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
340 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
341 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
342 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
343 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
344 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
345 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
347 /* Access information */
348 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
349 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
350 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
351 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
352 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
353 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
354 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
355 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
356 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
357 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
358 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
359 { "CSMSG_UC_H_TITLE", "network helper" },
360 { "CSMSG_LC_H_TITLE", "support helper" },
361 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
363 /* Seen information */
364 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
365 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
366 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
367 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
369 /* Names information */
370 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
371 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
373 /* Channel information */
374 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
375 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
376 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
377 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
378 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
379 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
380 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
381 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
382 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
383 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
384 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
385 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
393 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
394 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
396 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
397 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
398 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
399 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
400 { "CSMSG_PEEK_OPS", "$bOps:$b" },
401 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
403 /* Network information */
404 { "CSMSG_NETWORK_INFO", "Network Information:" },
405 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
406 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
407 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
408 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
409 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
410 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
411 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
412 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
415 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
416 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
417 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
419 /* Channel searches */
420 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
421 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
422 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
423 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
425 /* Channel configuration */
426 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
427 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
428 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
429 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
432 { "CSMSG_USER_OPTIONS", "User Options:" },
433 { "CSMSG_USER_PROTECTED", "That user is protected." },
436 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
437 { "CSMSG_PING_RESPONSE", "Pong!" },
438 { "CSMSG_WUT_RESPONSE", "wut" },
439 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
440 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
441 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
442 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
443 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
444 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
445 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
448 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
452 /* eject_user and unban_user flags */
453 #define ACTION_KICK 0x0001
454 #define ACTION_BAN 0x0002
455 #define ACTION_ADD_BAN 0x0004
456 #define ACTION_ADD_TIMED_BAN 0x0008
457 #define ACTION_UNBAN 0x0010
458 #define ACTION_DEL_BAN 0x0020
460 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
461 #define MODELEN 40 + KEYLEN
465 #define CSFUNC_ARGS user, channel, argc, argv, cmd
467 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
468 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
469 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
470 reply("MSG_MISSING_PARAMS", argv[0]); \
474 DECLARE_LIST(dnrList, struct do_not_register *);
475 DEFINE_LIST(dnrList, struct do_not_register *);
477 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
479 struct userNode *chanserv;
482 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
483 static struct log_type *CS_LOG;
487 struct channelList support_channels;
488 struct mod_chanmode default_modes;
490 unsigned long db_backup_frequency;
491 unsigned long channel_expire_frequency;
494 unsigned int adjust_delay;
495 long channel_expire_delay;
496 unsigned int nodelete_level;
498 unsigned int adjust_threshold;
499 int join_flood_threshold;
501 unsigned int greeting_length;
502 unsigned int refresh_period;
503 unsigned int giveownership_period;
505 unsigned int max_owned;
506 unsigned int max_chan_users;
507 unsigned int max_chan_bans;
508 unsigned int max_userinfo_length;
510 struct string_list *set_shows;
511 struct string_list *eightball;
512 struct string_list *old_ban_names;
514 const char *ctcp_short_ban_duration;
515 const char *ctcp_long_ban_duration;
517 const char *irc_operator_epithet;
518 const char *network_helper_epithet;
519 const char *support_helper_epithet;
524 struct userNode *user;
525 struct userNode *bot;
526 struct chanNode *channel;
528 unsigned short lowest;
529 unsigned short highest;
530 struct userData **users;
531 struct helpfile_table table;
534 enum note_access_type
536 NOTE_SET_CHANNEL_ACCESS,
537 NOTE_SET_CHANNEL_SETTER,
541 enum note_visible_type
544 NOTE_VIS_CHANNEL_USERS,
550 enum note_access_type set_access_type;
552 unsigned int min_opserv;
553 unsigned short min_ulevel;
555 enum note_visible_type visible_type;
556 unsigned int max_length;
563 struct note_type *type;
564 char setter[NICKSERV_HANDLE_LEN+1];
568 static unsigned int registered_channels;
569 static unsigned int banCount;
571 static const struct {
574 unsigned short level;
577 { "peon", "Peon", UL_PEON, '+' },
578 { "op", "Op", UL_OP, '@' },
579 { "master", "Master", UL_MASTER, '%' },
580 { "coowner", "Coowner", UL_COOWNER, '*' },
581 { "owner", "Owner", UL_OWNER, '!' },
582 { "helper", "BUG:", UL_HELPER, 'X' }
585 static const struct {
588 unsigned short default_value;
589 unsigned int old_idx;
590 unsigned int old_flag;
591 unsigned short flag_value;
593 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
594 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
595 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
596 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
597 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
598 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
599 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
600 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
601 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
602 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
603 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
606 struct charOptionValues {
609 } protectValues[] = {
610 { 'a', "CSMSG_PROTECT_ALL" },
611 { 'e', "CSMSG_PROTECT_EQUAL" },
612 { 'l', "CSMSG_PROTECT_LOWER" },
613 { 'n', "CSMSG_PROTECT_NONE" }
615 { 'd', "CSMSG_TOYS_DISABLED" },
616 { 'n', "CSMSG_TOYS_PRIVATE" },
617 { 'p', "CSMSG_TOYS_PUBLIC" }
618 }, topicRefreshValues[] = {
619 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
620 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
621 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
622 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
623 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
624 }, ctcpReactionValues[] = {
625 { 'k', "CSMSG_CTCPREACTION_KICK" },
626 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
627 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
628 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
631 static const struct {
635 unsigned int old_idx;
637 struct charOptionValues *values;
639 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
640 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
641 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
642 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
645 struct userData *helperList;
646 struct chanData *channelList;
647 static struct module *chanserv_module;
648 static unsigned int userCount;
650 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
651 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
652 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
655 user_level_from_name(const char *name, unsigned short clamp_level)
657 unsigned int level = 0, ii;
659 level = strtoul(name, NULL, 10);
660 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
661 if(!irccasecmp(name, accessLevels[ii].name))
662 level = accessLevels[ii].level;
663 if(level > clamp_level)
669 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
672 *minl = strtoul(arg, &sep, 10);
680 *maxl = strtoul(sep+1, &sep, 10);
688 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
690 struct userData *uData, **head;
692 if(!channel || !handle)
695 if(override && HANDLE_FLAGGED(handle, HELPING)
696 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
698 for(uData = helperList;
699 uData && uData->handle != handle;
700 uData = uData->next);
704 uData = calloc(1, sizeof(struct userData));
705 uData->handle = handle;
707 uData->access = UL_HELPER;
713 uData->next = helperList;
715 helperList->prev = uData;
723 for(uData = channel->users; uData; uData = uData->next)
724 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
727 head = &(channel->users);
730 if(uData && (uData != *head))
732 /* Shuffle the user to the head of whatever list he was in. */
734 uData->next->prev = uData->prev;
736 uData->prev->next = uData->next;
742 (**head).prev = uData;
749 /* Returns non-zero if user has at least the minimum access.
750 * exempt_owner is set when handling !set, so the owner can set things
753 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
755 struct userData *uData;
756 struct chanData *cData = channel->channel_info;
757 unsigned short minimum = cData->lvlOpts[opt];
760 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
763 if(minimum <= uData->access)
765 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
770 /* Scan for other users authenticated to the same handle
771 still in the channel. If so, keep them listed as present.
773 user is optional, if not null, it skips checking that userNode
774 (for the handle_part function) */
776 scan_user_presence(struct userData *uData, struct userNode *user)
780 if(IsSuspended(uData->channel)
781 || IsUserSuspended(uData)
782 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
794 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
796 unsigned int eflags, argc;
798 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
800 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
801 if(!channel->channel_info
802 || IsSuspended(channel->channel_info)
804 || !ircncasecmp(text, "ACTION ", 7))
806 /* Figure out the minimum level needed to CTCP the channel */
807 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
809 /* We need to enforce against them; do so. */
811 argv[0] = (char*)text;
812 argv[1] = user->nick;
814 if(GetUserMode(channel, user))
815 eflags |= ACTION_KICK;
816 switch(channel->channel_info->chOpts[chCTCPReaction]) {
817 default: case 'k': /* just do the kick */ break;
819 eflags |= ACTION_BAN;
822 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
823 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
826 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
827 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
830 argv[argc++] = bad_ctcp_reason;
831 eject_user(chanserv, channel, argc, argv, NULL, eflags);
835 chanserv_create_note_type(const char *name)
837 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
838 strcpy(ntype->name, name);
840 dict_insert(note_types, ntype->name, ntype);
845 chanserv_deref_note_type(void *data)
847 struct note_type *ntype = data;
849 if(--ntype->refs > 0)
855 chanserv_flush_note_type(struct note_type *ntype)
857 struct chanData *cData;
858 for(cData = channelList; cData; cData = cData->next)
859 dict_remove(cData->notes, ntype->name);
863 chanserv_truncate_notes(struct note_type *ntype)
865 struct chanData *cData;
867 unsigned int size = sizeof(*note) + ntype->max_length;
869 for(cData = channelList; cData; cData = cData->next) {
870 note = dict_find(cData->notes, ntype->name, NULL);
873 if(strlen(note->note) <= ntype->max_length)
875 dict_remove2(cData->notes, ntype->name, 1);
876 note = realloc(note, size);
877 note->note[ntype->max_length] = 0;
878 dict_insert(cData->notes, ntype->name, note);
882 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
885 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
888 unsigned int len = strlen(text);
890 if(len > type->max_length) len = type->max_length;
891 note = calloc(1, sizeof(*note) + len);
893 strncpy(note->setter, setter, sizeof(note->setter)-1);
894 memcpy(note->note, text, len);
896 dict_insert(channel->notes, type->name, note);
902 chanserv_free_note(void *data)
904 struct note *note = data;
906 chanserv_deref_note_type(note->type);
907 assert(note->type->refs > 0); /* must use delnote to remove the type */
911 static MODCMD_FUNC(cmd_createnote) {
912 struct note_type *ntype;
913 unsigned int arg = 1, existed = 0, max_length;
915 if((ntype = dict_find(note_types, argv[1], NULL)))
918 ntype = chanserv_create_note_type(argv[arg]);
919 if(!irccasecmp(argv[++arg], "privileged"))
922 ntype->set_access_type = NOTE_SET_PRIVILEGED;
923 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
925 else if(!irccasecmp(argv[arg], "channel"))
927 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
930 reply("CSMSG_INVALID_ACCESS", argv[arg]);
933 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
934 ntype->set_access.min_ulevel = ulvl;
936 else if(!irccasecmp(argv[arg], "setter"))
938 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
942 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
946 if(!irccasecmp(argv[++arg], "privileged"))
947 ntype->visible_type = NOTE_VIS_PRIVILEGED;
948 else if(!irccasecmp(argv[arg], "channel_users"))
949 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
950 else if(!irccasecmp(argv[arg], "all"))
951 ntype->visible_type = NOTE_VIS_ALL;
953 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
957 if((arg+1) >= argc) {
958 reply("MSG_MISSING_PARAMS", argv[0]);
961 max_length = strtoul(argv[++arg], NULL, 0);
962 if(max_length < 20 || max_length > 450)
964 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
967 if(existed && (max_length < ntype->max_length))
969 ntype->max_length = max_length;
970 chanserv_truncate_notes(ntype);
972 ntype->max_length = max_length;
975 reply("CSMSG_NOTE_MODIFIED", ntype->name);
977 reply("CSMSG_NOTE_CREATED", ntype->name);
982 dict_remove(note_types, ntype->name);
986 static MODCMD_FUNC(cmd_removenote) {
987 struct note_type *ntype;
990 ntype = dict_find(note_types, argv[1], NULL);
991 force = (argc > 2) && !irccasecmp(argv[2], "force");
994 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1001 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1004 chanserv_flush_note_type(ntype);
1006 dict_remove(note_types, argv[1]);
1007 reply("CSMSG_NOTE_DELETED", argv[1]);
1012 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1016 if(orig->modes_set & change->modes_clear)
1018 if(orig->modes_clear & change->modes_set)
1020 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1021 && strcmp(orig->new_key, change->new_key))
1023 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1024 && (orig->new_limit != change->new_limit))
1029 static char max_length_text[MAXLEN+1][16];
1031 static struct helpfile_expansion
1032 chanserv_expand_variable(const char *variable)
1034 struct helpfile_expansion exp;
1036 if(!irccasecmp(variable, "notes"))
1039 exp.type = HF_TABLE;
1040 exp.value.table.length = 1;
1041 exp.value.table.width = 3;
1042 exp.value.table.flags = 0;
1043 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1044 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1045 exp.value.table.contents[0][0] = "Note Type";
1046 exp.value.table.contents[0][1] = "Visibility";
1047 exp.value.table.contents[0][2] = "Max Length";
1048 for(it=dict_first(note_types); it; it=iter_next(it))
1050 struct note_type *ntype = iter_data(it);
1053 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1054 row = exp.value.table.length++;
1055 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1056 exp.value.table.contents[row][0] = ntype->name;
1057 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1058 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1060 if(!max_length_text[ntype->max_length][0])
1061 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1062 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1067 exp.type = HF_STRING;
1068 exp.value.str = NULL;
1072 static struct chanData*
1073 register_channel(struct chanNode *cNode, char *registrar)
1075 struct chanData *channel;
1076 enum levelOption lvlOpt;
1077 enum charOption chOpt;
1079 channel = calloc(1, sizeof(struct chanData));
1081 channel->notes = dict_new();
1082 dict_set_free_data(channel->notes, chanserv_free_note);
1084 channel->registrar = strdup(registrar);
1085 channel->registered = now;
1086 channel->visited = now;
1087 channel->limitAdjusted = now;
1088 channel->ownerTransfer = now;
1089 channel->flags = CHANNEL_DEFAULT_FLAGS;
1090 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1091 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1092 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1093 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1095 channel->prev = NULL;
1096 channel->next = channelList;
1099 channelList->prev = channel;
1100 channelList = channel;
1101 registered_channels++;
1103 channel->channel = cNode;
1105 cNode->channel_info = channel;
1110 static struct userData*
1111 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1113 struct userData *ud;
1115 if(access > UL_OWNER)
1118 ud = calloc(1, sizeof(*ud));
1119 ud->channel = channel;
1120 ud->handle = handle;
1122 ud->access = access;
1123 ud->info = info ? strdup(info) : NULL;
1126 ud->next = channel->users;
1128 channel->users->prev = ud;
1129 channel->users = ud;
1131 channel->userCount++;
1135 ud->u_next = ud->handle->channels;
1137 ud->u_next->u_prev = ud;
1138 ud->handle->channels = ud;
1143 static void unregister_channel(struct chanData *channel, const char *reason);
1146 del_channel_user(struct userData *user, int do_gc)
1148 struct chanData *channel = user->channel;
1150 channel->userCount--;
1154 user->prev->next = user->next;
1156 channel->users = user->next;
1158 user->next->prev = user->prev;
1161 user->u_prev->u_next = user->u_next;
1163 user->handle->channels = user->u_next;
1165 user->u_next->u_prev = user->u_prev;
1169 if(do_gc && !channel->users && !IsProtected(channel))
1170 unregister_channel(channel, "lost all users.");
1173 static void expire_ban(void *data);
1175 static struct banData*
1176 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1179 unsigned int ii, l1, l2;
1184 bd = malloc(sizeof(struct banData));
1186 bd->channel = channel;
1188 bd->triggered = triggered;
1189 bd->expires = expires;
1191 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1193 extern const char *hidden_host_suffix;
1194 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1198 l2 = strlen(old_name);
1201 if(irccasecmp(mask + l1 - l2, old_name))
1203 new_mask = alloca(MAXLEN);
1204 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1207 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1209 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1210 bd->reason = strdup(reason);
1213 timeq_add(expires, expire_ban, bd);
1216 bd->next = channel->bans;
1218 channel->bans->prev = bd;
1220 channel->banCount++;
1227 del_channel_ban(struct banData *ban)
1229 ban->channel->banCount--;
1233 ban->prev->next = ban->next;
1235 ban->channel->bans = ban->next;
1238 ban->next->prev = ban->prev;
1241 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1250 expire_ban(void *data)
1252 struct banData *bd = data;
1253 if(!IsSuspended(bd->channel))
1255 struct banList bans;
1256 struct mod_chanmode change;
1258 bans = bd->channel->channel->banlist;
1259 mod_chanmode_init(&change);
1260 for(ii=0; ii<bans.used; ii++)
1262 if(!strcmp(bans.list[ii]->ban, bd->mask))
1265 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1266 change.args[0].u.hostmask = bd->mask;
1267 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1273 del_channel_ban(bd);
1276 static void chanserv_expire_suspension(void *data);
1279 unregister_channel(struct chanData *channel, const char *reason)
1281 struct mod_chanmode change;
1282 char msgbuf[MAXLEN];
1284 /* After channel unregistration, the following must be cleaned
1286 - Channel information.
1289 - Channel suspension data.
1290 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1296 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1300 mod_chanmode_init(&change);
1301 change.modes_clear |= MODE_REGISTERED;
1302 mod_chanmode_announce(chanserv, channel->channel, &change);
1305 while(channel->users)
1306 del_channel_user(channel->users, 0);
1308 while(channel->bans)
1309 del_channel_ban(channel->bans);
1311 free(channel->topic);
1312 free(channel->registrar);
1313 free(channel->greeting);
1314 free(channel->user_greeting);
1315 free(channel->topic_mask);
1318 channel->prev->next = channel->next;
1320 channelList = channel->next;
1323 channel->next->prev = channel->prev;
1325 if(channel->suspended)
1327 struct chanNode *cNode = channel->channel;
1328 struct suspended *suspended, *next_suspended;
1330 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1332 next_suspended = suspended->previous;
1333 free(suspended->suspender);
1334 free(suspended->reason);
1335 if(suspended->expires)
1336 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1341 cNode->channel_info = NULL;
1343 channel->channel->channel_info = NULL;
1345 dict_delete(channel->notes);
1346 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1347 if(!IsSuspended(channel))
1348 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1349 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1350 UnlockChannel(channel->channel);
1352 registered_channels--;
1356 expire_channels(UNUSED_ARG(void *data))
1358 struct chanData *channel, *next;
1359 struct userData *user;
1360 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1362 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1363 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1365 for(channel = channelList; channel; channel = next)
1367 next = channel->next;
1369 /* See if the channel can be expired. */
1370 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1371 || IsProtected(channel))
1374 /* Make sure there are no high-ranking users still in the channel. */
1375 for(user=channel->users; user; user=user->next)
1376 if(user->present && (user->access >= UL_PRESENT))
1381 /* Unregister the channel */
1382 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1383 unregister_channel(channel, "registration expired.");
1386 if(chanserv_conf.channel_expire_frequency)
1387 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1391 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1393 char protect = channel->chOpts[chProtect];
1394 struct userData *cs_victim, *cs_aggressor;
1396 /* Don't protect if no one is to be protected, someone is attacking
1397 himself, or if the aggressor is an IRC Operator. */
1398 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1401 /* Don't protect if the victim isn't authenticated (because they
1402 can't be a channel user), unless we are to protect non-users
1404 cs_victim = GetChannelAccess(channel, victim->handle_info);
1405 if(protect != 'a' && !cs_victim)
1408 /* Protect if the aggressor isn't a user because at this point,
1409 the aggressor can only be less than or equal to the victim. */
1410 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1414 /* If the aggressor was a user, then the victim can't be helped. */
1421 if(cs_victim->access > cs_aggressor->access)
1426 if(cs_victim->access >= cs_aggressor->access)
1435 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1437 struct chanData *cData = channel->channel_info;
1438 struct userData *cs_victim;
1440 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1441 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1442 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1444 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1452 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1454 if(IsService(victim))
1456 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1460 if(protect_user(victim, user, channel->channel_info))
1462 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1469 static struct do_not_register *
1470 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1472 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1473 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1474 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1475 strcpy(dnr->reason, reason);
1477 if(dnr->chan_name[0] == '*')
1478 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1479 else if(strpbrk(dnr->chan_name, "*?"))
1480 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1482 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1486 static struct dnrList
1487 chanserv_find_dnrs(const char *chan_name, const char *handle)
1489 struct dnrList list;
1491 struct do_not_register *dnr;
1493 dnrList_init(&list);
1494 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1495 dnrList_append(&list, dnr);
1496 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1497 dnrList_append(&list, dnr);
1499 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1500 if(match_ircglob(chan_name, iter_key(it)))
1501 dnrList_append(&list, iter_data(it));
1506 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1508 struct dnrList list;
1509 struct do_not_register *dnr;
1511 char buf[INTERVALLEN];
1513 list = chanserv_find_dnrs(chan_name, handle);
1514 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1516 dnr = list.list[ii];
1519 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1520 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1523 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1526 reply("CSMSG_MORE_DNRS", list.used - ii);
1531 struct do_not_register *
1532 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1534 struct do_not_register *dnr;
1537 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1541 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1543 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1544 if(match_ircglob(chan_name, iter_key(it)))
1545 return iter_data(it);
1550 static CHANSERV_FUNC(cmd_noregister)
1553 struct do_not_register *dnr;
1554 char buf[INTERVALLEN];
1555 unsigned int matches;
1561 reply("CSMSG_DNR_SEARCH_RESULTS");
1563 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1565 dnr = iter_data(it);
1567 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1569 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1572 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1574 dnr = iter_data(it);
1576 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1578 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1581 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1583 dnr = iter_data(it);
1585 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1587 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1592 reply("MSG_MATCH_COUNT", matches);
1594 reply("MSG_NO_MATCHES");
1600 if(!IsChannelName(target) && (*target != '*'))
1602 reply("CSMSG_NOT_DNR", target);
1608 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1609 if((*target == '*') && !get_handle_info(target + 1))
1611 reply("MSG_HANDLE_UNKNOWN", target + 1);
1614 chanserv_add_dnr(target, user->handle_info->handle, reason);
1615 reply("CSMSG_NOREGISTER_CHANNEL", target);
1619 reply("CSMSG_DNR_SEARCH_RESULTS");
1621 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1623 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1625 reply("MSG_NO_MATCHES");
1629 static CHANSERV_FUNC(cmd_allowregister)
1631 const char *chan_name = argv[1];
1633 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1635 dict_remove(handle_dnrs, chan_name+1);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(plain_dnrs, chan_name, NULL))
1640 dict_remove(plain_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1643 else if(dict_find(mask_dnrs, chan_name, NULL))
1645 dict_remove(mask_dnrs, chan_name);
1646 reply("CSMSG_DNR_REMOVED", chan_name);
1650 reply("CSMSG_NO_SUCH_DNR", chan_name);
1657 chanserv_get_owned_count(struct handle_info *hi)
1659 struct userData *cList;
1662 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1663 if(cList->access == UL_OWNER)
1668 static CHANSERV_FUNC(cmd_register)
1670 struct handle_info *handle;
1671 struct chanData *cData;
1672 struct modeNode *mn;
1673 char reason[MAXLEN];
1675 unsigned int new_channel, force=0;
1676 struct do_not_register *dnr;
1680 if(channel->channel_info)
1682 reply("CSMSG_ALREADY_REGGED", channel->name);
1686 if(channel->bad_channel)
1688 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1693 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1695 reply("CSMSG_MUST_BE_OPPED", channel->name);
1700 chan_name = channel->name;
1704 if((argc < 2) || !IsChannelName(argv[1]))
1706 reply("MSG_NOT_CHANNEL_NAME");
1710 if(opserv_bad_channel(argv[1]))
1712 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1717 chan_name = argv[1];
1720 if(argc >= (new_channel+2))
1722 if(!IsHelping(user))
1724 reply("CSMSG_PROXY_FORBIDDEN");
1728 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1730 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 handle = user->handle_info;
1736 dnr = chanserv_is_dnr(chan_name, handle);
1740 if(!IsHelping(user))
1741 reply("CSMSG_DNR_CHANNEL", chan_name);
1743 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1747 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1749 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1754 channel = AddChannel(argv[1], now, NULL, NULL);
1756 cData = register_channel(channel, user->handle_info->handle);
1757 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1758 cData->modes = chanserv_conf.default_modes;
1760 cData->modes.modes_set |= MODE_REGISTERED;
1761 if (IsOffChannel(cData))
1763 mod_chanmode_announce(chanserv, channel, &cData->modes);
1767 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1768 change->args[change->argc].mode = MODE_CHANOP;
1769 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1771 mod_chanmode_announce(chanserv, channel, change);
1772 mod_chanmode_free(change);
1775 /* Initialize the channel's max user record. */
1776 cData->max = channel->members.used;
1778 if(handle != user->handle_info)
1779 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1781 reply("CSMSG_REG_SUCCESS", channel->name);
1783 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1784 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1789 make_confirmation_string(struct userData *uData)
1791 static char strbuf[16];
1796 for(src = uData->handle->handle; *src; )
1797 accum = accum * 31 + toupper(*src++);
1799 for(src = uData->channel->channel->name; *src; )
1800 accum = accum * 31 + toupper(*src++);
1801 sprintf(strbuf, "%08x", accum);
1805 static CHANSERV_FUNC(cmd_unregister)
1808 char reason[MAXLEN];
1809 struct chanData *cData;
1810 struct userData *uData;
1812 cData = channel->channel_info;
1815 reply("CSMSG_NOT_REGISTERED", channel->name);
1819 uData = GetChannelUser(cData, user->handle_info);
1820 if(!uData || (uData->access < UL_OWNER))
1822 reply("CSMSG_NO_ACCESS");
1826 if(IsProtected(cData))
1828 reply("CSMSG_UNREG_NODELETE", channel->name);
1832 if(!IsHelping(user))
1834 const char *confirm_string;
1835 if(IsSuspended(cData))
1837 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1840 confirm_string = make_confirmation_string(uData);
1841 if((argc < 2) || strcmp(argv[1], confirm_string))
1843 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1848 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1849 name = strdup(channel->name);
1850 unregister_channel(cData, reason);
1851 reply("CSMSG_UNREG_SUCCESS", name);
1856 static CHANSERV_FUNC(cmd_move)
1858 struct mod_chanmode change;
1859 struct chanNode *target;
1860 struct modeNode *mn;
1861 struct userData *uData;
1862 char reason[MAXLEN];
1863 struct do_not_register *dnr;
1867 if(IsProtected(channel->channel_info))
1869 reply("CSMSG_MOVE_NODELETE", channel->name);
1873 if(!IsChannelName(argv[1]))
1875 reply("MSG_NOT_CHANNEL_NAME");
1879 if(opserv_bad_channel(argv[1]))
1881 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1885 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1887 for(uData = channel->channel_info->users; uData; uData = uData->next)
1889 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1891 if(!IsHelping(user))
1892 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1894 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1900 mod_chanmode_init(&change);
1901 if(!(target = GetChannel(argv[1])))
1903 target = AddChannel(argv[1], now, NULL, NULL);
1904 if(!IsSuspended(channel->channel_info))
1905 AddChannelUser(chanserv, target);
1907 else if(target->channel_info)
1909 reply("CSMSG_ALREADY_REGGED", target->name);
1912 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1913 && !IsHelping(user))
1915 reply("CSMSG_MUST_BE_OPPED", target->name);
1918 else if(!IsSuspended(channel->channel_info))
1921 change.args[0].mode = MODE_CHANOP;
1922 change.args[0].u.member = AddChannelUser(chanserv, target);
1923 mod_chanmode_announce(chanserv, target, &change);
1928 /* Clear MODE_REGISTERED from old channel, add it to new. */
1930 change.modes_clear = MODE_REGISTERED;
1931 mod_chanmode_announce(chanserv, channel, &change);
1932 change.modes_clear = 0;
1933 change.modes_set = MODE_REGISTERED;
1934 mod_chanmode_announce(chanserv, target, &change);
1937 /* Move the channel_info to the target channel; it
1938 shouldn't be necessary to clear timeq callbacks
1939 for the old channel. */
1940 target->channel_info = channel->channel_info;
1941 target->channel_info->channel = target;
1942 channel->channel_info = NULL;
1944 reply("CSMSG_MOVE_SUCCESS", target->name);
1946 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1947 if(!IsSuspended(target->channel_info))
1949 char reason2[MAXLEN];
1950 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1951 DelChannelUser(chanserv, channel, reason2, 0);
1953 UnlockChannel(channel);
1954 LockChannel(target);
1955 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1960 merge_users(struct chanData *source, struct chanData *target)
1962 struct userData *suData, *tuData, *next;
1968 /* Insert the source's users into the scratch area. */
1969 for(suData = source->users; suData; suData = suData->next)
1970 dict_insert(merge, suData->handle->handle, suData);
1972 /* Iterate through the target's users, looking for
1973 users common to both channels. The lower access is
1974 removed from either the scratch area or target user
1976 for(tuData = target->users; tuData; tuData = next)
1978 struct userData *choice;
1980 next = tuData->next;
1982 /* If a source user exists with the same handle as a target
1983 channel's user, resolve the conflict by removing one. */
1984 suData = dict_find(merge, tuData->handle->handle, NULL);
1988 /* Pick the data we want to keep. */
1989 /* If the access is the same, use the later seen time. */
1990 if(suData->access == tuData->access)
1991 choice = (suData->seen > tuData->seen) ? suData : tuData;
1992 else /* Otherwise, keep the higher access level. */
1993 choice = (suData->access > tuData->access) ? suData : tuData;
1995 /* Remove the user that wasn't picked. */
1996 if(choice == tuData)
1998 dict_remove(merge, suData->handle->handle);
1999 del_channel_user(suData, 0);
2002 del_channel_user(tuData, 0);
2005 /* Move the remaining users to the target channel. */
2006 for(it = dict_first(merge); it; it = iter_next(it))
2008 suData = iter_data(it);
2010 /* Insert the user into the target channel's linked list. */
2011 suData->prev = NULL;
2012 suData->next = target->users;
2013 suData->channel = target;
2016 target->users->prev = suData;
2017 target->users = suData;
2019 /* Update the user counts for the target channel; the
2020 source counts are left alone. */
2021 target->userCount++;
2024 /* Possible to assert (source->users == NULL) here. */
2025 source->users = NULL;
2030 merge_bans(struct chanData *source, struct chanData *target)
2032 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2034 /* Hold on to the original head of the target ban list
2035 to avoid comparing source bans with source bans. */
2036 tFront = target->bans;
2038 /* Perform a totally expensive O(n*m) merge, ick. */
2039 for(sbData = source->bans; sbData; sbData = sNext)
2041 /* Flag to track whether the ban's been moved
2042 to the destination yet. */
2045 /* Possible to assert (sbData->prev == NULL) here. */
2046 sNext = sbData->next;
2048 for(tbData = tFront; tbData; tbData = tNext)
2050 tNext = tbData->next;
2052 /* Perform two comparisons between each source
2053 and target ban, conflicts are resolved by
2054 keeping the broader ban and copying the later
2055 expiration and triggered time. */
2056 if(match_ircglobs(tbData->mask, sbData->mask))
2058 /* There is a broader ban in the target channel that
2059 overrides one in the source channel; remove the
2060 source ban and break. */
2061 if(sbData->expires > tbData->expires)
2062 tbData->expires = sbData->expires;
2063 if(sbData->triggered > tbData->triggered)
2064 tbData->triggered = sbData->triggered;
2065 del_channel_ban(sbData);
2068 else if(match_ircglobs(sbData->mask, tbData->mask))
2070 /* There is a broader ban in the source channel that
2071 overrides one in the target channel; remove the
2072 target ban, fall through and move the source over. */
2073 if(tbData->expires > sbData->expires)
2074 sbData->expires = tbData->expires;
2075 if(tbData->triggered > sbData->triggered)
2076 sbData->triggered = tbData->triggered;
2077 if(tbData == tFront)
2079 del_channel_ban(tbData);
2082 /* Source bans can override multiple target bans, so
2083 we allow a source to run through this loop multiple
2084 times, but we can only move it once. */
2089 /* Remove the source ban from the source ban list. */
2091 sbData->next->prev = sbData->prev;
2093 /* Modify the source ban's associated channel. */
2094 sbData->channel = target;
2096 /* Insert the ban into the target channel's linked list. */
2097 sbData->prev = NULL;
2098 sbData->next = target->bans;
2101 target->bans->prev = sbData;
2102 target->bans = sbData;
2104 /* Update the user counts for the target channel. */
2109 /* Possible to assert (source->bans == NULL) here. */
2110 source->bans = NULL;
2114 merge_data(struct chanData *source, struct chanData *target)
2116 /* Use more recent visited and owner-transfer time; use older
2117 * registered time. Bitwise or may_opchan. Use higher max.
2118 * Do not touch last_refresh, ban count or user counts.
2120 if(source->visited > target->visited)
2121 target->visited = source->visited;
2122 if(source->registered < target->registered)
2123 target->registered = source->registered;
2124 if(source->ownerTransfer > target->ownerTransfer)
2125 target->ownerTransfer = source->ownerTransfer;
2126 if(source->may_opchan)
2127 target->may_opchan = 1;
2128 if(source->max > target->max)
2129 target->max = source->max;
2133 merge_channel(struct chanData *source, struct chanData *target)
2135 merge_users(source, target);
2136 merge_bans(source, target);
2137 merge_data(source, target);
2140 static CHANSERV_FUNC(cmd_merge)
2142 struct userData *target_user;
2143 struct chanNode *target;
2144 char reason[MAXLEN];
2148 /* Make sure the target channel exists and is registered to the user
2149 performing the command. */
2150 if(!(target = GetChannel(argv[1])))
2152 reply("MSG_INVALID_CHANNEL");
2156 if(!target->channel_info)
2158 reply("CSMSG_NOT_REGISTERED", target->name);
2162 if(IsProtected(channel->channel_info))
2164 reply("CSMSG_MERGE_NODELETE");
2168 if(IsSuspended(target->channel_info))
2170 reply("CSMSG_MERGE_SUSPENDED");
2174 if(channel == target)
2176 reply("CSMSG_MERGE_SELF");
2180 target_user = GetChannelUser(target->channel_info, user->handle_info);
2181 if(!target_user || (target_user->access < UL_OWNER))
2183 reply("CSMSG_MERGE_NOT_OWNER");
2187 /* Merge the channel structures and associated data. */
2188 merge_channel(channel->channel_info, target->channel_info);
2189 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2190 unregister_channel(channel->channel_info, reason);
2191 reply("CSMSG_MERGE_SUCCESS", target->name);
2195 static CHANSERV_FUNC(cmd_opchan)
2197 struct mod_chanmode change;
2198 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2200 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2203 channel->channel_info->may_opchan = 0;
2204 mod_chanmode_init(&change);
2206 change.args[0].mode = MODE_CHANOP;
2207 change.args[0].u.member = GetUserMode(channel, chanserv);
2208 mod_chanmode_announce(chanserv, channel, &change);
2209 reply("CSMSG_OPCHAN_DONE", channel->name);
2213 static CHANSERV_FUNC(cmd_adduser)
2215 struct userData *actee;
2216 struct userData *actor, *real_actor;
2217 struct handle_info *handle;
2218 unsigned short access, override = 0;
2222 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2224 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2228 access = user_level_from_name(argv[2], UL_OWNER);
2231 reply("CSMSG_INVALID_ACCESS", argv[2]);
2235 actor = GetChannelUser(channel->channel_info, user->handle_info);
2236 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2238 if(actor->access <= access)
2240 reply("CSMSG_NO_BUMP_ACCESS");
2244 // Trying to add someone with equal/more access
2245 if (!real_actor || real_actor->access <= access)
2246 override = CMD_LOG_OVERRIDE;
2248 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2251 if((actee = GetChannelAccess(channel->channel_info, handle)))
2253 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2257 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2258 scan_user_presence(actee, NULL);
2259 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2260 return 1 | override;
2263 static CHANSERV_FUNC(cmd_clvl)
2265 struct handle_info *handle;
2266 struct userData *victim;
2267 struct userData *actor, *real_actor;
2268 unsigned short new_access, override = 0;
2269 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2273 actor = GetChannelUser(channel->channel_info, user->handle_info);
2274 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2276 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2279 if(handle == user->handle_info && !privileged)
2281 reply("CSMSG_NO_SELF_CLVL");
2285 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2287 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2291 if(actor->access <= victim->access && !privileged)
2293 reply("MSG_USER_OUTRANKED", handle->handle);
2297 new_access = user_level_from_name(argv[2], UL_OWNER);
2301 reply("CSMSG_INVALID_ACCESS", argv[2]);
2305 if(new_access >= actor->access && !privileged)
2307 reply("CSMSG_NO_BUMP_ACCESS");
2311 // Trying to clvl a equal/higher user
2312 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2313 override = CMD_LOG_OVERRIDE;
2314 // Trying to clvl someone to equal/higher access
2315 if(!real_actor || new_access >= real_actor->access)
2316 override = CMD_LOG_OVERRIDE;
2317 // Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2318 // If they lower their own access it's not a big problem.
2320 victim->access = new_access;
2321 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2322 return 1 | override;
2325 static CHANSERV_FUNC(cmd_deluser)
2327 struct handle_info *handle;
2328 struct userData *victim;
2329 struct userData *actor, *real_actor;
2330 unsigned short access, override = 0;
2335 actor = GetChannelUser(channel->channel_info, user->handle_info);
2336 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2338 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2341 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2343 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2349 access = user_level_from_name(argv[1], UL_OWNER);
2352 reply("CSMSG_INVALID_ACCESS", argv[1]);
2355 if(access != victim->access)
2357 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2363 access = victim->access;
2366 if((actor->access <= victim->access) && !IsHelping(user))
2368 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2372 // If people delete themselves it is an override, but they could've used deleteme so we don't log it as an override
2373 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2374 override = CMD_LOG_OVERRIDE;
2376 chan_name = strdup(channel->name);
2377 del_channel_user(victim, 1);
2378 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2380 return 1 | override;
2384 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2386 struct userData *actor, *real_actor, *uData, *next;
2387 unsigned int override = 0;
2389 actor = GetChannelUser(channel->channel_info, user->handle_info);
2390 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2392 if(min_access > max_access)
2394 reply("CSMSG_BAD_RANGE", min_access, max_access);
2398 if((actor->access <= max_access) && !IsHelping(user))
2400 reply("CSMSG_NO_ACCESS");
2404 if(!real_actor || real_actor->access <= max_access)
2405 override = CMD_LOG_OVERRIDE;
2407 for(uData = channel->channel_info->users; uData; uData = next)
2411 if((uData->access >= min_access)
2412 && (uData->access <= max_access)
2413 && match_ircglob(uData->handle->handle, mask))
2414 del_channel_user(uData, 1);
2417 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2418 return 1 | override;
2421 static CHANSERV_FUNC(cmd_mdelowner)
2423 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2426 static CHANSERV_FUNC(cmd_mdelcoowner)
2428 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2431 static CHANSERV_FUNC(cmd_mdelmaster)
2433 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2436 static CHANSERV_FUNC(cmd_mdelop)
2438 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2441 static CHANSERV_FUNC(cmd_mdelpeon)
2443 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2447 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2449 struct banData *bData, *next;
2450 char interval[INTERVALLEN];
2455 limit = now - duration;
2456 for(bData = channel->channel_info->bans; bData; bData = next)
2460 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2463 del_channel_ban(bData);
2467 intervalString(interval, duration, user->handle_info);
2468 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2473 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2475 struct userData *actor, *uData, *next;
2476 char interval[INTERVALLEN];
2480 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2481 if(min_access > max_access)
2483 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2487 if(!actor || actor->access <= max_access)
2489 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2494 limit = now - duration;
2495 for(uData = channel->channel_info->users; uData; uData = next)
2499 if((uData->seen > limit)
2501 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2504 if(((uData->access >= min_access) && (uData->access <= max_access))
2505 || (!max_access && (uData->access < actor->access)))
2507 del_channel_user(uData, 1);
2515 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2517 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2521 static CHANSERV_FUNC(cmd_trim)
2523 unsigned long duration;
2524 unsigned short min_level, max_level;
2529 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2530 duration = ParseInterval(argv[2]);
2533 reply("CSMSG_CANNOT_TRIM");
2537 if(!irccasecmp(argv[1], "bans"))
2539 cmd_trim_bans(user, channel, duration);
2542 else if(!irccasecmp(argv[1], "users"))
2544 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2547 else if(parse_level_range(&min_level, &max_level, argv[1]))
2549 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2552 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2554 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2559 reply("CSMSG_INVALID_TRIM", argv[1]);
2564 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2565 to the user. cmd_all takes advantage of this. */
2566 static CHANSERV_FUNC(cmd_up)
2568 struct mod_chanmode change;
2569 struct userData *uData;
2572 mod_chanmode_init(&change);
2574 change.args[0].u.member = GetUserMode(channel, user);
2575 if(!change.args[0].u.member)
2578 reply("MSG_CHANNEL_ABSENT", channel->name);
2582 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2586 reply("CSMSG_GODMODE_UP", argv[0]);
2589 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2591 change.args[0].mode = MODE_CHANOP;
2592 errmsg = "CSMSG_ALREADY_OPPED";
2594 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2596 change.args[0].mode = MODE_VOICE;
2597 errmsg = "CSMSG_ALREADY_VOICED";
2602 reply("CSMSG_NO_ACCESS");
2605 change.args[0].mode &= ~change.args[0].u.member->modes;
2606 if(!change.args[0].mode)
2609 reply(errmsg, channel->name);
2612 modcmd_chanmode_announce(&change);
2616 static CHANSERV_FUNC(cmd_down)
2618 struct mod_chanmode change;
2620 mod_chanmode_init(&change);
2622 change.args[0].u.member = GetUserMode(channel, user);
2623 if(!change.args[0].u.member)
2626 reply("MSG_CHANNEL_ABSENT", channel->name);
2630 if(!change.args[0].u.member->modes)
2633 reply("CSMSG_ALREADY_DOWN", channel->name);
2637 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2638 modcmd_chanmode_announce(&change);
2642 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)
2644 struct userData *cList;
2646 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2648 if(IsSuspended(cList->channel)
2649 || IsUserSuspended(cList)
2650 || !GetUserMode(cList->channel->channel, user))
2653 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2659 static CHANSERV_FUNC(cmd_upall)
2661 return cmd_all(CSFUNC_ARGS, cmd_up);
2664 static CHANSERV_FUNC(cmd_downall)
2666 return cmd_all(CSFUNC_ARGS, cmd_down);
2669 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2670 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2673 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)
2675 unsigned int ii, valid;
2676 struct userNode *victim;
2677 struct mod_chanmode *change;
2679 change = mod_chanmode_alloc(argc - 1);
2681 for(ii=valid=0; ++ii < argc; )
2683 if(!(victim = GetUserH(argv[ii])))
2685 change->args[valid].mode = mode;
2686 change->args[valid].u.member = GetUserMode(channel, victim);
2687 if(!change->args[valid].u.member)
2689 if(validate && !validate(user, channel, victim))
2694 change->argc = valid;
2695 if(valid < (argc-1))
2696 reply("CSMSG_PROCESS_FAILED");
2699 modcmd_chanmode_announce(change);
2700 reply(action, channel->name);
2702 mod_chanmode_free(change);
2706 static CHANSERV_FUNC(cmd_op)
2708 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2711 static CHANSERV_FUNC(cmd_deop)
2713 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2716 static CHANSERV_FUNC(cmd_voice)
2718 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2721 static CHANSERV_FUNC(cmd_devoice)
2723 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2727 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2733 for(ii=0; ii<channel->members.used; ii++)
2735 struct modeNode *mn = channel->members.list[ii];
2737 if(IsService(mn->user))
2740 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2743 if(protect_user(mn->user, user, channel->channel_info))
2747 victims[(*victimCount)++] = mn;
2753 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2755 struct userNode *victim;
2756 struct modeNode **victims;
2757 unsigned int offset, n, victimCount, duration = 0;
2758 char *reason = "Bye.", *ban, *name;
2759 char interval[INTERVALLEN];
2761 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2762 REQUIRE_PARAMS(offset);
2765 reason = unsplit_string(argv + offset, argc - offset, NULL);
2766 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2768 /* Truncate the reason to a length of TOPICLEN, as
2769 the ircd does; however, leave room for an ellipsis
2770 and the kicker's nick. */
2771 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2775 if((victim = GetUserH(argv[1])))
2777 victims = alloca(sizeof(victims[0]));
2778 victims[0] = GetUserMode(channel, victim);
2779 /* XXX: The comparison with ACTION_KICK is just because all
2780 * other actions can work on users outside the channel, and we
2781 * want to allow those (e.g. unbans) in that case. If we add
2782 * some other ejection action for in-channel users, change
2784 victimCount = victims[0] ? 1 : 0;
2786 if(IsService(victim))
2788 reply("MSG_SERVICE_IMMUNE", victim->nick);
2792 if((action == ACTION_KICK) && !victimCount)
2794 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2798 if(protect_user(victim, user, channel->channel_info))
2800 reply("CSMSG_USER_PROTECTED", victim->nick);
2804 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2805 name = victim->nick;
2809 if(!is_ircmask(argv[1]))
2811 reply("MSG_NICK_UNKNOWN", argv[1]);
2815 victims = alloca(sizeof(victims[0]) * channel->members.used);
2817 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2819 reply("CSMSG_MASK_PROTECTED", argv[1]);
2823 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2825 reply("CSMSG_LAME_MASK", argv[1]);
2829 if((action == ACTION_KICK) && (victimCount == 0))
2831 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2835 name = ban = strdup(argv[1]);
2838 /* Truncate the ban in place if necessary; we must ensure
2839 that 'ban' is a valid ban mask before sanitizing it. */
2840 sanitize_ircmask(ban);
2842 if(action & ACTION_ADD_BAN)
2844 struct banData *bData, *next;
2846 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2848 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2853 if(action & ACTION_ADD_TIMED_BAN)
2855 duration = ParseInterval(argv[2]);
2859 reply("CSMSG_DURATION_TOO_LOW");
2863 else if(duration > (86400 * 365 * 2))
2865 reply("CSMSG_DURATION_TOO_HIGH");
2871 for(bData = channel->channel_info->bans; bData; bData = next)
2873 if(match_ircglobs(bData->mask, ban))
2875 int exact = !irccasecmp(bData->mask, ban);
2877 /* The ban is redundant; there is already a ban
2878 with the same effect in place. */
2882 free(bData->reason);
2883 bData->reason = strdup(reason);
2884 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2886 reply("CSMSG_REASON_CHANGE", ban);
2890 if(exact && bData->expires)
2894 /* If the ban matches an existing one exactly,
2895 extend the expiration time if the provided
2896 duration is longer. */
2897 if(duration && ((time_t)(now + duration) > bData->expires))
2899 bData->expires = now + duration;
2910 /* Delete the expiration timeq entry and
2911 requeue if necessary. */
2912 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2915 timeq_add(bData->expires, expire_ban, bData);
2919 /* automated kickban */
2922 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2924 reply("CSMSG_BAN_ADDED", name, channel->name);
2930 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2937 if(match_ircglobs(ban, bData->mask))
2939 /* The ban we are adding makes previously existing
2940 bans redundant; silently remove them. */
2941 del_channel_ban(bData);
2945 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);
2947 name = ban = strdup(bData->mask);
2951 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2953 extern const char *hidden_host_suffix;
2954 const char *old_name = chanserv_conf.old_ban_names->list[n];
2956 unsigned int l1, l2;
2959 l2 = strlen(old_name);
2962 if(irccasecmp(ban + l1 - l2, old_name))
2964 new_mask = malloc(MAXLEN);
2965 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2967 name = ban = new_mask;
2972 if(action & ACTION_BAN)
2974 unsigned int exists;
2975 struct mod_chanmode *change;
2977 if(channel->banlist.used >= MAXBANS)
2980 reply("CSMSG_BANLIST_FULL", channel->name);
2985 exists = ChannelBanExists(channel, ban);
2986 change = mod_chanmode_alloc(victimCount + 1);
2987 for(n = 0; n < victimCount; ++n)
2989 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2990 change->args[n].u.member = victims[n];
2994 change->args[n].mode = MODE_BAN;
2995 change->args[n++].u.hostmask = ban;
2999 modcmd_chanmode_announce(change);
3001 mod_chanmode_announce(chanserv, channel, change);
3002 mod_chanmode_free(change);
3004 if(exists && (action == ACTION_BAN))
3007 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3013 if(action & ACTION_KICK)
3015 char kick_reason[MAXLEN];
3016 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3018 for(n = 0; n < victimCount; n++)
3019 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3024 /* No response, since it was automated. */
3026 else if(action & ACTION_ADD_BAN)
3029 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3031 reply("CSMSG_BAN_ADDED", name, channel->name);
3033 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3034 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3035 else if(action & ACTION_BAN)
3036 reply("CSMSG_BAN_DONE", name, channel->name);
3037 else if(action & ACTION_KICK && victimCount)
3038 reply("CSMSG_KICK_DONE", name, channel->name);
3044 static CHANSERV_FUNC(cmd_kickban)
3046 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3049 static CHANSERV_FUNC(cmd_kick)
3051 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3054 static CHANSERV_FUNC(cmd_ban)
3056 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3059 static CHANSERV_FUNC(cmd_addban)
3061 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3064 static CHANSERV_FUNC(cmd_addtimedban)
3066 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3069 static struct mod_chanmode *
3070 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3072 struct mod_chanmode *change;
3073 unsigned char *match;
3074 unsigned int ii, count;
3076 match = alloca(bans->used);
3079 for(ii = count = 0; ii < bans->used; ++ii)
3081 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3082 MATCH_USENICK | MATCH_VISIBLE);
3089 for(ii = count = 0; ii < bans->used; ++ii)
3091 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3098 change = mod_chanmode_alloc(count);
3099 for(ii = count = 0; ii < bans->used; ++ii)
3103 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3104 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3106 assert(count == change->argc);
3111 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3113 struct userNode *actee;
3119 /* may want to allow a comma delimited list of users... */
3120 if(!(actee = GetUserH(argv[1])))
3122 if(!is_ircmask(argv[1]))
3124 reply("MSG_NICK_UNKNOWN", argv[1]);
3128 mask = strdup(argv[1]);
3131 /* We don't sanitize the mask here because ircu
3133 if(action & ACTION_UNBAN)
3135 struct mod_chanmode *change;
3136 change = find_matching_bans(&channel->banlist, actee, mask);
3141 modcmd_chanmode_announce(change);
3142 for(ii = 0; ii < change->argc; ++ii)
3143 free((char*)change->args[ii].u.hostmask);
3144 mod_chanmode_free(change);
3149 if(action & ACTION_DEL_BAN)
3151 struct banData *ban, *next;
3153 ban = channel->channel_info->bans;
3157 for( ; ban && !user_matches_glob(actee, ban->mask,
3158 MATCH_USENICK | MATCH_VISIBLE);
3161 for( ; ban && !match_ircglobs(mask, ban->mask);
3166 del_channel_ban(ban);
3173 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3175 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3181 static CHANSERV_FUNC(cmd_unban)
3183 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3186 static CHANSERV_FUNC(cmd_delban)
3188 /* it doesn't necessarily have to remove the channel ban - may want
3189 to make that an option. */
3190 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3193 static CHANSERV_FUNC(cmd_unbanme)
3195 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3196 long flags = ACTION_UNBAN;
3198 /* remove permanent bans if the user has the proper access. */
3199 if(uData->access >= UL_MASTER)
3200 flags |= ACTION_DEL_BAN;
3202 argv[1] = user->nick;
3203 return unban_user(user, channel, 2, argv, cmd, flags);
3206 static CHANSERV_FUNC(cmd_unbanall)
3208 struct mod_chanmode *change;
3211 if(!channel->banlist.used)
3213 reply("CSMSG_NO_BANS", channel->name);
3217 change = mod_chanmode_alloc(channel->banlist.used);
3218 for(ii=0; ii<channel->banlist.used; ii++)
3220 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3221 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3223 modcmd_chanmode_announce(change);
3224 for(ii = 0; ii < change->argc; ++ii)
3225 free((char*)change->args[ii].u.hostmask);
3226 mod_chanmode_free(change);
3227 reply("CSMSG_BANS_REMOVED", channel->name);
3231 static CHANSERV_FUNC(cmd_open)
3233 struct mod_chanmode *change;
3236 change = find_matching_bans(&channel->banlist, user, NULL);
3238 change = mod_chanmode_alloc(0);
3239 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3240 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3241 && channel->channel_info->modes.modes_set)
3242 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3243 modcmd_chanmode_announce(change);
3244 reply("CSMSG_CHANNEL_OPENED", channel->name);
3245 for(ii = 0; ii < change->argc; ++ii)
3246 free((char*)change->args[ii].u.hostmask);
3247 mod_chanmode_free(change);
3251 static CHANSERV_FUNC(cmd_myaccess)
3253 static struct string_buffer sbuf;
3254 struct handle_info *target_handle;
3255 struct userData *uData;
3258 target_handle = user->handle_info;
3259 else if(!IsHelping(user))
3261 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3264 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3267 if(!target_handle->channels)
3269 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3273 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3274 for(uData = target_handle->channels; uData; uData = uData->u_next)
3276 struct chanData *cData = uData->channel;
3278 if(uData->access > UL_OWNER)
3280 if(IsProtected(cData)
3281 && (target_handle != user->handle_info)
3282 && !GetTrueChannelAccess(cData, user->handle_info))
3285 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3286 if(uData->flags != USER_AUTO_OP)
3287 string_buffer_append(&sbuf, ',');
3288 if(IsUserSuspended(uData))
3289 string_buffer_append(&sbuf, 's');
3290 if(IsUserAutoOp(uData))
3292 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3293 string_buffer_append(&sbuf, 'o');
3294 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3295 string_buffer_append(&sbuf, 'v');
3297 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3298 string_buffer_append(&sbuf, 'i');
3300 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3302 string_buffer_append_string(&sbuf, ")]");
3303 string_buffer_append(&sbuf, '\0');
3304 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3310 static CHANSERV_FUNC(cmd_access)
3312 struct userNode *target;
3313 struct handle_info *target_handle;
3314 struct userData *uData;
3316 char prefix[MAXLEN];
3321 target_handle = target->handle_info;
3323 else if((target = GetUserH(argv[1])))
3325 target_handle = target->handle_info;
3327 else if(argv[1][0] == '*')
3329 if(!(target_handle = get_handle_info(argv[1]+1)))
3331 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3337 reply("MSG_NICK_UNKNOWN", argv[1]);
3341 assert(target || target_handle);
3343 if(target == chanserv)
3345 reply("CSMSG_IS_CHANSERV");
3353 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3358 reply("MSG_USER_AUTHENTICATE", target->nick);
3361 reply("MSG_AUTHENTICATE");
3367 const char *epithet = NULL, *type = NULL;
3370 epithet = chanserv_conf.irc_operator_epithet;
3371 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3373 else if(IsNetworkHelper(target))
3375 epithet = chanserv_conf.network_helper_epithet;
3376 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3378 else if(IsSupportHelper(target))
3380 epithet = chanserv_conf.support_helper_epithet;
3381 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3385 if(target_handle->epithet)
3386 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3388 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3390 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3394 sprintf(prefix, "%s", target_handle->handle);
3397 if(!channel->channel_info)
3399 reply("CSMSG_NOT_REGISTERED", channel->name);
3403 helping = HANDLE_FLAGGED(target_handle, HELPING)
3404 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3405 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3407 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3408 /* To prevent possible information leaks, only show infolines
3409 * if the requestor is in the channel or it's their own
3411 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3413 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3415 /* Likewise, only say it's suspended if the user has active
3416 * access in that channel or it's their own entry. */
3417 if(IsUserSuspended(uData)
3418 && (GetChannelUser(channel->channel_info, user->handle_info)
3419 || (user->handle_info == uData->handle)))
3421 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3426 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3433 zoot_list(struct listData *list)
3435 struct userData *uData;
3436 unsigned int start, curr, highest, lowest;
3437 struct helpfile_table tmp_table;
3438 const char **temp, *msg;
3440 if(list->table.length == 1)
3443 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3445 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3446 msg = user_find_message(list->user, "MSG_NONE");
3447 send_message_type(4, list->user, list->bot, " %s", msg);
3449 tmp_table.width = list->table.width;
3450 tmp_table.flags = list->table.flags;
3451 list->table.contents[0][0] = " ";
3452 highest = list->highest;
3453 if(list->lowest != 0)
3454 lowest = list->lowest;
3455 else if(highest < 100)
3458 lowest = highest - 100;
3459 for(start = curr = 1; curr < list->table.length; )
3461 uData = list->users[curr-1];
3462 list->table.contents[curr++][0] = " ";
3463 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3466 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3468 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3469 temp = list->table.contents[--start];
3470 list->table.contents[start] = list->table.contents[0];
3471 tmp_table.contents = list->table.contents + start;
3472 tmp_table.length = curr - start;
3473 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3474 list->table.contents[start] = temp;
3476 highest = lowest - 1;
3477 lowest = (highest < 100) ? 0 : (highest - 99);
3483 def_list(struct listData *list)
3487 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3489 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3490 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3491 if(list->table.length == 1)
3493 msg = user_find_message(list->user, "MSG_NONE");
3494 send_message_type(4, list->user, list->bot, " %s", msg);
3499 userData_access_comp(const void *arg_a, const void *arg_b)
3501 const struct userData *a = *(struct userData**)arg_a;
3502 const struct userData *b = *(struct userData**)arg_b;
3504 if(a->access != b->access)
3505 res = b->access - a->access;
3507 res = irccasecmp(a->handle->handle, b->handle->handle);
3512 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3514 void (*send_list)(struct listData *);
3515 struct userData *uData;
3516 struct listData lData;
3517 unsigned int matches;
3521 lData.bot = cmd->parent->bot;
3522 lData.channel = channel;
3523 lData.lowest = lowest;
3524 lData.highest = highest;
3525 lData.search = (argc > 1) ? argv[1] : NULL;
3526 send_list = def_list;
3527 (void)zoot_list; /* since it doesn't show user levels */
3529 if(user->handle_info)
3531 switch(user->handle_info->userlist_style)
3533 case HI_STYLE_DEF: send_list = def_list; break;
3534 case HI_STYLE_ZOOT: send_list = def_list; break;
3538 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3540 for(uData = channel->channel_info->users; uData; uData = uData->next)
3542 if((uData->access < lowest)
3543 || (uData->access > highest)
3544 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3546 lData.users[matches++] = uData;
3548 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3550 lData.table.length = matches+1;
3551 lData.table.width = 4;
3552 lData.table.flags = TABLE_NO_FREE;
3553 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3554 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3555 lData.table.contents[0] = ary;
3558 ary[2] = "Last Seen";
3560 for(matches = 1; matches < lData.table.length; ++matches)
3562 struct userData *uData = lData.users[matches-1];
3563 char seen[INTERVALLEN];
3565 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3566 lData.table.contents[matches] = ary;
3567 ary[0] = strtab(uData->access);
3568 ary[1] = uData->handle->handle;
3571 else if(!uData->seen)
3574 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3575 ary[2] = strdup(ary[2]);
3576 if(IsUserSuspended(uData))
3577 ary[3] = "Suspended";
3578 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3579 ary[3] = "Vacation";
3584 for(matches = 1; matches < lData.table.length; ++matches)
3586 free((char*)lData.table.contents[matches][2]);
3587 free(lData.table.contents[matches]);
3589 free(lData.table.contents[0]);
3590 free(lData.table.contents);
3594 static CHANSERV_FUNC(cmd_users)
3596 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3599 static CHANSERV_FUNC(cmd_wlist)
3601 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3604 static CHANSERV_FUNC(cmd_clist)
3606 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3609 static CHANSERV_FUNC(cmd_mlist)
3611 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3614 static CHANSERV_FUNC(cmd_olist)
3616 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3619 static CHANSERV_FUNC(cmd_plist)
3621 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3624 static CHANSERV_FUNC(cmd_bans)
3626 struct userNode *search_u = NULL;
3627 struct helpfile_table tbl;
3628 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3629 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3630 const char *msg_never, *triggered, *expires;
3631 struct banData *ban, **bans;
3635 else if(strchr(search = argv[1], '!'))
3638 search_wilds = search[strcspn(search, "?*")];
3640 else if(!(search_u = GetUserH(search)))
3641 reply("MSG_NICK_UNKNOWN", search);
3643 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3645 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3649 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3654 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3657 bans[matches++] = ban;
3662 tbl.length = matches + 1;
3663 tbl.width = 4 + timed;
3665 tbl.flags = TABLE_NO_FREE;
3666 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3667 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3668 tbl.contents[0][0] = "Mask";
3669 tbl.contents[0][1] = "Set By";
3670 tbl.contents[0][2] = "Triggered";
3673 tbl.contents[0][3] = "Expires";
3674 tbl.contents[0][4] = "Reason";
3677 tbl.contents[0][3] = "Reason";
3680 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3682 free(tbl.contents[0]);
3687 msg_never = user_find_message(user, "MSG_NEVER");
3688 for(ii = 0; ii < matches; )
3694 else if(ban->expires)
3695 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3697 expires = msg_never;
3700 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3702 triggered = msg_never;
3704 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3705 tbl.contents[ii][0] = ban->mask;
3706 tbl.contents[ii][1] = ban->owner;
3707 tbl.contents[ii][2] = strdup(triggered);
3710 tbl.contents[ii][3] = strdup(expires);
3711 tbl.contents[ii][4] = ban->reason;
3714 tbl.contents[ii][3] = ban->reason;
3716 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3717 reply("MSG_MATCH_COUNT", matches);
3718 for(ii = 1; ii < tbl.length; ++ii)
3720 free((char*)tbl.contents[ii][2]);
3722 free((char*)tbl.contents[ii][3]);
3723 free(tbl.contents[ii]);
3725 free(tbl.contents[0]);
3731 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3733 struct chanData *cData = channel->channel_info;
3734 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3736 if(cData->topic_mask)
3737 return !match_ircglob(new_topic, cData->topic_mask);
3738 else if(cData->topic)
3739 return irccasecmp(new_topic, cData->topic);
3744 static CHANSERV_FUNC(cmd_topic)
3746 struct chanData *cData;
3749 cData = channel->channel_info;
3754 SetChannelTopic(channel, chanserv, cData->topic, 1);
3755 reply("CSMSG_TOPIC_SET", cData->topic);
3759 reply("CSMSG_NO_TOPIC", channel->name);
3763 topic = unsplit_string(argv + 1, argc - 1, NULL);
3764 /* If they say "!topic *", use an empty topic. */
3765 if((topic[0] == '*') && (topic[1] == 0))
3767 if(bad_topic(channel, user, topic))
3769 char *topic_mask = cData->topic_mask;
3772 char new_topic[TOPICLEN+1], tchar;
3773 int pos=0, starpos=-1, dpos=0, len;
3775 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3782 len = strlen(topic);
3783 if((dpos + len) > TOPICLEN)
3784 len = TOPICLEN + 1 - dpos;
3785 memcpy(new_topic+dpos, topic, len);
3789 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3790 default: new_topic[dpos++] = tchar; break;
3793 if((dpos > TOPICLEN) || tchar)
3796 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3797 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3800 new_topic[dpos] = 0;
3801 SetChannelTopic(channel, chanserv, new_topic, 1);
3803 reply("CSMSG_TOPIC_LOCKED", channel->name);
3808 SetChannelTopic(channel, chanserv, topic, 1);
3810 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3812 /* Grab the topic and save it as the default topic. */
3814 cData->topic = strdup(channel->topic);
3820 static CHANSERV_FUNC(cmd_mode)
3822 struct userData *uData;
3823 struct mod_chanmode *change;
3828 change = &channel->channel_info->modes;
3829 if(change->modes_set || change->modes_clear) {
3830 modcmd_chanmode_announce(change);
3831 reply("CSMSG_DEFAULTED_MODES", channel->name);
3833 reply("CSMSG_NO_MODES", channel->name);
3837 uData = GetChannelUser(channel->channel_info, user->handle_info);
3839 base_oplevel = MAXOPLEVEL;
3840 else if (uData->access >= UL_OWNER)
3843 base_oplevel = 1 + UL_OWNER - uData->access;
3844 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3847 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3851 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3852 && mode_lock_violated(&channel->channel_info->modes, change))
3855 mod_chanmode_format(&channel->channel_info->modes, modes);
3856 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3860 modcmd_chanmode_announce(change);
3861 mod_chanmode_free(change);
3862 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3866 static CHANSERV_FUNC(cmd_invite)
3868 struct userData *uData;
3869 struct userNode *invite;
3871 uData = GetChannelUser(channel->channel_info, user->handle_info);
3875 if(!(invite = GetUserH(argv[1])))
3877 reply("MSG_NICK_UNKNOWN", argv[1]);
3884 if(GetUserMode(channel, invite))
3886 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3894 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3895 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3898 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3900 irc_invite(chanserv, invite, channel);
3902 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3907 static CHANSERV_FUNC(cmd_inviteme)
3909 if(GetUserMode(channel, user))
3911 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3914 if(channel->channel_info
3915 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3917 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3920 irc_invite(cmd->parent->bot, user, channel);
3925 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3928 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3930 /* We display things based on two dimensions:
3931 * - Issue time: present or absent
3932 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3933 * (in order of precedence, so something both expired and revoked
3934 * only counts as revoked)
3936 combo = (suspended->issued ? 4 : 0)
3937 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3939 case 0: /* no issue time, indefinite expiration */
3940 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3942 case 1: /* no issue time, expires in future */
3943 intervalString(buf1, suspended->expires-now, user->handle_info);
3944 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3946 case 2: /* no issue time, expired */
3947 intervalString(buf1, now-suspended->expires, user->handle_info);
3948 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3950 case 3: /* no issue time, revoked */
3951 intervalString(buf1, now-suspended->revoked, user->handle_info);
3952 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3954 case 4: /* issue time set, indefinite expiration */
3955 intervalString(buf1, now-suspended->issued, user->handle_info);
3956 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3958 case 5: /* issue time set, expires in future */
3959 intervalString(buf1, now-suspended->issued, user->handle_info);
3960 intervalString(buf2, suspended->expires-now, user->handle_info);
3961 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3963 case 6: /* issue time set, expired */
3964 intervalString(buf1, now-suspended->issued, user->handle_info);
3965 intervalString(buf2, now-suspended->expires, user->handle_info);
3966 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3968 case 7: /* issue time set, revoked */
3969 intervalString(buf1, now-suspended->issued, user->handle_info);
3970 intervalString(buf2, now-suspended->revoked, user->handle_info);
3971 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3974 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3979 static CHANSERV_FUNC(cmd_info)
3981 char modes[MAXLEN], buffer[INTERVALLEN];
3982 struct userData *uData, *owner;
3983 struct chanData *cData;
3984 struct do_not_register *dnr;
3989 cData = channel->channel_info;
3990 reply("CSMSG_CHANNEL_INFO", channel->name);
3992 uData = GetChannelUser(cData, user->handle_info);
3993 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3995 mod_chanmode_format(&cData->modes, modes);
3996 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3997 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4000 for(it = dict_first(cData->notes); it; it = iter_next(it))
4004 note = iter_data(it);
4005 if(!note_type_visible_to_user(cData, note->type, user))
4008 padding = PADLEN - 1 - strlen(iter_key(it));
4009 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4012 reply("CSMSG_CHANNEL_MAX", cData->max);
4013 for(owner = cData->users; owner; owner = owner->next)
4014 if(owner->access == UL_OWNER)
4015 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4016 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4017 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4018 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4020 privileged = IsStaff(user);
4022 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4023 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4024 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4026 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4027 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4029 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4031 struct suspended *suspended;
4032 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4033 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4034 show_suspension_info(cmd, user, suspended);
4036 else if(IsSuspended(cData))
4038 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4039 show_suspension_info(cmd, user, cData->suspended);
4044 static CHANSERV_FUNC(cmd_netinfo)
4046 extern time_t boot_time;
4047 extern unsigned long burst_length;
4048 char interval[INTERVALLEN];
4050 reply("CSMSG_NETWORK_INFO");
4051 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4052 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4053 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4054 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4055 reply("CSMSG_NETWORK_BANS", banCount);
4056 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4057 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4058 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4063 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4065 struct helpfile_table table;
4067 struct userNode *user;
4072 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4073 table.contents = alloca(list->used*sizeof(*table.contents));
4074 for(nn=0; nn<list->used; nn++)
4076 user = list->list[nn];
4077 if(user->modes & skip_flags)
4081 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4084 nick = alloca(strlen(user->nick)+3);
4085 sprintf(nick, "(%s)", user->nick);
4089 table.contents[table.length][0] = nick;
4092 table_send(chanserv, to->nick, 0, NULL, table);
4095 static CHANSERV_FUNC(cmd_ircops)
4097 reply("CSMSG_STAFF_OPERS");
4098 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4102 static CHANSERV_FUNC(cmd_helpers)
4104 reply("CSMSG_STAFF_HELPERS");
4105 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4109 static CHANSERV_FUNC(cmd_staff)
4111 reply("CSMSG_NETWORK_STAFF");
4112 cmd_ircops(CSFUNC_ARGS);
4113 cmd_helpers(CSFUNC_ARGS);
4117 static CHANSERV_FUNC(cmd_peek)
4119 struct modeNode *mn;
4120 char modes[MODELEN];
4122 struct helpfile_table table;
4124 irc_make_chanmode(channel, modes);
4126 reply("CSMSG_PEEK_INFO", channel->name);
4127 reply("CSMSG_PEEK_TOPIC", channel->topic);
4128 reply("CSMSG_PEEK_MODES", modes);
4129 reply("CSMSG_PEEK_USERS", channel->members.used);
4133 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4134 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4135 for(n = 0; n < channel->members.used; n++)
4137 mn = channel->members.list[n];
4138 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4140 table.contents[table.length] = alloca(sizeof(**table.contents));
4141 table.contents[table.length][0] = mn->user->nick;
4146 reply("CSMSG_PEEK_OPS");
4147 table_send(chanserv, user->nick, 0, NULL, table);
4150 reply("CSMSG_PEEK_NO_OPS");
4154 static MODCMD_FUNC(cmd_wipeinfo)
4156 struct handle_info *victim;
4157 struct userData *ud, *actor, *real_actor;
4158 unsigned int override = 0;
4161 actor = GetChannelUser(channel->channel_info, user->handle_info);
4162 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4163 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4165 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4167 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4170 if((ud->access >= actor->access) && (ud != actor))
4172 reply("MSG_USER_OUTRANKED", victim->handle);
4175 if((ud->access >= real_actor->access) && (ud != real_actor))
4176 override = CMD_LOG_OVERRIDE;
4180 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4181 return 1 | override;
4184 static CHANSERV_FUNC(cmd_resync)
4186 struct mod_chanmode *changes;
4187 struct chanData *cData = channel->channel_info;
4188 unsigned int ii, used;
4190 changes = mod_chanmode_alloc(channel->members.used * 2);
4191 for(ii = used = 0; ii < channel->members.used; ++ii)
4193 struct modeNode *mn = channel->members.list[ii];
4194 struct userData *uData;
4196 if(IsService(mn->user))
4199 uData = GetChannelAccess(cData, mn->user->handle_info);
4200 if(!cData->lvlOpts[lvlGiveOps]
4201 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4203 if(!(mn->modes & MODE_CHANOP))
4205 changes->args[used].mode = MODE_CHANOP;
4206 changes->args[used++].u.member = mn;
4209 else if(!cData->lvlOpts[lvlGiveVoice]
4210 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4212 if(mn->modes & MODE_CHANOP)
4214 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4215 changes->args[used++].u.member = mn;
4217 if(!(mn->modes & MODE_VOICE))
4219 changes->args[used].mode = MODE_VOICE;
4220 changes->args[used++].u.member = mn;
4227 changes->args[used].mode = MODE_REMOVE | mn->modes;
4228 changes->args[used++].u.member = mn;
4232 changes->argc = used;
4233 modcmd_chanmode_announce(changes);
4234 mod_chanmode_free(changes);
4235 reply("CSMSG_RESYNCED_USERS", channel->name);
4239 static CHANSERV_FUNC(cmd_seen)
4241 struct userData *uData;
4242 struct handle_info *handle;
4243 char seen[INTERVALLEN];
4247 if(!irccasecmp(argv[1], chanserv->nick))
4249 reply("CSMSG_IS_CHANSERV");
4253 if(!(handle = get_handle_info(argv[1])))
4255 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4259 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4261 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4266 reply("CSMSG_USER_PRESENT", handle->handle);
4267 else if(uData->seen)
4268 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4270 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4272 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4273 reply("CSMSG_USER_VACATION", handle->handle);
4278 static MODCMD_FUNC(cmd_names)
4280 struct userNode *targ;
4281 struct userData *targData;
4282 unsigned int ii, pos;
4285 for(ii=pos=0; ii<channel->members.used; ++ii)
4287 targ = channel->members.list[ii]->user;
4288 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4291 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4294 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4298 if(IsUserSuspended(targData))
4300 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4303 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4304 reply("CSMSG_END_NAMES", channel->name);
4309 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4311 switch(ntype->visible_type)
4313 case NOTE_VIS_ALL: return 1;
4314 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4315 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4320 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4322 struct userData *uData;
4324 switch(ntype->set_access_type)
4326 case NOTE_SET_CHANNEL_ACCESS:
4327 if(!user->handle_info)
4329 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4331 return uData->access >= ntype->set_access.min_ulevel;
4332 case NOTE_SET_CHANNEL_SETTER:
4333 return check_user_level(channel, user, lvlSetters, 1, 0);
4334 case NOTE_SET_PRIVILEGED: default:
4335 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4339 static CHANSERV_FUNC(cmd_note)
4341 struct chanData *cData;
4343 struct note_type *ntype;
4345 cData = channel->channel_info;
4348 reply("CSMSG_NOT_REGISTERED", channel->name);
4352 /* If no arguments, show all visible notes for the channel. */
4358 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4360 note = iter_data(it);
4361 if(!note_type_visible_to_user(cData, note->type, user))
4364 reply("CSMSG_NOTELIST_HEADER", channel->name);
4365 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4368 reply("CSMSG_NOTELIST_END", channel->name);
4370 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4372 /* If one argument, show the named note. */
4375 if((note = dict_find(cData->notes, argv[1], NULL))
4376 && note_type_visible_to_user(cData, note->type, user))
4378 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4380 else if((ntype = dict_find(note_types, argv[1], NULL))
4381 && note_type_visible_to_user(NULL, ntype, user))
4383 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4388 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4392 /* Assume they're trying to set a note. */
4396 ntype = dict_find(note_types, argv[1], NULL);
4399 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4402 else if(note_type_settable_by_user(channel, ntype, user))
4404 note_text = unsplit_string(argv+2, argc-2, NULL);
4405 if((note = dict_find(cData->notes, argv[1], NULL)))
4406 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4407 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4408 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4410 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4412 /* The note is viewable to staff only, so return 0
4413 to keep the invocation from getting logged (or
4414 regular users can see it in !events). */
4420 reply("CSMSG_NO_ACCESS");
4427 static CHANSERV_FUNC(cmd_delnote)
4432 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4433 || !note_type_settable_by_user(channel, note->type, user))
4435 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4438 dict_remove(channel->channel_info->notes, note->type->name);
4439 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4443 static CHANSERV_FUNC(cmd_events)
4445 struct logSearch discrim;
4446 struct logReport report;
4447 unsigned int matches, limit;
4449 limit = (argc > 1) ? atoi(argv[1]) : 10;
4450 if(limit < 1 || limit > 200)
4453 memset(&discrim, 0, sizeof(discrim));
4454 discrim.masks.bot = chanserv;
4455 discrim.masks.channel_name = channel->name;
4457 discrim.masks.command = argv[2];
4458 discrim.limit = limit;
4459 discrim.max_time = INT_MAX;
4460 discrim.severities = 1 << LOG_COMMAND;
4461 report.reporter = chanserv;
4463 reply("CSMSG_EVENT_SEARCH_RESULTS");
4464 matches = log_entry_search(&discrim, log_report_entry, &report);
4466 reply("MSG_MATCH_COUNT", matches);
4468 reply("MSG_NO_MATCHES");
4472 static CHANSERV_FUNC(cmd_say)
4478 msg = unsplit_string(argv + 1, argc - 1, NULL);
4479 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4481 else if(GetUserH(argv[1]))
4484 msg = unsplit_string(argv + 2, argc - 2, NULL);
4485 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4489 reply("MSG_NOT_TARGET_NAME");
4495 static CHANSERV_FUNC(cmd_emote)
4501 /* CTCP is so annoying. */
4502 msg = unsplit_string(argv + 1, argc - 1, NULL);
4503 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4505 else if(GetUserH(argv[1]))
4507 msg = unsplit_string(argv + 2, argc - 2, NULL);
4508 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4512 reply("MSG_NOT_TARGET_NAME");
4518 struct channelList *
4519 chanserv_support_channels(void)
4521 return &chanserv_conf.support_channels;
4524 static CHANSERV_FUNC(cmd_expire)
4526 int channel_count = registered_channels;
4527 expire_channels(NULL);
4528 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4533 chanserv_expire_suspension(void *data)
4535 struct suspended *suspended = data;
4536 struct chanNode *channel;
4538 if(!suspended->expires || (now < suspended->expires))
4539 suspended->revoked = now;
4540 channel = suspended->cData->channel;
4541 suspended->cData->channel = channel;
4542 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4543 if(!IsOffChannel(suspended->cData))
4545 struct mod_chanmode change;
4546 mod_chanmode_init(&change);
4548 change.args[0].mode = MODE_CHANOP;
4549 change.args[0].u.member = AddChannelUser(chanserv, channel);
4550 mod_chanmode_announce(chanserv, channel, &change);
4554 static CHANSERV_FUNC(cmd_csuspend)
4556 struct suspended *suspended;
4557 char reason[MAXLEN];
4558 time_t expiry, duration;
4559 struct userData *uData;
4563 if(IsProtected(channel->channel_info))
4565 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4569 if(argv[1][0] == '!')
4571 else if(IsSuspended(channel->channel_info))
4573 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4574 show_suspension_info(cmd, user, channel->channel_info->suspended);
4578 if(!strcmp(argv[1], "0"))
4580 else if((duration = ParseInterval(argv[1])))
4581 expiry = now + duration;
4584 reply("MSG_INVALID_DURATION", argv[1]);
4588 unsplit_string(argv + 2, argc - 2, reason);
4590 suspended = calloc(1, sizeof(*suspended));
4591 suspended->revoked = 0;
4592 suspended->issued = now;
4593 suspended->suspender = strdup(user->handle_info->handle);
4594 suspended->expires = expiry;
4595 suspended->reason = strdup(reason);
4596 suspended->cData = channel->channel_info;
4597 suspended->previous = suspended->cData->suspended;
4598 suspended->cData->suspended = suspended;
4600 if(suspended->expires)
4601 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4603 if(IsSuspended(channel->channel_info))
4605 suspended->previous->revoked = now;
4606 if(suspended->previous->expires)
4607 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4608 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4609 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4613 /* Mark all users in channel as absent. */
4614 for(uData = channel->channel_info->users; uData; uData = uData->next)
4623 /* Mark the channel as suspended, then part. */
4624 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4625 DelChannelUser(chanserv, channel, suspended->reason, 0);
4626 reply("CSMSG_SUSPENDED", channel->name);
4627 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4628 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4633 static CHANSERV_FUNC(cmd_cunsuspend)
4635 struct suspended *suspended;
4636 char message[MAXLEN];
4638 if(!IsSuspended(channel->channel_info))
4640 reply("CSMSG_NOT_SUSPENDED", channel->name);
4644 suspended = channel->channel_info->suspended;
4646 /* Expire the suspension and join ChanServ to the channel. */
4647 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4648 chanserv_expire_suspension(suspended);
4649 reply("CSMSG_UNSUSPENDED", channel->name);
4650 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4651 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4655 typedef struct chanservSearch
4663 unsigned long flags;
4667 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4670 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4675 search = malloc(sizeof(struct chanservSearch));
4676 memset(search, 0, sizeof(*search));
4679 for(i = 0; i < argc; i++)
4681 /* Assume all criteria require arguments. */
4684 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4688 if(!irccasecmp(argv[i], "name"))
4689 search->name = argv[++i];
4690 else if(!irccasecmp(argv[i], "registrar"))
4691 search->registrar = argv[++i];
4692 else if(!irccasecmp(argv[i], "unvisited"))
4693 search->unvisited = ParseInterval(argv[++i]);
4694 else if(!irccasecmp(argv[i], "registered"))
4695 search->registered = ParseInterval(argv[++i]);
4696 else if(!irccasecmp(argv[i], "flags"))
4699 if(!irccasecmp(argv[i], "nodelete"))
4700 search->flags |= CHANNEL_NODELETE;
4701 else if(!irccasecmp(argv[i], "suspended"))
4702 search->flags |= CHANNEL_SUSPENDED;
4705 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4709 else if(!irccasecmp(argv[i], "limit"))
4710 search->limit = strtoul(argv[++i], NULL, 10);
4713 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4718 if(search->name && !strcmp(search->name, "*"))
4720 if(search->registrar && !strcmp(search->registrar, "*"))
4721 search->registrar = 0;
4730 chanserv_channel_match(struct chanData *channel, search_t search)
4732 const char *name = channel->channel->name;
4733 if((search->name && !match_ircglob(name, search->name)) ||
4734 (search->registrar && !channel->registrar) ||
4735 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4736 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4737 (search->registered && (now - channel->registered) > search->registered) ||
4738 (search->flags && ((search->flags & channel->flags) != search->flags)))
4745 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4747 struct chanData *channel;
4748 unsigned int matches = 0;
4750 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4752 if(!chanserv_channel_match(channel, search))
4762 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4767 search_print(struct chanData *channel, void *data)
4769 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4772 static CHANSERV_FUNC(cmd_search)
4775 unsigned int matches;
4776 channel_search_func action;
4780 if(!irccasecmp(argv[1], "count"))
4781 action = search_count;
4782 else if(!irccasecmp(argv[1], "print"))
4783 action = search_print;
4786 reply("CSMSG_ACTION_INVALID", argv[1]);
4790 search = chanserv_search_create(user, argc - 2, argv + 2);
4794 if(action == search_count)
4795 search->limit = INT_MAX;
4797 if(action == search_print)
4798 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4800 matches = chanserv_channel_search(search, action, user);
4803 reply("MSG_MATCH_COUNT", matches);
4805 reply("MSG_NO_MATCHES");
4811 static CHANSERV_FUNC(cmd_unvisited)
4813 struct chanData *cData;
4814 time_t interval = chanserv_conf.channel_expire_delay;
4815 char buffer[INTERVALLEN];
4816 unsigned int limit = 25, matches = 0;
4820 interval = ParseInterval(argv[1]);
4822 limit = atoi(argv[2]);
4825 intervalString(buffer, interval, user->handle_info);
4826 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4828 for(cData = channelList; cData && matches < limit; cData = cData->next)
4830 if((now - cData->visited) < interval)
4833 intervalString(buffer, now - cData->visited, user->handle_info);
4834 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4841 static MODCMD_FUNC(chan_opt_defaulttopic)
4847 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4849 reply("CSMSG_TOPIC_LOCKED", channel->name);
4853 topic = unsplit_string(argv+1, argc-1, NULL);
4855 free(channel->channel_info->topic);
4856 if(topic[0] == '*' && topic[1] == 0)
4858 topic = channel->channel_info->topic = NULL;
4862 topic = channel->channel_info->topic = strdup(topic);
4863 if(channel->channel_info->topic_mask
4864 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4865 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4867 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4870 if(channel->channel_info->topic)
4871 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4873 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4877 static MODCMD_FUNC(chan_opt_topicmask)
4881 struct chanData *cData = channel->channel_info;
4884 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4886 reply("CSMSG_TOPIC_LOCKED", channel->name);
4890 mask = unsplit_string(argv+1, argc-1, NULL);
4892 if(cData->topic_mask)
4893 free(cData->topic_mask);
4894 if(mask[0] == '*' && mask[1] == 0)
4896 cData->topic_mask = 0;
4900 cData->topic_mask = strdup(mask);
4902 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4903 else if(!match_ircglob(cData->topic, cData->topic_mask))
4904 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4908 if(channel->channel_info->topic_mask)
4909 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4911 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4915 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4919 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4923 if(greeting[0] == '*' && greeting[1] == 0)
4927 unsigned int length = strlen(greeting);
4928 if(length > chanserv_conf.greeting_length)
4930 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4933 *data = strdup(greeting);
4942 reply(name, user_find_message(user, "MSG_NONE"));
4946 static MODCMD_FUNC(chan_opt_greeting)
4948 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4951 static MODCMD_FUNC(chan_opt_usergreeting)
4953 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4956 static MODCMD_FUNC(chan_opt_modes)
4958 struct mod_chanmode *new_modes;
4959 char modes[MODELEN];
4963 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4965 reply("CSMSG_NO_ACCESS");
4968 if(argv[1][0] == '*' && argv[1][1] == 0)
4970 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4972 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4974 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4977 else if(new_modes->argc > 1)
4979 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4980 mod_chanmode_free(new_modes);
4985 channel->channel_info->modes = *new_modes;
4986 modcmd_chanmode_announce(new_modes);
4987 mod_chanmode_free(new_modes);
4991 mod_chanmode_format(&channel->channel_info->modes, modes);
4993 reply("CSMSG_SET_MODES", modes);
4995 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4999 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5001 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5003 struct chanData *cData = channel->channel_info;
5008 /* Set flag according to value. */
5009 if(enabled_string(argv[1]))
5011 cData->flags |= mask;
5014 else if(disabled_string(argv[1]))
5016 cData->flags &= ~mask;
5021 reply("MSG_INVALID_BINARY", argv[1]);
5027 /* Find current option value. */
5028 value = (cData->flags & mask) ? 1 : 0;
5032 reply(name, user_find_message(user, "MSG_ON"));
5034 reply(name, user_find_message(user, "MSG_OFF"));
5038 static MODCMD_FUNC(chan_opt_nodelete)
5040 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5042 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5046 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5049 static MODCMD_FUNC(chan_opt_dynlimit)
5051 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5054 static MODCMD_FUNC(chan_opt_offchannel)
5056 struct chanData *cData = channel->channel_info;
5061 /* Set flag according to value. */
5062 if(enabled_string(argv[1]))
5064 if(!IsOffChannel(cData))
5065 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5066 cData->flags |= CHANNEL_OFFCHANNEL;
5069 else if(disabled_string(argv[1]))
5071 if(IsOffChannel(cData))
5073 struct mod_chanmode change;
5074 mod_chanmode_init(&change);
5076 change.args[0].mode = MODE_CHANOP;
5077 change.args[0].u.member = AddChannelUser(chanserv, channel);
5078 mod_chanmode_announce(chanserv, channel, &change);
5080 cData->flags &= ~CHANNEL_OFFCHANNEL;
5085 reply("MSG_INVALID_BINARY", argv[1]);
5091 /* Find current option value. */
5092 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5096 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5098 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5102 static MODCMD_FUNC(chan_opt_defaults)
5104 struct userData *uData;
5105 struct chanData *cData;
5106 const char *confirm;
5107 enum levelOption lvlOpt;
5108 enum charOption chOpt;
5110 cData = channel->channel_info;
5111 uData = GetChannelUser(cData, user->handle_info);
5112 if(!uData || (uData->access < UL_OWNER))
5114 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5117 confirm = make_confirmation_string(uData);
5118 if((argc < 2) || strcmp(argv[1], confirm))
5120 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5123 cData->flags = CHANNEL_DEFAULT_FLAGS;
5124 cData->modes = chanserv_conf.default_modes;
5125 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5126 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5127 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5128 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5129 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5134 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5136 struct chanData *cData = channel->channel_info;
5137 struct userData *uData;
5138 unsigned short value;
5142 if(!check_user_level(channel, user, option, 1, 1))
5144 reply("CSMSG_CANNOT_SET");
5147 value = user_level_from_name(argv[1], UL_OWNER+1);
5148 if(!value && strcmp(argv[1], "0"))
5150 reply("CSMSG_INVALID_ACCESS", argv[1]);
5153 uData = GetChannelUser(cData, user->handle_info);
5154 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5156 reply("CSMSG_BAD_SETLEVEL");
5162 if(value > cData->lvlOpts[lvlGiveOps])
5164 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5169 if(value < cData->lvlOpts[lvlGiveVoice])
5171 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5176 /* This test only applies to owners, since non-owners
5177 * trying to set an option to above their level get caught
5178 * by the CSMSG_BAD_SETLEVEL test above.
5180 if(value > uData->access)
5182 reply("CSMSG_BAD_SETTERS");
5189 cData->lvlOpts[option] = value;
5191 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5195 static MODCMD_FUNC(chan_opt_enfops)
5197 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5200 static MODCMD_FUNC(chan_opt_giveops)
5202 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5205 static MODCMD_FUNC(chan_opt_enfmodes)
5207 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5210 static MODCMD_FUNC(chan_opt_enftopic)
5212 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5215 static MODCMD_FUNC(chan_opt_pubcmd)
5217 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5220 static MODCMD_FUNC(chan_opt_setters)
5222 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5225 static MODCMD_FUNC(chan_opt_ctcpusers)
5227 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5230 static MODCMD_FUNC(chan_opt_userinfo)
5232 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5235 static MODCMD_FUNC(chan_opt_givevoice)
5237 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5240 static MODCMD_FUNC(chan_opt_topicsnarf)
5242 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5245 static MODCMD_FUNC(chan_opt_inviteme)
5247 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5251 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5253 struct chanData *cData = channel->channel_info;
5254 int count = charOptions[option].count, index;
5258 index = atoi(argv[1]);
5260 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5262 reply("CSMSG_INVALID_NUMERIC", index);
5263 /* Show possible values. */
5264 for(index = 0; index < count; index++)
5265 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5269 cData->chOpts[option] = charOptions[option].values[index].value;
5273 /* Find current option value. */
5276 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5280 /* Somehow, the option value is corrupt; reset it to the default. */
5281 cData->chOpts[option] = charOptions[option].default_value;
5286 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5290 static MODCMD_FUNC(chan_opt_protect)
5292 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5295 static MODCMD_FUNC(chan_opt_toys)
5297 return channel_multiple_option(chToys, CSFUNC_ARGS);
5300 static MODCMD_FUNC(chan_opt_ctcpreaction)
5302 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5305 static MODCMD_FUNC(chan_opt_topicrefresh)
5307 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5310 static struct svccmd_list set_shows_list;
5313 handle_svccmd_unbind(struct svccmd *target) {
5315 for(ii=0; ii<set_shows_list.used; ++ii)
5316 if(target == set_shows_list.list[ii])
5317 set_shows_list.used = 0;
5320 static CHANSERV_FUNC(cmd_set)
5322 struct svccmd *subcmd;
5326 /* Check if we need to (re-)initialize set_shows_list. */
5327 if(!set_shows_list.used)
5329 if(!set_shows_list.size)
5331 set_shows_list.size = chanserv_conf.set_shows->used;
5332 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5334 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5336 const char *name = chanserv_conf.set_shows->list[ii];
5337 sprintf(buf, "%s %s", argv[0], name);
5338 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5341 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5344 svccmd_list_append(&set_shows_list, subcmd);
5350 reply("CSMSG_CHANNEL_OPTIONS");
5351 for(ii = 0; ii < set_shows_list.used; ii++)
5353 subcmd = set_shows_list.list[ii];
5354 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5359 sprintf(buf, "%s %s", argv[0], argv[1]);
5360 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5363 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5366 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5368 reply("CSMSG_NO_ACCESS");
5372 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5376 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5378 struct userData *uData;
5380 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5383 reply("CSMSG_NOT_USER", channel->name);
5389 /* Just show current option value. */
5391 else if(enabled_string(argv[1]))
5393 uData->flags |= mask;
5395 else if(disabled_string(argv[1]))
5397 uData->flags &= ~mask;
5401 reply("MSG_INVALID_BINARY", argv[1]);
5405 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5409 static MODCMD_FUNC(user_opt_noautoop)
5411 struct userData *uData;
5413 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5416 reply("CSMSG_NOT_USER", channel->name);
5419 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5420 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5422 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5425 static MODCMD_FUNC(user_opt_autoinvite)
5427 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5430 static MODCMD_FUNC(user_opt_info)
5432 struct userData *uData;
5435 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5439 /* If they got past the command restrictions (which require access)
5440 * but fail this test, we have some fool with security override on.
5442 reply("CSMSG_NOT_USER", channel->name);
5449 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5450 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5452 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5455 bp = strcspn(infoline, "\001");
5458 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5463 if(infoline[0] == '*' && infoline[1] == 0)
5466 uData->info = strdup(infoline);
5469 reply("CSMSG_USET_INFO", uData->info);
5471 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5475 struct svccmd_list uset_shows_list;
5477 static CHANSERV_FUNC(cmd_uset)
5479 struct svccmd *subcmd;
5483 /* Check if we need to (re-)initialize uset_shows_list. */
5484 if(!uset_shows_list.used)
5488 "NoAutoOp", "AutoInvite", "Info"
5491 if(!uset_shows_list.size)
5493 uset_shows_list.size = ArrayLength(options);
5494 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5496 for(ii = 0; ii < ArrayLength(options); ii++)
5498 const char *name = options[ii];
5499 sprintf(buf, "%s %s", argv[0], name);
5500 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5503 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5506 svccmd_list_append(&uset_shows_list, subcmd);
5512 /* Do this so options are presented in a consistent order. */
5513 reply("CSMSG_USER_OPTIONS");
5514 for(ii = 0; ii < uset_shows_list.used; ii++)
5515 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5519 sprintf(buf, "%s %s", argv[0], argv[1]);
5520 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5523 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5527 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5530 static CHANSERV_FUNC(cmd_giveownership)
5532 struct handle_info *new_owner_hi;
5533 struct userData *new_owner;
5534 struct userData *curr_user;
5535 struct userData *invoker;
5536 struct chanData *cData = channel->channel_info;
5537 struct do_not_register *dnr;
5538 const char *confirm;
5540 unsigned short co_access;
5541 char reason[MAXLEN];
5544 curr_user = GetChannelAccess(cData, user->handle_info);
5545 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5546 if(!curr_user || (curr_user->access != UL_OWNER))
5548 struct userData *owner = NULL;
5549 for(curr_user = channel->channel_info->users;
5551 curr_user = curr_user->next)
5553 if(curr_user->access != UL_OWNER)
5557 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5564 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5566 char delay[INTERVALLEN];
5567 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5568 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5571 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5573 if(new_owner_hi == user->handle_info)
5575 reply("CSMSG_NO_TRANSFER_SELF");
5578 new_owner = GetChannelAccess(cData, new_owner_hi);
5583 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5587 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5591 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5593 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5596 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5597 if(!IsHelping(user))
5598 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5600 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5603 invoker = GetChannelUser(cData, user->handle_info);
5604 if(invoker->access <= UL_OWNER)
5606 confirm = make_confirmation_string(curr_user);
5607 if((argc < 3) || strcmp(argv[2], confirm))
5609 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5613 if(new_owner->access >= UL_COOWNER)
5614 co_access = new_owner->access;
5616 co_access = UL_COOWNER;
5617 new_owner->access = UL_OWNER;
5619 curr_user->access = co_access;
5620 cData->ownerTransfer = now;
5621 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5622 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5623 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5627 static CHANSERV_FUNC(cmd_suspend)
5629 struct handle_info *hi;
5630 struct userData *self, *real_self, *target;
5631 unsigned int override = 0;
5634 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5635 self = GetChannelUser(channel->channel_info, user->handle_info);
5636 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5637 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5639 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5642 if(target->access >= self->access)
5644 reply("MSG_USER_OUTRANKED", hi->handle);
5647 if(target->flags & USER_SUSPENDED)
5649 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5654 target->present = 0;
5657 if(!real_self || target->access >= real_self->access)
5658 override = CMD_LOG_OVERRIDE;
5659 target->flags |= USER_SUSPENDED;
5660 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5661 return 1 | override;
5664 static CHANSERV_FUNC(cmd_unsuspend)
5666 struct handle_info *hi;
5667 struct userData *self, *real_self, *target;
5668 unsigned int override = 0;
5671 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5672 self = GetChannelUser(channel->channel_info, user->handle_info);
5673 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5674 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5676 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5679 if(target->access >= self->access)
5681 reply("MSG_USER_OUTRANKED", hi->handle);
5684 if(!(target->flags & USER_SUSPENDED))
5686 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5689 if(!real_self || target->access >= real_self->access)
5690 override = CMD_LOG_OVERRIDE;
5691 target->flags &= ~USER_SUSPENDED;
5692 scan_user_presence(target, NULL);
5693 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5694 return 1 | override;
5697 static MODCMD_FUNC(cmd_deleteme)
5699 struct handle_info *hi;
5700 struct userData *target;
5701 const char *confirm_string;
5702 unsigned short access;
5705 hi = user->handle_info;
5706 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5708 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5711 if(target->access == UL_OWNER)
5713 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5716 confirm_string = make_confirmation_string(target);
5717 if((argc < 2) || strcmp(argv[1], confirm_string))
5719 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5722 access = target->access;
5723 channel_name = strdup(channel->name);
5724 del_channel_user(target, 1);
5725 reply("CSMSG_DELETED_YOU", access, channel_name);
5731 chanserv_refresh_topics(UNUSED_ARG(void *data))
5733 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5734 struct chanData *cData;
5737 for(cData = channelList; cData; cData = cData->next)
5739 if(IsSuspended(cData))
5741 opt = cData->chOpts[chTopicRefresh];
5744 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5747 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5748 cData->last_refresh = refresh_num;
5750 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5753 static CHANSERV_FUNC(cmd_unf)
5757 char response[MAXLEN];
5758 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5759 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5760 irc_privmsg(cmd->parent->bot, channel->name, response);
5763 reply("CSMSG_UNF_RESPONSE");
5767 static CHANSERV_FUNC(cmd_ping)
5771 char response[MAXLEN];
5772 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5773 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5774 irc_privmsg(cmd->parent->bot, channel->name, response);
5777 reply("CSMSG_PING_RESPONSE");
5781 static CHANSERV_FUNC(cmd_wut)
5785 char response[MAXLEN];
5786 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5787 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5788 irc_privmsg(cmd->parent->bot, channel->name, response);
5791 reply("CSMSG_WUT_RESPONSE");
5795 static CHANSERV_FUNC(cmd_8ball)
5797 unsigned int i, j, accum;
5802 for(i=1; i<argc; i++)
5803 for(j=0; argv[i][j]; j++)
5804 accum = (accum << 5) - accum + toupper(argv[i][j]);
5805 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5808 char response[MAXLEN];
5809 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5810 irc_privmsg(cmd->parent->bot, channel->name, response);
5813 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5817 static CHANSERV_FUNC(cmd_d)
5819 unsigned long sides, count, modifier, ii, total;
5820 char response[MAXLEN], *sep;
5824 if((count = strtoul(argv[1], &sep, 10)) < 1)
5834 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5835 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5839 else if((sep[0] == '-') && isdigit(sep[1]))
5840 modifier = strtoul(sep, NULL, 10);
5841 else if((sep[0] == '+') && isdigit(sep[1]))
5842 modifier = strtoul(sep+1, NULL, 10);
5849 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5854 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5857 for(total = ii = 0; ii < count; ++ii)
5858 total += (rand() % sides) + 1;
5861 if((count > 1) || modifier)
5863 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5864 sprintf(response, fmt, total, count, sides, modifier);
5868 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5869 sprintf(response, fmt, total, sides);
5872 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5874 send_message_type(4, user, cmd->parent->bot, "%s", response);
5878 static CHANSERV_FUNC(cmd_huggle)
5880 /* CTCP must be via PRIVMSG, never notice */
5882 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5884 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5889 chanserv_adjust_limit(void *data)
5891 struct mod_chanmode change;
5892 struct chanData *cData = data;
5893 struct chanNode *channel = cData->channel;
5896 if(IsSuspended(cData))
5899 cData->limitAdjusted = now;
5900 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5901 if(cData->modes.modes_set & MODE_LIMIT)
5903 if(limit > cData->modes.new_limit)
5904 limit = cData->modes.new_limit;
5905 else if(limit == cData->modes.new_limit)
5909 mod_chanmode_init(&change);
5910 change.modes_set = MODE_LIMIT;
5911 change.new_limit = limit;
5912 mod_chanmode_announce(chanserv, channel, &change);
5916 handle_new_channel(struct chanNode *channel)
5918 struct chanData *cData;
5920 if(!(cData = channel->channel_info))
5923 if(cData->modes.modes_set || cData->modes.modes_clear)
5924 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5926 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5927 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5930 /* Welcome to my worst nightmare. Warning: Read (or modify)
5931 the code below at your own risk. */
5933 handle_join(struct modeNode *mNode)
5935 struct mod_chanmode change;
5936 struct userNode *user = mNode->user;
5937 struct chanNode *channel = mNode->channel;
5938 struct chanData *cData;
5939 struct userData *uData = NULL;
5940 struct banData *bData;
5941 struct handle_info *handle;
5942 unsigned int modes = 0, info = 0;
5945 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5948 cData = channel->channel_info;
5949 if(channel->members.used > cData->max)
5950 cData->max = channel->members.used;
5952 /* Check for bans. If they're joining through a ban, one of two
5954 * 1: Join during a netburst, by riding the break. Kick them
5955 * unless they have ops or voice in the channel.
5956 * 2: They're allowed to join through the ban (an invite in
5957 * ircu2.10, or a +e on Hybrid, or something).
5958 * If they're not joining through a ban, and the banlist is not
5959 * full, see if they're on the banlist for the channel. If so,
5962 if(user->uplink->burst && !mNode->modes)
5965 for(ii = 0; ii < channel->banlist.used; ii++)
5967 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5969 /* Riding a netburst. Naughty. */
5970 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5976 mod_chanmode_init(&change);
5978 if(channel->banlist.used < MAXBANS)
5980 /* Not joining through a ban. */
5981 for(bData = cData->bans;
5982 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5983 bData = bData->next);
5987 char kick_reason[MAXLEN];
5988 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5990 bData->triggered = now;
5991 if(bData != cData->bans)
5993 /* Shuffle the ban to the head of the list. */
5995 bData->next->prev = bData->prev;
5997 bData->prev->next = bData->next;
6000 bData->next = cData->bans;
6003 cData->bans->prev = bData;
6004 cData->bans = bData;
6007 change.args[0].mode = MODE_BAN;
6008 change.args[0].u.hostmask = bData->mask;
6009 mod_chanmode_announce(chanserv, channel, &change);
6010 KickChannelUser(user, channel, chanserv, kick_reason);
6015 /* ChanServ will not modify the limits in join-flooded channels.
6016 It will also skip DynLimit processing when the user (or srvx)
6017 is bursting in, because there are likely more incoming. */
6018 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6019 && !user->uplink->burst
6020 && !channel->join_flooded
6021 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6023 /* The user count has begun "bumping" into the channel limit,
6024 so set a timer to raise the limit a bit. Any previous
6025 timers are removed so three incoming users within the delay
6026 results in one limit change, not three. */
6028 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6029 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6032 if(channel->join_flooded)
6034 /* don't automatically give ops or voice during a join flood */
6036 else if(cData->lvlOpts[lvlGiveOps] == 0)
6037 modes |= MODE_CHANOP;
6038 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6039 modes |= MODE_VOICE;
6041 greeting = cData->greeting;
6042 if(user->handle_info)
6044 handle = user->handle_info;
6046 if(IsHelper(user) && !IsHelping(user))
6049 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6051 if(channel == chanserv_conf.support_channels.list[ii])
6053 HANDLE_SET_FLAG(user->handle_info, HELPING);
6059 uData = GetTrueChannelAccess(cData, handle);
6060 if(uData && !IsUserSuspended(uData))
6062 /* Ops and above were handled by the above case. */
6063 if(IsUserAutoOp(uData))
6065 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6066 modes |= MODE_CHANOP;
6067 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6068 modes |= MODE_VOICE;
6070 if(uData->access >= UL_PRESENT)
6071 cData->visited = now;
6072 if(cData->user_greeting)
6073 greeting = cData->user_greeting;
6075 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6076 && ((now - uData->seen) >= chanserv_conf.info_delay)
6084 /* If user joining normally (not during burst), apply op or voice,
6085 * and send greeting/userinfo as appropriate.
6087 if(!user->uplink->burst)
6091 if(modes & MODE_CHANOP)
6092 modes &= ~MODE_VOICE;
6093 change.args[0].mode = modes;
6094 change.args[0].u.member = mNode;
6095 mod_chanmode_announce(chanserv, channel, &change);
6098 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6100 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6106 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6108 struct mod_chanmode change;
6109 struct userData *channel;
6110 unsigned int ii, jj;
6112 if(!user->handle_info)
6115 mod_chanmode_init(&change);
6117 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6119 struct chanNode *cn;
6120 struct modeNode *mn;
6121 if(IsUserSuspended(channel)
6122 || IsSuspended(channel->channel)
6123 || !(cn = channel->channel->channel))
6126 mn = GetUserMode(cn, user);
6129 if(!IsUserSuspended(channel)
6130 && IsUserAutoInvite(channel)
6131 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6133 && !user->uplink->burst)
6134 irc_invite(chanserv, user, cn);
6138 if(channel->access >= UL_PRESENT)
6139 channel->channel->visited = now;
6141 if(IsUserAutoOp(channel))
6143 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6144 change.args[0].mode = MODE_CHANOP;
6145 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6146 change.args[0].mode = MODE_VOICE;
6148 change.args[0].mode = 0;
6149 change.args[0].u.member = mn;
6150 if(change.args[0].mode)
6151 mod_chanmode_announce(chanserv, cn, &change);
6154 channel->seen = now;
6155 channel->present = 1;
6158 for(ii = 0; ii < user->channels.used; ++ii)
6160 struct chanNode *channel = user->channels.list[ii]->channel;
6161 struct banData *ban;
6163 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6164 || !channel->channel_info
6165 || IsSuspended(channel->channel_info))
6167 for(jj = 0; jj < channel->banlist.used; ++jj)
6168 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6170 if(jj < channel->banlist.used)
6172 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6174 char kick_reason[MAXLEN];
6175 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6177 change.args[0].mode = MODE_BAN;
6178 change.args[0].u.hostmask = ban->mask;
6179 mod_chanmode_announce(chanserv, channel, &change);
6180 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6181 KickChannelUser(user, channel, chanserv, kick_reason);
6182 ban->triggered = now;
6187 if(IsSupportHelper(user))
6189 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6191 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6193 HANDLE_SET_FLAG(user->handle_info, HELPING);
6201 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6203 struct chanData *cData;
6204 struct userData *uData;
6206 cData = mn->channel->channel_info;
6207 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6210 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6212 /* Allow for a bit of padding so that the limit doesn't
6213 track the user count exactly, which could get annoying. */
6214 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6216 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6217 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6221 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6223 scan_user_presence(uData, mn->user);
6225 if (uData->access >= UL_PRESENT)
6226 cData->visited = now;
6229 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6231 unsigned int ii, jj;
6232 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6234 for(jj = 0; jj < mn->user->channels.used; ++jj)
6235 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6237 if(jj < mn->user->channels.used)
6240 if(ii == chanserv_conf.support_channels.used)
6241 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6246 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6248 struct userData *uData;
6250 if(!channel->channel_info || !kicker || IsService(kicker)
6251 || (kicker == victim) || IsSuspended(channel->channel_info)
6252 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6255 if(protect_user(victim, kicker, channel->channel_info))
6257 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6258 KickChannelUser(kicker, channel, chanserv, reason);
6261 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6266 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6268 struct chanData *cData;
6270 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6273 cData = channel->channel_info;
6274 if(bad_topic(channel, user, channel->topic))
6276 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6277 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6278 SetChannelTopic(channel, chanserv, old_topic, 1);
6279 else if(cData->topic)
6280 SetChannelTopic(channel, chanserv, cData->topic, 1);
6283 /* With topicsnarf, grab the topic and save it as the default topic. */
6284 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6287 cData->topic = strdup(channel->topic);
6293 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6295 struct mod_chanmode *bounce = NULL;
6296 unsigned int bnc, ii;
6299 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6302 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6303 && mode_lock_violated(&channel->channel_info->modes, change))
6305 char correct[MAXLEN];
6306 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6307 mod_chanmode_format(&channel->channel_info->modes, correct);
6308 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6310 for(ii = bnc = 0; ii < change->argc; ++ii)
6312 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6314 const struct userNode *victim = change->args[ii].u.member->user;
6315 if(!protect_user(victim, user, channel->channel_info))
6318 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6321 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6322 bounce->args[bnc].u.member = GetUserMode(channel, user);
6323 if(bounce->args[bnc].u.member)
6327 bounce->args[bnc].mode = MODE_CHANOP;
6328 bounce->args[bnc].u.member = change->args[ii].u.member;
6330 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6332 else if(change->args[ii].mode & MODE_CHANOP)
6334 const struct userNode *victim = change->args[ii].u.member->user;
6335 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6338 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6339 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6340 bounce->args[bnc].u.member = change->args[ii].u.member;
6343 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6345 const char *ban = change->args[ii].u.hostmask;
6346 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6349 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6350 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6351 bounce->args[bnc].u.hostmask = strdup(ban);
6353 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6358 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6359 mod_chanmode_announce(chanserv, channel, bounce);
6360 for(ii = 0; ii < change->argc; ++ii)
6361 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6362 free((char*)bounce->args[ii].u.hostmask);
6363 mod_chanmode_free(bounce);
6368 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6370 struct chanNode *channel;
6371 struct banData *bData;
6372 struct mod_chanmode change;
6373 unsigned int ii, jj;
6374 char kick_reason[MAXLEN];
6376 mod_chanmode_init(&change);
6378 change.args[0].mode = MODE_BAN;
6379 for(ii = 0; ii < user->channels.used; ++ii)
6381 channel = user->channels.list[ii]->channel;
6382 /* Need not check for bans if they're opped or voiced. */
6383 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6385 /* Need not check for bans unless channel registration is active. */
6386 if(!channel->channel_info || IsSuspended(channel->channel_info))
6388 /* Look for a matching ban already on the channel. */
6389 for(jj = 0; jj < channel->banlist.used; ++jj)
6390 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6392 /* Need not act if we found one. */
6393 if(jj < channel->banlist.used)
6395 /* Look for a matching ban in this channel. */
6396 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6398 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6400 change.args[0].u.hostmask = bData->mask;
6401 mod_chanmode_announce(chanserv, channel, &change);
6402 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6403 KickChannelUser(user, channel, chanserv, kick_reason);
6404 bData->triggered = now;
6405 break; /* we don't need to check any more bans in the channel */
6410 static void handle_rename(struct handle_info *handle, const char *old_handle)
6412 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6416 dict_remove2(handle_dnrs, old_handle, 1);
6417 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6418 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6423 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6425 struct userNode *h_user;
6427 if(handle->channels)
6429 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6430 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6432 while(handle->channels)
6433 del_channel_user(handle->channels, 1);
6438 handle_server_link(UNUSED_ARG(struct server *server))
6440 struct chanData *cData;
6442 for(cData = channelList; cData; cData = cData->next)
6444 if(!IsSuspended(cData))
6445 cData->may_opchan = 1;
6446 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6447 && !cData->channel->join_flooded
6448 && ((cData->channel->limit - cData->channel->members.used)
6449 < chanserv_conf.adjust_threshold))
6451 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6452 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6458 chanserv_conf_read(void)
6462 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6463 struct mod_chanmode *change;
6464 struct string_list *strlist;
6465 struct chanNode *chan;
6468 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6470 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6473 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6474 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6475 chanserv_conf.support_channels.used = 0;
6476 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6478 for(ii = 0; ii < strlist->used; ++ii)
6480 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6483 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6485 channelList_append(&chanserv_conf.support_channels, chan);
6488 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6491 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6494 chan = AddChannel(str, now, str2, NULL);
6496 channelList_append(&chanserv_conf.support_channels, chan);
6498 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6499 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6500 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6501 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6502 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6503 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6504 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6505 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6506 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6507 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6508 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6509 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6510 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6511 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6512 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6513 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6514 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6515 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6516 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6517 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6518 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6519 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6520 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6522 NickChange(chanserv, str, 0);
6523 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6524 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6525 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6526 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6527 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6528 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6529 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6530 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6531 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6532 chanserv_conf.max_owned = str ? atoi(str) : 5;
6533 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6534 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6535 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6536 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6537 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6538 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6539 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6542 safestrncpy(mode_line, str, sizeof(mode_line));
6543 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6544 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6545 && (change->argc < 2))
6547 chanserv_conf.default_modes = *change;
6548 mod_chanmode_free(change);
6550 free_string_list(chanserv_conf.set_shows);
6551 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6553 strlist = string_list_copy(strlist);
6556 static const char *list[] = {
6557 /* free form text */
6558 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6559 /* options based on user level */
6560 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6561 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6562 /* multiple choice options */
6563 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6564 /* binary options */
6565 "DynLimit", "NoDelete",
6570 strlist = alloc_string_list(ArrayLength(list)-1);
6571 for(ii=0; list[ii]; ii++)
6572 string_list_append(strlist, strdup(list[ii]));
6574 chanserv_conf.set_shows = strlist;
6575 /* We don't look things up now, in case the list refers to options
6576 * defined by modules initialized after this point. Just mark the
6577 * function list as invalid, so it will be initialized.
6579 set_shows_list.used = 0;
6580 free_string_list(chanserv_conf.eightball);
6581 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6584 strlist = string_list_copy(strlist);
6588 strlist = alloc_string_list(4);
6589 string_list_append(strlist, strdup("Yes."));
6590 string_list_append(strlist, strdup("No."));
6591 string_list_append(strlist, strdup("Maybe so."));
6593 chanserv_conf.eightball = strlist;
6594 free_string_list(chanserv_conf.old_ban_names);
6595 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6597 strlist = string_list_copy(strlist);
6599 strlist = alloc_string_list(2);
6600 chanserv_conf.old_ban_names = strlist;
6601 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6602 off_channel = str ? atoi(str) : 0;
6606 chanserv_note_type_read(const char *key, struct record_data *rd)
6609 struct note_type *ntype;
6612 if(!(obj = GET_RECORD_OBJECT(rd)))
6614 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6617 if(!(ntype = chanserv_create_note_type(key)))
6619 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6623 /* Figure out set access */
6624 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6626 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6627 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6629 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6631 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6632 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6634 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6636 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6640 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6641 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6642 ntype->set_access.min_opserv = 0;
6645 /* Figure out visibility */
6646 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6647 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6648 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6649 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6650 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6651 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6652 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6653 ntype->visible_type = NOTE_VIS_ALL;
6655 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6657 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6658 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6662 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6664 struct handle_info *handle;
6665 struct userData *uData;
6666 char *seen, *inf, *flags;
6668 unsigned short access;
6670 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6672 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6676 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6677 if(access > UL_OWNER)
6679 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6683 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6684 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6685 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6686 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6687 handle = get_handle_info(key);
6690 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6694 uData = add_channel_user(chan, handle, access, last_seen, inf);
6695 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6699 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6701 struct banData *bData;
6702 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6703 time_t set_time, triggered_time, expires_time;
6705 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6707 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6711 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6712 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6713 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6714 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6715 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6716 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6717 if (!reason || !owner)
6720 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6721 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6723 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6725 expires_time = set_time + atoi(s_duration);
6729 if(!reason || (expires_time && (expires_time < now)))
6732 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6735 static struct suspended *
6736 chanserv_read_suspended(dict_t obj)
6738 struct suspended *suspended = calloc(1, sizeof(*suspended));
6742 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6743 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6744 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6745 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6746 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6747 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6748 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6749 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6750 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6751 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6756 chanserv_channel_read(const char *key, struct record_data *hir)
6758 struct suspended *suspended;
6759 struct mod_chanmode *modes;
6760 struct chanNode *cNode;
6761 struct chanData *cData;
6762 struct dict *channel, *obj;
6763 char *str, *argv[10];
6767 channel = hir->d.object;
6769 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6772 cNode = AddChannel(key, now, NULL, NULL);
6775 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6778 cData = register_channel(cNode, str);
6781 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6785 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6787 enum levelOption lvlOpt;
6788 enum charOption chOpt;
6790 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6791 cData->flags = atoi(str);
6793 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6795 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6797 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6798 else if(levelOptions[lvlOpt].old_flag)
6800 if(cData->flags & levelOptions[lvlOpt].old_flag)
6801 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6803 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6807 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6809 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6811 cData->chOpts[chOpt] = str[0];
6814 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6816 enum levelOption lvlOpt;
6817 enum charOption chOpt;
6820 cData->flags = base64toint(str, 5);
6821 count = strlen(str += 5);
6822 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6825 if(levelOptions[lvlOpt].old_flag)
6827 if(cData->flags & levelOptions[lvlOpt].old_flag)
6828 lvl = levelOptions[lvlOpt].flag_value;
6830 lvl = levelOptions[lvlOpt].default_value;
6832 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6834 case 'c': lvl = UL_COOWNER; break;
6835 case 'm': lvl = UL_MASTER; break;
6836 case 'n': lvl = UL_OWNER+1; break;
6837 case 'o': lvl = UL_OP; break;
6838 case 'p': lvl = UL_PEON; break;
6839 case 'w': lvl = UL_OWNER; break;
6840 default: lvl = 0; break;
6842 cData->lvlOpts[lvlOpt] = lvl;
6844 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6845 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6848 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6850 suspended = chanserv_read_suspended(obj);
6851 cData->suspended = suspended;
6852 suspended->cData = cData;
6853 /* We could use suspended->expires and suspended->revoked to
6854 * set the CHANNEL_SUSPENDED flag, but we don't. */
6856 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6858 suspended = calloc(1, sizeof(*suspended));
6859 suspended->issued = 0;
6860 suspended->revoked = 0;
6861 suspended->suspender = strdup(str);
6862 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6863 suspended->expires = str ? atoi(str) : 0;
6864 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6865 suspended->reason = strdup(str ? str : "No reason");
6866 suspended->previous = NULL;
6867 cData->suspended = suspended;
6868 suspended->cData = cData;
6872 cData->flags &= ~CHANNEL_SUSPENDED;
6873 suspended = NULL; /* to squelch a warning */
6876 if(IsSuspended(cData)) {
6877 if(suspended->expires > now)
6878 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6879 else if(suspended->expires)
6880 cData->flags &= ~CHANNEL_SUSPENDED;
6883 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6884 struct mod_chanmode change;
6885 mod_chanmode_init(&change);
6887 change.args[0].mode = MODE_CHANOP;
6888 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6889 mod_chanmode_announce(chanserv, cNode, &change);
6892 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6893 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6894 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6895 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6896 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6897 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6898 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6899 cData->max = str ? atoi(str) : 0;
6900 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6901 cData->greeting = str ? strdup(str) : NULL;
6902 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6903 cData->user_greeting = str ? strdup(str) : NULL;
6904 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6905 cData->topic_mask = str ? strdup(str) : NULL;
6906 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6907 cData->topic = str ? strdup(str) : NULL;
6909 if(!IsSuspended(cData)
6910 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6911 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6912 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6913 cData->modes = *modes;
6915 cData->modes.modes_set |= MODE_REGISTERED;
6916 if(cData->modes.argc > 1)
6917 cData->modes.argc = 1;
6918 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6919 mod_chanmode_free(modes);
6922 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6923 for(it = dict_first(obj); it; it = iter_next(it))
6924 user_read_helper(iter_key(it), iter_data(it), cData);
6926 if(!cData->users && !IsProtected(cData))
6928 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6929 unregister_channel(cData, "has empty user list.");
6933 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6934 for(it = dict_first(obj); it; it = iter_next(it))
6935 ban_read_helper(iter_key(it), iter_data(it), cData);
6937 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6938 for(it = dict_first(obj); it; it = iter_next(it))
6940 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6941 struct record_data *rd = iter_data(it);
6942 const char *note, *setter;
6944 if(rd->type != RECDB_OBJECT)
6946 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6950 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6952 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6954 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6958 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6959 if(!setter) setter = "<unknown>";
6960 chanserv_add_channel_note(cData, ntype, setter, note);
6968 chanserv_dnr_read(const char *key, struct record_data *hir)
6970 const char *setter, *reason, *str;
6971 struct do_not_register *dnr;
6973 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6976 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6979 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6982 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6985 dnr = chanserv_add_dnr(key, setter, reason);
6988 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6990 dnr->set = atoi(str);
6996 chanserv_saxdb_read(struct dict *database)
6998 struct dict *section;
7001 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7002 for(it = dict_first(section); it; it = iter_next(it))
7003 chanserv_note_type_read(iter_key(it), iter_data(it));
7005 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7006 for(it = dict_first(section); it; it = iter_next(it))
7007 chanserv_channel_read(iter_key(it), iter_data(it));
7009 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7010 for(it = dict_first(section); it; it = iter_next(it))
7011 chanserv_dnr_read(iter_key(it), iter_data(it));
7017 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7019 int high_present = 0;
7020 saxdb_start_record(ctx, KEY_USERS, 1);
7021 for(; uData; uData = uData->next)
7023 if((uData->access >= UL_PRESENT) && uData->present)
7025 saxdb_start_record(ctx, uData->handle->handle, 0);
7026 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7027 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7029 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7031 saxdb_write_string(ctx, KEY_INFO, uData->info);
7032 saxdb_end_record(ctx);
7034 saxdb_end_record(ctx);
7035 return high_present;
7039 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7043 saxdb_start_record(ctx, KEY_BANS, 1);
7044 for(; bData; bData = bData->next)
7046 saxdb_start_record(ctx, bData->mask, 0);
7047 saxdb_write_int(ctx, KEY_SET, bData->set);
7048 if(bData->triggered)
7049 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7051 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7053 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7055 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7056 saxdb_end_record(ctx);
7058 saxdb_end_record(ctx);
7062 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7064 saxdb_start_record(ctx, name, 0);
7065 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7066 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7068 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7070 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7072 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7074 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7075 saxdb_end_record(ctx);
7079 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7083 enum levelOption lvlOpt;
7084 enum charOption chOpt;
7086 saxdb_start_record(ctx, channel->channel->name, 1);
7088 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7089 saxdb_write_int(ctx, KEY_MAX, channel->max);
7091 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7092 if(channel->registrar)
7093 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7094 if(channel->greeting)
7095 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7096 if(channel->user_greeting)
7097 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7098 if(channel->topic_mask)
7099 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7100 if(channel->suspended)
7101 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7103 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7104 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7105 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7106 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7107 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7109 buf[0] = channel->chOpts[chOpt];
7111 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7113 saxdb_end_record(ctx);
7115 if(channel->modes.modes_set || channel->modes.modes_clear)
7117 mod_chanmode_format(&channel->modes, buf);
7118 saxdb_write_string(ctx, KEY_MODES, buf);
7121 high_present = chanserv_write_users(ctx, channel->users);
7122 chanserv_write_bans(ctx, channel->bans);
7124 if(dict_size(channel->notes))
7128 saxdb_start_record(ctx, KEY_NOTES, 1);
7129 for(it = dict_first(channel->notes); it; it = iter_next(it))
7131 struct note *note = iter_data(it);
7132 saxdb_start_record(ctx, iter_key(it), 0);
7133 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7134 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7135 saxdb_end_record(ctx);
7137 saxdb_end_record(ctx);
7140 if(channel->ownerTransfer)
7141 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7142 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7143 saxdb_end_record(ctx);
7147 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7151 saxdb_start_record(ctx, ntype->name, 0);
7152 switch(ntype->set_access_type)
7154 case NOTE_SET_CHANNEL_ACCESS:
7155 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7157 case NOTE_SET_CHANNEL_SETTER:
7158 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7160 case NOTE_SET_PRIVILEGED: default:
7161 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7164 switch(ntype->visible_type)
7166 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7167 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7168 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7170 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7171 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7172 saxdb_end_record(ctx);
7176 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7178 struct do_not_register *dnr;
7181 for(it = dict_first(dnrs); it; it = iter_next(it))
7183 dnr = iter_data(it);
7184 saxdb_start_record(ctx, dnr->chan_name, 0);
7186 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7187 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7188 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7189 saxdb_end_record(ctx);
7194 chanserv_saxdb_write(struct saxdb_context *ctx)
7197 struct chanData *channel;
7200 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7201 for(it = dict_first(note_types); it; it = iter_next(it))
7202 chanserv_write_note_type(ctx, iter_data(it));
7203 saxdb_end_record(ctx);
7206 saxdb_start_record(ctx, KEY_DNR, 1);
7207 write_dnrs_helper(ctx, handle_dnrs);
7208 write_dnrs_helper(ctx, plain_dnrs);
7209 write_dnrs_helper(ctx, mask_dnrs);
7210 saxdb_end_record(ctx);
7213 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7214 for(channel = channelList; channel; channel = channel->next)
7215 chanserv_write_channel(ctx, channel);
7216 saxdb_end_record(ctx);
7222 chanserv_db_cleanup(void) {
7224 unreg_part_func(handle_part);
7226 unregister_channel(channelList, "terminating.");
7227 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7228 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7229 free(chanserv_conf.support_channels.list);
7230 dict_delete(handle_dnrs);
7231 dict_delete(plain_dnrs);
7232 dict_delete(mask_dnrs);
7233 dict_delete(note_types);
7234 free_string_list(chanserv_conf.eightball);
7235 free_string_list(chanserv_conf.old_ban_names);
7236 free_string_list(chanserv_conf.set_shows);
7237 free(set_shows_list.list);
7238 free(uset_shows_list.list);
7241 struct userData *helper = helperList;
7242 helperList = helperList->next;
7247 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7248 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7249 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7252 init_chanserv(const char *nick)
7254 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7255 conf_register_reload(chanserv_conf_read);
7259 reg_server_link_func(handle_server_link);
7260 reg_new_channel_func(handle_new_channel);
7261 reg_join_func(handle_join);
7262 reg_part_func(handle_part);
7263 reg_kick_func(handle_kick);
7264 reg_topic_func(handle_topic);
7265 reg_mode_change_func(handle_mode);
7266 reg_nick_change_func(handle_nick_change);
7267 reg_auth_func(handle_auth);
7270 reg_handle_rename_func(handle_rename);
7271 reg_unreg_func(handle_unreg);
7273 handle_dnrs = dict_new();
7274 dict_set_free_data(handle_dnrs, free);
7275 plain_dnrs = dict_new();
7276 dict_set_free_data(plain_dnrs, free);
7277 mask_dnrs = dict_new();
7278 dict_set_free_data(mask_dnrs, free);
7280 reg_svccmd_unbind_func(handle_svccmd_unbind);
7281 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7282 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7283 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7284 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7285 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7286 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7287 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7288 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7289 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7291 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7292 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7294 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7295 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7296 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7297 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7298 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7300 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7301 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7302 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7303 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7304 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7306 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7307 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7308 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7309 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7311 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7312 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7313 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7314 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7315 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7316 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7317 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7318 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7320 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7321 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7322 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7323 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7324 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7325 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7326 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7327 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7328 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7329 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7330 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7331 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7332 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7333 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7335 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7336 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7337 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7338 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7339 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7341 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7342 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7344 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7345 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7346 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7347 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7348 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7349 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7350 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7351 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7352 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7353 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7354 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7356 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7357 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7359 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7360 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7361 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7362 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7364 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7365 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7366 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7367 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7368 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7370 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7371 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7372 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7373 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7374 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7375 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7377 /* Channel options */
7378 DEFINE_CHANNEL_OPTION(defaulttopic);
7379 DEFINE_CHANNEL_OPTION(topicmask);
7380 DEFINE_CHANNEL_OPTION(greeting);
7381 DEFINE_CHANNEL_OPTION(usergreeting);
7382 DEFINE_CHANNEL_OPTION(modes);
7383 DEFINE_CHANNEL_OPTION(enfops);
7384 DEFINE_CHANNEL_OPTION(giveops);
7385 DEFINE_CHANNEL_OPTION(protect);
7386 DEFINE_CHANNEL_OPTION(enfmodes);
7387 DEFINE_CHANNEL_OPTION(enftopic);
7388 DEFINE_CHANNEL_OPTION(pubcmd);
7389 DEFINE_CHANNEL_OPTION(givevoice);
7390 DEFINE_CHANNEL_OPTION(userinfo);
7391 DEFINE_CHANNEL_OPTION(dynlimit);
7392 DEFINE_CHANNEL_OPTION(topicsnarf);
7393 DEFINE_CHANNEL_OPTION(nodelete);
7394 DEFINE_CHANNEL_OPTION(toys);
7395 DEFINE_CHANNEL_OPTION(setters);
7396 DEFINE_CHANNEL_OPTION(topicrefresh);
7397 DEFINE_CHANNEL_OPTION(ctcpusers);
7398 DEFINE_CHANNEL_OPTION(ctcpreaction);
7399 DEFINE_CHANNEL_OPTION(inviteme);
7401 DEFINE_CHANNEL_OPTION(offchannel);
7402 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7404 /* Alias set topic to set defaulttopic for compatibility. */
7405 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7408 DEFINE_USER_OPTION(noautoop);
7409 DEFINE_USER_OPTION(autoinvite);
7410 DEFINE_USER_OPTION(info);
7412 /* Alias uset autovoice to uset autoop. */
7413 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7415 note_types = dict_new();
7416 dict_set_free_data(note_types, chanserv_deref_note_type);
7419 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7420 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7421 service_register(chanserv)->trigger = '!';
7422 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7424 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7426 if(chanserv_conf.channel_expire_frequency)
7427 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7429 if(chanserv_conf.refresh_period)
7431 time_t next_refresh;
7432 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7433 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7436 reg_exit_func(chanserv_db_cleanup);
7437 message_register_table(msgtab);