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_DNR_EXPIRE_FREQ "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
103 #define KEY_OWNER_TRANSFER "owner_transfer"
106 #define KEY_LEVEL "level"
107 #define KEY_INFO "info"
108 #define KEY_SEEN "seen"
111 #define KEY_OWNER "owner"
112 #define KEY_REASON "reason"
113 #define KEY_SET "set"
114 #define KEY_DURATION "duration"
115 #define KEY_EXPIRES "expires"
116 #define KEY_TRIGGERED "triggered"
118 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
119 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
121 /* Administrative messages */
122 static const struct message_entry msgtab[] = {
123 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
125 /* Channel registration */
126 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
127 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
128 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
129 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
130 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
131 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
133 /* Do-not-register channels */
134 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
135 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
136 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b; expires %s): %s" },
137 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
138 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
139 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
140 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
141 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
142 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
143 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
144 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
146 /* Channel unregistration */
147 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
148 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
149 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
150 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
153 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
154 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
156 /* Channel merging */
157 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
158 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
159 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
160 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
161 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
163 /* Handle unregistration */
164 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
167 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
168 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
169 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
170 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
171 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
172 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
173 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
174 { "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." },
175 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
176 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
177 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
178 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
179 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
181 /* Removing yourself from a channel. */
182 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
183 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
184 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
186 /* User management */
187 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
188 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
189 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
190 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
191 { "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." },
192 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
193 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
194 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
196 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
197 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
198 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
199 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
200 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
201 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
202 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
205 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
206 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
207 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
208 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
209 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
210 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
211 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
212 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
213 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
214 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
215 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
216 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
217 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
218 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
219 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
220 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
222 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
224 /* Channel management */
225 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
226 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
227 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
229 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
230 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
231 { "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" },
232 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
233 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
234 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
235 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
237 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
238 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
239 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
240 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
241 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
242 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
243 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
244 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
245 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
246 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
247 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
248 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
249 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
250 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
251 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
252 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
253 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
254 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
255 { "CSMSG_SET_MODES", "$bModes $b %s" },
256 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
257 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
258 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
259 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
260 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
261 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
262 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
263 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
264 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
265 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
266 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
267 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
268 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
269 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
270 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
271 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
272 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
273 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
274 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
275 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
276 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
277 { "CSMSG_USET_INFO", "$bInfo $b %s" },
279 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
280 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
281 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
282 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
283 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
284 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
285 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
286 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
287 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
288 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
289 { "CSMSG_PROTECT_NONE", "No users will be protected." },
290 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
291 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
292 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
293 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
294 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
295 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
296 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
297 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
298 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
300 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
301 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
303 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
304 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
305 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
306 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
307 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
308 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
309 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
310 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
312 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
313 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
314 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
316 /* Channel userlist */
317 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
318 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
319 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
320 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
322 /* Channel note list */
323 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
324 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
325 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
326 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
327 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
328 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
329 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
330 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
331 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
332 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
333 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
334 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
335 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
336 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
337 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
339 /* Channel [un]suspension */
340 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
341 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
342 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
343 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
344 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
345 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
346 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
348 /* Access information */
349 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
350 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
351 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
352 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
353 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
354 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
355 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
356 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
357 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
358 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
359 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
360 { "CSMSG_UC_H_TITLE", "network helper" },
361 { "CSMSG_LC_H_TITLE", "support helper" },
362 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
364 /* Seen information */
365 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
366 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
367 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
368 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
370 /* Names information */
371 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
372 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
374 /* Channel information */
375 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
376 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
377 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
378 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
379 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
380 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
381 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
382 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
383 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
384 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
385 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
386 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
389 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
394 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
395 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
397 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
398 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
399 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
400 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
401 { "CSMSG_PEEK_OPS", "$bOps:$b" },
402 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
404 /* Network information */
405 { "CSMSG_NETWORK_INFO", "Network Information:" },
406 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
407 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
408 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
409 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
410 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
411 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
412 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
413 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
416 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
417 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
418 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
420 /* Channel searches */
421 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
422 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
423 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
424 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
426 /* Channel configuration */
427 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
428 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
429 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
430 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
433 { "CSMSG_USER_OPTIONS", "User Options:" },
434 { "CSMSG_USER_PROTECTED", "That user is protected." },
437 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
438 { "CSMSG_PING_RESPONSE", "Pong!" },
439 { "CSMSG_WUT_RESPONSE", "wut" },
440 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
441 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
442 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
443 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
444 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
445 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
446 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
449 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
453 /* eject_user and unban_user flags */
454 #define ACTION_KICK 0x0001
455 #define ACTION_BAN 0x0002
456 #define ACTION_ADD_BAN 0x0004
457 #define ACTION_ADD_TIMED_BAN 0x0008
458 #define ACTION_UNBAN 0x0010
459 #define ACTION_DEL_BAN 0x0020
461 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
462 #define MODELEN 40 + KEYLEN
466 #define CSFUNC_ARGS user, channel, argc, argv, cmd
468 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
469 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
470 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
471 reply("MSG_MISSING_PARAMS", argv[0]); \
475 DECLARE_LIST(dnrList, struct do_not_register *);
476 DEFINE_LIST(dnrList, struct do_not_register *);
478 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
480 struct userNode *chanserv;
483 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
484 static struct log_type *CS_LOG;
488 struct channelList support_channels;
489 struct mod_chanmode default_modes;
491 unsigned long db_backup_frequency;
492 unsigned long channel_expire_frequency;
493 unsigned long dnr_expire_frequency;
496 unsigned int adjust_delay;
497 long channel_expire_delay;
498 unsigned int nodelete_level;
500 unsigned int adjust_threshold;
501 int join_flood_threshold;
503 unsigned int greeting_length;
504 unsigned int refresh_period;
505 unsigned int giveownership_period;
507 unsigned int max_owned;
508 unsigned int max_chan_users;
509 unsigned int max_chan_bans;
510 unsigned int max_userinfo_length;
512 struct string_list *set_shows;
513 struct string_list *eightball;
514 struct string_list *old_ban_names;
516 const char *ctcp_short_ban_duration;
517 const char *ctcp_long_ban_duration;
519 const char *irc_operator_epithet;
520 const char *network_helper_epithet;
521 const char *support_helper_epithet;
526 struct userNode *user;
527 struct userNode *bot;
528 struct chanNode *channel;
530 unsigned short lowest;
531 unsigned short highest;
532 struct userData **users;
533 struct helpfile_table table;
536 enum note_access_type
538 NOTE_SET_CHANNEL_ACCESS,
539 NOTE_SET_CHANNEL_SETTER,
543 enum note_visible_type
546 NOTE_VIS_CHANNEL_USERS,
552 enum note_access_type set_access_type;
554 unsigned int min_opserv;
555 unsigned short min_ulevel;
557 enum note_visible_type visible_type;
558 unsigned int max_length;
565 struct note_type *type;
566 char setter[NICKSERV_HANDLE_LEN+1];
570 static unsigned int registered_channels;
571 static unsigned int banCount;
573 static const struct {
576 unsigned short level;
579 { "peon", "Peon", UL_PEON, '+' },
580 { "op", "Op", UL_OP, '@' },
581 { "master", "Master", UL_MASTER, '%' },
582 { "coowner", "Coowner", UL_COOWNER, '*' },
583 { "owner", "Owner", UL_OWNER, '!' },
584 { "helper", "BUG:", UL_HELPER, 'X' }
587 static const struct {
590 unsigned short default_value;
591 unsigned int old_idx;
592 unsigned int old_flag;
593 unsigned short flag_value;
595 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
596 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
597 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
598 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
599 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
600 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
601 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
602 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
603 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
604 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
605 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
608 struct charOptionValues {
611 } protectValues[] = {
612 { 'a', "CSMSG_PROTECT_ALL" },
613 { 'e', "CSMSG_PROTECT_EQUAL" },
614 { 'l', "CSMSG_PROTECT_LOWER" },
615 { 'n', "CSMSG_PROTECT_NONE" }
617 { 'd', "CSMSG_TOYS_DISABLED" },
618 { 'n', "CSMSG_TOYS_PRIVATE" },
619 { 'p', "CSMSG_TOYS_PUBLIC" }
620 }, topicRefreshValues[] = {
621 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
622 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
623 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
624 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
625 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
626 }, ctcpReactionValues[] = {
627 { 'k', "CSMSG_CTCPREACTION_KICK" },
628 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
629 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
630 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
633 static const struct {
637 unsigned int old_idx;
639 struct charOptionValues *values;
641 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
642 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
643 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
644 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
647 struct userData *helperList;
648 struct chanData *channelList;
649 static struct module *chanserv_module;
650 static unsigned int userCount;
652 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
653 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
654 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
657 user_level_from_name(const char *name, unsigned short clamp_level)
659 unsigned int level = 0, ii;
661 level = strtoul(name, NULL, 10);
662 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
663 if(!irccasecmp(name, accessLevels[ii].name))
664 level = accessLevels[ii].level;
665 if(level > clamp_level)
671 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
674 *minl = strtoul(arg, &sep, 10);
682 *maxl = strtoul(sep+1, &sep, 10);
690 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
692 struct userData *uData, **head;
694 if(!channel || !handle)
697 if(override && HANDLE_FLAGGED(handle, HELPING)
698 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
700 for(uData = helperList;
701 uData && uData->handle != handle;
702 uData = uData->next);
706 uData = calloc(1, sizeof(struct userData));
707 uData->handle = handle;
709 uData->access = UL_HELPER;
715 uData->next = helperList;
717 helperList->prev = uData;
725 for(uData = channel->users; uData; uData = uData->next)
726 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
729 head = &(channel->users);
732 if(uData && (uData != *head))
734 /* Shuffle the user to the head of whatever list he was in. */
736 uData->next->prev = uData->prev;
738 uData->prev->next = uData->next;
744 (**head).prev = uData;
751 /* Returns non-zero if user has at least the minimum access.
752 * exempt_owner is set when handling !set, so the owner can set things
755 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
757 struct userData *uData;
758 struct chanData *cData = channel->channel_info;
759 unsigned short minimum = cData->lvlOpts[opt];
762 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
765 if(minimum <= uData->access)
767 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
772 /* Scan for other users authenticated to the same handle
773 still in the channel. If so, keep them listed as present.
775 user is optional, if not null, it skips checking that userNode
776 (for the handle_part function) */
778 scan_user_presence(struct userData *uData, struct userNode *user)
782 if(IsSuspended(uData->channel)
783 || IsUserSuspended(uData)
784 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
796 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
798 unsigned int eflags, argc;
800 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
802 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
803 if(!channel->channel_info
804 || IsSuspended(channel->channel_info)
806 || !ircncasecmp(text, "ACTION ", 7))
808 /* Figure out the minimum level needed to CTCP the channel */
809 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
811 /* We need to enforce against them; do so. */
813 argv[0] = (char*)text;
814 argv[1] = user->nick;
816 if(GetUserMode(channel, user))
817 eflags |= ACTION_KICK;
818 switch(channel->channel_info->chOpts[chCTCPReaction]) {
819 default: case 'k': /* just do the kick */ break;
821 eflags |= ACTION_BAN;
824 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
825 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
828 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
829 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
832 argv[argc++] = bad_ctcp_reason;
833 eject_user(chanserv, channel, argc, argv, NULL, eflags);
837 chanserv_create_note_type(const char *name)
839 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
840 strcpy(ntype->name, name);
842 dict_insert(note_types, ntype->name, ntype);
847 chanserv_deref_note_type(void *data)
849 struct note_type *ntype = data;
851 if(--ntype->refs > 0)
857 chanserv_flush_note_type(struct note_type *ntype)
859 struct chanData *cData;
860 for(cData = channelList; cData; cData = cData->next)
861 dict_remove(cData->notes, ntype->name);
865 chanserv_truncate_notes(struct note_type *ntype)
867 struct chanData *cData;
869 unsigned int size = sizeof(*note) + ntype->max_length;
871 for(cData = channelList; cData; cData = cData->next) {
872 note = dict_find(cData->notes, ntype->name, NULL);
875 if(strlen(note->note) <= ntype->max_length)
877 dict_remove2(cData->notes, ntype->name, 1);
878 note = realloc(note, size);
879 note->note[ntype->max_length] = 0;
880 dict_insert(cData->notes, ntype->name, note);
884 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
887 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
890 unsigned int len = strlen(text);
892 if(len > type->max_length) len = type->max_length;
893 note = calloc(1, sizeof(*note) + len);
895 strncpy(note->setter, setter, sizeof(note->setter)-1);
896 memcpy(note->note, text, len);
898 dict_insert(channel->notes, type->name, note);
904 chanserv_free_note(void *data)
906 struct note *note = data;
908 chanserv_deref_note_type(note->type);
909 assert(note->type->refs > 0); /* must use delnote to remove the type */
913 static MODCMD_FUNC(cmd_createnote) {
914 struct note_type *ntype;
915 unsigned int arg = 1, existed = 0, max_length;
917 if((ntype = dict_find(note_types, argv[1], NULL)))
920 ntype = chanserv_create_note_type(argv[arg]);
921 if(!irccasecmp(argv[++arg], "privileged"))
924 ntype->set_access_type = NOTE_SET_PRIVILEGED;
925 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
927 else if(!irccasecmp(argv[arg], "channel"))
929 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
932 reply("CSMSG_INVALID_ACCESS", argv[arg]);
935 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
936 ntype->set_access.min_ulevel = ulvl;
938 else if(!irccasecmp(argv[arg], "setter"))
940 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
944 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
948 if(!irccasecmp(argv[++arg], "privileged"))
949 ntype->visible_type = NOTE_VIS_PRIVILEGED;
950 else if(!irccasecmp(argv[arg], "channel_users"))
951 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
952 else if(!irccasecmp(argv[arg], "all"))
953 ntype->visible_type = NOTE_VIS_ALL;
955 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
959 if((arg+1) >= argc) {
960 reply("MSG_MISSING_PARAMS", argv[0]);
963 max_length = strtoul(argv[++arg], NULL, 0);
964 if(max_length < 20 || max_length > 450)
966 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
969 if(existed && (max_length < ntype->max_length))
971 ntype->max_length = max_length;
972 chanserv_truncate_notes(ntype);
974 ntype->max_length = max_length;
977 reply("CSMSG_NOTE_MODIFIED", ntype->name);
979 reply("CSMSG_NOTE_CREATED", ntype->name);
984 dict_remove(note_types, ntype->name);
988 static MODCMD_FUNC(cmd_removenote) {
989 struct note_type *ntype;
992 ntype = dict_find(note_types, argv[1], NULL);
993 force = (argc > 2) && !irccasecmp(argv[2], "force");
996 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1003 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1006 chanserv_flush_note_type(ntype);
1008 dict_remove(note_types, argv[1]);
1009 reply("CSMSG_NOTE_DELETED", argv[1]);
1014 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1018 if(orig->modes_set & change->modes_clear)
1020 if(orig->modes_clear & change->modes_set)
1022 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1023 && strcmp(orig->new_key, change->new_key))
1025 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1026 && (orig->new_limit != change->new_limit))
1031 static char max_length_text[MAXLEN+1][16];
1033 static struct helpfile_expansion
1034 chanserv_expand_variable(const char *variable)
1036 struct helpfile_expansion exp;
1038 if(!irccasecmp(variable, "notes"))
1041 exp.type = HF_TABLE;
1042 exp.value.table.length = 1;
1043 exp.value.table.width = 3;
1044 exp.value.table.flags = 0;
1045 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1046 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1047 exp.value.table.contents[0][0] = "Note Type";
1048 exp.value.table.contents[0][1] = "Visibility";
1049 exp.value.table.contents[0][2] = "Max Length";
1050 for(it=dict_first(note_types); it; it=iter_next(it))
1052 struct note_type *ntype = iter_data(it);
1055 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1056 row = exp.value.table.length++;
1057 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1058 exp.value.table.contents[row][0] = ntype->name;
1059 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1060 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1062 if(!max_length_text[ntype->max_length][0])
1063 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1064 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1069 exp.type = HF_STRING;
1070 exp.value.str = NULL;
1074 static struct chanData*
1075 register_channel(struct chanNode *cNode, char *registrar)
1077 struct chanData *channel;
1078 enum levelOption lvlOpt;
1079 enum charOption chOpt;
1081 channel = calloc(1, sizeof(struct chanData));
1083 channel->notes = dict_new();
1084 dict_set_free_data(channel->notes, chanserv_free_note);
1086 channel->registrar = strdup(registrar);
1087 channel->registered = now;
1088 channel->visited = now;
1089 channel->limitAdjusted = now;
1090 channel->ownerTransfer = now;
1091 channel->flags = CHANNEL_DEFAULT_FLAGS;
1092 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1093 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1094 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1095 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1097 channel->prev = NULL;
1098 channel->next = channelList;
1101 channelList->prev = channel;
1102 channelList = channel;
1103 registered_channels++;
1105 channel->channel = cNode;
1107 cNode->channel_info = channel;
1112 static struct userData*
1113 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1115 struct userData *ud;
1117 if(access > UL_OWNER)
1120 ud = calloc(1, sizeof(*ud));
1121 ud->channel = channel;
1122 ud->handle = handle;
1124 ud->access = access;
1125 ud->info = info ? strdup(info) : NULL;
1128 ud->next = channel->users;
1130 channel->users->prev = ud;
1131 channel->users = ud;
1133 channel->userCount++;
1137 ud->u_next = ud->handle->channels;
1139 ud->u_next->u_prev = ud;
1140 ud->handle->channels = ud;
1145 static void unregister_channel(struct chanData *channel, const char *reason);
1148 del_channel_user(struct userData *user, int do_gc)
1150 struct chanData *channel = user->channel;
1152 channel->userCount--;
1156 user->prev->next = user->next;
1158 channel->users = user->next;
1160 user->next->prev = user->prev;
1163 user->u_prev->u_next = user->u_next;
1165 user->handle->channels = user->u_next;
1167 user->u_next->u_prev = user->u_prev;
1171 if(do_gc && !channel->users && !IsProtected(channel))
1172 unregister_channel(channel, "lost all users.");
1175 static void expire_ban(void *data);
1177 static struct banData*
1178 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1181 unsigned int ii, l1, l2;
1186 bd = malloc(sizeof(struct banData));
1188 bd->channel = channel;
1190 bd->triggered = triggered;
1191 bd->expires = expires;
1193 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1195 extern const char *hidden_host_suffix;
1196 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1200 l2 = strlen(old_name);
1203 if(irccasecmp(mask + l1 - l2, old_name))
1205 new_mask = alloca(MAXLEN);
1206 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1209 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1211 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1212 bd->reason = strdup(reason);
1215 timeq_add(expires, expire_ban, bd);
1218 bd->next = channel->bans;
1220 channel->bans->prev = bd;
1222 channel->banCount++;
1229 del_channel_ban(struct banData *ban)
1231 ban->channel->banCount--;
1235 ban->prev->next = ban->next;
1237 ban->channel->bans = ban->next;
1240 ban->next->prev = ban->prev;
1243 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1252 expire_ban(void *data)
1254 struct banData *bd = data;
1255 if(!IsSuspended(bd->channel))
1257 struct banList bans;
1258 struct mod_chanmode change;
1260 bans = bd->channel->channel->banlist;
1261 mod_chanmode_init(&change);
1262 for(ii=0; ii<bans.used; ii++)
1264 if(!strcmp(bans.list[ii]->ban, bd->mask))
1267 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1268 change.args[0].u.hostmask = bd->mask;
1269 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1275 del_channel_ban(bd);
1278 static void chanserv_expire_suspension(void *data);
1281 unregister_channel(struct chanData *channel, const char *reason)
1283 struct mod_chanmode change;
1284 char msgbuf[MAXLEN];
1286 /* After channel unregistration, the following must be cleaned
1288 - Channel information.
1291 - Channel suspension data.
1292 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1298 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1302 mod_chanmode_init(&change);
1303 change.modes_clear |= MODE_REGISTERED;
1304 mod_chanmode_announce(chanserv, channel->channel, &change);
1307 while(channel->users)
1308 del_channel_user(channel->users, 0);
1310 while(channel->bans)
1311 del_channel_ban(channel->bans);
1313 free(channel->topic);
1314 free(channel->registrar);
1315 free(channel->greeting);
1316 free(channel->user_greeting);
1317 free(channel->topic_mask);
1320 channel->prev->next = channel->next;
1322 channelList = channel->next;
1325 channel->next->prev = channel->prev;
1327 if(channel->suspended)
1329 struct chanNode *cNode = channel->channel;
1330 struct suspended *suspended, *next_suspended;
1332 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1334 next_suspended = suspended->previous;
1335 free(suspended->suspender);
1336 free(suspended->reason);
1337 if(suspended->expires)
1338 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1343 cNode->channel_info = NULL;
1345 channel->channel->channel_info = NULL;
1347 dict_delete(channel->notes);
1348 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1349 if(!IsSuspended(channel))
1350 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1351 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1352 UnlockChannel(channel->channel);
1354 registered_channels--;
1358 expire_channels(UNUSED_ARG(void *data))
1360 struct chanData *channel, *next;
1361 struct userData *user;
1362 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1364 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1365 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1367 for(channel = channelList; channel; channel = next)
1369 next = channel->next;
1371 /* See if the channel can be expired. */
1372 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1373 || IsProtected(channel))
1376 /* Make sure there are no high-ranking users still in the channel. */
1377 for(user=channel->users; user; user=user->next)
1378 if(user->present && (user->access >= UL_PRESENT))
1383 /* Unregister the channel */
1384 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1385 unregister_channel(channel, "registration expired.");
1388 if(chanserv_conf.channel_expire_frequency)
1389 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1393 expire_dnrs(UNUSED_ARG(void *data))
1396 struct do_not_register *dnr;
1398 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1400 dnr = iter_data(it);
1401 if(!dnr->expires || dnr->expires > now)
1403 dict_remove(handle_dnrs, dnr->chan_name + 1);
1405 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1407 dnr = iter_data(it);
1408 if(!dnr->expires || dnr->expires > now)
1410 dict_remove(plain_dnrs, dnr->chan_name);
1412 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1414 dnr = iter_data(it);
1415 if(!dnr->expires || dnr->expires > now)
1417 dict_remove(mask_dnrs, dnr->chan_name);
1420 if(chanserv_conf.dnr_expire_frequency)
1421 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1425 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1427 char protect = channel->chOpts[chProtect];
1428 struct userData *cs_victim, *cs_aggressor;
1430 /* Don't protect if no one is to be protected, someone is attacking
1431 himself, or if the aggressor is an IRC Operator. */
1432 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1435 /* Don't protect if the victim isn't authenticated (because they
1436 can't be a channel user), unless we are to protect non-users
1438 cs_victim = GetChannelAccess(channel, victim->handle_info);
1439 if(protect != 'a' && !cs_victim)
1442 /* Protect if the aggressor isn't a user because at this point,
1443 the aggressor can only be less than or equal to the victim. */
1444 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1448 /* If the aggressor was a user, then the victim can't be helped. */
1455 if(cs_victim->access > cs_aggressor->access)
1460 if(cs_victim->access >= cs_aggressor->access)
1469 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1471 struct chanData *cData = channel->channel_info;
1472 struct userData *cs_victim;
1474 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1475 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1476 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1478 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1486 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1488 if(IsService(victim))
1490 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1494 if(protect_user(victim, user, channel->channel_info))
1496 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1503 static struct do_not_register *
1504 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1506 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1507 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1508 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1509 strcpy(dnr->reason, reason);
1511 dnr->expires = expires;
1512 if(dnr->chan_name[0] == '*')
1513 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1514 else if(strpbrk(dnr->chan_name, "*?"))
1515 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1517 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1521 static struct dnrList
1522 chanserv_find_dnrs(const char *chan_name, const char *handle)
1524 struct dnrList list;
1526 struct do_not_register *dnr;
1528 dnrList_init(&list);
1529 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)) && (!dnr->expires || dnr->expires > now))
1530 dnrList_append(&list, dnr);
1531 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)) && (!dnr->expires || dnr->expires > now))
1532 dnrList_append(&list, dnr);
1534 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1535 if(match_ircglob(chan_name, iter_key(it)) && (!dnr->expires || dnr->expires > now))
1536 dnrList_append(&list, iter_data(it));
1541 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1543 struct dnrList list;
1544 struct do_not_register *dnr;
1546 char buf[INTERVALLEN], buf2[INTERVALLEN];
1548 list = chanserv_find_dnrs(chan_name, handle);
1549 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1551 dnr = list.list[ii];
1552 if(dnr->expires && dnr->expires <= now)
1554 else if(dnr->expires)
1555 intervalString(buf2, dnr->expires - now, user->handle_info);
1559 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1560 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1563 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1566 reply("CSMSG_MORE_DNRS", list.used - ii);
1571 struct do_not_register *
1572 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1574 struct do_not_register *dnr;
1577 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)) && (!dnr->expires || dnr->expires > now))
1581 if((dnr = dict_find(plain_dnrs, chan_name, NULL)) && (!dnr->expires || dnr->expires > now))
1583 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1584 if(match_ircglob(chan_name, iter_key(it)) && (!dnr->expires || dnr->expires > now))
1585 return iter_data(it);
1590 static CHANSERV_FUNC(cmd_noregister)
1593 time_t expiry, duration;
1594 struct do_not_register *dnr;
1595 char buf[INTERVALLEN], buf2[INTERVALLEN];
1596 unsigned int matches;
1602 reply("CSMSG_DNR_SEARCH_RESULTS");
1604 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1606 dnr = iter_data(it);
1607 if(dnr->expires && dnr->expires <= now)
1609 else if(dnr->expires)
1610 intervalString(buf2, dnr->expires - now, user->handle_info);
1613 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1615 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1618 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1620 dnr = iter_data(it);
1621 if(dnr->expires && dnr->expires <= now)
1623 else if(dnr->expires)
1624 intervalString(buf2, dnr->expires - now, user->handle_info);
1627 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1629 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1632 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1634 dnr = iter_data(it);
1635 if(dnr->expires && dnr->expires <= now)
1637 else if(dnr->expires)
1638 intervalString(buf2, dnr->expires - now, user->handle_info);
1641 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1643 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1648 reply("MSG_MATCH_COUNT", matches);
1650 reply("MSG_NO_MATCHES");
1656 if(!IsChannelName(target) && (*target != '*'))
1658 reply("CSMSG_NOT_DNR", target);
1666 reply("MSG_INVALID_DURATION", argv[2]);
1670 if(!strcmp(argv[2], "0"))
1672 else if((duration = ParseInterval(argv[2])))
1673 expiry = now + duration;
1676 reply("MSG_INVALID_DURATION", argv[2]);
1680 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1681 if((*target == '*') && !get_handle_info(target + 1))
1683 reply("MSG_HANDLE_UNKNOWN", target + 1);
1686 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1687 reply("CSMSG_NOREGISTER_CHANNEL", target);
1691 reply("CSMSG_DNR_SEARCH_RESULTS");
1693 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1695 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1697 reply("MSG_NO_MATCHES");
1701 static CHANSERV_FUNC(cmd_allowregister)
1703 const char *chan_name = argv[1];
1705 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1707 dict_remove(handle_dnrs, chan_name+1);
1708 reply("CSMSG_DNR_REMOVED", chan_name);
1710 else if(dict_find(plain_dnrs, chan_name, NULL))
1712 dict_remove(plain_dnrs, chan_name);
1713 reply("CSMSG_DNR_REMOVED", chan_name);
1715 else if(dict_find(mask_dnrs, chan_name, NULL))
1717 dict_remove(mask_dnrs, chan_name);
1718 reply("CSMSG_DNR_REMOVED", chan_name);
1722 reply("CSMSG_NO_SUCH_DNR", chan_name);
1729 chanserv_get_owned_count(struct handle_info *hi)
1731 struct userData *cList;
1734 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1735 if(cList->access == UL_OWNER)
1740 static CHANSERV_FUNC(cmd_register)
1742 struct handle_info *handle;
1743 struct chanData *cData;
1744 struct modeNode *mn;
1745 char reason[MAXLEN];
1747 unsigned int new_channel, force=0;
1748 struct do_not_register *dnr;
1752 if(channel->channel_info)
1754 reply("CSMSG_ALREADY_REGGED", channel->name);
1758 if(channel->bad_channel)
1760 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1765 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1767 reply("CSMSG_MUST_BE_OPPED", channel->name);
1772 chan_name = channel->name;
1776 if((argc < 2) || !IsChannelName(argv[1]))
1778 reply("MSG_NOT_CHANNEL_NAME");
1782 if(opserv_bad_channel(argv[1]))
1784 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1789 chan_name = argv[1];
1792 if(argc >= (new_channel+2))
1794 if(!IsHelping(user))
1796 reply("CSMSG_PROXY_FORBIDDEN");
1800 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1802 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1803 dnr = chanserv_is_dnr(chan_name, handle);
1807 handle = user->handle_info;
1808 dnr = chanserv_is_dnr(chan_name, handle);
1812 if(!IsHelping(user))
1813 reply("CSMSG_DNR_CHANNEL", chan_name);
1815 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1819 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1821 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1826 channel = AddChannel(argv[1], now, NULL, NULL);
1828 cData = register_channel(channel, user->handle_info->handle);
1829 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1830 cData->modes = chanserv_conf.default_modes;
1832 cData->modes.modes_set |= MODE_REGISTERED;
1833 if (IsOffChannel(cData))
1835 mod_chanmode_announce(chanserv, channel, &cData->modes);
1839 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1840 change->args[change->argc].mode = MODE_CHANOP;
1841 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1843 mod_chanmode_announce(chanserv, channel, change);
1844 mod_chanmode_free(change);
1847 /* Initialize the channel's max user record. */
1848 cData->max = channel->members.used;
1850 if(handle != user->handle_info)
1851 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1853 reply("CSMSG_REG_SUCCESS", channel->name);
1855 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1856 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1861 make_confirmation_string(struct userData *uData)
1863 static char strbuf[16];
1868 for(src = uData->handle->handle; *src; )
1869 accum = accum * 31 + toupper(*src++);
1871 for(src = uData->channel->channel->name; *src; )
1872 accum = accum * 31 + toupper(*src++);
1873 sprintf(strbuf, "%08x", accum);
1877 static CHANSERV_FUNC(cmd_unregister)
1880 char reason[MAXLEN];
1881 struct chanData *cData;
1882 struct userData *uData;
1884 cData = channel->channel_info;
1887 reply("CSMSG_NOT_REGISTERED", channel->name);
1891 uData = GetChannelUser(cData, user->handle_info);
1892 if(!uData || (uData->access < UL_OWNER))
1894 reply("CSMSG_NO_ACCESS");
1898 if(IsProtected(cData))
1900 reply("CSMSG_UNREG_NODELETE", channel->name);
1904 if(!IsHelping(user))
1906 const char *confirm_string;
1907 if(IsSuspended(cData))
1909 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1912 confirm_string = make_confirmation_string(uData);
1913 if((argc < 2) || strcmp(argv[1], confirm_string))
1915 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1920 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1921 name = strdup(channel->name);
1922 unregister_channel(cData, reason);
1923 reply("CSMSG_UNREG_SUCCESS", name);
1928 static CHANSERV_FUNC(cmd_move)
1930 struct mod_chanmode change;
1931 struct chanNode *target;
1932 struct modeNode *mn;
1933 struct userData *uData;
1934 char reason[MAXLEN];
1935 struct do_not_register *dnr;
1939 if(IsProtected(channel->channel_info))
1941 reply("CSMSG_MOVE_NODELETE", channel->name);
1945 if(!IsChannelName(argv[1]))
1947 reply("MSG_NOT_CHANNEL_NAME");
1951 if(opserv_bad_channel(argv[1]))
1953 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1957 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1959 for(uData = channel->channel_info->users; uData; uData = uData->next)
1961 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1963 if(!IsHelping(user))
1964 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1966 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1972 mod_chanmode_init(&change);
1973 if(!(target = GetChannel(argv[1])))
1975 target = AddChannel(argv[1], now, NULL, NULL);
1976 if(!IsSuspended(channel->channel_info))
1977 AddChannelUser(chanserv, target);
1979 else if(target->channel_info)
1981 reply("CSMSG_ALREADY_REGGED", target->name);
1984 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1985 && !IsHelping(user))
1987 reply("CSMSG_MUST_BE_OPPED", target->name);
1990 else if(!IsSuspended(channel->channel_info))
1993 change.args[0].mode = MODE_CHANOP;
1994 change.args[0].u.member = AddChannelUser(chanserv, target);
1995 mod_chanmode_announce(chanserv, target, &change);
2000 /* Clear MODE_REGISTERED from old channel, add it to new. */
2002 change.modes_clear = MODE_REGISTERED;
2003 mod_chanmode_announce(chanserv, channel, &change);
2004 change.modes_clear = 0;
2005 change.modes_set = MODE_REGISTERED;
2006 mod_chanmode_announce(chanserv, target, &change);
2009 /* Move the channel_info to the target channel; it
2010 shouldn't be necessary to clear timeq callbacks
2011 for the old channel. */
2012 target->channel_info = channel->channel_info;
2013 target->channel_info->channel = target;
2014 channel->channel_info = NULL;
2016 reply("CSMSG_MOVE_SUCCESS", target->name);
2018 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2019 if(!IsSuspended(target->channel_info))
2021 char reason2[MAXLEN];
2022 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2023 DelChannelUser(chanserv, channel, reason2, 0);
2025 UnlockChannel(channel);
2026 LockChannel(target);
2027 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2032 merge_users(struct chanData *source, struct chanData *target)
2034 struct userData *suData, *tuData, *next;
2040 /* Insert the source's users into the scratch area. */
2041 for(suData = source->users; suData; suData = suData->next)
2042 dict_insert(merge, suData->handle->handle, suData);
2044 /* Iterate through the target's users, looking for
2045 users common to both channels. The lower access is
2046 removed from either the scratch area or target user
2048 for(tuData = target->users; tuData; tuData = next)
2050 struct userData *choice;
2052 next = tuData->next;
2054 /* If a source user exists with the same handle as a target
2055 channel's user, resolve the conflict by removing one. */
2056 suData = dict_find(merge, tuData->handle->handle, NULL);
2060 /* Pick the data we want to keep. */
2061 /* If the access is the same, use the later seen time. */
2062 if(suData->access == tuData->access)
2063 choice = (suData->seen > tuData->seen) ? suData : tuData;
2064 else /* Otherwise, keep the higher access level. */
2065 choice = (suData->access > tuData->access) ? suData : tuData;
2067 /* Remove the user that wasn't picked. */
2068 if(choice == tuData)
2070 dict_remove(merge, suData->handle->handle);
2071 del_channel_user(suData, 0);
2074 del_channel_user(tuData, 0);
2077 /* Move the remaining users to the target channel. */
2078 for(it = dict_first(merge); it; it = iter_next(it))
2080 suData = iter_data(it);
2082 /* Insert the user into the target channel's linked list. */
2083 suData->prev = NULL;
2084 suData->next = target->users;
2085 suData->channel = target;
2088 target->users->prev = suData;
2089 target->users = suData;
2091 /* Update the user counts for the target channel; the
2092 source counts are left alone. */
2093 target->userCount++;
2096 /* Possible to assert (source->users == NULL) here. */
2097 source->users = NULL;
2102 merge_bans(struct chanData *source, struct chanData *target)
2104 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2106 /* Hold on to the original head of the target ban list
2107 to avoid comparing source bans with source bans. */
2108 tFront = target->bans;
2110 /* Perform a totally expensive O(n*m) merge, ick. */
2111 for(sbData = source->bans; sbData; sbData = sNext)
2113 /* Flag to track whether the ban's been moved
2114 to the destination yet. */
2117 /* Possible to assert (sbData->prev == NULL) here. */
2118 sNext = sbData->next;
2120 for(tbData = tFront; tbData; tbData = tNext)
2122 tNext = tbData->next;
2124 /* Perform two comparisons between each source
2125 and target ban, conflicts are resolved by
2126 keeping the broader ban and copying the later
2127 expiration and triggered time. */
2128 if(match_ircglobs(tbData->mask, sbData->mask))
2130 /* There is a broader ban in the target channel that
2131 overrides one in the source channel; remove the
2132 source ban and break. */
2133 if(sbData->expires > tbData->expires)
2134 tbData->expires = sbData->expires;
2135 if(sbData->triggered > tbData->triggered)
2136 tbData->triggered = sbData->triggered;
2137 del_channel_ban(sbData);
2140 else if(match_ircglobs(sbData->mask, tbData->mask))
2142 /* There is a broader ban in the source channel that
2143 overrides one in the target channel; remove the
2144 target ban, fall through and move the source over. */
2145 if(tbData->expires > sbData->expires)
2146 sbData->expires = tbData->expires;
2147 if(tbData->triggered > sbData->triggered)
2148 sbData->triggered = tbData->triggered;
2149 if(tbData == tFront)
2151 del_channel_ban(tbData);
2154 /* Source bans can override multiple target bans, so
2155 we allow a source to run through this loop multiple
2156 times, but we can only move it once. */
2161 /* Remove the source ban from the source ban list. */
2163 sbData->next->prev = sbData->prev;
2165 /* Modify the source ban's associated channel. */
2166 sbData->channel = target;
2168 /* Insert the ban into the target channel's linked list. */
2169 sbData->prev = NULL;
2170 sbData->next = target->bans;
2173 target->bans->prev = sbData;
2174 target->bans = sbData;
2176 /* Update the user counts for the target channel. */
2181 /* Possible to assert (source->bans == NULL) here. */
2182 source->bans = NULL;
2186 merge_data(struct chanData *source, struct chanData *target)
2188 /* Use more recent visited and owner-transfer time; use older
2189 * registered time. Bitwise or may_opchan. Use higher max.
2190 * Do not touch last_refresh, ban count or user counts.
2192 if(source->visited > target->visited)
2193 target->visited = source->visited;
2194 if(source->registered < target->registered)
2195 target->registered = source->registered;
2196 if(source->ownerTransfer > target->ownerTransfer)
2197 target->ownerTransfer = source->ownerTransfer;
2198 if(source->may_opchan)
2199 target->may_opchan = 1;
2200 if(source->max > target->max)
2201 target->max = source->max;
2205 merge_channel(struct chanData *source, struct chanData *target)
2207 merge_users(source, target);
2208 merge_bans(source, target);
2209 merge_data(source, target);
2212 static CHANSERV_FUNC(cmd_merge)
2214 struct userData *target_user;
2215 struct chanNode *target;
2216 char reason[MAXLEN];
2220 /* Make sure the target channel exists and is registered to the user
2221 performing the command. */
2222 if(!(target = GetChannel(argv[1])))
2224 reply("MSG_INVALID_CHANNEL");
2228 if(!target->channel_info)
2230 reply("CSMSG_NOT_REGISTERED", target->name);
2234 if(IsProtected(channel->channel_info))
2236 reply("CSMSG_MERGE_NODELETE");
2240 if(IsSuspended(target->channel_info))
2242 reply("CSMSG_MERGE_SUSPENDED");
2246 if(channel == target)
2248 reply("CSMSG_MERGE_SELF");
2252 target_user = GetChannelUser(target->channel_info, user->handle_info);
2253 if(!target_user || (target_user->access < UL_OWNER))
2255 reply("CSMSG_MERGE_NOT_OWNER");
2259 /* Merge the channel structures and associated data. */
2260 merge_channel(channel->channel_info, target->channel_info);
2261 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2262 unregister_channel(channel->channel_info, reason);
2263 reply("CSMSG_MERGE_SUCCESS", target->name);
2267 static CHANSERV_FUNC(cmd_opchan)
2269 struct mod_chanmode change;
2270 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2272 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2275 channel->channel_info->may_opchan = 0;
2276 mod_chanmode_init(&change);
2278 change.args[0].mode = MODE_CHANOP;
2279 change.args[0].u.member = GetUserMode(channel, chanserv);
2280 mod_chanmode_announce(chanserv, channel, &change);
2281 reply("CSMSG_OPCHAN_DONE", channel->name);
2285 static CHANSERV_FUNC(cmd_adduser)
2287 struct userData *actee;
2288 struct userData *actor, *real_actor;
2289 struct handle_info *handle;
2290 unsigned short access, override = 0;
2294 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2296 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2300 access = user_level_from_name(argv[2], UL_OWNER);
2303 reply("CSMSG_INVALID_ACCESS", argv[2]);
2307 actor = GetChannelUser(channel->channel_info, user->handle_info);
2308 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2310 if(actor->access <= access)
2312 reply("CSMSG_NO_BUMP_ACCESS");
2316 /* Trying to add someone with equal/more access? */
2317 if (!real_actor || real_actor->access <= access)
2318 override = CMD_LOG_OVERRIDE;
2320 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2323 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2325 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2329 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2330 scan_user_presence(actee, NULL);
2331 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2332 return 1 | override;
2335 static CHANSERV_FUNC(cmd_clvl)
2337 struct handle_info *handle;
2338 struct userData *victim;
2339 struct userData *actor, *real_actor;
2340 unsigned short new_access, override = 0;
2341 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2345 actor = GetChannelUser(channel->channel_info, user->handle_info);
2346 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2348 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2351 if(handle == user->handle_info && !privileged)
2353 reply("CSMSG_NO_SELF_CLVL");
2357 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2359 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2363 if(actor->access <= victim->access && !privileged)
2365 reply("MSG_USER_OUTRANKED", handle->handle);
2369 new_access = user_level_from_name(argv[2], UL_OWNER);
2373 reply("CSMSG_INVALID_ACCESS", argv[2]);
2377 if(new_access >= actor->access && !privileged)
2379 reply("CSMSG_NO_BUMP_ACCESS");
2383 /* Trying to clvl a equal/higher user? */
2384 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2385 override = CMD_LOG_OVERRIDE;
2386 /* Trying to clvl someone to equal/higher access? */
2387 if(!real_actor || new_access >= real_actor->access)
2388 override = CMD_LOG_OVERRIDE;
2389 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2390 * If they lower their own access it's not a big problem.
2393 victim->access = new_access;
2394 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2395 return 1 | override;
2398 static CHANSERV_FUNC(cmd_deluser)
2400 struct handle_info *handle;
2401 struct userData *victim;
2402 struct userData *actor, *real_actor;
2403 unsigned short access, override = 0;
2408 actor = GetChannelUser(channel->channel_info, user->handle_info);
2409 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2411 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2414 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2416 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2422 access = user_level_from_name(argv[1], UL_OWNER);
2425 reply("CSMSG_INVALID_ACCESS", argv[1]);
2428 if(access != victim->access)
2430 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2436 access = victim->access;
2439 if((actor->access <= victim->access) && !IsHelping(user))
2441 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2445 /* If people delete themselves it is an override, but they
2446 * could've used deleteme so we don't log it as an override
2448 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2449 override = CMD_LOG_OVERRIDE;
2451 chan_name = strdup(channel->name);
2452 del_channel_user(victim, 1);
2453 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2455 return 1 | override;
2459 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2461 struct userData *actor, *real_actor, *uData, *next;
2462 unsigned int override = 0;
2464 actor = GetChannelUser(channel->channel_info, user->handle_info);
2465 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2467 if(min_access > max_access)
2469 reply("CSMSG_BAD_RANGE", min_access, max_access);
2473 if((actor->access <= max_access) && !IsHelping(user))
2475 reply("CSMSG_NO_ACCESS");
2479 if(!real_actor || real_actor->access <= max_access)
2480 override = CMD_LOG_OVERRIDE;
2482 for(uData = channel->channel_info->users; uData; uData = next)
2486 if((uData->access >= min_access)
2487 && (uData->access <= max_access)
2488 && match_ircglob(uData->handle->handle, mask))
2489 del_channel_user(uData, 1);
2492 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2493 return 1 | override;
2496 static CHANSERV_FUNC(cmd_mdelowner)
2498 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2501 static CHANSERV_FUNC(cmd_mdelcoowner)
2503 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2506 static CHANSERV_FUNC(cmd_mdelmaster)
2508 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2511 static CHANSERV_FUNC(cmd_mdelop)
2513 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2516 static CHANSERV_FUNC(cmd_mdelpeon)
2518 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2522 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2524 struct banData *bData, *next;
2525 char interval[INTERVALLEN];
2530 limit = now - duration;
2531 for(bData = channel->channel_info->bans; bData; bData = next)
2535 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2538 del_channel_ban(bData);
2542 intervalString(interval, duration, user->handle_info);
2543 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2548 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2550 struct userData *actor, *uData, *next;
2551 char interval[INTERVALLEN];
2555 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2556 if(min_access > max_access)
2558 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2562 if(!actor || actor->access <= max_access)
2564 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2569 limit = now - duration;
2570 for(uData = channel->channel_info->users; uData; uData = next)
2574 if((uData->seen > limit)
2576 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2579 if(((uData->access >= min_access) && (uData->access <= max_access))
2580 || (!max_access && (uData->access < actor->access)))
2582 del_channel_user(uData, 1);
2590 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2592 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2596 static CHANSERV_FUNC(cmd_trim)
2598 unsigned long duration;
2599 unsigned short min_level, max_level;
2604 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2605 duration = ParseInterval(argv[2]);
2608 reply("CSMSG_CANNOT_TRIM");
2612 if(!irccasecmp(argv[1], "bans"))
2614 cmd_trim_bans(user, channel, duration);
2617 else if(!irccasecmp(argv[1], "users"))
2619 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2622 else if(parse_level_range(&min_level, &max_level, argv[1]))
2624 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2627 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2629 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2634 reply("CSMSG_INVALID_TRIM", argv[1]);
2639 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2640 to the user. cmd_all takes advantage of this. */
2641 static CHANSERV_FUNC(cmd_up)
2643 struct mod_chanmode change;
2644 struct userData *uData;
2647 mod_chanmode_init(&change);
2649 change.args[0].u.member = GetUserMode(channel, user);
2650 if(!change.args[0].u.member)
2653 reply("MSG_CHANNEL_ABSENT", channel->name);
2657 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2661 reply("CSMSG_GODMODE_UP", argv[0]);
2664 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2666 change.args[0].mode = MODE_CHANOP;
2667 errmsg = "CSMSG_ALREADY_OPPED";
2669 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2671 change.args[0].mode = MODE_VOICE;
2672 errmsg = "CSMSG_ALREADY_VOICED";
2677 reply("CSMSG_NO_ACCESS");
2680 change.args[0].mode &= ~change.args[0].u.member->modes;
2681 if(!change.args[0].mode)
2684 reply(errmsg, channel->name);
2687 modcmd_chanmode_announce(&change);
2691 static CHANSERV_FUNC(cmd_down)
2693 struct mod_chanmode change;
2695 mod_chanmode_init(&change);
2697 change.args[0].u.member = GetUserMode(channel, user);
2698 if(!change.args[0].u.member)
2701 reply("MSG_CHANNEL_ABSENT", channel->name);
2705 if(!change.args[0].u.member->modes)
2708 reply("CSMSG_ALREADY_DOWN", channel->name);
2712 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2713 modcmd_chanmode_announce(&change);
2717 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)
2719 struct userData *cList;
2721 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2723 if(IsSuspended(cList->channel)
2724 || IsUserSuspended(cList)
2725 || !GetUserMode(cList->channel->channel, user))
2728 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2734 static CHANSERV_FUNC(cmd_upall)
2736 return cmd_all(CSFUNC_ARGS, cmd_up);
2739 static CHANSERV_FUNC(cmd_downall)
2741 return cmd_all(CSFUNC_ARGS, cmd_down);
2744 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2745 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2748 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)
2750 unsigned int ii, valid;
2751 struct userNode *victim;
2752 struct mod_chanmode *change;
2754 change = mod_chanmode_alloc(argc - 1);
2756 for(ii=valid=0; ++ii < argc; )
2758 if(!(victim = GetUserH(argv[ii])))
2760 change->args[valid].mode = mode;
2761 change->args[valid].u.member = GetUserMode(channel, victim);
2762 if(!change->args[valid].u.member)
2764 if(validate && !validate(user, channel, victim))
2769 change->argc = valid;
2770 if(valid < (argc-1))
2771 reply("CSMSG_PROCESS_FAILED");
2774 modcmd_chanmode_announce(change);
2775 reply(action, channel->name);
2777 mod_chanmode_free(change);
2781 static CHANSERV_FUNC(cmd_op)
2783 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2786 static CHANSERV_FUNC(cmd_deop)
2788 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2791 static CHANSERV_FUNC(cmd_voice)
2793 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2796 static CHANSERV_FUNC(cmd_devoice)
2798 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2802 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2808 for(ii=0; ii<channel->members.used; ii++)
2810 struct modeNode *mn = channel->members.list[ii];
2812 if(IsService(mn->user))
2815 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2818 if(protect_user(mn->user, user, channel->channel_info))
2822 victims[(*victimCount)++] = mn;
2828 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2830 struct userNode *victim;
2831 struct modeNode **victims;
2832 unsigned int offset, n, victimCount, duration = 0;
2833 char *reason = "Bye.", *ban, *name;
2834 char interval[INTERVALLEN];
2836 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2837 REQUIRE_PARAMS(offset);
2840 reason = unsplit_string(argv + offset, argc - offset, NULL);
2841 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2843 /* Truncate the reason to a length of TOPICLEN, as
2844 the ircd does; however, leave room for an ellipsis
2845 and the kicker's nick. */
2846 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2850 if((victim = GetUserH(argv[1])))
2852 victims = alloca(sizeof(victims[0]));
2853 victims[0] = GetUserMode(channel, victim);
2854 /* XXX: The comparison with ACTION_KICK is just because all
2855 * other actions can work on users outside the channel, and we
2856 * want to allow those (e.g. unbans) in that case. If we add
2857 * some other ejection action for in-channel users, change
2859 victimCount = victims[0] ? 1 : 0;
2861 if(IsService(victim))
2863 reply("MSG_SERVICE_IMMUNE", victim->nick);
2867 if((action == ACTION_KICK) && !victimCount)
2869 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2873 if(protect_user(victim, user, channel->channel_info))
2875 reply("CSMSG_USER_PROTECTED", victim->nick);
2879 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2880 name = victim->nick;
2884 if(!is_ircmask(argv[1]))
2886 reply("MSG_NICK_UNKNOWN", argv[1]);
2890 victims = alloca(sizeof(victims[0]) * channel->members.used);
2892 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2894 reply("CSMSG_MASK_PROTECTED", argv[1]);
2898 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2900 reply("CSMSG_LAME_MASK", argv[1]);
2904 if((action == ACTION_KICK) && (victimCount == 0))
2906 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2910 name = ban = strdup(argv[1]);
2913 /* Truncate the ban in place if necessary; we must ensure
2914 that 'ban' is a valid ban mask before sanitizing it. */
2915 sanitize_ircmask(ban);
2917 if(action & ACTION_ADD_BAN)
2919 struct banData *bData, *next;
2921 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2923 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2928 if(action & ACTION_ADD_TIMED_BAN)
2930 duration = ParseInterval(argv[2]);
2934 reply("CSMSG_DURATION_TOO_LOW");
2938 else if(duration > (86400 * 365 * 2))
2940 reply("CSMSG_DURATION_TOO_HIGH");
2946 for(bData = channel->channel_info->bans; bData; bData = next)
2948 if(match_ircglobs(bData->mask, ban))
2950 int exact = !irccasecmp(bData->mask, ban);
2952 /* The ban is redundant; there is already a ban
2953 with the same effect in place. */
2957 free(bData->reason);
2958 bData->reason = strdup(reason);
2959 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2961 reply("CSMSG_REASON_CHANGE", ban);
2965 if(exact && bData->expires)
2969 /* If the ban matches an existing one exactly,
2970 extend the expiration time if the provided
2971 duration is longer. */
2972 if(duration && ((time_t)(now + duration) > bData->expires))
2974 bData->expires = now + duration;
2985 /* Delete the expiration timeq entry and
2986 requeue if necessary. */
2987 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2990 timeq_add(bData->expires, expire_ban, bData);
2994 /* automated kickban */
2997 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2999 reply("CSMSG_BAN_ADDED", name, channel->name);
3005 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3012 if(match_ircglobs(ban, bData->mask))
3014 /* The ban we are adding makes previously existing
3015 bans redundant; silently remove them. */
3016 del_channel_ban(bData);
3020 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);
3022 name = ban = strdup(bData->mask);
3026 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3028 extern const char *hidden_host_suffix;
3029 const char *old_name = chanserv_conf.old_ban_names->list[n];
3031 unsigned int l1, l2;
3034 l2 = strlen(old_name);
3037 if(irccasecmp(ban + l1 - l2, old_name))
3039 new_mask = malloc(MAXLEN);
3040 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3042 name = ban = new_mask;
3047 if(action & ACTION_BAN)
3049 unsigned int exists;
3050 struct mod_chanmode *change;
3052 if(channel->banlist.used >= MAXBANS)
3055 reply("CSMSG_BANLIST_FULL", channel->name);
3060 exists = ChannelBanExists(channel, ban);
3061 change = mod_chanmode_alloc(victimCount + 1);
3062 for(n = 0; n < victimCount; ++n)
3064 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3065 change->args[n].u.member = victims[n];
3069 change->args[n].mode = MODE_BAN;
3070 change->args[n++].u.hostmask = ban;
3074 modcmd_chanmode_announce(change);
3076 mod_chanmode_announce(chanserv, channel, change);
3077 mod_chanmode_free(change);
3079 if(exists && (action == ACTION_BAN))
3082 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3088 if(action & ACTION_KICK)
3090 char kick_reason[MAXLEN];
3091 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3093 for(n = 0; n < victimCount; n++)
3094 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3099 /* No response, since it was automated. */
3101 else if(action & ACTION_ADD_BAN)
3104 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3106 reply("CSMSG_BAN_ADDED", name, channel->name);
3108 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3109 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3110 else if(action & ACTION_BAN)
3111 reply("CSMSG_BAN_DONE", name, channel->name);
3112 else if(action & ACTION_KICK && victimCount)
3113 reply("CSMSG_KICK_DONE", name, channel->name);
3119 static CHANSERV_FUNC(cmd_kickban)
3121 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3124 static CHANSERV_FUNC(cmd_kick)
3126 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3129 static CHANSERV_FUNC(cmd_ban)
3131 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3134 static CHANSERV_FUNC(cmd_addban)
3136 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3139 static CHANSERV_FUNC(cmd_addtimedban)
3141 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3144 static struct mod_chanmode *
3145 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3147 struct mod_chanmode *change;
3148 unsigned char *match;
3149 unsigned int ii, count;
3151 match = alloca(bans->used);
3154 for(ii = count = 0; ii < bans->used; ++ii)
3156 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3157 MATCH_USENICK | MATCH_VISIBLE);
3164 for(ii = count = 0; ii < bans->used; ++ii)
3166 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3173 change = mod_chanmode_alloc(count);
3174 for(ii = count = 0; ii < bans->used; ++ii)
3178 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3179 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3181 assert(count == change->argc);
3186 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3188 struct userNode *actee;
3194 /* may want to allow a comma delimited list of users... */
3195 if(!(actee = GetUserH(argv[1])))
3197 if(!is_ircmask(argv[1]))
3199 reply("MSG_NICK_UNKNOWN", argv[1]);
3203 mask = strdup(argv[1]);
3206 /* We don't sanitize the mask here because ircu
3208 if(action & ACTION_UNBAN)
3210 struct mod_chanmode *change;
3211 change = find_matching_bans(&channel->banlist, actee, mask);
3216 modcmd_chanmode_announce(change);
3217 for(ii = 0; ii < change->argc; ++ii)
3218 free((char*)change->args[ii].u.hostmask);
3219 mod_chanmode_free(change);
3224 if(action & ACTION_DEL_BAN)
3226 struct banData *ban, *next;
3228 ban = channel->channel_info->bans;
3232 for( ; ban && !user_matches_glob(actee, ban->mask,
3233 MATCH_USENICK | MATCH_VISIBLE);
3236 for( ; ban && !match_ircglobs(mask, ban->mask);
3241 del_channel_ban(ban);
3248 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3250 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3256 static CHANSERV_FUNC(cmd_unban)
3258 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3261 static CHANSERV_FUNC(cmd_delban)
3263 /* it doesn't necessarily have to remove the channel ban - may want
3264 to make that an option. */
3265 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3268 static CHANSERV_FUNC(cmd_unbanme)
3270 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3271 long flags = ACTION_UNBAN;
3273 /* remove permanent bans if the user has the proper access. */
3274 if(uData->access >= UL_MASTER)
3275 flags |= ACTION_DEL_BAN;
3277 argv[1] = user->nick;
3278 return unban_user(user, channel, 2, argv, cmd, flags);
3281 static CHANSERV_FUNC(cmd_unbanall)
3283 struct mod_chanmode *change;
3286 if(!channel->banlist.used)
3288 reply("CSMSG_NO_BANS", channel->name);
3292 change = mod_chanmode_alloc(channel->banlist.used);
3293 for(ii=0; ii<channel->banlist.used; ii++)
3295 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3296 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3298 modcmd_chanmode_announce(change);
3299 for(ii = 0; ii < change->argc; ++ii)
3300 free((char*)change->args[ii].u.hostmask);
3301 mod_chanmode_free(change);
3302 reply("CSMSG_BANS_REMOVED", channel->name);
3306 static CHANSERV_FUNC(cmd_open)
3308 struct mod_chanmode *change;
3311 change = find_matching_bans(&channel->banlist, user, NULL);
3313 change = mod_chanmode_alloc(0);
3314 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3315 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3316 && channel->channel_info->modes.modes_set)
3317 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3318 modcmd_chanmode_announce(change);
3319 reply("CSMSG_CHANNEL_OPENED", channel->name);
3320 for(ii = 0; ii < change->argc; ++ii)
3321 free((char*)change->args[ii].u.hostmask);
3322 mod_chanmode_free(change);
3326 static CHANSERV_FUNC(cmd_myaccess)
3328 static struct string_buffer sbuf;
3329 struct handle_info *target_handle;
3330 struct userData *uData;
3333 target_handle = user->handle_info;
3334 else if(!IsHelping(user))
3336 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3339 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3342 if(!target_handle->channels)
3344 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3348 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3349 for(uData = target_handle->channels; uData; uData = uData->u_next)
3351 struct chanData *cData = uData->channel;
3353 if(uData->access > UL_OWNER)
3355 if(IsProtected(cData)
3356 && (target_handle != user->handle_info)
3357 && !GetTrueChannelAccess(cData, user->handle_info))
3360 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3361 if(uData->flags != USER_AUTO_OP)
3362 string_buffer_append(&sbuf, ',');
3363 if(IsUserSuspended(uData))
3364 string_buffer_append(&sbuf, 's');
3365 if(IsUserAutoOp(uData))
3367 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3368 string_buffer_append(&sbuf, 'o');
3369 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3370 string_buffer_append(&sbuf, 'v');
3372 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3373 string_buffer_append(&sbuf, 'i');
3375 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3377 string_buffer_append_string(&sbuf, ")]");
3378 string_buffer_append(&sbuf, '\0');
3379 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3385 static CHANSERV_FUNC(cmd_access)
3387 struct userNode *target;
3388 struct handle_info *target_handle;
3389 struct userData *uData;
3391 char prefix[MAXLEN];
3396 target_handle = target->handle_info;
3398 else if((target = GetUserH(argv[1])))
3400 target_handle = target->handle_info;
3402 else if(argv[1][0] == '*')
3404 if(!(target_handle = get_handle_info(argv[1]+1)))
3406 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3412 reply("MSG_NICK_UNKNOWN", argv[1]);
3416 assert(target || target_handle);
3418 if(target == chanserv)
3420 reply("CSMSG_IS_CHANSERV");
3428 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3433 reply("MSG_USER_AUTHENTICATE", target->nick);
3436 reply("MSG_AUTHENTICATE");
3442 const char *epithet = NULL, *type = NULL;
3445 epithet = chanserv_conf.irc_operator_epithet;
3446 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3448 else if(IsNetworkHelper(target))
3450 epithet = chanserv_conf.network_helper_epithet;
3451 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3453 else if(IsSupportHelper(target))
3455 epithet = chanserv_conf.support_helper_epithet;
3456 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3460 if(target_handle->epithet)
3461 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3463 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3465 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3469 sprintf(prefix, "%s", target_handle->handle);
3472 if(!channel->channel_info)
3474 reply("CSMSG_NOT_REGISTERED", channel->name);
3478 helping = HANDLE_FLAGGED(target_handle, HELPING)
3479 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3480 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3482 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3483 /* To prevent possible information leaks, only show infolines
3484 * if the requestor is in the channel or it's their own
3486 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3488 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3490 /* Likewise, only say it's suspended if the user has active
3491 * access in that channel or it's their own entry. */
3492 if(IsUserSuspended(uData)
3493 && (GetChannelUser(channel->channel_info, user->handle_info)
3494 || (user->handle_info == uData->handle)))
3496 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3501 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3508 zoot_list(struct listData *list)
3510 struct userData *uData;
3511 unsigned int start, curr, highest, lowest;
3512 struct helpfile_table tmp_table;
3513 const char **temp, *msg;
3515 if(list->table.length == 1)
3518 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3520 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3521 msg = user_find_message(list->user, "MSG_NONE");
3522 send_message_type(4, list->user, list->bot, " %s", msg);
3524 tmp_table.width = list->table.width;
3525 tmp_table.flags = list->table.flags;
3526 list->table.contents[0][0] = " ";
3527 highest = list->highest;
3528 if(list->lowest != 0)
3529 lowest = list->lowest;
3530 else if(highest < 100)
3533 lowest = highest - 100;
3534 for(start = curr = 1; curr < list->table.length; )
3536 uData = list->users[curr-1];
3537 list->table.contents[curr++][0] = " ";
3538 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3541 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3543 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3544 temp = list->table.contents[--start];
3545 list->table.contents[start] = list->table.contents[0];
3546 tmp_table.contents = list->table.contents + start;
3547 tmp_table.length = curr - start;
3548 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3549 list->table.contents[start] = temp;
3551 highest = lowest - 1;
3552 lowest = (highest < 100) ? 0 : (highest - 99);
3558 def_list(struct listData *list)
3562 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3564 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3565 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3566 if(list->table.length == 1)
3568 msg = user_find_message(list->user, "MSG_NONE");
3569 send_message_type(4, list->user, list->bot, " %s", msg);
3574 userData_access_comp(const void *arg_a, const void *arg_b)
3576 const struct userData *a = *(struct userData**)arg_a;
3577 const struct userData *b = *(struct userData**)arg_b;
3579 if(a->access != b->access)
3580 res = b->access - a->access;
3582 res = irccasecmp(a->handle->handle, b->handle->handle);
3587 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3589 void (*send_list)(struct listData *);
3590 struct userData *uData;
3591 struct listData lData;
3592 unsigned int matches;
3596 lData.bot = cmd->parent->bot;
3597 lData.channel = channel;
3598 lData.lowest = lowest;
3599 lData.highest = highest;
3600 lData.search = (argc > 1) ? argv[1] : NULL;
3601 send_list = def_list;
3602 (void)zoot_list; /* since it doesn't show user levels */
3604 if(user->handle_info)
3606 switch(user->handle_info->userlist_style)
3608 case HI_STYLE_DEF: send_list = def_list; break;
3609 case HI_STYLE_ZOOT: send_list = def_list; break;
3613 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3615 for(uData = channel->channel_info->users; uData; uData = uData->next)
3617 if((uData->access < lowest)
3618 || (uData->access > highest)
3619 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3621 lData.users[matches++] = uData;
3623 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3625 lData.table.length = matches+1;
3626 lData.table.width = 4;
3627 lData.table.flags = TABLE_NO_FREE;
3628 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3629 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3630 lData.table.contents[0] = ary;
3633 ary[2] = "Last Seen";
3635 for(matches = 1; matches < lData.table.length; ++matches)
3637 struct userData *uData = lData.users[matches-1];
3638 char seen[INTERVALLEN];
3640 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3641 lData.table.contents[matches] = ary;
3642 ary[0] = strtab(uData->access);
3643 ary[1] = uData->handle->handle;
3646 else if(!uData->seen)
3649 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3650 ary[2] = strdup(ary[2]);
3651 if(IsUserSuspended(uData))
3652 ary[3] = "Suspended";
3653 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3654 ary[3] = "Vacation";
3659 for(matches = 1; matches < lData.table.length; ++matches)
3661 free((char*)lData.table.contents[matches][2]);
3662 free(lData.table.contents[matches]);
3664 free(lData.table.contents[0]);
3665 free(lData.table.contents);
3669 static CHANSERV_FUNC(cmd_users)
3671 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3674 static CHANSERV_FUNC(cmd_wlist)
3676 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3679 static CHANSERV_FUNC(cmd_clist)
3681 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3684 static CHANSERV_FUNC(cmd_mlist)
3686 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3689 static CHANSERV_FUNC(cmd_olist)
3691 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3694 static CHANSERV_FUNC(cmd_plist)
3696 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3699 static CHANSERV_FUNC(cmd_bans)
3701 struct userNode *search_u = NULL;
3702 struct helpfile_table tbl;
3703 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3704 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3705 const char *msg_never, *triggered, *expires;
3706 struct banData *ban, **bans;
3710 else if(strchr(search = argv[1], '!'))
3713 search_wilds = search[strcspn(search, "?*")];
3715 else if(!(search_u = GetUserH(search)))
3716 reply("MSG_NICK_UNKNOWN", search);
3718 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3720 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3724 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3729 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3732 bans[matches++] = ban;
3737 tbl.length = matches + 1;
3738 tbl.width = 4 + timed;
3740 tbl.flags = TABLE_NO_FREE;
3741 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3742 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3743 tbl.contents[0][0] = "Mask";
3744 tbl.contents[0][1] = "Set By";
3745 tbl.contents[0][2] = "Triggered";
3748 tbl.contents[0][3] = "Expires";
3749 tbl.contents[0][4] = "Reason";
3752 tbl.contents[0][3] = "Reason";
3755 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3757 free(tbl.contents[0]);
3762 msg_never = user_find_message(user, "MSG_NEVER");
3763 for(ii = 0; ii < matches; )
3769 else if(ban->expires)
3770 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3772 expires = msg_never;
3775 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3777 triggered = msg_never;
3779 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3780 tbl.contents[ii][0] = ban->mask;
3781 tbl.contents[ii][1] = ban->owner;
3782 tbl.contents[ii][2] = strdup(triggered);
3785 tbl.contents[ii][3] = strdup(expires);
3786 tbl.contents[ii][4] = ban->reason;
3789 tbl.contents[ii][3] = ban->reason;
3791 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3792 reply("MSG_MATCH_COUNT", matches);
3793 for(ii = 1; ii < tbl.length; ++ii)
3795 free((char*)tbl.contents[ii][2]);
3797 free((char*)tbl.contents[ii][3]);
3798 free(tbl.contents[ii]);
3800 free(tbl.contents[0]);
3806 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3808 struct chanData *cData = channel->channel_info;
3809 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3811 if(cData->topic_mask)
3812 return !match_ircglob(new_topic, cData->topic_mask);
3813 else if(cData->topic)
3814 return irccasecmp(new_topic, cData->topic);
3819 static CHANSERV_FUNC(cmd_topic)
3821 struct chanData *cData;
3824 cData = channel->channel_info;
3829 SetChannelTopic(channel, chanserv, cData->topic, 1);
3830 reply("CSMSG_TOPIC_SET", cData->topic);
3834 reply("CSMSG_NO_TOPIC", channel->name);
3838 topic = unsplit_string(argv + 1, argc - 1, NULL);
3839 /* If they say "!topic *", use an empty topic. */
3840 if((topic[0] == '*') && (topic[1] == 0))
3842 if(bad_topic(channel, user, topic))
3844 char *topic_mask = cData->topic_mask;
3847 char new_topic[TOPICLEN+1], tchar;
3848 int pos=0, starpos=-1, dpos=0, len;
3850 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3857 len = strlen(topic);
3858 if((dpos + len) > TOPICLEN)
3859 len = TOPICLEN + 1 - dpos;
3860 memcpy(new_topic+dpos, topic, len);
3864 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3865 default: new_topic[dpos++] = tchar; break;
3868 if((dpos > TOPICLEN) || tchar)
3871 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3872 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3875 new_topic[dpos] = 0;
3876 SetChannelTopic(channel, chanserv, new_topic, 1);
3878 reply("CSMSG_TOPIC_LOCKED", channel->name);
3883 SetChannelTopic(channel, chanserv, topic, 1);
3885 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3887 /* Grab the topic and save it as the default topic. */
3889 cData->topic = strdup(channel->topic);
3895 static CHANSERV_FUNC(cmd_mode)
3897 struct userData *uData;
3898 struct mod_chanmode *change;
3903 change = &channel->channel_info->modes;
3904 if(change->modes_set || change->modes_clear) {
3905 modcmd_chanmode_announce(change);
3906 reply("CSMSG_DEFAULTED_MODES", channel->name);
3908 reply("CSMSG_NO_MODES", channel->name);
3912 uData = GetChannelUser(channel->channel_info, user->handle_info);
3914 base_oplevel = MAXOPLEVEL;
3915 else if (uData->access >= UL_OWNER)
3918 base_oplevel = 1 + UL_OWNER - uData->access;
3919 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3922 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3926 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3927 && mode_lock_violated(&channel->channel_info->modes, change))
3930 mod_chanmode_format(&channel->channel_info->modes, modes);
3931 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3935 modcmd_chanmode_announce(change);
3936 mod_chanmode_free(change);
3937 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3941 static CHANSERV_FUNC(cmd_invite)
3943 struct userData *uData;
3944 struct userNode *invite;
3946 uData = GetChannelUser(channel->channel_info, user->handle_info);
3950 if(!(invite = GetUserH(argv[1])))
3952 reply("MSG_NICK_UNKNOWN", argv[1]);
3959 if(GetUserMode(channel, invite))
3961 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3969 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3970 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3973 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3975 irc_invite(chanserv, invite, channel);
3977 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3982 static CHANSERV_FUNC(cmd_inviteme)
3984 if(GetUserMode(channel, user))
3986 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3989 if(channel->channel_info
3990 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3992 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3995 irc_invite(cmd->parent->bot, user, channel);
4000 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4003 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4005 /* We display things based on two dimensions:
4006 * - Issue time: present or absent
4007 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4008 * (in order of precedence, so something both expired and revoked
4009 * only counts as revoked)
4011 combo = (suspended->issued ? 4 : 0)
4012 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4014 case 0: /* no issue time, indefinite expiration */
4015 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4017 case 1: /* no issue time, expires in future */
4018 intervalString(buf1, suspended->expires-now, user->handle_info);
4019 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4021 case 2: /* no issue time, expired */
4022 intervalString(buf1, now-suspended->expires, user->handle_info);
4023 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4025 case 3: /* no issue time, revoked */
4026 intervalString(buf1, now-suspended->revoked, user->handle_info);
4027 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4029 case 4: /* issue time set, indefinite expiration */
4030 intervalString(buf1, now-suspended->issued, user->handle_info);
4031 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4033 case 5: /* issue time set, expires in future */
4034 intervalString(buf1, now-suspended->issued, user->handle_info);
4035 intervalString(buf2, suspended->expires-now, user->handle_info);
4036 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4038 case 6: /* issue time set, expired */
4039 intervalString(buf1, now-suspended->issued, user->handle_info);
4040 intervalString(buf2, now-suspended->expires, user->handle_info);
4041 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4043 case 7: /* issue time set, revoked */
4044 intervalString(buf1, now-suspended->issued, user->handle_info);
4045 intervalString(buf2, now-suspended->revoked, user->handle_info);
4046 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4049 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4054 static CHANSERV_FUNC(cmd_info)
4056 char modes[MAXLEN], buffer[INTERVALLEN];
4057 struct userData *uData, *owner;
4058 struct chanData *cData;
4059 struct do_not_register *dnr;
4064 cData = channel->channel_info;
4065 reply("CSMSG_CHANNEL_INFO", channel->name);
4067 uData = GetChannelUser(cData, user->handle_info);
4068 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4070 mod_chanmode_format(&cData->modes, modes);
4071 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4072 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4075 for(it = dict_first(cData->notes); it; it = iter_next(it))
4079 note = iter_data(it);
4080 if(!note_type_visible_to_user(cData, note->type, user))
4083 padding = PADLEN - 1 - strlen(iter_key(it));
4084 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4087 reply("CSMSG_CHANNEL_MAX", cData->max);
4088 for(owner = cData->users; owner; owner = owner->next)
4089 if(owner->access == UL_OWNER)
4090 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4091 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4092 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4093 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4095 privileged = IsStaff(user);
4097 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4098 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4099 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4101 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4102 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4104 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4106 struct suspended *suspended;
4107 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4108 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4109 show_suspension_info(cmd, user, suspended);
4111 else if(IsSuspended(cData))
4113 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4114 show_suspension_info(cmd, user, cData->suspended);
4119 static CHANSERV_FUNC(cmd_netinfo)
4121 extern time_t boot_time;
4122 extern unsigned long burst_length;
4123 char interval[INTERVALLEN];
4125 reply("CSMSG_NETWORK_INFO");
4126 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4127 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4128 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4129 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4130 reply("CSMSG_NETWORK_BANS", banCount);
4131 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4132 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4133 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4138 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4140 struct helpfile_table table;
4142 struct userNode *user;
4147 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4148 table.contents = alloca(list->used*sizeof(*table.contents));
4149 for(nn=0; nn<list->used; nn++)
4151 user = list->list[nn];
4152 if(user->modes & skip_flags)
4156 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4159 nick = alloca(strlen(user->nick)+3);
4160 sprintf(nick, "(%s)", user->nick);
4164 table.contents[table.length][0] = nick;
4167 table_send(chanserv, to->nick, 0, NULL, table);
4170 static CHANSERV_FUNC(cmd_ircops)
4172 reply("CSMSG_STAFF_OPERS");
4173 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4177 static CHANSERV_FUNC(cmd_helpers)
4179 reply("CSMSG_STAFF_HELPERS");
4180 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4184 static CHANSERV_FUNC(cmd_staff)
4186 reply("CSMSG_NETWORK_STAFF");
4187 cmd_ircops(CSFUNC_ARGS);
4188 cmd_helpers(CSFUNC_ARGS);
4192 static CHANSERV_FUNC(cmd_peek)
4194 struct modeNode *mn;
4195 char modes[MODELEN];
4197 struct helpfile_table table;
4199 irc_make_chanmode(channel, modes);
4201 reply("CSMSG_PEEK_INFO", channel->name);
4202 reply("CSMSG_PEEK_TOPIC", channel->topic);
4203 reply("CSMSG_PEEK_MODES", modes);
4204 reply("CSMSG_PEEK_USERS", channel->members.used);
4208 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4209 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4210 for(n = 0; n < channel->members.used; n++)
4212 mn = channel->members.list[n];
4213 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4215 table.contents[table.length] = alloca(sizeof(**table.contents));
4216 table.contents[table.length][0] = mn->user->nick;
4221 reply("CSMSG_PEEK_OPS");
4222 table_send(chanserv, user->nick, 0, NULL, table);
4225 reply("CSMSG_PEEK_NO_OPS");
4229 static MODCMD_FUNC(cmd_wipeinfo)
4231 struct handle_info *victim;
4232 struct userData *ud, *actor, *real_actor;
4233 unsigned int override = 0;
4236 actor = GetChannelUser(channel->channel_info, user->handle_info);
4237 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4238 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4240 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4242 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4245 if((ud->access >= actor->access) && (ud != actor))
4247 reply("MSG_USER_OUTRANKED", victim->handle);
4250 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4251 override = CMD_LOG_OVERRIDE;
4255 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4256 return 1 | override;
4259 static CHANSERV_FUNC(cmd_resync)
4261 struct mod_chanmode *changes;
4262 struct chanData *cData = channel->channel_info;
4263 unsigned int ii, used;
4265 changes = mod_chanmode_alloc(channel->members.used * 2);
4266 for(ii = used = 0; ii < channel->members.used; ++ii)
4268 struct modeNode *mn = channel->members.list[ii];
4269 struct userData *uData;
4271 if(IsService(mn->user))
4274 uData = GetChannelAccess(cData, mn->user->handle_info);
4275 if(!cData->lvlOpts[lvlGiveOps]
4276 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4278 if(!(mn->modes & MODE_CHANOP))
4280 changes->args[used].mode = MODE_CHANOP;
4281 changes->args[used++].u.member = mn;
4284 else if(!cData->lvlOpts[lvlGiveVoice]
4285 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4287 if(mn->modes & MODE_CHANOP)
4289 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4290 changes->args[used++].u.member = mn;
4292 if(!(mn->modes & MODE_VOICE))
4294 changes->args[used].mode = MODE_VOICE;
4295 changes->args[used++].u.member = mn;
4302 changes->args[used].mode = MODE_REMOVE | mn->modes;
4303 changes->args[used++].u.member = mn;
4307 changes->argc = used;
4308 modcmd_chanmode_announce(changes);
4309 mod_chanmode_free(changes);
4310 reply("CSMSG_RESYNCED_USERS", channel->name);
4314 static CHANSERV_FUNC(cmd_seen)
4316 struct userData *uData;
4317 struct handle_info *handle;
4318 char seen[INTERVALLEN];
4322 if(!irccasecmp(argv[1], chanserv->nick))
4324 reply("CSMSG_IS_CHANSERV");
4328 if(!(handle = get_handle_info(argv[1])))
4330 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4334 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4336 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4341 reply("CSMSG_USER_PRESENT", handle->handle);
4342 else if(uData->seen)
4343 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4345 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4347 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4348 reply("CSMSG_USER_VACATION", handle->handle);
4353 static MODCMD_FUNC(cmd_names)
4355 struct userNode *targ;
4356 struct userData *targData;
4357 unsigned int ii, pos;
4360 for(ii=pos=0; ii<channel->members.used; ++ii)
4362 targ = channel->members.list[ii]->user;
4363 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4366 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4369 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4373 if(IsUserSuspended(targData))
4375 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4378 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4379 reply("CSMSG_END_NAMES", channel->name);
4384 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4386 switch(ntype->visible_type)
4388 case NOTE_VIS_ALL: return 1;
4389 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4390 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4395 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4397 struct userData *uData;
4399 switch(ntype->set_access_type)
4401 case NOTE_SET_CHANNEL_ACCESS:
4402 if(!user->handle_info)
4404 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4406 return uData->access >= ntype->set_access.min_ulevel;
4407 case NOTE_SET_CHANNEL_SETTER:
4408 return check_user_level(channel, user, lvlSetters, 1, 0);
4409 case NOTE_SET_PRIVILEGED: default:
4410 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4414 static CHANSERV_FUNC(cmd_note)
4416 struct chanData *cData;
4418 struct note_type *ntype;
4420 cData = channel->channel_info;
4423 reply("CSMSG_NOT_REGISTERED", channel->name);
4427 /* If no arguments, show all visible notes for the channel. */
4433 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4435 note = iter_data(it);
4436 if(!note_type_visible_to_user(cData, note->type, user))
4439 reply("CSMSG_NOTELIST_HEADER", channel->name);
4440 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4443 reply("CSMSG_NOTELIST_END", channel->name);
4445 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4447 /* If one argument, show the named note. */
4450 if((note = dict_find(cData->notes, argv[1], NULL))
4451 && note_type_visible_to_user(cData, note->type, user))
4453 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4455 else if((ntype = dict_find(note_types, argv[1], NULL))
4456 && note_type_visible_to_user(NULL, ntype, user))
4458 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4463 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4467 /* Assume they're trying to set a note. */
4471 ntype = dict_find(note_types, argv[1], NULL);
4474 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4477 else if(note_type_settable_by_user(channel, ntype, user))
4479 note_text = unsplit_string(argv+2, argc-2, NULL);
4480 if((note = dict_find(cData->notes, argv[1], NULL)))
4481 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4482 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4483 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4485 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4487 /* The note is viewable to staff only, so return 0
4488 to keep the invocation from getting logged (or
4489 regular users can see it in !events). */
4495 reply("CSMSG_NO_ACCESS");
4502 static CHANSERV_FUNC(cmd_delnote)
4507 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4508 || !note_type_settable_by_user(channel, note->type, user))
4510 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4513 dict_remove(channel->channel_info->notes, note->type->name);
4514 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4518 static CHANSERV_FUNC(cmd_events)
4520 struct logSearch discrim;
4521 struct logReport report;
4522 unsigned int matches, limit;
4524 limit = (argc > 1) ? atoi(argv[1]) : 10;
4525 if(limit < 1 || limit > 200)
4528 memset(&discrim, 0, sizeof(discrim));
4529 discrim.masks.bot = chanserv;
4530 discrim.masks.channel_name = channel->name;
4532 discrim.masks.command = argv[2];
4533 discrim.limit = limit;
4534 discrim.max_time = INT_MAX;
4535 discrim.severities = 1 << LOG_COMMAND;
4536 report.reporter = chanserv;
4538 reply("CSMSG_EVENT_SEARCH_RESULTS");
4539 matches = log_entry_search(&discrim, log_report_entry, &report);
4541 reply("MSG_MATCH_COUNT", matches);
4543 reply("MSG_NO_MATCHES");
4547 static CHANSERV_FUNC(cmd_say)
4553 msg = unsplit_string(argv + 1, argc - 1, NULL);
4554 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4556 else if(GetUserH(argv[1]))
4559 msg = unsplit_string(argv + 2, argc - 2, NULL);
4560 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4564 reply("MSG_NOT_TARGET_NAME");
4570 static CHANSERV_FUNC(cmd_emote)
4576 /* CTCP is so annoying. */
4577 msg = unsplit_string(argv + 1, argc - 1, NULL);
4578 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4580 else if(GetUserH(argv[1]))
4582 msg = unsplit_string(argv + 2, argc - 2, NULL);
4583 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4587 reply("MSG_NOT_TARGET_NAME");
4593 struct channelList *
4594 chanserv_support_channels(void)
4596 return &chanserv_conf.support_channels;
4599 static CHANSERV_FUNC(cmd_expire)
4601 int channel_count = registered_channels;
4602 expire_channels(NULL);
4603 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4608 chanserv_expire_suspension(void *data)
4610 struct suspended *suspended = data;
4611 struct chanNode *channel;
4613 if(!suspended->expires || (now < suspended->expires))
4614 suspended->revoked = now;
4615 channel = suspended->cData->channel;
4616 suspended->cData->channel = channel;
4617 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4618 if(!IsOffChannel(suspended->cData))
4620 struct mod_chanmode change;
4621 mod_chanmode_init(&change);
4623 change.args[0].mode = MODE_CHANOP;
4624 change.args[0].u.member = AddChannelUser(chanserv, channel);
4625 mod_chanmode_announce(chanserv, channel, &change);
4629 static CHANSERV_FUNC(cmd_csuspend)
4631 struct suspended *suspended;
4632 char reason[MAXLEN];
4633 time_t expiry, duration;
4634 struct userData *uData;
4638 if(IsProtected(channel->channel_info))
4640 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4644 if(argv[1][0] == '!')
4646 else if(IsSuspended(channel->channel_info))
4648 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4649 show_suspension_info(cmd, user, channel->channel_info->suspended);
4653 if(!strcmp(argv[1], "0"))
4655 else if((duration = ParseInterval(argv[1])))
4656 expiry = now + duration;
4659 reply("MSG_INVALID_DURATION", argv[1]);
4663 unsplit_string(argv + 2, argc - 2, reason);
4665 suspended = calloc(1, sizeof(*suspended));
4666 suspended->revoked = 0;
4667 suspended->issued = now;
4668 suspended->suspender = strdup(user->handle_info->handle);
4669 suspended->expires = expiry;
4670 suspended->reason = strdup(reason);
4671 suspended->cData = channel->channel_info;
4672 suspended->previous = suspended->cData->suspended;
4673 suspended->cData->suspended = suspended;
4675 if(suspended->expires)
4676 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4678 if(IsSuspended(channel->channel_info))
4680 suspended->previous->revoked = now;
4681 if(suspended->previous->expires)
4682 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4683 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4684 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4688 /* Mark all users in channel as absent. */
4689 for(uData = channel->channel_info->users; uData; uData = uData->next)
4698 /* Mark the channel as suspended, then part. */
4699 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4700 DelChannelUser(chanserv, channel, suspended->reason, 0);
4701 reply("CSMSG_SUSPENDED", channel->name);
4702 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4703 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4708 static CHANSERV_FUNC(cmd_cunsuspend)
4710 struct suspended *suspended;
4711 char message[MAXLEN];
4713 if(!IsSuspended(channel->channel_info))
4715 reply("CSMSG_NOT_SUSPENDED", channel->name);
4719 suspended = channel->channel_info->suspended;
4721 /* Expire the suspension and join ChanServ to the channel. */
4722 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4723 chanserv_expire_suspension(suspended);
4724 reply("CSMSG_UNSUSPENDED", channel->name);
4725 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4726 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4730 typedef struct chanservSearch
4738 unsigned long flags;
4742 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4745 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4750 search = malloc(sizeof(struct chanservSearch));
4751 memset(search, 0, sizeof(*search));
4754 for(i = 0; i < argc; i++)
4756 /* Assume all criteria require arguments. */
4759 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4763 if(!irccasecmp(argv[i], "name"))
4764 search->name = argv[++i];
4765 else if(!irccasecmp(argv[i], "registrar"))
4766 search->registrar = argv[++i];
4767 else if(!irccasecmp(argv[i], "unvisited"))
4768 search->unvisited = ParseInterval(argv[++i]);
4769 else if(!irccasecmp(argv[i], "registered"))
4770 search->registered = ParseInterval(argv[++i]);
4771 else if(!irccasecmp(argv[i], "flags"))
4774 if(!irccasecmp(argv[i], "nodelete"))
4775 search->flags |= CHANNEL_NODELETE;
4776 else if(!irccasecmp(argv[i], "suspended"))
4777 search->flags |= CHANNEL_SUSPENDED;
4780 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4784 else if(!irccasecmp(argv[i], "limit"))
4785 search->limit = strtoul(argv[++i], NULL, 10);
4788 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4793 if(search->name && !strcmp(search->name, "*"))
4795 if(search->registrar && !strcmp(search->registrar, "*"))
4796 search->registrar = 0;
4805 chanserv_channel_match(struct chanData *channel, search_t search)
4807 const char *name = channel->channel->name;
4808 if((search->name && !match_ircglob(name, search->name)) ||
4809 (search->registrar && !channel->registrar) ||
4810 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4811 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4812 (search->registered && (now - channel->registered) > search->registered) ||
4813 (search->flags && ((search->flags & channel->flags) != search->flags)))
4820 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4822 struct chanData *channel;
4823 unsigned int matches = 0;
4825 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4827 if(!chanserv_channel_match(channel, search))
4837 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4842 search_print(struct chanData *channel, void *data)
4844 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4847 static CHANSERV_FUNC(cmd_search)
4850 unsigned int matches;
4851 channel_search_func action;
4855 if(!irccasecmp(argv[1], "count"))
4856 action = search_count;
4857 else if(!irccasecmp(argv[1], "print"))
4858 action = search_print;
4861 reply("CSMSG_ACTION_INVALID", argv[1]);
4865 search = chanserv_search_create(user, argc - 2, argv + 2);
4869 if(action == search_count)
4870 search->limit = INT_MAX;
4872 if(action == search_print)
4873 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4875 matches = chanserv_channel_search(search, action, user);
4878 reply("MSG_MATCH_COUNT", matches);
4880 reply("MSG_NO_MATCHES");
4886 static CHANSERV_FUNC(cmd_unvisited)
4888 struct chanData *cData;
4889 time_t interval = chanserv_conf.channel_expire_delay;
4890 char buffer[INTERVALLEN];
4891 unsigned int limit = 25, matches = 0;
4895 interval = ParseInterval(argv[1]);
4897 limit = atoi(argv[2]);
4900 intervalString(buffer, interval, user->handle_info);
4901 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4903 for(cData = channelList; cData && matches < limit; cData = cData->next)
4905 if((now - cData->visited) < interval)
4908 intervalString(buffer, now - cData->visited, user->handle_info);
4909 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4916 static MODCMD_FUNC(chan_opt_defaulttopic)
4922 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4924 reply("CSMSG_TOPIC_LOCKED", channel->name);
4928 topic = unsplit_string(argv+1, argc-1, NULL);
4930 free(channel->channel_info->topic);
4931 if(topic[0] == '*' && topic[1] == 0)
4933 topic = channel->channel_info->topic = NULL;
4937 topic = channel->channel_info->topic = strdup(topic);
4938 if(channel->channel_info->topic_mask
4939 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4940 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4942 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4945 if(channel->channel_info->topic)
4946 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4948 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4952 static MODCMD_FUNC(chan_opt_topicmask)
4956 struct chanData *cData = channel->channel_info;
4959 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4961 reply("CSMSG_TOPIC_LOCKED", channel->name);
4965 mask = unsplit_string(argv+1, argc-1, NULL);
4967 if(cData->topic_mask)
4968 free(cData->topic_mask);
4969 if(mask[0] == '*' && mask[1] == 0)
4971 cData->topic_mask = 0;
4975 cData->topic_mask = strdup(mask);
4977 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4978 else if(!match_ircglob(cData->topic, cData->topic_mask))
4979 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4983 if(channel->channel_info->topic_mask)
4984 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4986 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4990 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4994 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4998 if(greeting[0] == '*' && greeting[1] == 0)
5002 unsigned int length = strlen(greeting);
5003 if(length > chanserv_conf.greeting_length)
5005 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5008 *data = strdup(greeting);
5017 reply(name, user_find_message(user, "MSG_NONE"));
5021 static MODCMD_FUNC(chan_opt_greeting)
5023 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5026 static MODCMD_FUNC(chan_opt_usergreeting)
5028 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5031 static MODCMD_FUNC(chan_opt_modes)
5033 struct mod_chanmode *new_modes;
5034 char modes[MODELEN];
5038 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5040 reply("CSMSG_NO_ACCESS");
5043 if(argv[1][0] == '*' && argv[1][1] == 0)
5045 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5047 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5049 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5052 else if(new_modes->argc > 1)
5054 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5055 mod_chanmode_free(new_modes);
5060 channel->channel_info->modes = *new_modes;
5061 modcmd_chanmode_announce(new_modes);
5062 mod_chanmode_free(new_modes);
5066 mod_chanmode_format(&channel->channel_info->modes, modes);
5068 reply("CSMSG_SET_MODES", modes);
5070 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5074 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5076 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5078 struct chanData *cData = channel->channel_info;
5083 /* Set flag according to value. */
5084 if(enabled_string(argv[1]))
5086 cData->flags |= mask;
5089 else if(disabled_string(argv[1]))
5091 cData->flags &= ~mask;
5096 reply("MSG_INVALID_BINARY", argv[1]);
5102 /* Find current option value. */
5103 value = (cData->flags & mask) ? 1 : 0;
5107 reply(name, user_find_message(user, "MSG_ON"));
5109 reply(name, user_find_message(user, "MSG_OFF"));
5113 static MODCMD_FUNC(chan_opt_nodelete)
5115 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5117 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5121 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5124 static MODCMD_FUNC(chan_opt_dynlimit)
5126 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5129 static MODCMD_FUNC(chan_opt_offchannel)
5131 struct chanData *cData = channel->channel_info;
5136 /* Set flag according to value. */
5137 if(enabled_string(argv[1]))
5139 if(!IsOffChannel(cData))
5140 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5141 cData->flags |= CHANNEL_OFFCHANNEL;
5144 else if(disabled_string(argv[1]))
5146 if(IsOffChannel(cData))
5148 struct mod_chanmode change;
5149 mod_chanmode_init(&change);
5151 change.args[0].mode = MODE_CHANOP;
5152 change.args[0].u.member = AddChannelUser(chanserv, channel);
5153 mod_chanmode_announce(chanserv, channel, &change);
5155 cData->flags &= ~CHANNEL_OFFCHANNEL;
5160 reply("MSG_INVALID_BINARY", argv[1]);
5166 /* Find current option value. */
5167 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5171 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5173 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5177 static MODCMD_FUNC(chan_opt_defaults)
5179 struct userData *uData;
5180 struct chanData *cData;
5181 const char *confirm;
5182 enum levelOption lvlOpt;
5183 enum charOption chOpt;
5185 cData = channel->channel_info;
5186 uData = GetChannelUser(cData, user->handle_info);
5187 if(!uData || (uData->access < UL_OWNER))
5189 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5192 confirm = make_confirmation_string(uData);
5193 if((argc < 2) || strcmp(argv[1], confirm))
5195 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5198 cData->flags = CHANNEL_DEFAULT_FLAGS;
5199 cData->modes = chanserv_conf.default_modes;
5200 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5201 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5202 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5203 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5204 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5209 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5211 struct chanData *cData = channel->channel_info;
5212 struct userData *uData;
5213 unsigned short value;
5217 if(!check_user_level(channel, user, option, 1, 1))
5219 reply("CSMSG_CANNOT_SET");
5222 value = user_level_from_name(argv[1], UL_OWNER+1);
5223 if(!value && strcmp(argv[1], "0"))
5225 reply("CSMSG_INVALID_ACCESS", argv[1]);
5228 uData = GetChannelUser(cData, user->handle_info);
5229 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5231 reply("CSMSG_BAD_SETLEVEL");
5237 if(value > cData->lvlOpts[lvlGiveOps])
5239 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5244 if(value < cData->lvlOpts[lvlGiveVoice])
5246 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5251 /* This test only applies to owners, since non-owners
5252 * trying to set an option to above their level get caught
5253 * by the CSMSG_BAD_SETLEVEL test above.
5255 if(value > uData->access)
5257 reply("CSMSG_BAD_SETTERS");
5264 cData->lvlOpts[option] = value;
5266 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5270 static MODCMD_FUNC(chan_opt_enfops)
5272 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5275 static MODCMD_FUNC(chan_opt_giveops)
5277 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5280 static MODCMD_FUNC(chan_opt_enfmodes)
5282 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5285 static MODCMD_FUNC(chan_opt_enftopic)
5287 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5290 static MODCMD_FUNC(chan_opt_pubcmd)
5292 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5295 static MODCMD_FUNC(chan_opt_setters)
5297 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5300 static MODCMD_FUNC(chan_opt_ctcpusers)
5302 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5305 static MODCMD_FUNC(chan_opt_userinfo)
5307 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5310 static MODCMD_FUNC(chan_opt_givevoice)
5312 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5315 static MODCMD_FUNC(chan_opt_topicsnarf)
5317 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5320 static MODCMD_FUNC(chan_opt_inviteme)
5322 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5326 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5328 struct chanData *cData = channel->channel_info;
5329 int count = charOptions[option].count, index;
5333 index = atoi(argv[1]);
5335 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5337 reply("CSMSG_INVALID_NUMERIC", index);
5338 /* Show possible values. */
5339 for(index = 0; index < count; index++)
5340 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5344 cData->chOpts[option] = charOptions[option].values[index].value;
5348 /* Find current option value. */
5351 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5355 /* Somehow, the option value is corrupt; reset it to the default. */
5356 cData->chOpts[option] = charOptions[option].default_value;
5361 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5365 static MODCMD_FUNC(chan_opt_protect)
5367 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5370 static MODCMD_FUNC(chan_opt_toys)
5372 return channel_multiple_option(chToys, CSFUNC_ARGS);
5375 static MODCMD_FUNC(chan_opt_ctcpreaction)
5377 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5380 static MODCMD_FUNC(chan_opt_topicrefresh)
5382 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5385 static struct svccmd_list set_shows_list;
5388 handle_svccmd_unbind(struct svccmd *target) {
5390 for(ii=0; ii<set_shows_list.used; ++ii)
5391 if(target == set_shows_list.list[ii])
5392 set_shows_list.used = 0;
5395 static CHANSERV_FUNC(cmd_set)
5397 struct svccmd *subcmd;
5401 /* Check if we need to (re-)initialize set_shows_list. */
5402 if(!set_shows_list.used)
5404 if(!set_shows_list.size)
5406 set_shows_list.size = chanserv_conf.set_shows->used;
5407 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5409 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5411 const char *name = chanserv_conf.set_shows->list[ii];
5412 sprintf(buf, "%s %s", argv[0], name);
5413 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5416 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5419 svccmd_list_append(&set_shows_list, subcmd);
5425 reply("CSMSG_CHANNEL_OPTIONS");
5426 for(ii = 0; ii < set_shows_list.used; ii++)
5428 subcmd = set_shows_list.list[ii];
5429 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5434 sprintf(buf, "%s %s", argv[0], argv[1]);
5435 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5438 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5441 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5443 reply("CSMSG_NO_ACCESS");
5447 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5451 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5453 struct userData *uData;
5455 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5458 reply("CSMSG_NOT_USER", channel->name);
5464 /* Just show current option value. */
5466 else if(enabled_string(argv[1]))
5468 uData->flags |= mask;
5470 else if(disabled_string(argv[1]))
5472 uData->flags &= ~mask;
5476 reply("MSG_INVALID_BINARY", argv[1]);
5480 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5484 static MODCMD_FUNC(user_opt_noautoop)
5486 struct userData *uData;
5488 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5491 reply("CSMSG_NOT_USER", channel->name);
5494 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5495 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5497 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5500 static MODCMD_FUNC(user_opt_autoinvite)
5502 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5505 static MODCMD_FUNC(user_opt_info)
5507 struct userData *uData;
5510 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5514 /* If they got past the command restrictions (which require access)
5515 * but fail this test, we have some fool with security override on.
5517 reply("CSMSG_NOT_USER", channel->name);
5524 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5525 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5527 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5530 bp = strcspn(infoline, "\001");
5533 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5538 if(infoline[0] == '*' && infoline[1] == 0)
5541 uData->info = strdup(infoline);
5544 reply("CSMSG_USET_INFO", uData->info);
5546 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5550 struct svccmd_list uset_shows_list;
5552 static CHANSERV_FUNC(cmd_uset)
5554 struct svccmd *subcmd;
5558 /* Check if we need to (re-)initialize uset_shows_list. */
5559 if(!uset_shows_list.used)
5563 "NoAutoOp", "AutoInvite", "Info"
5566 if(!uset_shows_list.size)
5568 uset_shows_list.size = ArrayLength(options);
5569 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5571 for(ii = 0; ii < ArrayLength(options); ii++)
5573 const char *name = options[ii];
5574 sprintf(buf, "%s %s", argv[0], name);
5575 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5578 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5581 svccmd_list_append(&uset_shows_list, subcmd);
5587 /* Do this so options are presented in a consistent order. */
5588 reply("CSMSG_USER_OPTIONS");
5589 for(ii = 0; ii < uset_shows_list.used; ii++)
5590 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5594 sprintf(buf, "%s %s", argv[0], argv[1]);
5595 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5598 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5602 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5605 static CHANSERV_FUNC(cmd_giveownership)
5607 struct handle_info *new_owner_hi;
5608 struct userData *new_owner;
5609 struct userData *curr_user;
5610 struct userData *invoker;
5611 struct chanData *cData = channel->channel_info;
5612 struct do_not_register *dnr;
5613 const char *confirm;
5615 unsigned short co_access;
5616 char reason[MAXLEN];
5619 curr_user = GetChannelAccess(cData, user->handle_info);
5620 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5621 if(!curr_user || (curr_user->access != UL_OWNER))
5623 struct userData *owner = NULL;
5624 for(curr_user = channel->channel_info->users;
5626 curr_user = curr_user->next)
5628 if(curr_user->access != UL_OWNER)
5632 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5639 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5641 char delay[INTERVALLEN];
5642 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5643 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5646 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5648 if(new_owner_hi == user->handle_info)
5650 reply("CSMSG_NO_TRANSFER_SELF");
5653 new_owner = GetChannelAccess(cData, new_owner_hi);
5658 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5662 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5666 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5668 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5671 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5672 if(!IsHelping(user))
5673 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5675 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5678 invoker = GetChannelUser(cData, user->handle_info);
5679 if(invoker->access <= UL_OWNER)
5681 confirm = make_confirmation_string(curr_user);
5682 if((argc < 3) || strcmp(argv[2], confirm))
5684 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5688 if(new_owner->access >= UL_COOWNER)
5689 co_access = new_owner->access;
5691 co_access = UL_COOWNER;
5692 new_owner->access = UL_OWNER;
5694 curr_user->access = co_access;
5695 cData->ownerTransfer = now;
5696 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5697 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5698 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5702 static CHANSERV_FUNC(cmd_suspend)
5704 struct handle_info *hi;
5705 struct userData *self, *real_self, *target;
5706 unsigned int override = 0;
5709 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5710 self = GetChannelUser(channel->channel_info, user->handle_info);
5711 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5712 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5714 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5717 if(target->access >= self->access)
5719 reply("MSG_USER_OUTRANKED", hi->handle);
5722 if(target->flags & USER_SUSPENDED)
5724 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5729 target->present = 0;
5732 if(!real_self || target->access >= real_self->access)
5733 override = CMD_LOG_OVERRIDE;
5734 target->flags |= USER_SUSPENDED;
5735 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5736 return 1 | override;
5739 static CHANSERV_FUNC(cmd_unsuspend)
5741 struct handle_info *hi;
5742 struct userData *self, *real_self, *target;
5743 unsigned int override = 0;
5746 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5747 self = GetChannelUser(channel->channel_info, user->handle_info);
5748 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5749 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5751 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5754 if(target->access >= self->access)
5756 reply("MSG_USER_OUTRANKED", hi->handle);
5759 if(!(target->flags & USER_SUSPENDED))
5761 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5764 if(!real_self || target->access >= real_self->access)
5765 override = CMD_LOG_OVERRIDE;
5766 target->flags &= ~USER_SUSPENDED;
5767 scan_user_presence(target, NULL);
5768 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5769 return 1 | override;
5772 static MODCMD_FUNC(cmd_deleteme)
5774 struct handle_info *hi;
5775 struct userData *target;
5776 const char *confirm_string;
5777 unsigned short access;
5780 hi = user->handle_info;
5781 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5783 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5786 if(target->access == UL_OWNER)
5788 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5791 confirm_string = make_confirmation_string(target);
5792 if((argc < 2) || strcmp(argv[1], confirm_string))
5794 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5797 access = target->access;
5798 channel_name = strdup(channel->name);
5799 del_channel_user(target, 1);
5800 reply("CSMSG_DELETED_YOU", access, channel_name);
5806 chanserv_refresh_topics(UNUSED_ARG(void *data))
5808 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5809 struct chanData *cData;
5812 for(cData = channelList; cData; cData = cData->next)
5814 if(IsSuspended(cData))
5816 opt = cData->chOpts[chTopicRefresh];
5819 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5822 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5823 cData->last_refresh = refresh_num;
5825 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5828 static CHANSERV_FUNC(cmd_unf)
5832 char response[MAXLEN];
5833 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5834 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5835 irc_privmsg(cmd->parent->bot, channel->name, response);
5838 reply("CSMSG_UNF_RESPONSE");
5842 static CHANSERV_FUNC(cmd_ping)
5846 char response[MAXLEN];
5847 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5848 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5849 irc_privmsg(cmd->parent->bot, channel->name, response);
5852 reply("CSMSG_PING_RESPONSE");
5856 static CHANSERV_FUNC(cmd_wut)
5860 char response[MAXLEN];
5861 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5862 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5863 irc_privmsg(cmd->parent->bot, channel->name, response);
5866 reply("CSMSG_WUT_RESPONSE");
5870 static CHANSERV_FUNC(cmd_8ball)
5872 unsigned int i, j, accum;
5877 for(i=1; i<argc; i++)
5878 for(j=0; argv[i][j]; j++)
5879 accum = (accum << 5) - accum + toupper(argv[i][j]);
5880 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5883 char response[MAXLEN];
5884 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5885 irc_privmsg(cmd->parent->bot, channel->name, response);
5888 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5892 static CHANSERV_FUNC(cmd_d)
5894 unsigned long sides, count, modifier, ii, total;
5895 char response[MAXLEN], *sep;
5899 if((count = strtoul(argv[1], &sep, 10)) < 1)
5909 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5910 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5914 else if((sep[0] == '-') && isdigit(sep[1]))
5915 modifier = strtoul(sep, NULL, 10);
5916 else if((sep[0] == '+') && isdigit(sep[1]))
5917 modifier = strtoul(sep+1, NULL, 10);
5924 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5929 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5932 for(total = ii = 0; ii < count; ++ii)
5933 total += (rand() % sides) + 1;
5936 if((count > 1) || modifier)
5938 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5939 sprintf(response, fmt, total, count, sides, modifier);
5943 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5944 sprintf(response, fmt, total, sides);
5947 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5949 send_message_type(4, user, cmd->parent->bot, "%s", response);
5953 static CHANSERV_FUNC(cmd_huggle)
5955 /* CTCP must be via PRIVMSG, never notice */
5957 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5959 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5964 chanserv_adjust_limit(void *data)
5966 struct mod_chanmode change;
5967 struct chanData *cData = data;
5968 struct chanNode *channel = cData->channel;
5971 if(IsSuspended(cData))
5974 cData->limitAdjusted = now;
5975 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5976 if(cData->modes.modes_set & MODE_LIMIT)
5978 if(limit > cData->modes.new_limit)
5979 limit = cData->modes.new_limit;
5980 else if(limit == cData->modes.new_limit)
5984 mod_chanmode_init(&change);
5985 change.modes_set = MODE_LIMIT;
5986 change.new_limit = limit;
5987 mod_chanmode_announce(chanserv, channel, &change);
5991 handle_new_channel(struct chanNode *channel)
5993 struct chanData *cData;
5995 if(!(cData = channel->channel_info))
5998 if(cData->modes.modes_set || cData->modes.modes_clear)
5999 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6001 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6002 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6005 /* Welcome to my worst nightmare. Warning: Read (or modify)
6006 the code below at your own risk. */
6008 handle_join(struct modeNode *mNode)
6010 struct mod_chanmode change;
6011 struct userNode *user = mNode->user;
6012 struct chanNode *channel = mNode->channel;
6013 struct chanData *cData;
6014 struct userData *uData = NULL;
6015 struct banData *bData;
6016 struct handle_info *handle;
6017 unsigned int modes = 0, info = 0;
6020 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6023 cData = channel->channel_info;
6024 if(channel->members.used > cData->max)
6025 cData->max = channel->members.used;
6027 /* Check for bans. If they're joining through a ban, one of two
6029 * 1: Join during a netburst, by riding the break. Kick them
6030 * unless they have ops or voice in the channel.
6031 * 2: They're allowed to join through the ban (an invite in
6032 * ircu2.10, or a +e on Hybrid, or something).
6033 * If they're not joining through a ban, and the banlist is not
6034 * full, see if they're on the banlist for the channel. If so,
6037 if(user->uplink->burst && !mNode->modes)
6040 for(ii = 0; ii < channel->banlist.used; ii++)
6042 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6044 /* Riding a netburst. Naughty. */
6045 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6051 mod_chanmode_init(&change);
6053 if(channel->banlist.used < MAXBANS)
6055 /* Not joining through a ban. */
6056 for(bData = cData->bans;
6057 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6058 bData = bData->next);
6062 char kick_reason[MAXLEN];
6063 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6065 bData->triggered = now;
6066 if(bData != cData->bans)
6068 /* Shuffle the ban to the head of the list. */
6070 bData->next->prev = bData->prev;
6072 bData->prev->next = bData->next;
6075 bData->next = cData->bans;
6078 cData->bans->prev = bData;
6079 cData->bans = bData;
6082 change.args[0].mode = MODE_BAN;
6083 change.args[0].u.hostmask = bData->mask;
6084 mod_chanmode_announce(chanserv, channel, &change);
6085 KickChannelUser(user, channel, chanserv, kick_reason);
6090 /* ChanServ will not modify the limits in join-flooded channels.
6091 It will also skip DynLimit processing when the user (or srvx)
6092 is bursting in, because there are likely more incoming. */
6093 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6094 && !user->uplink->burst
6095 && !channel->join_flooded
6096 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6098 /* The user count has begun "bumping" into the channel limit,
6099 so set a timer to raise the limit a bit. Any previous
6100 timers are removed so three incoming users within the delay
6101 results in one limit change, not three. */
6103 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6104 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6107 if(channel->join_flooded)
6109 /* don't automatically give ops or voice during a join flood */
6111 else if(cData->lvlOpts[lvlGiveOps] == 0)
6112 modes |= MODE_CHANOP;
6113 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6114 modes |= MODE_VOICE;
6116 greeting = cData->greeting;
6117 if(user->handle_info)
6119 handle = user->handle_info;
6121 if(IsHelper(user) && !IsHelping(user))
6124 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6126 if(channel == chanserv_conf.support_channels.list[ii])
6128 HANDLE_SET_FLAG(user->handle_info, HELPING);
6134 uData = GetTrueChannelAccess(cData, handle);
6135 if(uData && !IsUserSuspended(uData))
6137 /* Ops and above were handled by the above case. */
6138 if(IsUserAutoOp(uData))
6140 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6141 modes |= MODE_CHANOP;
6142 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6143 modes |= MODE_VOICE;
6145 if(uData->access >= UL_PRESENT)
6146 cData->visited = now;
6147 if(cData->user_greeting)
6148 greeting = cData->user_greeting;
6150 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6151 && ((now - uData->seen) >= chanserv_conf.info_delay)
6159 /* If user joining normally (not during burst), apply op or voice,
6160 * and send greeting/userinfo as appropriate.
6162 if(!user->uplink->burst)
6166 if(modes & MODE_CHANOP)
6167 modes &= ~MODE_VOICE;
6168 change.args[0].mode = modes;
6169 change.args[0].u.member = mNode;
6170 mod_chanmode_announce(chanserv, channel, &change);
6173 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6175 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6181 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6183 struct mod_chanmode change;
6184 struct userData *channel;
6185 unsigned int ii, jj;
6187 if(!user->handle_info)
6190 mod_chanmode_init(&change);
6192 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6194 struct chanNode *cn;
6195 struct modeNode *mn;
6196 if(IsUserSuspended(channel)
6197 || IsSuspended(channel->channel)
6198 || !(cn = channel->channel->channel))
6201 mn = GetUserMode(cn, user);
6204 if(!IsUserSuspended(channel)
6205 && IsUserAutoInvite(channel)
6206 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6208 && !user->uplink->burst)
6209 irc_invite(chanserv, user, cn);
6213 if(channel->access >= UL_PRESENT)
6214 channel->channel->visited = now;
6216 if(IsUserAutoOp(channel))
6218 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6219 change.args[0].mode = MODE_CHANOP;
6220 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6221 change.args[0].mode = MODE_VOICE;
6223 change.args[0].mode = 0;
6224 change.args[0].u.member = mn;
6225 if(change.args[0].mode)
6226 mod_chanmode_announce(chanserv, cn, &change);
6229 channel->seen = now;
6230 channel->present = 1;
6233 for(ii = 0; ii < user->channels.used; ++ii)
6235 struct chanNode *channel = user->channels.list[ii]->channel;
6236 struct banData *ban;
6238 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6239 || !channel->channel_info
6240 || IsSuspended(channel->channel_info))
6242 for(jj = 0; jj < channel->banlist.used; ++jj)
6243 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6245 if(jj < channel->banlist.used)
6247 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6249 char kick_reason[MAXLEN];
6250 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6252 change.args[0].mode = MODE_BAN;
6253 change.args[0].u.hostmask = ban->mask;
6254 mod_chanmode_announce(chanserv, channel, &change);
6255 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6256 KickChannelUser(user, channel, chanserv, kick_reason);
6257 ban->triggered = now;
6262 if(IsSupportHelper(user))
6264 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6266 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6268 HANDLE_SET_FLAG(user->handle_info, HELPING);
6276 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6278 struct chanData *cData;
6279 struct userData *uData;
6281 cData = mn->channel->channel_info;
6282 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6285 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6287 /* Allow for a bit of padding so that the limit doesn't
6288 track the user count exactly, which could get annoying. */
6289 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6291 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6292 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6296 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6298 scan_user_presence(uData, mn->user);
6300 if (uData->access >= UL_PRESENT)
6301 cData->visited = now;
6304 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6306 unsigned int ii, jj;
6307 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6309 for(jj = 0; jj < mn->user->channels.used; ++jj)
6310 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6312 if(jj < mn->user->channels.used)
6315 if(ii == chanserv_conf.support_channels.used)
6316 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6321 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6323 struct userData *uData;
6325 if(!channel->channel_info || !kicker || IsService(kicker)
6326 || (kicker == victim) || IsSuspended(channel->channel_info)
6327 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6330 if(protect_user(victim, kicker, channel->channel_info))
6332 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6333 KickChannelUser(kicker, channel, chanserv, reason);
6336 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6341 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6343 struct chanData *cData;
6345 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6348 cData = channel->channel_info;
6349 if(bad_topic(channel, user, channel->topic))
6351 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6352 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6353 SetChannelTopic(channel, chanserv, old_topic, 1);
6354 else if(cData->topic)
6355 SetChannelTopic(channel, chanserv, cData->topic, 1);
6358 /* With topicsnarf, grab the topic and save it as the default topic. */
6359 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6362 cData->topic = strdup(channel->topic);
6368 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6370 struct mod_chanmode *bounce = NULL;
6371 unsigned int bnc, ii;
6374 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6377 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6378 && mode_lock_violated(&channel->channel_info->modes, change))
6380 char correct[MAXLEN];
6381 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6382 mod_chanmode_format(&channel->channel_info->modes, correct);
6383 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6385 for(ii = bnc = 0; ii < change->argc; ++ii)
6387 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6389 const struct userNode *victim = change->args[ii].u.member->user;
6390 if(!protect_user(victim, user, channel->channel_info))
6393 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6396 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6397 bounce->args[bnc].u.member = GetUserMode(channel, user);
6398 if(bounce->args[bnc].u.member)
6402 bounce->args[bnc].mode = MODE_CHANOP;
6403 bounce->args[bnc].u.member = change->args[ii].u.member;
6405 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6407 else if(change->args[ii].mode & MODE_CHANOP)
6409 const struct userNode *victim = change->args[ii].u.member->user;
6410 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6413 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6414 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6415 bounce->args[bnc].u.member = change->args[ii].u.member;
6418 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6420 const char *ban = change->args[ii].u.hostmask;
6421 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6424 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6425 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6426 bounce->args[bnc].u.hostmask = strdup(ban);
6428 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6433 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6434 mod_chanmode_announce(chanserv, channel, bounce);
6435 for(ii = 0; ii < change->argc; ++ii)
6436 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6437 free((char*)bounce->args[ii].u.hostmask);
6438 mod_chanmode_free(bounce);
6443 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6445 struct chanNode *channel;
6446 struct banData *bData;
6447 struct mod_chanmode change;
6448 unsigned int ii, jj;
6449 char kick_reason[MAXLEN];
6451 mod_chanmode_init(&change);
6453 change.args[0].mode = MODE_BAN;
6454 for(ii = 0; ii < user->channels.used; ++ii)
6456 channel = user->channels.list[ii]->channel;
6457 /* Need not check for bans if they're opped or voiced. */
6458 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6460 /* Need not check for bans unless channel registration is active. */
6461 if(!channel->channel_info || IsSuspended(channel->channel_info))
6463 /* Look for a matching ban already on the channel. */
6464 for(jj = 0; jj < channel->banlist.used; ++jj)
6465 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6467 /* Need not act if we found one. */
6468 if(jj < channel->banlist.used)
6470 /* Look for a matching ban in this channel. */
6471 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6473 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6475 change.args[0].u.hostmask = bData->mask;
6476 mod_chanmode_announce(chanserv, channel, &change);
6477 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6478 KickChannelUser(user, channel, chanserv, kick_reason);
6479 bData->triggered = now;
6480 break; /* we don't need to check any more bans in the channel */
6485 static void handle_rename(struct handle_info *handle, const char *old_handle)
6487 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6491 dict_remove2(handle_dnrs, old_handle, 1);
6492 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6493 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6498 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6500 struct userNode *h_user;
6502 if(handle->channels)
6504 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6505 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6507 while(handle->channels)
6508 del_channel_user(handle->channels, 1);
6513 handle_server_link(UNUSED_ARG(struct server *server))
6515 struct chanData *cData;
6517 for(cData = channelList; cData; cData = cData->next)
6519 if(!IsSuspended(cData))
6520 cData->may_opchan = 1;
6521 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6522 && !cData->channel->join_flooded
6523 && ((cData->channel->limit - cData->channel->members.used)
6524 < chanserv_conf.adjust_threshold))
6526 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6527 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6533 chanserv_conf_read(void)
6537 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6538 struct mod_chanmode *change;
6539 struct string_list *strlist;
6540 struct chanNode *chan;
6543 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6545 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6548 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6549 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6550 chanserv_conf.support_channels.used = 0;
6551 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6553 for(ii = 0; ii < strlist->used; ++ii)
6555 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6558 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6560 channelList_append(&chanserv_conf.support_channels, chan);
6563 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6566 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6569 chan = AddChannel(str, now, str2, NULL);
6571 channelList_append(&chanserv_conf.support_channels, chan);
6573 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6574 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6575 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6576 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6577 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6578 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6579 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6580 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6581 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6582 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6583 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6584 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6585 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6586 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6587 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6588 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6589 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6590 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6591 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6592 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6593 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6594 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6595 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6596 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6597 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6599 NickChange(chanserv, str, 0);
6600 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6601 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6602 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6603 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6604 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6605 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6606 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6607 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6608 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6609 chanserv_conf.max_owned = str ? atoi(str) : 5;
6610 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6611 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6612 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6613 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6614 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6615 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6616 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6619 safestrncpy(mode_line, str, sizeof(mode_line));
6620 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6621 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6622 && (change->argc < 2))
6624 chanserv_conf.default_modes = *change;
6625 mod_chanmode_free(change);
6627 free_string_list(chanserv_conf.set_shows);
6628 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6630 strlist = string_list_copy(strlist);
6633 static const char *list[] = {
6634 /* free form text */
6635 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6636 /* options based on user level */
6637 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6638 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6639 /* multiple choice options */
6640 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6641 /* binary options */
6642 "DynLimit", "NoDelete",
6647 strlist = alloc_string_list(ArrayLength(list)-1);
6648 for(ii=0; list[ii]; ii++)
6649 string_list_append(strlist, strdup(list[ii]));
6651 chanserv_conf.set_shows = strlist;
6652 /* We don't look things up now, in case the list refers to options
6653 * defined by modules initialized after this point. Just mark the
6654 * function list as invalid, so it will be initialized.
6656 set_shows_list.used = 0;
6657 free_string_list(chanserv_conf.eightball);
6658 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6661 strlist = string_list_copy(strlist);
6665 strlist = alloc_string_list(4);
6666 string_list_append(strlist, strdup("Yes."));
6667 string_list_append(strlist, strdup("No."));
6668 string_list_append(strlist, strdup("Maybe so."));
6670 chanserv_conf.eightball = strlist;
6671 free_string_list(chanserv_conf.old_ban_names);
6672 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6674 strlist = string_list_copy(strlist);
6676 strlist = alloc_string_list(2);
6677 chanserv_conf.old_ban_names = strlist;
6678 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6679 off_channel = str ? atoi(str) : 0;
6683 chanserv_note_type_read(const char *key, struct record_data *rd)
6686 struct note_type *ntype;
6689 if(!(obj = GET_RECORD_OBJECT(rd)))
6691 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6694 if(!(ntype = chanserv_create_note_type(key)))
6696 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6700 /* Figure out set access */
6701 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6703 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6704 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6706 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6708 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6709 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6711 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6713 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6717 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6718 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6719 ntype->set_access.min_opserv = 0;
6722 /* Figure out visibility */
6723 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6724 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6725 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6726 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6727 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6728 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6729 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6730 ntype->visible_type = NOTE_VIS_ALL;
6732 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6734 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6735 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6739 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6741 struct handle_info *handle;
6742 struct userData *uData;
6743 char *seen, *inf, *flags;
6745 unsigned short access;
6747 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6749 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6753 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6754 if(access > UL_OWNER)
6756 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6760 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6761 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6762 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6763 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6764 handle = get_handle_info(key);
6767 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6771 uData = add_channel_user(chan, handle, access, last_seen, inf);
6772 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6776 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6778 struct banData *bData;
6779 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6780 time_t set_time, triggered_time, expires_time;
6782 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6784 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6788 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6789 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6790 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6791 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6792 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6793 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6794 if (!reason || !owner)
6797 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6798 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6800 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6802 expires_time = set_time + atoi(s_duration);
6806 if(!reason || (expires_time && (expires_time < now)))
6809 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6812 static struct suspended *
6813 chanserv_read_suspended(dict_t obj)
6815 struct suspended *suspended = calloc(1, sizeof(*suspended));
6819 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6820 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6821 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6822 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6823 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6824 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6825 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6826 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6827 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6828 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6833 chanserv_channel_read(const char *key, struct record_data *hir)
6835 struct suspended *suspended;
6836 struct mod_chanmode *modes;
6837 struct chanNode *cNode;
6838 struct chanData *cData;
6839 struct dict *channel, *obj;
6840 char *str, *argv[10];
6844 channel = hir->d.object;
6846 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6849 cNode = AddChannel(key, now, NULL, NULL);
6852 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6855 cData = register_channel(cNode, str);
6858 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6862 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6864 enum levelOption lvlOpt;
6865 enum charOption chOpt;
6867 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6868 cData->flags = atoi(str);
6870 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6872 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6874 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6875 else if(levelOptions[lvlOpt].old_flag)
6877 if(cData->flags & levelOptions[lvlOpt].old_flag)
6878 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6880 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6884 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6886 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6888 cData->chOpts[chOpt] = str[0];
6891 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6893 enum levelOption lvlOpt;
6894 enum charOption chOpt;
6897 cData->flags = base64toint(str, 5);
6898 count = strlen(str += 5);
6899 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6902 if(levelOptions[lvlOpt].old_flag)
6904 if(cData->flags & levelOptions[lvlOpt].old_flag)
6905 lvl = levelOptions[lvlOpt].flag_value;
6907 lvl = levelOptions[lvlOpt].default_value;
6909 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6911 case 'c': lvl = UL_COOWNER; break;
6912 case 'm': lvl = UL_MASTER; break;
6913 case 'n': lvl = UL_OWNER+1; break;
6914 case 'o': lvl = UL_OP; break;
6915 case 'p': lvl = UL_PEON; break;
6916 case 'w': lvl = UL_OWNER; break;
6917 default: lvl = 0; break;
6919 cData->lvlOpts[lvlOpt] = lvl;
6921 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6922 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6925 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6927 suspended = chanserv_read_suspended(obj);
6928 cData->suspended = suspended;
6929 suspended->cData = cData;
6930 /* We could use suspended->expires and suspended->revoked to
6931 * set the CHANNEL_SUSPENDED flag, but we don't. */
6933 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6935 suspended = calloc(1, sizeof(*suspended));
6936 suspended->issued = 0;
6937 suspended->revoked = 0;
6938 suspended->suspender = strdup(str);
6939 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6940 suspended->expires = str ? atoi(str) : 0;
6941 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6942 suspended->reason = strdup(str ? str : "No reason");
6943 suspended->previous = NULL;
6944 cData->suspended = suspended;
6945 suspended->cData = cData;
6949 cData->flags &= ~CHANNEL_SUSPENDED;
6950 suspended = NULL; /* to squelch a warning */
6953 if(IsSuspended(cData)) {
6954 if(suspended->expires > now)
6955 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6956 else if(suspended->expires)
6957 cData->flags &= ~CHANNEL_SUSPENDED;
6960 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6961 struct mod_chanmode change;
6962 mod_chanmode_init(&change);
6964 change.args[0].mode = MODE_CHANOP;
6965 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6966 mod_chanmode_announce(chanserv, cNode, &change);
6969 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6970 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6971 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6972 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6973 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6974 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6975 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6976 cData->max = str ? atoi(str) : 0;
6977 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6978 cData->greeting = str ? strdup(str) : NULL;
6979 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6980 cData->user_greeting = str ? strdup(str) : NULL;
6981 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6982 cData->topic_mask = str ? strdup(str) : NULL;
6983 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6984 cData->topic = str ? strdup(str) : NULL;
6986 if(!IsSuspended(cData)
6987 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6988 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6989 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6990 cData->modes = *modes;
6992 cData->modes.modes_set |= MODE_REGISTERED;
6993 if(cData->modes.argc > 1)
6994 cData->modes.argc = 1;
6995 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6996 mod_chanmode_free(modes);
6999 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7000 for(it = dict_first(obj); it; it = iter_next(it))
7001 user_read_helper(iter_key(it), iter_data(it), cData);
7003 if(!cData->users && !IsProtected(cData))
7005 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7006 unregister_channel(cData, "has empty user list.");
7010 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7011 for(it = dict_first(obj); it; it = iter_next(it))
7012 ban_read_helper(iter_key(it), iter_data(it), cData);
7014 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7015 for(it = dict_first(obj); it; it = iter_next(it))
7017 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7018 struct record_data *rd = iter_data(it);
7019 const char *note, *setter;
7021 if(rd->type != RECDB_OBJECT)
7023 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7027 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7029 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7031 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7035 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7036 if(!setter) setter = "<unknown>";
7037 chanserv_add_channel_note(cData, ntype, setter, note);
7045 chanserv_dnr_read(const char *key, struct record_data *hir)
7047 const char *setter, *reason, *str;
7048 struct do_not_register *dnr;
7051 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7054 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7057 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7060 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7063 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7064 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7065 if(expiry && expiry <= now)
7067 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7070 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7072 dnr->set = atoi(str);
7078 chanserv_saxdb_read(struct dict *database)
7080 struct dict *section;
7083 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7084 for(it = dict_first(section); it; it = iter_next(it))
7085 chanserv_note_type_read(iter_key(it), iter_data(it));
7087 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7088 for(it = dict_first(section); it; it = iter_next(it))
7089 chanserv_channel_read(iter_key(it), iter_data(it));
7091 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7092 for(it = dict_first(section); it; it = iter_next(it))
7093 chanserv_dnr_read(iter_key(it), iter_data(it));
7099 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7101 int high_present = 0;
7102 saxdb_start_record(ctx, KEY_USERS, 1);
7103 for(; uData; uData = uData->next)
7105 if((uData->access >= UL_PRESENT) && uData->present)
7107 saxdb_start_record(ctx, uData->handle->handle, 0);
7108 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7109 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7111 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7113 saxdb_write_string(ctx, KEY_INFO, uData->info);
7114 saxdb_end_record(ctx);
7116 saxdb_end_record(ctx);
7117 return high_present;
7121 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7125 saxdb_start_record(ctx, KEY_BANS, 1);
7126 for(; bData; bData = bData->next)
7128 saxdb_start_record(ctx, bData->mask, 0);
7129 saxdb_write_int(ctx, KEY_SET, bData->set);
7130 if(bData->triggered)
7131 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7133 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7135 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7137 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7138 saxdb_end_record(ctx);
7140 saxdb_end_record(ctx);
7144 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7146 saxdb_start_record(ctx, name, 0);
7147 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7148 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7150 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7152 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7154 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7156 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7157 saxdb_end_record(ctx);
7161 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7165 enum levelOption lvlOpt;
7166 enum charOption chOpt;
7168 saxdb_start_record(ctx, channel->channel->name, 1);
7170 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7171 saxdb_write_int(ctx, KEY_MAX, channel->max);
7173 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7174 if(channel->registrar)
7175 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7176 if(channel->greeting)
7177 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7178 if(channel->user_greeting)
7179 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7180 if(channel->topic_mask)
7181 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7182 if(channel->suspended)
7183 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7185 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7186 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7187 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7188 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7189 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7191 buf[0] = channel->chOpts[chOpt];
7193 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7195 saxdb_end_record(ctx);
7197 if(channel->modes.modes_set || channel->modes.modes_clear)
7199 mod_chanmode_format(&channel->modes, buf);
7200 saxdb_write_string(ctx, KEY_MODES, buf);
7203 high_present = chanserv_write_users(ctx, channel->users);
7204 chanserv_write_bans(ctx, channel->bans);
7206 if(dict_size(channel->notes))
7210 saxdb_start_record(ctx, KEY_NOTES, 1);
7211 for(it = dict_first(channel->notes); it; it = iter_next(it))
7213 struct note *note = iter_data(it);
7214 saxdb_start_record(ctx, iter_key(it), 0);
7215 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7216 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7217 saxdb_end_record(ctx);
7219 saxdb_end_record(ctx);
7222 if(channel->ownerTransfer)
7223 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7224 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7225 saxdb_end_record(ctx);
7229 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7233 saxdb_start_record(ctx, ntype->name, 0);
7234 switch(ntype->set_access_type)
7236 case NOTE_SET_CHANNEL_ACCESS:
7237 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7239 case NOTE_SET_CHANNEL_SETTER:
7240 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7242 case NOTE_SET_PRIVILEGED: default:
7243 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7246 switch(ntype->visible_type)
7248 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7249 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7250 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7252 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7253 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7254 saxdb_end_record(ctx);
7258 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7260 struct do_not_register *dnr;
7263 for(it = dict_first(dnrs); it; it = iter_next(it))
7265 dnr = iter_data(it);
7266 if(dnr->expires && dnr->expires <= now)
7268 saxdb_start_record(ctx, dnr->chan_name, 0);
7270 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7272 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7273 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7274 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7275 saxdb_end_record(ctx);
7280 chanserv_saxdb_write(struct saxdb_context *ctx)
7283 struct chanData *channel;
7286 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7287 for(it = dict_first(note_types); it; it = iter_next(it))
7288 chanserv_write_note_type(ctx, iter_data(it));
7289 saxdb_end_record(ctx);
7292 saxdb_start_record(ctx, KEY_DNR, 1);
7293 write_dnrs_helper(ctx, handle_dnrs);
7294 write_dnrs_helper(ctx, plain_dnrs);
7295 write_dnrs_helper(ctx, mask_dnrs);
7296 saxdb_end_record(ctx);
7299 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7300 for(channel = channelList; channel; channel = channel->next)
7301 chanserv_write_channel(ctx, channel);
7302 saxdb_end_record(ctx);
7308 chanserv_db_cleanup(void) {
7310 unreg_part_func(handle_part);
7312 unregister_channel(channelList, "terminating.");
7313 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7314 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7315 free(chanserv_conf.support_channels.list);
7316 dict_delete(handle_dnrs);
7317 dict_delete(plain_dnrs);
7318 dict_delete(mask_dnrs);
7319 dict_delete(note_types);
7320 free_string_list(chanserv_conf.eightball);
7321 free_string_list(chanserv_conf.old_ban_names);
7322 free_string_list(chanserv_conf.set_shows);
7323 free(set_shows_list.list);
7324 free(uset_shows_list.list);
7327 struct userData *helper = helperList;
7328 helperList = helperList->next;
7333 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7334 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7335 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7338 init_chanserv(const char *nick)
7340 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7341 conf_register_reload(chanserv_conf_read);
7345 reg_server_link_func(handle_server_link);
7346 reg_new_channel_func(handle_new_channel);
7347 reg_join_func(handle_join);
7348 reg_part_func(handle_part);
7349 reg_kick_func(handle_kick);
7350 reg_topic_func(handle_topic);
7351 reg_mode_change_func(handle_mode);
7352 reg_nick_change_func(handle_nick_change);
7353 reg_auth_func(handle_auth);
7356 reg_handle_rename_func(handle_rename);
7357 reg_unreg_func(handle_unreg);
7359 handle_dnrs = dict_new();
7360 dict_set_free_data(handle_dnrs, free);
7361 plain_dnrs = dict_new();
7362 dict_set_free_data(plain_dnrs, free);
7363 mask_dnrs = dict_new();
7364 dict_set_free_data(mask_dnrs, free);
7366 reg_svccmd_unbind_func(handle_svccmd_unbind);
7367 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7368 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7369 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7370 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7371 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7372 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7373 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7374 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7375 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7377 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7378 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7380 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7381 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7382 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7383 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7384 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7386 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7387 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7388 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7389 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7390 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7392 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7393 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7394 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7395 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7397 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7398 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7399 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7400 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7401 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7402 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7403 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7404 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7406 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7407 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7408 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7409 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7410 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7411 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7412 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7413 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7414 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7415 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7416 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7417 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7418 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7419 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7421 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7422 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7423 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7424 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7425 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7427 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7428 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7430 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7431 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7432 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7433 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7434 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7435 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7436 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7437 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7438 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7439 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7440 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7442 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7443 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7445 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7446 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7447 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7448 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7450 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7451 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7452 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7453 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7454 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7456 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7457 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7458 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7459 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7460 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7461 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7463 /* Channel options */
7464 DEFINE_CHANNEL_OPTION(defaulttopic);
7465 DEFINE_CHANNEL_OPTION(topicmask);
7466 DEFINE_CHANNEL_OPTION(greeting);
7467 DEFINE_CHANNEL_OPTION(usergreeting);
7468 DEFINE_CHANNEL_OPTION(modes);
7469 DEFINE_CHANNEL_OPTION(enfops);
7470 DEFINE_CHANNEL_OPTION(giveops);
7471 DEFINE_CHANNEL_OPTION(protect);
7472 DEFINE_CHANNEL_OPTION(enfmodes);
7473 DEFINE_CHANNEL_OPTION(enftopic);
7474 DEFINE_CHANNEL_OPTION(pubcmd);
7475 DEFINE_CHANNEL_OPTION(givevoice);
7476 DEFINE_CHANNEL_OPTION(userinfo);
7477 DEFINE_CHANNEL_OPTION(dynlimit);
7478 DEFINE_CHANNEL_OPTION(topicsnarf);
7479 DEFINE_CHANNEL_OPTION(nodelete);
7480 DEFINE_CHANNEL_OPTION(toys);
7481 DEFINE_CHANNEL_OPTION(setters);
7482 DEFINE_CHANNEL_OPTION(topicrefresh);
7483 DEFINE_CHANNEL_OPTION(ctcpusers);
7484 DEFINE_CHANNEL_OPTION(ctcpreaction);
7485 DEFINE_CHANNEL_OPTION(inviteme);
7487 DEFINE_CHANNEL_OPTION(offchannel);
7488 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7490 /* Alias set topic to set defaulttopic for compatibility. */
7491 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7494 DEFINE_USER_OPTION(noautoop);
7495 DEFINE_USER_OPTION(autoinvite);
7496 DEFINE_USER_OPTION(info);
7498 /* Alias uset autovoice to uset autoop. */
7499 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7501 note_types = dict_new();
7502 dict_set_free_data(note_types, chanserv_deref_note_type);
7505 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7506 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7507 service_register(chanserv)->trigger = '!';
7508 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7510 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7512 if(chanserv_conf.channel_expire_frequency)
7513 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7515 if(chanserv_conf.dnr_expire_frequency)
7516 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7518 if(chanserv_conf.refresh_period)
7520 time_t next_refresh;
7521 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7522 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7525 reg_exit_func(chanserv_db_cleanup);
7526 message_register_table(msgtab);