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 = GetTrueChannelAccess(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.
2321 victim->access = new_access;
2322 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2323 return 1 | override;
2326 static CHANSERV_FUNC(cmd_deluser)
2328 struct handle_info *handle;
2329 struct userData *victim;
2330 struct userData *actor, *real_actor;
2331 unsigned short access, override = 0;
2336 actor = GetChannelUser(channel->channel_info, user->handle_info);
2337 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2339 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2342 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2344 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2350 access = user_level_from_name(argv[1], UL_OWNER);
2353 reply("CSMSG_INVALID_ACCESS", argv[1]);
2356 if(access != victim->access)
2358 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2364 access = victim->access;
2367 if((actor->access <= victim->access) && !IsHelping(user))
2369 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2373 /* If people delete themselves it is an override, but they
2374 * could've used deleteme so we don't log it as an override
2376 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2377 override = CMD_LOG_OVERRIDE;
2379 chan_name = strdup(channel->name);
2380 del_channel_user(victim, 1);
2381 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2383 return 1 | override;
2387 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2389 struct userData *actor, *real_actor, *uData, *next;
2390 unsigned int override = 0;
2392 actor = GetChannelUser(channel->channel_info, user->handle_info);
2393 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2395 if(min_access > max_access)
2397 reply("CSMSG_BAD_RANGE", min_access, max_access);
2401 if((actor->access <= max_access) && !IsHelping(user))
2403 reply("CSMSG_NO_ACCESS");
2407 if(!real_actor || real_actor->access <= max_access)
2408 override = CMD_LOG_OVERRIDE;
2410 for(uData = channel->channel_info->users; uData; uData = next)
2414 if((uData->access >= min_access)
2415 && (uData->access <= max_access)
2416 && match_ircglob(uData->handle->handle, mask))
2417 del_channel_user(uData, 1);
2420 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2421 return 1 | override;
2424 static CHANSERV_FUNC(cmd_mdelowner)
2426 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2429 static CHANSERV_FUNC(cmd_mdelcoowner)
2431 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2434 static CHANSERV_FUNC(cmd_mdelmaster)
2436 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2439 static CHANSERV_FUNC(cmd_mdelop)
2441 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2444 static CHANSERV_FUNC(cmd_mdelpeon)
2446 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2450 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2452 struct banData *bData, *next;
2453 char interval[INTERVALLEN];
2458 limit = now - duration;
2459 for(bData = channel->channel_info->bans; bData; bData = next)
2463 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2466 del_channel_ban(bData);
2470 intervalString(interval, duration, user->handle_info);
2471 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2476 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2478 struct userData *actor, *uData, *next;
2479 char interval[INTERVALLEN];
2483 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2484 if(min_access > max_access)
2486 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2490 if(!actor || actor->access <= max_access)
2492 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2497 limit = now - duration;
2498 for(uData = channel->channel_info->users; uData; uData = next)
2502 if((uData->seen > limit)
2504 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2507 if(((uData->access >= min_access) && (uData->access <= max_access))
2508 || (!max_access && (uData->access < actor->access)))
2510 del_channel_user(uData, 1);
2518 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2520 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2524 static CHANSERV_FUNC(cmd_trim)
2526 unsigned long duration;
2527 unsigned short min_level, max_level;
2532 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2533 duration = ParseInterval(argv[2]);
2536 reply("CSMSG_CANNOT_TRIM");
2540 if(!irccasecmp(argv[1], "bans"))
2542 cmd_trim_bans(user, channel, duration);
2545 else if(!irccasecmp(argv[1], "users"))
2547 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2550 else if(parse_level_range(&min_level, &max_level, argv[1]))
2552 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2555 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2557 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2562 reply("CSMSG_INVALID_TRIM", argv[1]);
2567 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2568 to the user. cmd_all takes advantage of this. */
2569 static CHANSERV_FUNC(cmd_up)
2571 struct mod_chanmode change;
2572 struct userData *uData;
2575 mod_chanmode_init(&change);
2577 change.args[0].u.member = GetUserMode(channel, user);
2578 if(!change.args[0].u.member)
2581 reply("MSG_CHANNEL_ABSENT", channel->name);
2585 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2589 reply("CSMSG_GODMODE_UP", argv[0]);
2592 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2594 change.args[0].mode = MODE_CHANOP;
2595 errmsg = "CSMSG_ALREADY_OPPED";
2597 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2599 change.args[0].mode = MODE_VOICE;
2600 errmsg = "CSMSG_ALREADY_VOICED";
2605 reply("CSMSG_NO_ACCESS");
2608 change.args[0].mode &= ~change.args[0].u.member->modes;
2609 if(!change.args[0].mode)
2612 reply(errmsg, channel->name);
2615 modcmd_chanmode_announce(&change);
2619 static CHANSERV_FUNC(cmd_down)
2621 struct mod_chanmode change;
2623 mod_chanmode_init(&change);
2625 change.args[0].u.member = GetUserMode(channel, user);
2626 if(!change.args[0].u.member)
2629 reply("MSG_CHANNEL_ABSENT", channel->name);
2633 if(!change.args[0].u.member->modes)
2636 reply("CSMSG_ALREADY_DOWN", channel->name);
2640 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2641 modcmd_chanmode_announce(&change);
2645 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)
2647 struct userData *cList;
2649 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2651 if(IsSuspended(cList->channel)
2652 || IsUserSuspended(cList)
2653 || !GetUserMode(cList->channel->channel, user))
2656 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2662 static CHANSERV_FUNC(cmd_upall)
2664 return cmd_all(CSFUNC_ARGS, cmd_up);
2667 static CHANSERV_FUNC(cmd_downall)
2669 return cmd_all(CSFUNC_ARGS, cmd_down);
2672 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2673 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2676 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)
2678 unsigned int ii, valid;
2679 struct userNode *victim;
2680 struct mod_chanmode *change;
2682 change = mod_chanmode_alloc(argc - 1);
2684 for(ii=valid=0; ++ii < argc; )
2686 if(!(victim = GetUserH(argv[ii])))
2688 change->args[valid].mode = mode;
2689 change->args[valid].u.member = GetUserMode(channel, victim);
2690 if(!change->args[valid].u.member)
2692 if(validate && !validate(user, channel, victim))
2697 change->argc = valid;
2698 if(valid < (argc-1))
2699 reply("CSMSG_PROCESS_FAILED");
2702 modcmd_chanmode_announce(change);
2703 reply(action, channel->name);
2705 mod_chanmode_free(change);
2709 static CHANSERV_FUNC(cmd_op)
2711 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2714 static CHANSERV_FUNC(cmd_deop)
2716 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2719 static CHANSERV_FUNC(cmd_voice)
2721 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2724 static CHANSERV_FUNC(cmd_devoice)
2726 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2730 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2736 for(ii=0; ii<channel->members.used; ii++)
2738 struct modeNode *mn = channel->members.list[ii];
2740 if(IsService(mn->user))
2743 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2746 if(protect_user(mn->user, user, channel->channel_info))
2750 victims[(*victimCount)++] = mn;
2756 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2758 struct userNode *victim;
2759 struct modeNode **victims;
2760 unsigned int offset, n, victimCount, duration = 0;
2761 char *reason = "Bye.", *ban, *name;
2762 char interval[INTERVALLEN];
2764 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2765 REQUIRE_PARAMS(offset);
2768 reason = unsplit_string(argv + offset, argc - offset, NULL);
2769 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2771 /* Truncate the reason to a length of TOPICLEN, as
2772 the ircd does; however, leave room for an ellipsis
2773 and the kicker's nick. */
2774 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2778 if((victim = GetUserH(argv[1])))
2780 victims = alloca(sizeof(victims[0]));
2781 victims[0] = GetUserMode(channel, victim);
2782 /* XXX: The comparison with ACTION_KICK is just because all
2783 * other actions can work on users outside the channel, and we
2784 * want to allow those (e.g. unbans) in that case. If we add
2785 * some other ejection action for in-channel users, change
2787 victimCount = victims[0] ? 1 : 0;
2789 if(IsService(victim))
2791 reply("MSG_SERVICE_IMMUNE", victim->nick);
2795 if((action == ACTION_KICK) && !victimCount)
2797 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2801 if(protect_user(victim, user, channel->channel_info))
2803 reply("CSMSG_USER_PROTECTED", victim->nick);
2807 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2808 name = victim->nick;
2812 if(!is_ircmask(argv[1]))
2814 reply("MSG_NICK_UNKNOWN", argv[1]);
2818 victims = alloca(sizeof(victims[0]) * channel->members.used);
2820 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2822 reply("CSMSG_MASK_PROTECTED", argv[1]);
2826 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2828 reply("CSMSG_LAME_MASK", argv[1]);
2832 if((action == ACTION_KICK) && (victimCount == 0))
2834 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2838 name = ban = strdup(argv[1]);
2841 /* Truncate the ban in place if necessary; we must ensure
2842 that 'ban' is a valid ban mask before sanitizing it. */
2843 sanitize_ircmask(ban);
2845 if(action & ACTION_ADD_BAN)
2847 struct banData *bData, *next;
2849 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2851 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2856 if(action & ACTION_ADD_TIMED_BAN)
2858 duration = ParseInterval(argv[2]);
2862 reply("CSMSG_DURATION_TOO_LOW");
2866 else if(duration > (86400 * 365 * 2))
2868 reply("CSMSG_DURATION_TOO_HIGH");
2874 for(bData = channel->channel_info->bans; bData; bData = next)
2876 if(match_ircglobs(bData->mask, ban))
2878 int exact = !irccasecmp(bData->mask, ban);
2880 /* The ban is redundant; there is already a ban
2881 with the same effect in place. */
2885 free(bData->reason);
2886 bData->reason = strdup(reason);
2887 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2889 reply("CSMSG_REASON_CHANGE", ban);
2893 if(exact && bData->expires)
2897 /* If the ban matches an existing one exactly,
2898 extend the expiration time if the provided
2899 duration is longer. */
2900 if(duration && ((time_t)(now + duration) > bData->expires))
2902 bData->expires = now + duration;
2913 /* Delete the expiration timeq entry and
2914 requeue if necessary. */
2915 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2918 timeq_add(bData->expires, expire_ban, bData);
2922 /* automated kickban */
2925 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2927 reply("CSMSG_BAN_ADDED", name, channel->name);
2933 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2940 if(match_ircglobs(ban, bData->mask))
2942 /* The ban we are adding makes previously existing
2943 bans redundant; silently remove them. */
2944 del_channel_ban(bData);
2948 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);
2950 name = ban = strdup(bData->mask);
2954 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2956 extern const char *hidden_host_suffix;
2957 const char *old_name = chanserv_conf.old_ban_names->list[n];
2959 unsigned int l1, l2;
2962 l2 = strlen(old_name);
2965 if(irccasecmp(ban + l1 - l2, old_name))
2967 new_mask = malloc(MAXLEN);
2968 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2970 name = ban = new_mask;
2975 if(action & ACTION_BAN)
2977 unsigned int exists;
2978 struct mod_chanmode *change;
2980 if(channel->banlist.used >= MAXBANS)
2983 reply("CSMSG_BANLIST_FULL", channel->name);
2988 exists = ChannelBanExists(channel, ban);
2989 change = mod_chanmode_alloc(victimCount + 1);
2990 for(n = 0; n < victimCount; ++n)
2992 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2993 change->args[n].u.member = victims[n];
2997 change->args[n].mode = MODE_BAN;
2998 change->args[n++].u.hostmask = ban;
3002 modcmd_chanmode_announce(change);
3004 mod_chanmode_announce(chanserv, channel, change);
3005 mod_chanmode_free(change);
3007 if(exists && (action == ACTION_BAN))
3010 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3016 if(action & ACTION_KICK)
3018 char kick_reason[MAXLEN];
3019 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3021 for(n = 0; n < victimCount; n++)
3022 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3027 /* No response, since it was automated. */
3029 else if(action & ACTION_ADD_BAN)
3032 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3034 reply("CSMSG_BAN_ADDED", name, channel->name);
3036 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3037 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3038 else if(action & ACTION_BAN)
3039 reply("CSMSG_BAN_DONE", name, channel->name);
3040 else if(action & ACTION_KICK && victimCount)
3041 reply("CSMSG_KICK_DONE", name, channel->name);
3047 static CHANSERV_FUNC(cmd_kickban)
3049 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3052 static CHANSERV_FUNC(cmd_kick)
3054 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3057 static CHANSERV_FUNC(cmd_ban)
3059 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3062 static CHANSERV_FUNC(cmd_addban)
3064 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3067 static CHANSERV_FUNC(cmd_addtimedban)
3069 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3072 static struct mod_chanmode *
3073 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3075 struct mod_chanmode *change;
3076 unsigned char *match;
3077 unsigned int ii, count;
3079 match = alloca(bans->used);
3082 for(ii = count = 0; ii < bans->used; ++ii)
3084 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3085 MATCH_USENICK | MATCH_VISIBLE);
3092 for(ii = count = 0; ii < bans->used; ++ii)
3094 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3101 change = mod_chanmode_alloc(count);
3102 for(ii = count = 0; ii < bans->used; ++ii)
3106 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3107 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3109 assert(count == change->argc);
3114 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3116 struct userNode *actee;
3122 /* may want to allow a comma delimited list of users... */
3123 if(!(actee = GetUserH(argv[1])))
3125 if(!is_ircmask(argv[1]))
3127 reply("MSG_NICK_UNKNOWN", argv[1]);
3131 mask = strdup(argv[1]);
3134 /* We don't sanitize the mask here because ircu
3136 if(action & ACTION_UNBAN)
3138 struct mod_chanmode *change;
3139 change = find_matching_bans(&channel->banlist, actee, mask);
3144 modcmd_chanmode_announce(change);
3145 for(ii = 0; ii < change->argc; ++ii)
3146 free((char*)change->args[ii].u.hostmask);
3147 mod_chanmode_free(change);
3152 if(action & ACTION_DEL_BAN)
3154 struct banData *ban, *next;
3156 ban = channel->channel_info->bans;
3160 for( ; ban && !user_matches_glob(actee, ban->mask,
3161 MATCH_USENICK | MATCH_VISIBLE);
3164 for( ; ban && !match_ircglobs(mask, ban->mask);
3169 del_channel_ban(ban);
3176 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3178 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3184 static CHANSERV_FUNC(cmd_unban)
3186 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3189 static CHANSERV_FUNC(cmd_delban)
3191 /* it doesn't necessarily have to remove the channel ban - may want
3192 to make that an option. */
3193 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3196 static CHANSERV_FUNC(cmd_unbanme)
3198 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3199 long flags = ACTION_UNBAN;
3201 /* remove permanent bans if the user has the proper access. */
3202 if(uData->access >= UL_MASTER)
3203 flags |= ACTION_DEL_BAN;
3205 argv[1] = user->nick;
3206 return unban_user(user, channel, 2, argv, cmd, flags);
3209 static CHANSERV_FUNC(cmd_unbanall)
3211 struct mod_chanmode *change;
3214 if(!channel->banlist.used)
3216 reply("CSMSG_NO_BANS", channel->name);
3220 change = mod_chanmode_alloc(channel->banlist.used);
3221 for(ii=0; ii<channel->banlist.used; ii++)
3223 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3224 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3226 modcmd_chanmode_announce(change);
3227 for(ii = 0; ii < change->argc; ++ii)
3228 free((char*)change->args[ii].u.hostmask);
3229 mod_chanmode_free(change);
3230 reply("CSMSG_BANS_REMOVED", channel->name);
3234 static CHANSERV_FUNC(cmd_open)
3236 struct mod_chanmode *change;
3239 change = find_matching_bans(&channel->banlist, user, NULL);
3241 change = mod_chanmode_alloc(0);
3242 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3243 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3244 && channel->channel_info->modes.modes_set)
3245 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3246 modcmd_chanmode_announce(change);
3247 reply("CSMSG_CHANNEL_OPENED", channel->name);
3248 for(ii = 0; ii < change->argc; ++ii)
3249 free((char*)change->args[ii].u.hostmask);
3250 mod_chanmode_free(change);
3254 static CHANSERV_FUNC(cmd_myaccess)
3256 static struct string_buffer sbuf;
3257 struct handle_info *target_handle;
3258 struct userData *uData;
3261 target_handle = user->handle_info;
3262 else if(!IsHelping(user))
3264 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3267 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3270 if(!target_handle->channels)
3272 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3276 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3277 for(uData = target_handle->channels; uData; uData = uData->u_next)
3279 struct chanData *cData = uData->channel;
3281 if(uData->access > UL_OWNER)
3283 if(IsProtected(cData)
3284 && (target_handle != user->handle_info)
3285 && !GetTrueChannelAccess(cData, user->handle_info))
3288 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3289 if(uData->flags != USER_AUTO_OP)
3290 string_buffer_append(&sbuf, ',');
3291 if(IsUserSuspended(uData))
3292 string_buffer_append(&sbuf, 's');
3293 if(IsUserAutoOp(uData))
3295 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3296 string_buffer_append(&sbuf, 'o');
3297 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3298 string_buffer_append(&sbuf, 'v');
3300 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3301 string_buffer_append(&sbuf, 'i');
3303 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3305 string_buffer_append_string(&sbuf, ")]");
3306 string_buffer_append(&sbuf, '\0');
3307 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3313 static CHANSERV_FUNC(cmd_access)
3315 struct userNode *target;
3316 struct handle_info *target_handle;
3317 struct userData *uData;
3319 char prefix[MAXLEN];
3324 target_handle = target->handle_info;
3326 else if((target = GetUserH(argv[1])))
3328 target_handle = target->handle_info;
3330 else if(argv[1][0] == '*')
3332 if(!(target_handle = get_handle_info(argv[1]+1)))
3334 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3340 reply("MSG_NICK_UNKNOWN", argv[1]);
3344 assert(target || target_handle);
3346 if(target == chanserv)
3348 reply("CSMSG_IS_CHANSERV");
3356 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3361 reply("MSG_USER_AUTHENTICATE", target->nick);
3364 reply("MSG_AUTHENTICATE");
3370 const char *epithet = NULL, *type = NULL;
3373 epithet = chanserv_conf.irc_operator_epithet;
3374 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3376 else if(IsNetworkHelper(target))
3378 epithet = chanserv_conf.network_helper_epithet;
3379 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3381 else if(IsSupportHelper(target))
3383 epithet = chanserv_conf.support_helper_epithet;
3384 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3388 if(target_handle->epithet)
3389 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3391 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3393 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3397 sprintf(prefix, "%s", target_handle->handle);
3400 if(!channel->channel_info)
3402 reply("CSMSG_NOT_REGISTERED", channel->name);
3406 helping = HANDLE_FLAGGED(target_handle, HELPING)
3407 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3408 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3410 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3411 /* To prevent possible information leaks, only show infolines
3412 * if the requestor is in the channel or it's their own
3414 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3416 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3418 /* Likewise, only say it's suspended if the user has active
3419 * access in that channel or it's their own entry. */
3420 if(IsUserSuspended(uData)
3421 && (GetChannelUser(channel->channel_info, user->handle_info)
3422 || (user->handle_info == uData->handle)))
3424 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3429 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3436 zoot_list(struct listData *list)
3438 struct userData *uData;
3439 unsigned int start, curr, highest, lowest;
3440 struct helpfile_table tmp_table;
3441 const char **temp, *msg;
3443 if(list->table.length == 1)
3446 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3448 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3449 msg = user_find_message(list->user, "MSG_NONE");
3450 send_message_type(4, list->user, list->bot, " %s", msg);
3452 tmp_table.width = list->table.width;
3453 tmp_table.flags = list->table.flags;
3454 list->table.contents[0][0] = " ";
3455 highest = list->highest;
3456 if(list->lowest != 0)
3457 lowest = list->lowest;
3458 else if(highest < 100)
3461 lowest = highest - 100;
3462 for(start = curr = 1; curr < list->table.length; )
3464 uData = list->users[curr-1];
3465 list->table.contents[curr++][0] = " ";
3466 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3469 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3471 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3472 temp = list->table.contents[--start];
3473 list->table.contents[start] = list->table.contents[0];
3474 tmp_table.contents = list->table.contents + start;
3475 tmp_table.length = curr - start;
3476 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3477 list->table.contents[start] = temp;
3479 highest = lowest - 1;
3480 lowest = (highest < 100) ? 0 : (highest - 99);
3486 def_list(struct listData *list)
3490 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3492 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3493 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3494 if(list->table.length == 1)
3496 msg = user_find_message(list->user, "MSG_NONE");
3497 send_message_type(4, list->user, list->bot, " %s", msg);
3502 userData_access_comp(const void *arg_a, const void *arg_b)
3504 const struct userData *a = *(struct userData**)arg_a;
3505 const struct userData *b = *(struct userData**)arg_b;
3507 if(a->access != b->access)
3508 res = b->access - a->access;
3510 res = irccasecmp(a->handle->handle, b->handle->handle);
3515 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3517 void (*send_list)(struct listData *);
3518 struct userData *uData;
3519 struct listData lData;
3520 unsigned int matches;
3524 lData.bot = cmd->parent->bot;
3525 lData.channel = channel;
3526 lData.lowest = lowest;
3527 lData.highest = highest;
3528 lData.search = (argc > 1) ? argv[1] : NULL;
3529 send_list = def_list;
3530 (void)zoot_list; /* since it doesn't show user levels */
3532 if(user->handle_info)
3534 switch(user->handle_info->userlist_style)
3536 case HI_STYLE_DEF: send_list = def_list; break;
3537 case HI_STYLE_ZOOT: send_list = def_list; break;
3541 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3543 for(uData = channel->channel_info->users; uData; uData = uData->next)
3545 if((uData->access < lowest)
3546 || (uData->access > highest)
3547 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3549 lData.users[matches++] = uData;
3551 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3553 lData.table.length = matches+1;
3554 lData.table.width = 4;
3555 lData.table.flags = TABLE_NO_FREE;
3556 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3557 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3558 lData.table.contents[0] = ary;
3561 ary[2] = "Last Seen";
3563 for(matches = 1; matches < lData.table.length; ++matches)
3565 struct userData *uData = lData.users[matches-1];
3566 char seen[INTERVALLEN];
3568 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3569 lData.table.contents[matches] = ary;
3570 ary[0] = strtab(uData->access);
3571 ary[1] = uData->handle->handle;
3574 else if(!uData->seen)
3577 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3578 ary[2] = strdup(ary[2]);
3579 if(IsUserSuspended(uData))
3580 ary[3] = "Suspended";
3581 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3582 ary[3] = "Vacation";
3587 for(matches = 1; matches < lData.table.length; ++matches)
3589 free((char*)lData.table.contents[matches][2]);
3590 free(lData.table.contents[matches]);
3592 free(lData.table.contents[0]);
3593 free(lData.table.contents);
3597 static CHANSERV_FUNC(cmd_users)
3599 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3602 static CHANSERV_FUNC(cmd_wlist)
3604 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3607 static CHANSERV_FUNC(cmd_clist)
3609 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3612 static CHANSERV_FUNC(cmd_mlist)
3614 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3617 static CHANSERV_FUNC(cmd_olist)
3619 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3622 static CHANSERV_FUNC(cmd_plist)
3624 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3627 static CHANSERV_FUNC(cmd_bans)
3629 struct userNode *search_u = NULL;
3630 struct helpfile_table tbl;
3631 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3632 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3633 const char *msg_never, *triggered, *expires;
3634 struct banData *ban, **bans;
3638 else if(strchr(search = argv[1], '!'))
3641 search_wilds = search[strcspn(search, "?*")];
3643 else if(!(search_u = GetUserH(search)))
3644 reply("MSG_NICK_UNKNOWN", search);
3646 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3648 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3652 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3657 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3660 bans[matches++] = ban;
3665 tbl.length = matches + 1;
3666 tbl.width = 4 + timed;
3668 tbl.flags = TABLE_NO_FREE;
3669 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3670 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3671 tbl.contents[0][0] = "Mask";
3672 tbl.contents[0][1] = "Set By";
3673 tbl.contents[0][2] = "Triggered";
3676 tbl.contents[0][3] = "Expires";
3677 tbl.contents[0][4] = "Reason";
3680 tbl.contents[0][3] = "Reason";
3683 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3685 free(tbl.contents[0]);
3690 msg_never = user_find_message(user, "MSG_NEVER");
3691 for(ii = 0; ii < matches; )
3697 else if(ban->expires)
3698 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3700 expires = msg_never;
3703 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3705 triggered = msg_never;
3707 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3708 tbl.contents[ii][0] = ban->mask;
3709 tbl.contents[ii][1] = ban->owner;
3710 tbl.contents[ii][2] = strdup(triggered);
3713 tbl.contents[ii][3] = strdup(expires);
3714 tbl.contents[ii][4] = ban->reason;
3717 tbl.contents[ii][3] = ban->reason;
3719 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3720 reply("MSG_MATCH_COUNT", matches);
3721 for(ii = 1; ii < tbl.length; ++ii)
3723 free((char*)tbl.contents[ii][2]);
3725 free((char*)tbl.contents[ii][3]);
3726 free(tbl.contents[ii]);
3728 free(tbl.contents[0]);
3734 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3736 struct chanData *cData = channel->channel_info;
3737 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3739 if(cData->topic_mask)
3740 return !match_ircglob(new_topic, cData->topic_mask);
3741 else if(cData->topic)
3742 return irccasecmp(new_topic, cData->topic);
3747 static CHANSERV_FUNC(cmd_topic)
3749 struct chanData *cData;
3752 cData = channel->channel_info;
3757 SetChannelTopic(channel, chanserv, cData->topic, 1);
3758 reply("CSMSG_TOPIC_SET", cData->topic);
3762 reply("CSMSG_NO_TOPIC", channel->name);
3766 topic = unsplit_string(argv + 1, argc - 1, NULL);
3767 /* If they say "!topic *", use an empty topic. */
3768 if((topic[0] == '*') && (topic[1] == 0))
3770 if(bad_topic(channel, user, topic))
3772 char *topic_mask = cData->topic_mask;
3775 char new_topic[TOPICLEN+1], tchar;
3776 int pos=0, starpos=-1, dpos=0, len;
3778 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3785 len = strlen(topic);
3786 if((dpos + len) > TOPICLEN)
3787 len = TOPICLEN + 1 - dpos;
3788 memcpy(new_topic+dpos, topic, len);
3792 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3793 default: new_topic[dpos++] = tchar; break;
3796 if((dpos > TOPICLEN) || tchar)
3799 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3800 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3803 new_topic[dpos] = 0;
3804 SetChannelTopic(channel, chanserv, new_topic, 1);
3806 reply("CSMSG_TOPIC_LOCKED", channel->name);
3811 SetChannelTopic(channel, chanserv, topic, 1);
3813 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3815 /* Grab the topic and save it as the default topic. */
3817 cData->topic = strdup(channel->topic);
3823 static CHANSERV_FUNC(cmd_mode)
3825 struct userData *uData;
3826 struct mod_chanmode *change;
3831 change = &channel->channel_info->modes;
3832 if(change->modes_set || change->modes_clear) {
3833 modcmd_chanmode_announce(change);
3834 reply("CSMSG_DEFAULTED_MODES", channel->name);
3836 reply("CSMSG_NO_MODES", channel->name);
3840 uData = GetChannelUser(channel->channel_info, user->handle_info);
3842 base_oplevel = MAXOPLEVEL;
3843 else if (uData->access >= UL_OWNER)
3846 base_oplevel = 1 + UL_OWNER - uData->access;
3847 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3850 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3854 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3855 && mode_lock_violated(&channel->channel_info->modes, change))
3858 mod_chanmode_format(&channel->channel_info->modes, modes);
3859 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3863 modcmd_chanmode_announce(change);
3864 mod_chanmode_free(change);
3865 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3869 static CHANSERV_FUNC(cmd_invite)
3871 struct userData *uData;
3872 struct userNode *invite;
3874 uData = GetChannelUser(channel->channel_info, user->handle_info);
3878 if(!(invite = GetUserH(argv[1])))
3880 reply("MSG_NICK_UNKNOWN", argv[1]);
3887 if(GetUserMode(channel, invite))
3889 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3897 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3898 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3901 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3903 irc_invite(chanserv, invite, channel);
3905 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3910 static CHANSERV_FUNC(cmd_inviteme)
3912 if(GetUserMode(channel, user))
3914 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3917 if(channel->channel_info
3918 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3920 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3923 irc_invite(cmd->parent->bot, user, channel);
3928 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3931 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3933 /* We display things based on two dimensions:
3934 * - Issue time: present or absent
3935 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3936 * (in order of precedence, so something both expired and revoked
3937 * only counts as revoked)
3939 combo = (suspended->issued ? 4 : 0)
3940 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3942 case 0: /* no issue time, indefinite expiration */
3943 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3945 case 1: /* no issue time, expires in future */
3946 intervalString(buf1, suspended->expires-now, user->handle_info);
3947 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3949 case 2: /* no issue time, expired */
3950 intervalString(buf1, now-suspended->expires, user->handle_info);
3951 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3953 case 3: /* no issue time, revoked */
3954 intervalString(buf1, now-suspended->revoked, user->handle_info);
3955 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3957 case 4: /* issue time set, indefinite expiration */
3958 intervalString(buf1, now-suspended->issued, user->handle_info);
3959 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3961 case 5: /* issue time set, expires in future */
3962 intervalString(buf1, now-suspended->issued, user->handle_info);
3963 intervalString(buf2, suspended->expires-now, user->handle_info);
3964 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3966 case 6: /* issue time set, expired */
3967 intervalString(buf1, now-suspended->issued, user->handle_info);
3968 intervalString(buf2, now-suspended->expires, user->handle_info);
3969 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3971 case 7: /* issue time set, revoked */
3972 intervalString(buf1, now-suspended->issued, user->handle_info);
3973 intervalString(buf2, now-suspended->revoked, user->handle_info);
3974 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3977 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3982 static CHANSERV_FUNC(cmd_info)
3984 char modes[MAXLEN], buffer[INTERVALLEN];
3985 struct userData *uData, *owner;
3986 struct chanData *cData;
3987 struct do_not_register *dnr;
3992 cData = channel->channel_info;
3993 reply("CSMSG_CHANNEL_INFO", channel->name);
3995 uData = GetChannelUser(cData, user->handle_info);
3996 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3998 mod_chanmode_format(&cData->modes, modes);
3999 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4000 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4003 for(it = dict_first(cData->notes); it; it = iter_next(it))
4007 note = iter_data(it);
4008 if(!note_type_visible_to_user(cData, note->type, user))
4011 padding = PADLEN - 1 - strlen(iter_key(it));
4012 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4015 reply("CSMSG_CHANNEL_MAX", cData->max);
4016 for(owner = cData->users; owner; owner = owner->next)
4017 if(owner->access == UL_OWNER)
4018 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4019 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4020 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4021 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4023 privileged = IsStaff(user);
4025 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4026 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4027 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4029 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4030 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4032 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4034 struct suspended *suspended;
4035 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4036 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4037 show_suspension_info(cmd, user, suspended);
4039 else if(IsSuspended(cData))
4041 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4042 show_suspension_info(cmd, user, cData->suspended);
4047 static CHANSERV_FUNC(cmd_netinfo)
4049 extern time_t boot_time;
4050 extern unsigned long burst_length;
4051 char interval[INTERVALLEN];
4053 reply("CSMSG_NETWORK_INFO");
4054 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4055 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4056 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4057 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4058 reply("CSMSG_NETWORK_BANS", banCount);
4059 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4060 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4061 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4066 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4068 struct helpfile_table table;
4070 struct userNode *user;
4075 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4076 table.contents = alloca(list->used*sizeof(*table.contents));
4077 for(nn=0; nn<list->used; nn++)
4079 user = list->list[nn];
4080 if(user->modes & skip_flags)
4084 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4087 nick = alloca(strlen(user->nick)+3);
4088 sprintf(nick, "(%s)", user->nick);
4092 table.contents[table.length][0] = nick;
4095 table_send(chanserv, to->nick, 0, NULL, table);
4098 static CHANSERV_FUNC(cmd_ircops)
4100 reply("CSMSG_STAFF_OPERS");
4101 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4105 static CHANSERV_FUNC(cmd_helpers)
4107 reply("CSMSG_STAFF_HELPERS");
4108 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4112 static CHANSERV_FUNC(cmd_staff)
4114 reply("CSMSG_NETWORK_STAFF");
4115 cmd_ircops(CSFUNC_ARGS);
4116 cmd_helpers(CSFUNC_ARGS);
4120 static CHANSERV_FUNC(cmd_peek)
4122 struct modeNode *mn;
4123 char modes[MODELEN];
4125 struct helpfile_table table;
4127 irc_make_chanmode(channel, modes);
4129 reply("CSMSG_PEEK_INFO", channel->name);
4130 reply("CSMSG_PEEK_TOPIC", channel->topic);
4131 reply("CSMSG_PEEK_MODES", modes);
4132 reply("CSMSG_PEEK_USERS", channel->members.used);
4136 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4137 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4138 for(n = 0; n < channel->members.used; n++)
4140 mn = channel->members.list[n];
4141 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4143 table.contents[table.length] = alloca(sizeof(**table.contents));
4144 table.contents[table.length][0] = mn->user->nick;
4149 reply("CSMSG_PEEK_OPS");
4150 table_send(chanserv, user->nick, 0, NULL, table);
4153 reply("CSMSG_PEEK_NO_OPS");
4157 static MODCMD_FUNC(cmd_wipeinfo)
4159 struct handle_info *victim;
4160 struct userData *ud, *actor, *real_actor;
4161 unsigned int override = 0;
4164 actor = GetChannelUser(channel->channel_info, user->handle_info);
4165 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4166 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4168 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4170 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4173 if((ud->access >= actor->access) && (ud != actor))
4175 reply("MSG_USER_OUTRANKED", victim->handle);
4178 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4179 override = CMD_LOG_OVERRIDE;
4183 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4184 return 1 | override;
4187 static CHANSERV_FUNC(cmd_resync)
4189 struct mod_chanmode *changes;
4190 struct chanData *cData = channel->channel_info;
4191 unsigned int ii, used;
4193 changes = mod_chanmode_alloc(channel->members.used * 2);
4194 for(ii = used = 0; ii < channel->members.used; ++ii)
4196 struct modeNode *mn = channel->members.list[ii];
4197 struct userData *uData;
4199 if(IsService(mn->user))
4202 uData = GetChannelAccess(cData, mn->user->handle_info);
4203 if(!cData->lvlOpts[lvlGiveOps]
4204 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4206 if(!(mn->modes & MODE_CHANOP))
4208 changes->args[used].mode = MODE_CHANOP;
4209 changes->args[used++].u.member = mn;
4212 else if(!cData->lvlOpts[lvlGiveVoice]
4213 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4215 if(mn->modes & MODE_CHANOP)
4217 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4218 changes->args[used++].u.member = mn;
4220 if(!(mn->modes & MODE_VOICE))
4222 changes->args[used].mode = MODE_VOICE;
4223 changes->args[used++].u.member = mn;
4230 changes->args[used].mode = MODE_REMOVE | mn->modes;
4231 changes->args[used++].u.member = mn;
4235 changes->argc = used;
4236 modcmd_chanmode_announce(changes);
4237 mod_chanmode_free(changes);
4238 reply("CSMSG_RESYNCED_USERS", channel->name);
4242 static CHANSERV_FUNC(cmd_seen)
4244 struct userData *uData;
4245 struct handle_info *handle;
4246 char seen[INTERVALLEN];
4250 if(!irccasecmp(argv[1], chanserv->nick))
4252 reply("CSMSG_IS_CHANSERV");
4256 if(!(handle = get_handle_info(argv[1])))
4258 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4262 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4264 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4269 reply("CSMSG_USER_PRESENT", handle->handle);
4270 else if(uData->seen)
4271 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4273 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4275 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4276 reply("CSMSG_USER_VACATION", handle->handle);
4281 static MODCMD_FUNC(cmd_names)
4283 struct userNode *targ;
4284 struct userData *targData;
4285 unsigned int ii, pos;
4288 for(ii=pos=0; ii<channel->members.used; ++ii)
4290 targ = channel->members.list[ii]->user;
4291 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4294 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4297 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4301 if(IsUserSuspended(targData))
4303 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4306 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4307 reply("CSMSG_END_NAMES", channel->name);
4312 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4314 switch(ntype->visible_type)
4316 case NOTE_VIS_ALL: return 1;
4317 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4318 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4323 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4325 struct userData *uData;
4327 switch(ntype->set_access_type)
4329 case NOTE_SET_CHANNEL_ACCESS:
4330 if(!user->handle_info)
4332 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4334 return uData->access >= ntype->set_access.min_ulevel;
4335 case NOTE_SET_CHANNEL_SETTER:
4336 return check_user_level(channel, user, lvlSetters, 1, 0);
4337 case NOTE_SET_PRIVILEGED: default:
4338 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4342 static CHANSERV_FUNC(cmd_note)
4344 struct chanData *cData;
4346 struct note_type *ntype;
4348 cData = channel->channel_info;
4351 reply("CSMSG_NOT_REGISTERED", channel->name);
4355 /* If no arguments, show all visible notes for the channel. */
4361 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4363 note = iter_data(it);
4364 if(!note_type_visible_to_user(cData, note->type, user))
4367 reply("CSMSG_NOTELIST_HEADER", channel->name);
4368 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4371 reply("CSMSG_NOTELIST_END", channel->name);
4373 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4375 /* If one argument, show the named note. */
4378 if((note = dict_find(cData->notes, argv[1], NULL))
4379 && note_type_visible_to_user(cData, note->type, user))
4381 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4383 else if((ntype = dict_find(note_types, argv[1], NULL))
4384 && note_type_visible_to_user(NULL, ntype, user))
4386 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4391 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4395 /* Assume they're trying to set a note. */
4399 ntype = dict_find(note_types, argv[1], NULL);
4402 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4405 else if(note_type_settable_by_user(channel, ntype, user))
4407 note_text = unsplit_string(argv+2, argc-2, NULL);
4408 if((note = dict_find(cData->notes, argv[1], NULL)))
4409 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4410 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4411 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4413 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4415 /* The note is viewable to staff only, so return 0
4416 to keep the invocation from getting logged (or
4417 regular users can see it in !events). */
4423 reply("CSMSG_NO_ACCESS");
4430 static CHANSERV_FUNC(cmd_delnote)
4435 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4436 || !note_type_settable_by_user(channel, note->type, user))
4438 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4441 dict_remove(channel->channel_info->notes, note->type->name);
4442 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4446 static CHANSERV_FUNC(cmd_events)
4448 struct logSearch discrim;
4449 struct logReport report;
4450 unsigned int matches, limit;
4452 limit = (argc > 1) ? atoi(argv[1]) : 10;
4453 if(limit < 1 || limit > 200)
4456 memset(&discrim, 0, sizeof(discrim));
4457 discrim.masks.bot = chanserv;
4458 discrim.masks.channel_name = channel->name;
4460 discrim.masks.command = argv[2];
4461 discrim.limit = limit;
4462 discrim.max_time = INT_MAX;
4463 discrim.severities = 1 << LOG_COMMAND;
4464 report.reporter = chanserv;
4466 reply("CSMSG_EVENT_SEARCH_RESULTS");
4467 matches = log_entry_search(&discrim, log_report_entry, &report);
4469 reply("MSG_MATCH_COUNT", matches);
4471 reply("MSG_NO_MATCHES");
4475 static CHANSERV_FUNC(cmd_say)
4481 msg = unsplit_string(argv + 1, argc - 1, NULL);
4482 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4484 else if(GetUserH(argv[1]))
4487 msg = unsplit_string(argv + 2, argc - 2, NULL);
4488 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4492 reply("MSG_NOT_TARGET_NAME");
4498 static CHANSERV_FUNC(cmd_emote)
4504 /* CTCP is so annoying. */
4505 msg = unsplit_string(argv + 1, argc - 1, NULL);
4506 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4508 else if(GetUserH(argv[1]))
4510 msg = unsplit_string(argv + 2, argc - 2, NULL);
4511 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4515 reply("MSG_NOT_TARGET_NAME");
4521 struct channelList *
4522 chanserv_support_channels(void)
4524 return &chanserv_conf.support_channels;
4527 static CHANSERV_FUNC(cmd_expire)
4529 int channel_count = registered_channels;
4530 expire_channels(NULL);
4531 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4536 chanserv_expire_suspension(void *data)
4538 struct suspended *suspended = data;
4539 struct chanNode *channel;
4541 if(!suspended->expires || (now < suspended->expires))
4542 suspended->revoked = now;
4543 channel = suspended->cData->channel;
4544 suspended->cData->channel = channel;
4545 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4546 if(!IsOffChannel(suspended->cData))
4548 struct mod_chanmode change;
4549 mod_chanmode_init(&change);
4551 change.args[0].mode = MODE_CHANOP;
4552 change.args[0].u.member = AddChannelUser(chanserv, channel);
4553 mod_chanmode_announce(chanserv, channel, &change);
4557 static CHANSERV_FUNC(cmd_csuspend)
4559 struct suspended *suspended;
4560 char reason[MAXLEN];
4561 time_t expiry, duration;
4562 struct userData *uData;
4566 if(IsProtected(channel->channel_info))
4568 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4572 if(argv[1][0] == '!')
4574 else if(IsSuspended(channel->channel_info))
4576 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4577 show_suspension_info(cmd, user, channel->channel_info->suspended);
4581 if(!strcmp(argv[1], "0"))
4583 else if((duration = ParseInterval(argv[1])))
4584 expiry = now + duration;
4587 reply("MSG_INVALID_DURATION", argv[1]);
4591 unsplit_string(argv + 2, argc - 2, reason);
4593 suspended = calloc(1, sizeof(*suspended));
4594 suspended->revoked = 0;
4595 suspended->issued = now;
4596 suspended->suspender = strdup(user->handle_info->handle);
4597 suspended->expires = expiry;
4598 suspended->reason = strdup(reason);
4599 suspended->cData = channel->channel_info;
4600 suspended->previous = suspended->cData->suspended;
4601 suspended->cData->suspended = suspended;
4603 if(suspended->expires)
4604 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4606 if(IsSuspended(channel->channel_info))
4608 suspended->previous->revoked = now;
4609 if(suspended->previous->expires)
4610 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4611 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4612 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4616 /* Mark all users in channel as absent. */
4617 for(uData = channel->channel_info->users; uData; uData = uData->next)
4626 /* Mark the channel as suspended, then part. */
4627 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4628 DelChannelUser(chanserv, channel, suspended->reason, 0);
4629 reply("CSMSG_SUSPENDED", channel->name);
4630 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4631 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4636 static CHANSERV_FUNC(cmd_cunsuspend)
4638 struct suspended *suspended;
4639 char message[MAXLEN];
4641 if(!IsSuspended(channel->channel_info))
4643 reply("CSMSG_NOT_SUSPENDED", channel->name);
4647 suspended = channel->channel_info->suspended;
4649 /* Expire the suspension and join ChanServ to the channel. */
4650 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4651 chanserv_expire_suspension(suspended);
4652 reply("CSMSG_UNSUSPENDED", channel->name);
4653 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4654 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4658 typedef struct chanservSearch
4666 unsigned long flags;
4670 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4673 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4678 search = malloc(sizeof(struct chanservSearch));
4679 memset(search, 0, sizeof(*search));
4682 for(i = 0; i < argc; i++)
4684 /* Assume all criteria require arguments. */
4687 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4691 if(!irccasecmp(argv[i], "name"))
4692 search->name = argv[++i];
4693 else if(!irccasecmp(argv[i], "registrar"))
4694 search->registrar = argv[++i];
4695 else if(!irccasecmp(argv[i], "unvisited"))
4696 search->unvisited = ParseInterval(argv[++i]);
4697 else if(!irccasecmp(argv[i], "registered"))
4698 search->registered = ParseInterval(argv[++i]);
4699 else if(!irccasecmp(argv[i], "flags"))
4702 if(!irccasecmp(argv[i], "nodelete"))
4703 search->flags |= CHANNEL_NODELETE;
4704 else if(!irccasecmp(argv[i], "suspended"))
4705 search->flags |= CHANNEL_SUSPENDED;
4708 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4712 else if(!irccasecmp(argv[i], "limit"))
4713 search->limit = strtoul(argv[++i], NULL, 10);
4716 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4721 if(search->name && !strcmp(search->name, "*"))
4723 if(search->registrar && !strcmp(search->registrar, "*"))
4724 search->registrar = 0;
4733 chanserv_channel_match(struct chanData *channel, search_t search)
4735 const char *name = channel->channel->name;
4736 if((search->name && !match_ircglob(name, search->name)) ||
4737 (search->registrar && !channel->registrar) ||
4738 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4739 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4740 (search->registered && (now - channel->registered) > search->registered) ||
4741 (search->flags && ((search->flags & channel->flags) != search->flags)))
4748 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4750 struct chanData *channel;
4751 unsigned int matches = 0;
4753 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4755 if(!chanserv_channel_match(channel, search))
4765 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4770 search_print(struct chanData *channel, void *data)
4772 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4775 static CHANSERV_FUNC(cmd_search)
4778 unsigned int matches;
4779 channel_search_func action;
4783 if(!irccasecmp(argv[1], "count"))
4784 action = search_count;
4785 else if(!irccasecmp(argv[1], "print"))
4786 action = search_print;
4789 reply("CSMSG_ACTION_INVALID", argv[1]);
4793 search = chanserv_search_create(user, argc - 2, argv + 2);
4797 if(action == search_count)
4798 search->limit = INT_MAX;
4800 if(action == search_print)
4801 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4803 matches = chanserv_channel_search(search, action, user);
4806 reply("MSG_MATCH_COUNT", matches);
4808 reply("MSG_NO_MATCHES");
4814 static CHANSERV_FUNC(cmd_unvisited)
4816 struct chanData *cData;
4817 time_t interval = chanserv_conf.channel_expire_delay;
4818 char buffer[INTERVALLEN];
4819 unsigned int limit = 25, matches = 0;
4823 interval = ParseInterval(argv[1]);
4825 limit = atoi(argv[2]);
4828 intervalString(buffer, interval, user->handle_info);
4829 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4831 for(cData = channelList; cData && matches < limit; cData = cData->next)
4833 if((now - cData->visited) < interval)
4836 intervalString(buffer, now - cData->visited, user->handle_info);
4837 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4844 static MODCMD_FUNC(chan_opt_defaulttopic)
4850 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4852 reply("CSMSG_TOPIC_LOCKED", channel->name);
4856 topic = unsplit_string(argv+1, argc-1, NULL);
4858 free(channel->channel_info->topic);
4859 if(topic[0] == '*' && topic[1] == 0)
4861 topic = channel->channel_info->topic = NULL;
4865 topic = channel->channel_info->topic = strdup(topic);
4866 if(channel->channel_info->topic_mask
4867 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4868 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4870 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4873 if(channel->channel_info->topic)
4874 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4876 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4880 static MODCMD_FUNC(chan_opt_topicmask)
4884 struct chanData *cData = channel->channel_info;
4887 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4889 reply("CSMSG_TOPIC_LOCKED", channel->name);
4893 mask = unsplit_string(argv+1, argc-1, NULL);
4895 if(cData->topic_mask)
4896 free(cData->topic_mask);
4897 if(mask[0] == '*' && mask[1] == 0)
4899 cData->topic_mask = 0;
4903 cData->topic_mask = strdup(mask);
4905 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4906 else if(!match_ircglob(cData->topic, cData->topic_mask))
4907 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4911 if(channel->channel_info->topic_mask)
4912 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4914 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4918 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4922 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4926 if(greeting[0] == '*' && greeting[1] == 0)
4930 unsigned int length = strlen(greeting);
4931 if(length > chanserv_conf.greeting_length)
4933 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4936 *data = strdup(greeting);
4945 reply(name, user_find_message(user, "MSG_NONE"));
4949 static MODCMD_FUNC(chan_opt_greeting)
4951 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4954 static MODCMD_FUNC(chan_opt_usergreeting)
4956 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4959 static MODCMD_FUNC(chan_opt_modes)
4961 struct mod_chanmode *new_modes;
4962 char modes[MODELEN];
4966 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4968 reply("CSMSG_NO_ACCESS");
4971 if(argv[1][0] == '*' && argv[1][1] == 0)
4973 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4975 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
4977 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4980 else if(new_modes->argc > 1)
4982 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4983 mod_chanmode_free(new_modes);
4988 channel->channel_info->modes = *new_modes;
4989 modcmd_chanmode_announce(new_modes);
4990 mod_chanmode_free(new_modes);
4994 mod_chanmode_format(&channel->channel_info->modes, modes);
4996 reply("CSMSG_SET_MODES", modes);
4998 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5002 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5004 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5006 struct chanData *cData = channel->channel_info;
5011 /* Set flag according to value. */
5012 if(enabled_string(argv[1]))
5014 cData->flags |= mask;
5017 else if(disabled_string(argv[1]))
5019 cData->flags &= ~mask;
5024 reply("MSG_INVALID_BINARY", argv[1]);
5030 /* Find current option value. */
5031 value = (cData->flags & mask) ? 1 : 0;
5035 reply(name, user_find_message(user, "MSG_ON"));
5037 reply(name, user_find_message(user, "MSG_OFF"));
5041 static MODCMD_FUNC(chan_opt_nodelete)
5043 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5045 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5049 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5052 static MODCMD_FUNC(chan_opt_dynlimit)
5054 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5057 static MODCMD_FUNC(chan_opt_offchannel)
5059 struct chanData *cData = channel->channel_info;
5064 /* Set flag according to value. */
5065 if(enabled_string(argv[1]))
5067 if(!IsOffChannel(cData))
5068 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5069 cData->flags |= CHANNEL_OFFCHANNEL;
5072 else if(disabled_string(argv[1]))
5074 if(IsOffChannel(cData))
5076 struct mod_chanmode change;
5077 mod_chanmode_init(&change);
5079 change.args[0].mode = MODE_CHANOP;
5080 change.args[0].u.member = AddChannelUser(chanserv, channel);
5081 mod_chanmode_announce(chanserv, channel, &change);
5083 cData->flags &= ~CHANNEL_OFFCHANNEL;
5088 reply("MSG_INVALID_BINARY", argv[1]);
5094 /* Find current option value. */
5095 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5099 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5101 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5105 static MODCMD_FUNC(chan_opt_defaults)
5107 struct userData *uData;
5108 struct chanData *cData;
5109 const char *confirm;
5110 enum levelOption lvlOpt;
5111 enum charOption chOpt;
5113 cData = channel->channel_info;
5114 uData = GetChannelUser(cData, user->handle_info);
5115 if(!uData || (uData->access < UL_OWNER))
5117 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5120 confirm = make_confirmation_string(uData);
5121 if((argc < 2) || strcmp(argv[1], confirm))
5123 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5126 cData->flags = CHANNEL_DEFAULT_FLAGS;
5127 cData->modes = chanserv_conf.default_modes;
5128 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5129 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5130 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5131 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5132 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5137 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5139 struct chanData *cData = channel->channel_info;
5140 struct userData *uData;
5141 unsigned short value;
5145 if(!check_user_level(channel, user, option, 1, 1))
5147 reply("CSMSG_CANNOT_SET");
5150 value = user_level_from_name(argv[1], UL_OWNER+1);
5151 if(!value && strcmp(argv[1], "0"))
5153 reply("CSMSG_INVALID_ACCESS", argv[1]);
5156 uData = GetChannelUser(cData, user->handle_info);
5157 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5159 reply("CSMSG_BAD_SETLEVEL");
5165 if(value > cData->lvlOpts[lvlGiveOps])
5167 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5172 if(value < cData->lvlOpts[lvlGiveVoice])
5174 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5179 /* This test only applies to owners, since non-owners
5180 * trying to set an option to above their level get caught
5181 * by the CSMSG_BAD_SETLEVEL test above.
5183 if(value > uData->access)
5185 reply("CSMSG_BAD_SETTERS");
5192 cData->lvlOpts[option] = value;
5194 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5198 static MODCMD_FUNC(chan_opt_enfops)
5200 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5203 static MODCMD_FUNC(chan_opt_giveops)
5205 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5208 static MODCMD_FUNC(chan_opt_enfmodes)
5210 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5213 static MODCMD_FUNC(chan_opt_enftopic)
5215 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5218 static MODCMD_FUNC(chan_opt_pubcmd)
5220 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5223 static MODCMD_FUNC(chan_opt_setters)
5225 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5228 static MODCMD_FUNC(chan_opt_ctcpusers)
5230 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5233 static MODCMD_FUNC(chan_opt_userinfo)
5235 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5238 static MODCMD_FUNC(chan_opt_givevoice)
5240 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5243 static MODCMD_FUNC(chan_opt_topicsnarf)
5245 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5248 static MODCMD_FUNC(chan_opt_inviteme)
5250 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5254 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5256 struct chanData *cData = channel->channel_info;
5257 int count = charOptions[option].count, index;
5261 index = atoi(argv[1]);
5263 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5265 reply("CSMSG_INVALID_NUMERIC", index);
5266 /* Show possible values. */
5267 for(index = 0; index < count; index++)
5268 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5272 cData->chOpts[option] = charOptions[option].values[index].value;
5276 /* Find current option value. */
5279 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5283 /* Somehow, the option value is corrupt; reset it to the default. */
5284 cData->chOpts[option] = charOptions[option].default_value;
5289 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5293 static MODCMD_FUNC(chan_opt_protect)
5295 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5298 static MODCMD_FUNC(chan_opt_toys)
5300 return channel_multiple_option(chToys, CSFUNC_ARGS);
5303 static MODCMD_FUNC(chan_opt_ctcpreaction)
5305 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5308 static MODCMD_FUNC(chan_opt_topicrefresh)
5310 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5313 static struct svccmd_list set_shows_list;
5316 handle_svccmd_unbind(struct svccmd *target) {
5318 for(ii=0; ii<set_shows_list.used; ++ii)
5319 if(target == set_shows_list.list[ii])
5320 set_shows_list.used = 0;
5323 static CHANSERV_FUNC(cmd_set)
5325 struct svccmd *subcmd;
5329 /* Check if we need to (re-)initialize set_shows_list. */
5330 if(!set_shows_list.used)
5332 if(!set_shows_list.size)
5334 set_shows_list.size = chanserv_conf.set_shows->used;
5335 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5337 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5339 const char *name = chanserv_conf.set_shows->list[ii];
5340 sprintf(buf, "%s %s", argv[0], name);
5341 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5344 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5347 svccmd_list_append(&set_shows_list, subcmd);
5353 reply("CSMSG_CHANNEL_OPTIONS");
5354 for(ii = 0; ii < set_shows_list.used; ii++)
5356 subcmd = set_shows_list.list[ii];
5357 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5362 sprintf(buf, "%s %s", argv[0], argv[1]);
5363 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5366 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5369 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5371 reply("CSMSG_NO_ACCESS");
5375 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5379 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5381 struct userData *uData;
5383 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5386 reply("CSMSG_NOT_USER", channel->name);
5392 /* Just show current option value. */
5394 else if(enabled_string(argv[1]))
5396 uData->flags |= mask;
5398 else if(disabled_string(argv[1]))
5400 uData->flags &= ~mask;
5404 reply("MSG_INVALID_BINARY", argv[1]);
5408 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5412 static MODCMD_FUNC(user_opt_noautoop)
5414 struct userData *uData;
5416 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5419 reply("CSMSG_NOT_USER", channel->name);
5422 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5423 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5425 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5428 static MODCMD_FUNC(user_opt_autoinvite)
5430 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5433 static MODCMD_FUNC(user_opt_info)
5435 struct userData *uData;
5438 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5442 /* If they got past the command restrictions (which require access)
5443 * but fail this test, we have some fool with security override on.
5445 reply("CSMSG_NOT_USER", channel->name);
5452 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5453 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5455 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5458 bp = strcspn(infoline, "\001");
5461 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5466 if(infoline[0] == '*' && infoline[1] == 0)
5469 uData->info = strdup(infoline);
5472 reply("CSMSG_USET_INFO", uData->info);
5474 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5478 struct svccmd_list uset_shows_list;
5480 static CHANSERV_FUNC(cmd_uset)
5482 struct svccmd *subcmd;
5486 /* Check if we need to (re-)initialize uset_shows_list. */
5487 if(!uset_shows_list.used)
5491 "NoAutoOp", "AutoInvite", "Info"
5494 if(!uset_shows_list.size)
5496 uset_shows_list.size = ArrayLength(options);
5497 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5499 for(ii = 0; ii < ArrayLength(options); ii++)
5501 const char *name = options[ii];
5502 sprintf(buf, "%s %s", argv[0], name);
5503 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5506 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5509 svccmd_list_append(&uset_shows_list, subcmd);
5515 /* Do this so options are presented in a consistent order. */
5516 reply("CSMSG_USER_OPTIONS");
5517 for(ii = 0; ii < uset_shows_list.used; ii++)
5518 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5522 sprintf(buf, "%s %s", argv[0], argv[1]);
5523 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5526 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5530 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5533 static CHANSERV_FUNC(cmd_giveownership)
5535 struct handle_info *new_owner_hi;
5536 struct userData *new_owner;
5537 struct userData *curr_user;
5538 struct userData *invoker;
5539 struct chanData *cData = channel->channel_info;
5540 struct do_not_register *dnr;
5541 const char *confirm;
5543 unsigned short co_access;
5544 char reason[MAXLEN];
5547 curr_user = GetChannelAccess(cData, user->handle_info);
5548 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5549 if(!curr_user || (curr_user->access != UL_OWNER))
5551 struct userData *owner = NULL;
5552 for(curr_user = channel->channel_info->users;
5554 curr_user = curr_user->next)
5556 if(curr_user->access != UL_OWNER)
5560 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5567 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5569 char delay[INTERVALLEN];
5570 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5571 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5574 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5576 if(new_owner_hi == user->handle_info)
5578 reply("CSMSG_NO_TRANSFER_SELF");
5581 new_owner = GetChannelAccess(cData, new_owner_hi);
5586 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5590 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5594 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5596 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5599 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5600 if(!IsHelping(user))
5601 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5603 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5606 invoker = GetChannelUser(cData, user->handle_info);
5607 if(invoker->access <= UL_OWNER)
5609 confirm = make_confirmation_string(curr_user);
5610 if((argc < 3) || strcmp(argv[2], confirm))
5612 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5616 if(new_owner->access >= UL_COOWNER)
5617 co_access = new_owner->access;
5619 co_access = UL_COOWNER;
5620 new_owner->access = UL_OWNER;
5622 curr_user->access = co_access;
5623 cData->ownerTransfer = now;
5624 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5625 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5626 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5630 static CHANSERV_FUNC(cmd_suspend)
5632 struct handle_info *hi;
5633 struct userData *self, *real_self, *target;
5634 unsigned int override = 0;
5637 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5638 self = GetChannelUser(channel->channel_info, user->handle_info);
5639 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5640 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5642 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5645 if(target->access >= self->access)
5647 reply("MSG_USER_OUTRANKED", hi->handle);
5650 if(target->flags & USER_SUSPENDED)
5652 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5657 target->present = 0;
5660 if(!real_self || target->access >= real_self->access)
5661 override = CMD_LOG_OVERRIDE;
5662 target->flags |= USER_SUSPENDED;
5663 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5664 return 1 | override;
5667 static CHANSERV_FUNC(cmd_unsuspend)
5669 struct handle_info *hi;
5670 struct userData *self, *real_self, *target;
5671 unsigned int override = 0;
5674 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5675 self = GetChannelUser(channel->channel_info, user->handle_info);
5676 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5677 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5679 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5682 if(target->access >= self->access)
5684 reply("MSG_USER_OUTRANKED", hi->handle);
5687 if(!(target->flags & USER_SUSPENDED))
5689 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5692 if(!real_self || target->access >= real_self->access)
5693 override = CMD_LOG_OVERRIDE;
5694 target->flags &= ~USER_SUSPENDED;
5695 scan_user_presence(target, NULL);
5696 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5697 return 1 | override;
5700 static MODCMD_FUNC(cmd_deleteme)
5702 struct handle_info *hi;
5703 struct userData *target;
5704 const char *confirm_string;
5705 unsigned short access;
5708 hi = user->handle_info;
5709 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5711 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5714 if(target->access == UL_OWNER)
5716 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5719 confirm_string = make_confirmation_string(target);
5720 if((argc < 2) || strcmp(argv[1], confirm_string))
5722 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5725 access = target->access;
5726 channel_name = strdup(channel->name);
5727 del_channel_user(target, 1);
5728 reply("CSMSG_DELETED_YOU", access, channel_name);
5734 chanserv_refresh_topics(UNUSED_ARG(void *data))
5736 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5737 struct chanData *cData;
5740 for(cData = channelList; cData; cData = cData->next)
5742 if(IsSuspended(cData))
5744 opt = cData->chOpts[chTopicRefresh];
5747 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5750 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5751 cData->last_refresh = refresh_num;
5753 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5756 static CHANSERV_FUNC(cmd_unf)
5760 char response[MAXLEN];
5761 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5762 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5763 irc_privmsg(cmd->parent->bot, channel->name, response);
5766 reply("CSMSG_UNF_RESPONSE");
5770 static CHANSERV_FUNC(cmd_ping)
5774 char response[MAXLEN];
5775 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5776 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5777 irc_privmsg(cmd->parent->bot, channel->name, response);
5780 reply("CSMSG_PING_RESPONSE");
5784 static CHANSERV_FUNC(cmd_wut)
5788 char response[MAXLEN];
5789 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5790 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5791 irc_privmsg(cmd->parent->bot, channel->name, response);
5794 reply("CSMSG_WUT_RESPONSE");
5798 static CHANSERV_FUNC(cmd_8ball)
5800 unsigned int i, j, accum;
5805 for(i=1; i<argc; i++)
5806 for(j=0; argv[i][j]; j++)
5807 accum = (accum << 5) - accum + toupper(argv[i][j]);
5808 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5811 char response[MAXLEN];
5812 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5813 irc_privmsg(cmd->parent->bot, channel->name, response);
5816 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5820 static CHANSERV_FUNC(cmd_d)
5822 unsigned long sides, count, modifier, ii, total;
5823 char response[MAXLEN], *sep;
5827 if((count = strtoul(argv[1], &sep, 10)) < 1)
5837 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5838 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5842 else if((sep[0] == '-') && isdigit(sep[1]))
5843 modifier = strtoul(sep, NULL, 10);
5844 else if((sep[0] == '+') && isdigit(sep[1]))
5845 modifier = strtoul(sep+1, NULL, 10);
5852 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5857 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5860 for(total = ii = 0; ii < count; ++ii)
5861 total += (rand() % sides) + 1;
5864 if((count > 1) || modifier)
5866 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5867 sprintf(response, fmt, total, count, sides, modifier);
5871 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5872 sprintf(response, fmt, total, sides);
5875 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5877 send_message_type(4, user, cmd->parent->bot, "%s", response);
5881 static CHANSERV_FUNC(cmd_huggle)
5883 /* CTCP must be via PRIVMSG, never notice */
5885 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5887 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5892 chanserv_adjust_limit(void *data)
5894 struct mod_chanmode change;
5895 struct chanData *cData = data;
5896 struct chanNode *channel = cData->channel;
5899 if(IsSuspended(cData))
5902 cData->limitAdjusted = now;
5903 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5904 if(cData->modes.modes_set & MODE_LIMIT)
5906 if(limit > cData->modes.new_limit)
5907 limit = cData->modes.new_limit;
5908 else if(limit == cData->modes.new_limit)
5912 mod_chanmode_init(&change);
5913 change.modes_set = MODE_LIMIT;
5914 change.new_limit = limit;
5915 mod_chanmode_announce(chanserv, channel, &change);
5919 handle_new_channel(struct chanNode *channel)
5921 struct chanData *cData;
5923 if(!(cData = channel->channel_info))
5926 if(cData->modes.modes_set || cData->modes.modes_clear)
5927 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5929 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5930 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5933 /* Welcome to my worst nightmare. Warning: Read (or modify)
5934 the code below at your own risk. */
5936 handle_join(struct modeNode *mNode)
5938 struct mod_chanmode change;
5939 struct userNode *user = mNode->user;
5940 struct chanNode *channel = mNode->channel;
5941 struct chanData *cData;
5942 struct userData *uData = NULL;
5943 struct banData *bData;
5944 struct handle_info *handle;
5945 unsigned int modes = 0, info = 0;
5948 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5951 cData = channel->channel_info;
5952 if(channel->members.used > cData->max)
5953 cData->max = channel->members.used;
5955 /* Check for bans. If they're joining through a ban, one of two
5957 * 1: Join during a netburst, by riding the break. Kick them
5958 * unless they have ops or voice in the channel.
5959 * 2: They're allowed to join through the ban (an invite in
5960 * ircu2.10, or a +e on Hybrid, or something).
5961 * If they're not joining through a ban, and the banlist is not
5962 * full, see if they're on the banlist for the channel. If so,
5965 if(user->uplink->burst && !mNode->modes)
5968 for(ii = 0; ii < channel->banlist.used; ii++)
5970 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
5972 /* Riding a netburst. Naughty. */
5973 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5979 mod_chanmode_init(&change);
5981 if(channel->banlist.used < MAXBANS)
5983 /* Not joining through a ban. */
5984 for(bData = cData->bans;
5985 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
5986 bData = bData->next);
5990 char kick_reason[MAXLEN];
5991 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5993 bData->triggered = now;
5994 if(bData != cData->bans)
5996 /* Shuffle the ban to the head of the list. */
5998 bData->next->prev = bData->prev;
6000 bData->prev->next = bData->next;
6003 bData->next = cData->bans;
6006 cData->bans->prev = bData;
6007 cData->bans = bData;
6010 change.args[0].mode = MODE_BAN;
6011 change.args[0].u.hostmask = bData->mask;
6012 mod_chanmode_announce(chanserv, channel, &change);
6013 KickChannelUser(user, channel, chanserv, kick_reason);
6018 /* ChanServ will not modify the limits in join-flooded channels.
6019 It will also skip DynLimit processing when the user (or srvx)
6020 is bursting in, because there are likely more incoming. */
6021 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6022 && !user->uplink->burst
6023 && !channel->join_flooded
6024 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6026 /* The user count has begun "bumping" into the channel limit,
6027 so set a timer to raise the limit a bit. Any previous
6028 timers are removed so three incoming users within the delay
6029 results in one limit change, not three. */
6031 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6032 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6035 if(channel->join_flooded)
6037 /* don't automatically give ops or voice during a join flood */
6039 else if(cData->lvlOpts[lvlGiveOps] == 0)
6040 modes |= MODE_CHANOP;
6041 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6042 modes |= MODE_VOICE;
6044 greeting = cData->greeting;
6045 if(user->handle_info)
6047 handle = user->handle_info;
6049 if(IsHelper(user) && !IsHelping(user))
6052 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6054 if(channel == chanserv_conf.support_channels.list[ii])
6056 HANDLE_SET_FLAG(user->handle_info, HELPING);
6062 uData = GetTrueChannelAccess(cData, handle);
6063 if(uData && !IsUserSuspended(uData))
6065 /* Ops and above were handled by the above case. */
6066 if(IsUserAutoOp(uData))
6068 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6069 modes |= MODE_CHANOP;
6070 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6071 modes |= MODE_VOICE;
6073 if(uData->access >= UL_PRESENT)
6074 cData->visited = now;
6075 if(cData->user_greeting)
6076 greeting = cData->user_greeting;
6078 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6079 && ((now - uData->seen) >= chanserv_conf.info_delay)
6087 /* If user joining normally (not during burst), apply op or voice,
6088 * and send greeting/userinfo as appropriate.
6090 if(!user->uplink->burst)
6094 if(modes & MODE_CHANOP)
6095 modes &= ~MODE_VOICE;
6096 change.args[0].mode = modes;
6097 change.args[0].u.member = mNode;
6098 mod_chanmode_announce(chanserv, channel, &change);
6101 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6103 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6109 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6111 struct mod_chanmode change;
6112 struct userData *channel;
6113 unsigned int ii, jj;
6115 if(!user->handle_info)
6118 mod_chanmode_init(&change);
6120 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6122 struct chanNode *cn;
6123 struct modeNode *mn;
6124 if(IsUserSuspended(channel)
6125 || IsSuspended(channel->channel)
6126 || !(cn = channel->channel->channel))
6129 mn = GetUserMode(cn, user);
6132 if(!IsUserSuspended(channel)
6133 && IsUserAutoInvite(channel)
6134 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6136 && !user->uplink->burst)
6137 irc_invite(chanserv, user, cn);
6141 if(channel->access >= UL_PRESENT)
6142 channel->channel->visited = now;
6144 if(IsUserAutoOp(channel))
6146 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6147 change.args[0].mode = MODE_CHANOP;
6148 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6149 change.args[0].mode = MODE_VOICE;
6151 change.args[0].mode = 0;
6152 change.args[0].u.member = mn;
6153 if(change.args[0].mode)
6154 mod_chanmode_announce(chanserv, cn, &change);
6157 channel->seen = now;
6158 channel->present = 1;
6161 for(ii = 0; ii < user->channels.used; ++ii)
6163 struct chanNode *channel = user->channels.list[ii]->channel;
6164 struct banData *ban;
6166 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6167 || !channel->channel_info
6168 || IsSuspended(channel->channel_info))
6170 for(jj = 0; jj < channel->banlist.used; ++jj)
6171 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6173 if(jj < channel->banlist.used)
6175 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6177 char kick_reason[MAXLEN];
6178 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6180 change.args[0].mode = MODE_BAN;
6181 change.args[0].u.hostmask = ban->mask;
6182 mod_chanmode_announce(chanserv, channel, &change);
6183 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6184 KickChannelUser(user, channel, chanserv, kick_reason);
6185 ban->triggered = now;
6190 if(IsSupportHelper(user))
6192 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6194 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6196 HANDLE_SET_FLAG(user->handle_info, HELPING);
6204 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6206 struct chanData *cData;
6207 struct userData *uData;
6209 cData = mn->channel->channel_info;
6210 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6213 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6215 /* Allow for a bit of padding so that the limit doesn't
6216 track the user count exactly, which could get annoying. */
6217 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6219 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6220 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6224 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6226 scan_user_presence(uData, mn->user);
6228 if (uData->access >= UL_PRESENT)
6229 cData->visited = now;
6232 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6234 unsigned int ii, jj;
6235 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6237 for(jj = 0; jj < mn->user->channels.used; ++jj)
6238 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6240 if(jj < mn->user->channels.used)
6243 if(ii == chanserv_conf.support_channels.used)
6244 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6249 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6251 struct userData *uData;
6253 if(!channel->channel_info || !kicker || IsService(kicker)
6254 || (kicker == victim) || IsSuspended(channel->channel_info)
6255 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6258 if(protect_user(victim, kicker, channel->channel_info))
6260 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6261 KickChannelUser(kicker, channel, chanserv, reason);
6264 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6269 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6271 struct chanData *cData;
6273 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6276 cData = channel->channel_info;
6277 if(bad_topic(channel, user, channel->topic))
6279 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6280 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6281 SetChannelTopic(channel, chanserv, old_topic, 1);
6282 else if(cData->topic)
6283 SetChannelTopic(channel, chanserv, cData->topic, 1);
6286 /* With topicsnarf, grab the topic and save it as the default topic. */
6287 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6290 cData->topic = strdup(channel->topic);
6296 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6298 struct mod_chanmode *bounce = NULL;
6299 unsigned int bnc, ii;
6302 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6305 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6306 && mode_lock_violated(&channel->channel_info->modes, change))
6308 char correct[MAXLEN];
6309 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6310 mod_chanmode_format(&channel->channel_info->modes, correct);
6311 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6313 for(ii = bnc = 0; ii < change->argc; ++ii)
6315 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6317 const struct userNode *victim = change->args[ii].u.member->user;
6318 if(!protect_user(victim, user, channel->channel_info))
6321 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6324 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6325 bounce->args[bnc].u.member = GetUserMode(channel, user);
6326 if(bounce->args[bnc].u.member)
6330 bounce->args[bnc].mode = MODE_CHANOP;
6331 bounce->args[bnc].u.member = change->args[ii].u.member;
6333 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6335 else if(change->args[ii].mode & MODE_CHANOP)
6337 const struct userNode *victim = change->args[ii].u.member->user;
6338 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6341 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6342 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6343 bounce->args[bnc].u.member = change->args[ii].u.member;
6346 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6348 const char *ban = change->args[ii].u.hostmask;
6349 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6352 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6353 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6354 bounce->args[bnc].u.hostmask = strdup(ban);
6356 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6361 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6362 mod_chanmode_announce(chanserv, channel, bounce);
6363 for(ii = 0; ii < change->argc; ++ii)
6364 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6365 free((char*)bounce->args[ii].u.hostmask);
6366 mod_chanmode_free(bounce);
6371 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6373 struct chanNode *channel;
6374 struct banData *bData;
6375 struct mod_chanmode change;
6376 unsigned int ii, jj;
6377 char kick_reason[MAXLEN];
6379 mod_chanmode_init(&change);
6381 change.args[0].mode = MODE_BAN;
6382 for(ii = 0; ii < user->channels.used; ++ii)
6384 channel = user->channels.list[ii]->channel;
6385 /* Need not check for bans if they're opped or voiced. */
6386 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6388 /* Need not check for bans unless channel registration is active. */
6389 if(!channel->channel_info || IsSuspended(channel->channel_info))
6391 /* Look for a matching ban already on the channel. */
6392 for(jj = 0; jj < channel->banlist.used; ++jj)
6393 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6395 /* Need not act if we found one. */
6396 if(jj < channel->banlist.used)
6398 /* Look for a matching ban in this channel. */
6399 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6401 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6403 change.args[0].u.hostmask = bData->mask;
6404 mod_chanmode_announce(chanserv, channel, &change);
6405 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6406 KickChannelUser(user, channel, chanserv, kick_reason);
6407 bData->triggered = now;
6408 break; /* we don't need to check any more bans in the channel */
6413 static void handle_rename(struct handle_info *handle, const char *old_handle)
6415 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6419 dict_remove2(handle_dnrs, old_handle, 1);
6420 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6421 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6426 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6428 struct userNode *h_user;
6430 if(handle->channels)
6432 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6433 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6435 while(handle->channels)
6436 del_channel_user(handle->channels, 1);
6441 handle_server_link(UNUSED_ARG(struct server *server))
6443 struct chanData *cData;
6445 for(cData = channelList; cData; cData = cData->next)
6447 if(!IsSuspended(cData))
6448 cData->may_opchan = 1;
6449 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6450 && !cData->channel->join_flooded
6451 && ((cData->channel->limit - cData->channel->members.used)
6452 < chanserv_conf.adjust_threshold))
6454 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6455 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6461 chanserv_conf_read(void)
6465 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6466 struct mod_chanmode *change;
6467 struct string_list *strlist;
6468 struct chanNode *chan;
6471 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6473 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6476 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6477 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6478 chanserv_conf.support_channels.used = 0;
6479 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6481 for(ii = 0; ii < strlist->used; ++ii)
6483 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6486 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6488 channelList_append(&chanserv_conf.support_channels, chan);
6491 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6494 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6497 chan = AddChannel(str, now, str2, NULL);
6499 channelList_append(&chanserv_conf.support_channels, chan);
6501 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6502 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6503 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6504 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6505 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6506 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6507 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6508 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6509 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6510 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6511 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6512 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6513 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6514 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6515 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6516 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6517 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6518 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6519 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6520 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6521 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6522 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6523 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6525 NickChange(chanserv, str, 0);
6526 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6527 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6528 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6529 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6530 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6531 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6532 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6533 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6534 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6535 chanserv_conf.max_owned = str ? atoi(str) : 5;
6536 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6537 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6538 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6539 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6540 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6541 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6542 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6545 safestrncpy(mode_line, str, sizeof(mode_line));
6546 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6547 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6548 && (change->argc < 2))
6550 chanserv_conf.default_modes = *change;
6551 mod_chanmode_free(change);
6553 free_string_list(chanserv_conf.set_shows);
6554 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6556 strlist = string_list_copy(strlist);
6559 static const char *list[] = {
6560 /* free form text */
6561 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6562 /* options based on user level */
6563 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6564 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6565 /* multiple choice options */
6566 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6567 /* binary options */
6568 "DynLimit", "NoDelete",
6573 strlist = alloc_string_list(ArrayLength(list)-1);
6574 for(ii=0; list[ii]; ii++)
6575 string_list_append(strlist, strdup(list[ii]));
6577 chanserv_conf.set_shows = strlist;
6578 /* We don't look things up now, in case the list refers to options
6579 * defined by modules initialized after this point. Just mark the
6580 * function list as invalid, so it will be initialized.
6582 set_shows_list.used = 0;
6583 free_string_list(chanserv_conf.eightball);
6584 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6587 strlist = string_list_copy(strlist);
6591 strlist = alloc_string_list(4);
6592 string_list_append(strlist, strdup("Yes."));
6593 string_list_append(strlist, strdup("No."));
6594 string_list_append(strlist, strdup("Maybe so."));
6596 chanserv_conf.eightball = strlist;
6597 free_string_list(chanserv_conf.old_ban_names);
6598 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6600 strlist = string_list_copy(strlist);
6602 strlist = alloc_string_list(2);
6603 chanserv_conf.old_ban_names = strlist;
6604 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6605 off_channel = str ? atoi(str) : 0;
6609 chanserv_note_type_read(const char *key, struct record_data *rd)
6612 struct note_type *ntype;
6615 if(!(obj = GET_RECORD_OBJECT(rd)))
6617 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6620 if(!(ntype = chanserv_create_note_type(key)))
6622 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6626 /* Figure out set access */
6627 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6629 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6630 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6632 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6634 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6635 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6637 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6639 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6643 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6644 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6645 ntype->set_access.min_opserv = 0;
6648 /* Figure out visibility */
6649 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6650 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6651 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6652 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6653 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6654 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6655 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6656 ntype->visible_type = NOTE_VIS_ALL;
6658 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6660 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6661 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6665 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6667 struct handle_info *handle;
6668 struct userData *uData;
6669 char *seen, *inf, *flags;
6671 unsigned short access;
6673 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6675 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6679 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6680 if(access > UL_OWNER)
6682 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6686 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6687 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6688 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6689 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6690 handle = get_handle_info(key);
6693 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6697 uData = add_channel_user(chan, handle, access, last_seen, inf);
6698 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6702 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6704 struct banData *bData;
6705 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6706 time_t set_time, triggered_time, expires_time;
6708 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6710 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6714 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6715 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6716 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6717 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6718 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6719 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6720 if (!reason || !owner)
6723 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6724 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6726 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6728 expires_time = set_time + atoi(s_duration);
6732 if(!reason || (expires_time && (expires_time < now)))
6735 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6738 static struct suspended *
6739 chanserv_read_suspended(dict_t obj)
6741 struct suspended *suspended = calloc(1, sizeof(*suspended));
6745 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6746 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6747 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6748 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6749 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6750 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6751 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6752 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6753 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6754 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6759 chanserv_channel_read(const char *key, struct record_data *hir)
6761 struct suspended *suspended;
6762 struct mod_chanmode *modes;
6763 struct chanNode *cNode;
6764 struct chanData *cData;
6765 struct dict *channel, *obj;
6766 char *str, *argv[10];
6770 channel = hir->d.object;
6772 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6775 cNode = AddChannel(key, now, NULL, NULL);
6778 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6781 cData = register_channel(cNode, str);
6784 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6788 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6790 enum levelOption lvlOpt;
6791 enum charOption chOpt;
6793 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6794 cData->flags = atoi(str);
6796 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6798 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6800 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6801 else if(levelOptions[lvlOpt].old_flag)
6803 if(cData->flags & levelOptions[lvlOpt].old_flag)
6804 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6806 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6810 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6812 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6814 cData->chOpts[chOpt] = str[0];
6817 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6819 enum levelOption lvlOpt;
6820 enum charOption chOpt;
6823 cData->flags = base64toint(str, 5);
6824 count = strlen(str += 5);
6825 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6828 if(levelOptions[lvlOpt].old_flag)
6830 if(cData->flags & levelOptions[lvlOpt].old_flag)
6831 lvl = levelOptions[lvlOpt].flag_value;
6833 lvl = levelOptions[lvlOpt].default_value;
6835 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6837 case 'c': lvl = UL_COOWNER; break;
6838 case 'm': lvl = UL_MASTER; break;
6839 case 'n': lvl = UL_OWNER+1; break;
6840 case 'o': lvl = UL_OP; break;
6841 case 'p': lvl = UL_PEON; break;
6842 case 'w': lvl = UL_OWNER; break;
6843 default: lvl = 0; break;
6845 cData->lvlOpts[lvlOpt] = lvl;
6847 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6848 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6851 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6853 suspended = chanserv_read_suspended(obj);
6854 cData->suspended = suspended;
6855 suspended->cData = cData;
6856 /* We could use suspended->expires and suspended->revoked to
6857 * set the CHANNEL_SUSPENDED flag, but we don't. */
6859 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6861 suspended = calloc(1, sizeof(*suspended));
6862 suspended->issued = 0;
6863 suspended->revoked = 0;
6864 suspended->suspender = strdup(str);
6865 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6866 suspended->expires = str ? atoi(str) : 0;
6867 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6868 suspended->reason = strdup(str ? str : "No reason");
6869 suspended->previous = NULL;
6870 cData->suspended = suspended;
6871 suspended->cData = cData;
6875 cData->flags &= ~CHANNEL_SUSPENDED;
6876 suspended = NULL; /* to squelch a warning */
6879 if(IsSuspended(cData)) {
6880 if(suspended->expires > now)
6881 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6882 else if(suspended->expires)
6883 cData->flags &= ~CHANNEL_SUSPENDED;
6886 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6887 struct mod_chanmode change;
6888 mod_chanmode_init(&change);
6890 change.args[0].mode = MODE_CHANOP;
6891 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6892 mod_chanmode_announce(chanserv, cNode, &change);
6895 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6896 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6897 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6898 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6899 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6900 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6901 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6902 cData->max = str ? atoi(str) : 0;
6903 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6904 cData->greeting = str ? strdup(str) : NULL;
6905 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6906 cData->user_greeting = str ? strdup(str) : NULL;
6907 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6908 cData->topic_mask = str ? strdup(str) : NULL;
6909 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6910 cData->topic = str ? strdup(str) : NULL;
6912 if(!IsSuspended(cData)
6913 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6914 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6915 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6916 cData->modes = *modes;
6918 cData->modes.modes_set |= MODE_REGISTERED;
6919 if(cData->modes.argc > 1)
6920 cData->modes.argc = 1;
6921 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6922 mod_chanmode_free(modes);
6925 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6926 for(it = dict_first(obj); it; it = iter_next(it))
6927 user_read_helper(iter_key(it), iter_data(it), cData);
6929 if(!cData->users && !IsProtected(cData))
6931 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6932 unregister_channel(cData, "has empty user list.");
6936 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6937 for(it = dict_first(obj); it; it = iter_next(it))
6938 ban_read_helper(iter_key(it), iter_data(it), cData);
6940 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6941 for(it = dict_first(obj); it; it = iter_next(it))
6943 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6944 struct record_data *rd = iter_data(it);
6945 const char *note, *setter;
6947 if(rd->type != RECDB_OBJECT)
6949 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6953 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6955 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6957 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6961 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6962 if(!setter) setter = "<unknown>";
6963 chanserv_add_channel_note(cData, ntype, setter, note);
6971 chanserv_dnr_read(const char *key, struct record_data *hir)
6973 const char *setter, *reason, *str;
6974 struct do_not_register *dnr;
6976 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6979 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6982 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6985 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6988 dnr = chanserv_add_dnr(key, setter, reason);
6991 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6993 dnr->set = atoi(str);
6999 chanserv_saxdb_read(struct dict *database)
7001 struct dict *section;
7004 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7005 for(it = dict_first(section); it; it = iter_next(it))
7006 chanserv_note_type_read(iter_key(it), iter_data(it));
7008 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7009 for(it = dict_first(section); it; it = iter_next(it))
7010 chanserv_channel_read(iter_key(it), iter_data(it));
7012 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7013 for(it = dict_first(section); it; it = iter_next(it))
7014 chanserv_dnr_read(iter_key(it), iter_data(it));
7020 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7022 int high_present = 0;
7023 saxdb_start_record(ctx, KEY_USERS, 1);
7024 for(; uData; uData = uData->next)
7026 if((uData->access >= UL_PRESENT) && uData->present)
7028 saxdb_start_record(ctx, uData->handle->handle, 0);
7029 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7030 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7032 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7034 saxdb_write_string(ctx, KEY_INFO, uData->info);
7035 saxdb_end_record(ctx);
7037 saxdb_end_record(ctx);
7038 return high_present;
7042 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7046 saxdb_start_record(ctx, KEY_BANS, 1);
7047 for(; bData; bData = bData->next)
7049 saxdb_start_record(ctx, bData->mask, 0);
7050 saxdb_write_int(ctx, KEY_SET, bData->set);
7051 if(bData->triggered)
7052 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7054 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7056 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7058 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7059 saxdb_end_record(ctx);
7061 saxdb_end_record(ctx);
7065 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7067 saxdb_start_record(ctx, name, 0);
7068 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7069 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7071 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7073 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7075 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7077 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7078 saxdb_end_record(ctx);
7082 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7086 enum levelOption lvlOpt;
7087 enum charOption chOpt;
7089 saxdb_start_record(ctx, channel->channel->name, 1);
7091 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7092 saxdb_write_int(ctx, KEY_MAX, channel->max);
7094 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7095 if(channel->registrar)
7096 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7097 if(channel->greeting)
7098 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7099 if(channel->user_greeting)
7100 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7101 if(channel->topic_mask)
7102 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7103 if(channel->suspended)
7104 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7106 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7107 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7108 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7109 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7110 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7112 buf[0] = channel->chOpts[chOpt];
7114 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7116 saxdb_end_record(ctx);
7118 if(channel->modes.modes_set || channel->modes.modes_clear)
7120 mod_chanmode_format(&channel->modes, buf);
7121 saxdb_write_string(ctx, KEY_MODES, buf);
7124 high_present = chanserv_write_users(ctx, channel->users);
7125 chanserv_write_bans(ctx, channel->bans);
7127 if(dict_size(channel->notes))
7131 saxdb_start_record(ctx, KEY_NOTES, 1);
7132 for(it = dict_first(channel->notes); it; it = iter_next(it))
7134 struct note *note = iter_data(it);
7135 saxdb_start_record(ctx, iter_key(it), 0);
7136 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7137 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7138 saxdb_end_record(ctx);
7140 saxdb_end_record(ctx);
7143 if(channel->ownerTransfer)
7144 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7145 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7146 saxdb_end_record(ctx);
7150 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7154 saxdb_start_record(ctx, ntype->name, 0);
7155 switch(ntype->set_access_type)
7157 case NOTE_SET_CHANNEL_ACCESS:
7158 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7160 case NOTE_SET_CHANNEL_SETTER:
7161 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7163 case NOTE_SET_PRIVILEGED: default:
7164 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7167 switch(ntype->visible_type)
7169 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7170 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7171 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7173 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7174 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7175 saxdb_end_record(ctx);
7179 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7181 struct do_not_register *dnr;
7184 for(it = dict_first(dnrs); it; it = iter_next(it))
7186 dnr = iter_data(it);
7187 saxdb_start_record(ctx, dnr->chan_name, 0);
7189 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7190 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7191 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7192 saxdb_end_record(ctx);
7197 chanserv_saxdb_write(struct saxdb_context *ctx)
7200 struct chanData *channel;
7203 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7204 for(it = dict_first(note_types); it; it = iter_next(it))
7205 chanserv_write_note_type(ctx, iter_data(it));
7206 saxdb_end_record(ctx);
7209 saxdb_start_record(ctx, KEY_DNR, 1);
7210 write_dnrs_helper(ctx, handle_dnrs);
7211 write_dnrs_helper(ctx, plain_dnrs);
7212 write_dnrs_helper(ctx, mask_dnrs);
7213 saxdb_end_record(ctx);
7216 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7217 for(channel = channelList; channel; channel = channel->next)
7218 chanserv_write_channel(ctx, channel);
7219 saxdb_end_record(ctx);
7225 chanserv_db_cleanup(void) {
7227 unreg_part_func(handle_part);
7229 unregister_channel(channelList, "terminating.");
7230 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7231 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7232 free(chanserv_conf.support_channels.list);
7233 dict_delete(handle_dnrs);
7234 dict_delete(plain_dnrs);
7235 dict_delete(mask_dnrs);
7236 dict_delete(note_types);
7237 free_string_list(chanserv_conf.eightball);
7238 free_string_list(chanserv_conf.old_ban_names);
7239 free_string_list(chanserv_conf.set_shows);
7240 free(set_shows_list.list);
7241 free(uset_shows_list.list);
7244 struct userData *helper = helperList;
7245 helperList = helperList->next;
7250 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7251 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7252 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7255 init_chanserv(const char *nick)
7257 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7258 conf_register_reload(chanserv_conf_read);
7262 reg_server_link_func(handle_server_link);
7263 reg_new_channel_func(handle_new_channel);
7264 reg_join_func(handle_join);
7265 reg_part_func(handle_part);
7266 reg_kick_func(handle_kick);
7267 reg_topic_func(handle_topic);
7268 reg_mode_change_func(handle_mode);
7269 reg_nick_change_func(handle_nick_change);
7270 reg_auth_func(handle_auth);
7273 reg_handle_rename_func(handle_rename);
7274 reg_unreg_func(handle_unreg);
7276 handle_dnrs = dict_new();
7277 dict_set_free_data(handle_dnrs, free);
7278 plain_dnrs = dict_new();
7279 dict_set_free_data(plain_dnrs, free);
7280 mask_dnrs = dict_new();
7281 dict_set_free_data(mask_dnrs, free);
7283 reg_svccmd_unbind_func(handle_svccmd_unbind);
7284 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7285 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7286 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7287 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7288 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7289 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7290 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7291 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7292 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7294 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7295 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7297 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7298 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7299 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7300 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7301 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7303 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7304 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7305 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7306 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7307 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7309 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7310 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7311 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7312 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7314 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7315 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7316 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7317 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7318 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7319 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7320 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7321 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7323 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7324 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7325 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7326 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7327 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7328 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7329 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7330 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7331 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7332 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7333 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7334 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7335 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7336 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7338 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7339 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7340 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7341 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7342 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7344 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7345 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7347 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7348 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7349 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7350 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7351 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7352 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7353 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7354 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7355 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7356 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7357 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7359 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7360 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7362 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7363 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7364 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7365 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7367 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7368 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7369 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7370 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7371 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7373 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7374 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7375 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7376 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7377 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7378 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7380 /* Channel options */
7381 DEFINE_CHANNEL_OPTION(defaulttopic);
7382 DEFINE_CHANNEL_OPTION(topicmask);
7383 DEFINE_CHANNEL_OPTION(greeting);
7384 DEFINE_CHANNEL_OPTION(usergreeting);
7385 DEFINE_CHANNEL_OPTION(modes);
7386 DEFINE_CHANNEL_OPTION(enfops);
7387 DEFINE_CHANNEL_OPTION(giveops);
7388 DEFINE_CHANNEL_OPTION(protect);
7389 DEFINE_CHANNEL_OPTION(enfmodes);
7390 DEFINE_CHANNEL_OPTION(enftopic);
7391 DEFINE_CHANNEL_OPTION(pubcmd);
7392 DEFINE_CHANNEL_OPTION(givevoice);
7393 DEFINE_CHANNEL_OPTION(userinfo);
7394 DEFINE_CHANNEL_OPTION(dynlimit);
7395 DEFINE_CHANNEL_OPTION(topicsnarf);
7396 DEFINE_CHANNEL_OPTION(nodelete);
7397 DEFINE_CHANNEL_OPTION(toys);
7398 DEFINE_CHANNEL_OPTION(setters);
7399 DEFINE_CHANNEL_OPTION(topicrefresh);
7400 DEFINE_CHANNEL_OPTION(ctcpusers);
7401 DEFINE_CHANNEL_OPTION(ctcpreaction);
7402 DEFINE_CHANNEL_OPTION(inviteme);
7404 DEFINE_CHANNEL_OPTION(offchannel);
7405 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7407 /* Alias set topic to set defaulttopic for compatibility. */
7408 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7411 DEFINE_USER_OPTION(noautoop);
7412 DEFINE_USER_OPTION(autoinvite);
7413 DEFINE_USER_OPTION(info);
7415 /* Alias uset autovoice to uset autoop. */
7416 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7418 note_types = dict_new();
7419 dict_set_free_data(note_types, chanserv_deref_note_type);
7422 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7423 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7424 service_register(chanserv)->trigger = '!';
7425 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7427 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7429 if(chanserv_conf.channel_expire_frequency)
7430 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7432 if(chanserv_conf.refresh_period)
7434 time_t next_refresh;
7435 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7436 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7439 reg_exit_func(chanserv_db_cleanup);
7440 message_register_table(msgtab);