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 ago 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, unsigned int max)
1524 struct dnrList list;
1525 dict_iterator_t it, next;
1526 struct do_not_register *dnr;
1528 dnrList_init(&list);
1530 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1532 if(dnr->expires && dnr->expires <= now)
1533 dict_remove(handle_dnrs, handle);
1534 else if (list.used < max)
1535 dnrList_append(&list, dnr);
1538 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1540 if(dnr->expires && dnr->expires <= now)
1541 dict_remove(plain_dnrs, chan_name);
1542 else if (list.used < max)
1543 dnrList_append(&list, dnr);
1548 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1550 next = iter_next(it);
1551 if(!match_ircglob(chan_name, iter_key(it)))
1553 dnr = iter_data(it);
1554 if(dnr->expires && dnr->expires <= now)
1555 dict_remove(mask_dnrs, iter_key(it));
1557 dnrList_append(&list, dnr);
1565 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1567 struct dnrList list;
1568 struct do_not_register *dnr;
1570 char buf[INTERVALLEN], buf2[INTERVALLEN];
1572 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1573 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1575 dnr = list.list[ii];
1577 intervalString(buf2, dnr->expires - now, user->handle_info);
1581 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1582 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1585 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, (dnr->expires ? buf2 : "never"), dnr->reason);
1588 reply("CSMSG_MORE_DNRS", list.used - ii);
1593 struct do_not_register *
1594 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1596 struct dnrList list;
1597 struct do_not_register *dnr;
1599 list = chanserv_find_dnrs(chan_name, handle->handle, 1);
1600 dnr = list.used ? list.list[0] : NULL;
1605 static unsigned int send_dnrs(struct userNode *user, struct svccmd *cmd, dict_t dict)
1607 struct do_not_register *dnr;
1608 dict_iterator_t it, next;
1609 unsigned int matches = 0;
1610 char buf[INTERVALLEN];
1611 char buf2[INTERVALLEN];
1613 for(it = dict_first(dict); it; it = next)
1615 dnr = iter_data(it);
1616 next = iter_next(it);
1617 if(dnr->expires && dnr->expires <= now)
1619 dict_remove(dict, iter_key(it));
1623 intervalString(buf2, dnr->expires - now, user->handle_info);
1625 strcpy(buf2, "never");
1628 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, buf2, dnr->reason);
1630 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, buf2, dnr->reason);
1637 static CHANSERV_FUNC(cmd_noregister)
1640 time_t expiry, duration;
1641 unsigned int matches;
1645 reply("CSMSG_DNR_SEARCH_RESULTS");
1646 matches = send_dnrs(user, cmd, handle_dnrs);
1647 matches += send_dnrs(user, cmd, plain_dnrs);
1648 matches += send_dnrs(user, cmd, mask_dnrs);
1650 reply("MSG_MATCH_COUNT", matches);
1652 reply("MSG_NO_MATCHES");
1658 if(!IsChannelName(target) && (*target != '*'))
1660 reply("CSMSG_NOT_DNR", target);
1668 reply("MSG_INVALID_DURATION", argv[2]);
1672 if(!strcmp(argv[2], "0"))
1674 else if((duration = ParseInterval(argv[2])))
1675 expiry = now + duration;
1678 reply("MSG_INVALID_DURATION", argv[2]);
1682 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1683 if((*target == '*') && !get_handle_info(target + 1))
1685 reply("MSG_HANDLE_UNKNOWN", target + 1);
1688 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1689 reply("CSMSG_NOREGISTER_CHANNEL", target);
1693 reply("CSMSG_DNR_SEARCH_RESULTS");
1695 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1697 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1699 reply("MSG_NO_MATCHES");
1703 static CHANSERV_FUNC(cmd_allowregister)
1705 const char *chan_name = argv[1];
1707 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1709 dict_remove(handle_dnrs, chan_name+1);
1710 reply("CSMSG_DNR_REMOVED", chan_name);
1712 else if(dict_find(plain_dnrs, chan_name, NULL))
1714 dict_remove(plain_dnrs, chan_name);
1715 reply("CSMSG_DNR_REMOVED", chan_name);
1717 else if(dict_find(mask_dnrs, chan_name, NULL))
1719 dict_remove(mask_dnrs, chan_name);
1720 reply("CSMSG_DNR_REMOVED", chan_name);
1724 reply("CSMSG_NO_SUCH_DNR", chan_name);
1731 chanserv_get_owned_count(struct handle_info *hi)
1733 struct userData *cList;
1736 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1737 if(cList->access == UL_OWNER)
1742 static CHANSERV_FUNC(cmd_register)
1744 struct handle_info *handle;
1745 struct chanData *cData;
1746 struct modeNode *mn;
1747 char reason[MAXLEN];
1749 unsigned int new_channel, force=0;
1750 struct do_not_register *dnr;
1754 if(channel->channel_info)
1756 reply("CSMSG_ALREADY_REGGED", channel->name);
1760 if(channel->bad_channel)
1762 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1767 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1769 reply("CSMSG_MUST_BE_OPPED", channel->name);
1774 chan_name = channel->name;
1778 if((argc < 2) || !IsChannelName(argv[1]))
1780 reply("MSG_NOT_CHANNEL_NAME");
1784 if(opserv_bad_channel(argv[1]))
1786 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1791 chan_name = argv[1];
1794 if(argc >= (new_channel+2))
1796 if(!IsHelping(user))
1798 reply("CSMSG_PROXY_FORBIDDEN");
1802 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1804 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1805 dnr = chanserv_is_dnr(chan_name, handle);
1809 handle = user->handle_info;
1810 dnr = chanserv_is_dnr(chan_name, handle);
1814 if(!IsHelping(user))
1815 reply("CSMSG_DNR_CHANNEL", chan_name);
1817 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1821 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1823 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1828 channel = AddChannel(argv[1], now, NULL, NULL);
1830 cData = register_channel(channel, user->handle_info->handle);
1831 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1832 cData->modes = chanserv_conf.default_modes;
1834 cData->modes.modes_set |= MODE_REGISTERED;
1835 if (IsOffChannel(cData))
1837 mod_chanmode_announce(chanserv, channel, &cData->modes);
1841 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1842 change->args[change->argc].mode = MODE_CHANOP;
1843 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1845 mod_chanmode_announce(chanserv, channel, change);
1846 mod_chanmode_free(change);
1849 /* Initialize the channel's max user record. */
1850 cData->max = channel->members.used;
1852 if(handle != user->handle_info)
1853 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1855 reply("CSMSG_REG_SUCCESS", channel->name);
1857 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1858 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1863 make_confirmation_string(struct userData *uData)
1865 static char strbuf[16];
1870 for(src = uData->handle->handle; *src; )
1871 accum = accum * 31 + toupper(*src++);
1873 for(src = uData->channel->channel->name; *src; )
1874 accum = accum * 31 + toupper(*src++);
1875 sprintf(strbuf, "%08x", accum);
1879 static CHANSERV_FUNC(cmd_unregister)
1882 char reason[MAXLEN];
1883 struct chanData *cData;
1884 struct userData *uData;
1886 cData = channel->channel_info;
1889 reply("CSMSG_NOT_REGISTERED", channel->name);
1893 uData = GetChannelUser(cData, user->handle_info);
1894 if(!uData || (uData->access < UL_OWNER))
1896 reply("CSMSG_NO_ACCESS");
1900 if(IsProtected(cData))
1902 reply("CSMSG_UNREG_NODELETE", channel->name);
1906 if(!IsHelping(user))
1908 const char *confirm_string;
1909 if(IsSuspended(cData))
1911 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1914 confirm_string = make_confirmation_string(uData);
1915 if((argc < 2) || strcmp(argv[1], confirm_string))
1917 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1922 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1923 name = strdup(channel->name);
1924 unregister_channel(cData, reason);
1925 reply("CSMSG_UNREG_SUCCESS", name);
1930 static CHANSERV_FUNC(cmd_move)
1932 struct mod_chanmode change;
1933 struct chanNode *target;
1934 struct modeNode *mn;
1935 struct userData *uData;
1936 char reason[MAXLEN];
1937 struct do_not_register *dnr;
1941 if(IsProtected(channel->channel_info))
1943 reply("CSMSG_MOVE_NODELETE", channel->name);
1947 if(!IsChannelName(argv[1]))
1949 reply("MSG_NOT_CHANNEL_NAME");
1953 if(opserv_bad_channel(argv[1]))
1955 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1959 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1961 for(uData = channel->channel_info->users; uData; uData = uData->next)
1963 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1965 if(!IsHelping(user))
1966 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1968 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1974 mod_chanmode_init(&change);
1975 if(!(target = GetChannel(argv[1])))
1977 target = AddChannel(argv[1], now, NULL, NULL);
1978 if(!IsSuspended(channel->channel_info))
1979 AddChannelUser(chanserv, target);
1981 else if(target->channel_info)
1983 reply("CSMSG_ALREADY_REGGED", target->name);
1986 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1987 && !IsHelping(user))
1989 reply("CSMSG_MUST_BE_OPPED", target->name);
1992 else if(!IsSuspended(channel->channel_info))
1995 change.args[0].mode = MODE_CHANOP;
1996 change.args[0].u.member = AddChannelUser(chanserv, target);
1997 mod_chanmode_announce(chanserv, target, &change);
2002 /* Clear MODE_REGISTERED from old channel, add it to new. */
2004 change.modes_clear = MODE_REGISTERED;
2005 mod_chanmode_announce(chanserv, channel, &change);
2006 change.modes_clear = 0;
2007 change.modes_set = MODE_REGISTERED;
2008 mod_chanmode_announce(chanserv, target, &change);
2011 /* Move the channel_info to the target channel; it
2012 shouldn't be necessary to clear timeq callbacks
2013 for the old channel. */
2014 target->channel_info = channel->channel_info;
2015 target->channel_info->channel = target;
2016 channel->channel_info = NULL;
2018 reply("CSMSG_MOVE_SUCCESS", target->name);
2020 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2021 if(!IsSuspended(target->channel_info))
2023 char reason2[MAXLEN];
2024 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2025 DelChannelUser(chanserv, channel, reason2, 0);
2027 UnlockChannel(channel);
2028 LockChannel(target);
2029 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2034 merge_users(struct chanData *source, struct chanData *target)
2036 struct userData *suData, *tuData, *next;
2042 /* Insert the source's users into the scratch area. */
2043 for(suData = source->users; suData; suData = suData->next)
2044 dict_insert(merge, suData->handle->handle, suData);
2046 /* Iterate through the target's users, looking for
2047 users common to both channels. The lower access is
2048 removed from either the scratch area or target user
2050 for(tuData = target->users; tuData; tuData = next)
2052 struct userData *choice;
2054 next = tuData->next;
2056 /* If a source user exists with the same handle as a target
2057 channel's user, resolve the conflict by removing one. */
2058 suData = dict_find(merge, tuData->handle->handle, NULL);
2062 /* Pick the data we want to keep. */
2063 /* If the access is the same, use the later seen time. */
2064 if(suData->access == tuData->access)
2065 choice = (suData->seen > tuData->seen) ? suData : tuData;
2066 else /* Otherwise, keep the higher access level. */
2067 choice = (suData->access > tuData->access) ? suData : tuData;
2069 /* Remove the user that wasn't picked. */
2070 if(choice == tuData)
2072 dict_remove(merge, suData->handle->handle);
2073 del_channel_user(suData, 0);
2076 del_channel_user(tuData, 0);
2079 /* Move the remaining users to the target channel. */
2080 for(it = dict_first(merge); it; it = iter_next(it))
2082 suData = iter_data(it);
2084 /* Insert the user into the target channel's linked list. */
2085 suData->prev = NULL;
2086 suData->next = target->users;
2087 suData->channel = target;
2090 target->users->prev = suData;
2091 target->users = suData;
2093 /* Update the user counts for the target channel; the
2094 source counts are left alone. */
2095 target->userCount++;
2098 /* Possible to assert (source->users == NULL) here. */
2099 source->users = NULL;
2104 merge_bans(struct chanData *source, struct chanData *target)
2106 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2108 /* Hold on to the original head of the target ban list
2109 to avoid comparing source bans with source bans. */
2110 tFront = target->bans;
2112 /* Perform a totally expensive O(n*m) merge, ick. */
2113 for(sbData = source->bans; sbData; sbData = sNext)
2115 /* Flag to track whether the ban's been moved
2116 to the destination yet. */
2119 /* Possible to assert (sbData->prev == NULL) here. */
2120 sNext = sbData->next;
2122 for(tbData = tFront; tbData; tbData = tNext)
2124 tNext = tbData->next;
2126 /* Perform two comparisons between each source
2127 and target ban, conflicts are resolved by
2128 keeping the broader ban and copying the later
2129 expiration and triggered time. */
2130 if(match_ircglobs(tbData->mask, sbData->mask))
2132 /* There is a broader ban in the target channel that
2133 overrides one in the source channel; remove the
2134 source ban and break. */
2135 if(sbData->expires > tbData->expires)
2136 tbData->expires = sbData->expires;
2137 if(sbData->triggered > tbData->triggered)
2138 tbData->triggered = sbData->triggered;
2139 del_channel_ban(sbData);
2142 else if(match_ircglobs(sbData->mask, tbData->mask))
2144 /* There is a broader ban in the source channel that
2145 overrides one in the target channel; remove the
2146 target ban, fall through and move the source over. */
2147 if(tbData->expires > sbData->expires)
2148 sbData->expires = tbData->expires;
2149 if(tbData->triggered > sbData->triggered)
2150 sbData->triggered = tbData->triggered;
2151 if(tbData == tFront)
2153 del_channel_ban(tbData);
2156 /* Source bans can override multiple target bans, so
2157 we allow a source to run through this loop multiple
2158 times, but we can only move it once. */
2163 /* Remove the source ban from the source ban list. */
2165 sbData->next->prev = sbData->prev;
2167 /* Modify the source ban's associated channel. */
2168 sbData->channel = target;
2170 /* Insert the ban into the target channel's linked list. */
2171 sbData->prev = NULL;
2172 sbData->next = target->bans;
2175 target->bans->prev = sbData;
2176 target->bans = sbData;
2178 /* Update the user counts for the target channel. */
2183 /* Possible to assert (source->bans == NULL) here. */
2184 source->bans = NULL;
2188 merge_data(struct chanData *source, struct chanData *target)
2190 /* Use more recent visited and owner-transfer time; use older
2191 * registered time. Bitwise or may_opchan. Use higher max.
2192 * Do not touch last_refresh, ban count or user counts.
2194 if(source->visited > target->visited)
2195 target->visited = source->visited;
2196 if(source->registered < target->registered)
2197 target->registered = source->registered;
2198 if(source->ownerTransfer > target->ownerTransfer)
2199 target->ownerTransfer = source->ownerTransfer;
2200 if(source->may_opchan)
2201 target->may_opchan = 1;
2202 if(source->max > target->max)
2203 target->max = source->max;
2207 merge_channel(struct chanData *source, struct chanData *target)
2209 merge_users(source, target);
2210 merge_bans(source, target);
2211 merge_data(source, target);
2214 static CHANSERV_FUNC(cmd_merge)
2216 struct userData *target_user;
2217 struct chanNode *target;
2218 char reason[MAXLEN];
2222 /* Make sure the target channel exists and is registered to the user
2223 performing the command. */
2224 if(!(target = GetChannel(argv[1])))
2226 reply("MSG_INVALID_CHANNEL");
2230 if(!target->channel_info)
2232 reply("CSMSG_NOT_REGISTERED", target->name);
2236 if(IsProtected(channel->channel_info))
2238 reply("CSMSG_MERGE_NODELETE");
2242 if(IsSuspended(target->channel_info))
2244 reply("CSMSG_MERGE_SUSPENDED");
2248 if(channel == target)
2250 reply("CSMSG_MERGE_SELF");
2254 target_user = GetChannelUser(target->channel_info, user->handle_info);
2255 if(!target_user || (target_user->access < UL_OWNER))
2257 reply("CSMSG_MERGE_NOT_OWNER");
2261 /* Merge the channel structures and associated data. */
2262 merge_channel(channel->channel_info, target->channel_info);
2263 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2264 unregister_channel(channel->channel_info, reason);
2265 reply("CSMSG_MERGE_SUCCESS", target->name);
2269 static CHANSERV_FUNC(cmd_opchan)
2271 struct mod_chanmode change;
2272 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2274 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2277 channel->channel_info->may_opchan = 0;
2278 mod_chanmode_init(&change);
2280 change.args[0].mode = MODE_CHANOP;
2281 change.args[0].u.member = GetUserMode(channel, chanserv);
2282 mod_chanmode_announce(chanserv, channel, &change);
2283 reply("CSMSG_OPCHAN_DONE", channel->name);
2287 static CHANSERV_FUNC(cmd_adduser)
2289 struct userData *actee;
2290 struct userData *actor, *real_actor;
2291 struct handle_info *handle;
2292 unsigned short access, override = 0;
2296 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2298 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2302 access = user_level_from_name(argv[2], UL_OWNER);
2305 reply("CSMSG_INVALID_ACCESS", argv[2]);
2309 actor = GetChannelUser(channel->channel_info, user->handle_info);
2310 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2312 if(actor->access <= access)
2314 reply("CSMSG_NO_BUMP_ACCESS");
2318 /* Trying to add someone with equal/more access? */
2319 if (!real_actor || real_actor->access <= access)
2320 override = CMD_LOG_OVERRIDE;
2322 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2325 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2327 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2331 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2332 scan_user_presence(actee, NULL);
2333 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2334 return 1 | override;
2337 static CHANSERV_FUNC(cmd_clvl)
2339 struct handle_info *handle;
2340 struct userData *victim;
2341 struct userData *actor, *real_actor;
2342 unsigned short new_access, override = 0;
2343 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2347 actor = GetChannelUser(channel->channel_info, user->handle_info);
2348 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2350 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2353 if(handle == user->handle_info && !privileged)
2355 reply("CSMSG_NO_SELF_CLVL");
2359 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2361 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2365 if(actor->access <= victim->access && !privileged)
2367 reply("MSG_USER_OUTRANKED", handle->handle);
2371 new_access = user_level_from_name(argv[2], UL_OWNER);
2375 reply("CSMSG_INVALID_ACCESS", argv[2]);
2379 if(new_access >= actor->access && !privileged)
2381 reply("CSMSG_NO_BUMP_ACCESS");
2385 /* Trying to clvl a equal/higher user? */
2386 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2387 override = CMD_LOG_OVERRIDE;
2388 /* Trying to clvl someone to equal/higher access? */
2389 if(!real_actor || new_access >= real_actor->access)
2390 override = CMD_LOG_OVERRIDE;
2391 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2392 * If they lower their own access it's not a big problem.
2395 victim->access = new_access;
2396 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2397 return 1 | override;
2400 static CHANSERV_FUNC(cmd_deluser)
2402 struct handle_info *handle;
2403 struct userData *victim;
2404 struct userData *actor, *real_actor;
2405 unsigned short access, override = 0;
2410 actor = GetChannelUser(channel->channel_info, user->handle_info);
2411 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2413 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2416 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2418 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2424 access = user_level_from_name(argv[1], UL_OWNER);
2427 reply("CSMSG_INVALID_ACCESS", argv[1]);
2430 if(access != victim->access)
2432 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2438 access = victim->access;
2441 if((actor->access <= victim->access) && !IsHelping(user))
2443 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2447 /* If people delete themselves it is an override, but they
2448 * could've used deleteme so we don't log it as an override
2450 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2451 override = CMD_LOG_OVERRIDE;
2453 chan_name = strdup(channel->name);
2454 del_channel_user(victim, 1);
2455 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2457 return 1 | override;
2461 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2463 struct userData *actor, *real_actor, *uData, *next;
2464 unsigned int override = 0;
2466 actor = GetChannelUser(channel->channel_info, user->handle_info);
2467 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2469 if(min_access > max_access)
2471 reply("CSMSG_BAD_RANGE", min_access, max_access);
2475 if((actor->access <= max_access) && !IsHelping(user))
2477 reply("CSMSG_NO_ACCESS");
2481 if(!real_actor || real_actor->access <= max_access)
2482 override = CMD_LOG_OVERRIDE;
2484 for(uData = channel->channel_info->users; uData; uData = next)
2488 if((uData->access >= min_access)
2489 && (uData->access <= max_access)
2490 && match_ircglob(uData->handle->handle, mask))
2491 del_channel_user(uData, 1);
2494 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2495 return 1 | override;
2498 static CHANSERV_FUNC(cmd_mdelowner)
2500 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2503 static CHANSERV_FUNC(cmd_mdelcoowner)
2505 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2508 static CHANSERV_FUNC(cmd_mdelmaster)
2510 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2513 static CHANSERV_FUNC(cmd_mdelop)
2515 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2518 static CHANSERV_FUNC(cmd_mdelpeon)
2520 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2524 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2526 struct banData *bData, *next;
2527 char interval[INTERVALLEN];
2532 limit = now - duration;
2533 for(bData = channel->channel_info->bans; bData; bData = next)
2537 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2540 del_channel_ban(bData);
2544 intervalString(interval, duration, user->handle_info);
2545 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2550 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2552 struct userData *actor, *uData, *next;
2553 char interval[INTERVALLEN];
2557 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2558 if(min_access > max_access)
2560 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2564 if(!actor || actor->access <= max_access)
2566 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2571 limit = now - duration;
2572 for(uData = channel->channel_info->users; uData; uData = next)
2576 if((uData->seen > limit)
2578 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2581 if(((uData->access >= min_access) && (uData->access <= max_access))
2582 || (!max_access && (uData->access < actor->access)))
2584 del_channel_user(uData, 1);
2592 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2594 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2598 static CHANSERV_FUNC(cmd_trim)
2600 unsigned long duration;
2601 unsigned short min_level, max_level;
2606 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2607 duration = ParseInterval(argv[2]);
2610 reply("CSMSG_CANNOT_TRIM");
2614 if(!irccasecmp(argv[1], "bans"))
2616 cmd_trim_bans(user, channel, duration);
2619 else if(!irccasecmp(argv[1], "users"))
2621 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2624 else if(parse_level_range(&min_level, &max_level, argv[1]))
2626 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2629 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2631 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2636 reply("CSMSG_INVALID_TRIM", argv[1]);
2641 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2642 to the user. cmd_all takes advantage of this. */
2643 static CHANSERV_FUNC(cmd_up)
2645 struct mod_chanmode change;
2646 struct userData *uData;
2649 mod_chanmode_init(&change);
2651 change.args[0].u.member = GetUserMode(channel, user);
2652 if(!change.args[0].u.member)
2655 reply("MSG_CHANNEL_ABSENT", channel->name);
2659 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2663 reply("CSMSG_GODMODE_UP", argv[0]);
2666 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2668 change.args[0].mode = MODE_CHANOP;
2669 errmsg = "CSMSG_ALREADY_OPPED";
2671 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2673 change.args[0].mode = MODE_VOICE;
2674 errmsg = "CSMSG_ALREADY_VOICED";
2679 reply("CSMSG_NO_ACCESS");
2682 change.args[0].mode &= ~change.args[0].u.member->modes;
2683 if(!change.args[0].mode)
2686 reply(errmsg, channel->name);
2689 modcmd_chanmode_announce(&change);
2693 static CHANSERV_FUNC(cmd_down)
2695 struct mod_chanmode change;
2697 mod_chanmode_init(&change);
2699 change.args[0].u.member = GetUserMode(channel, user);
2700 if(!change.args[0].u.member)
2703 reply("MSG_CHANNEL_ABSENT", channel->name);
2707 if(!change.args[0].u.member->modes)
2710 reply("CSMSG_ALREADY_DOWN", channel->name);
2714 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2715 modcmd_chanmode_announce(&change);
2719 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)
2721 struct userData *cList;
2723 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2725 if(IsSuspended(cList->channel)
2726 || IsUserSuspended(cList)
2727 || !GetUserMode(cList->channel->channel, user))
2730 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2736 static CHANSERV_FUNC(cmd_upall)
2738 return cmd_all(CSFUNC_ARGS, cmd_up);
2741 static CHANSERV_FUNC(cmd_downall)
2743 return cmd_all(CSFUNC_ARGS, cmd_down);
2746 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2747 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2750 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)
2752 unsigned int ii, valid;
2753 struct userNode *victim;
2754 struct mod_chanmode *change;
2756 change = mod_chanmode_alloc(argc - 1);
2758 for(ii=valid=0; ++ii < argc; )
2760 if(!(victim = GetUserH(argv[ii])))
2762 change->args[valid].mode = mode;
2763 change->args[valid].u.member = GetUserMode(channel, victim);
2764 if(!change->args[valid].u.member)
2766 if(validate && !validate(user, channel, victim))
2771 change->argc = valid;
2772 if(valid < (argc-1))
2773 reply("CSMSG_PROCESS_FAILED");
2776 modcmd_chanmode_announce(change);
2777 reply(action, channel->name);
2779 mod_chanmode_free(change);
2783 static CHANSERV_FUNC(cmd_op)
2785 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2788 static CHANSERV_FUNC(cmd_deop)
2790 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2793 static CHANSERV_FUNC(cmd_voice)
2795 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2798 static CHANSERV_FUNC(cmd_devoice)
2800 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2804 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2810 for(ii=0; ii<channel->members.used; ii++)
2812 struct modeNode *mn = channel->members.list[ii];
2814 if(IsService(mn->user))
2817 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
2820 if(protect_user(mn->user, user, channel->channel_info))
2824 victims[(*victimCount)++] = mn;
2830 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2832 struct userNode *victim;
2833 struct modeNode **victims;
2834 unsigned int offset, n, victimCount, duration = 0;
2835 char *reason = "Bye.", *ban, *name;
2836 char interval[INTERVALLEN];
2838 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2839 REQUIRE_PARAMS(offset);
2842 reason = unsplit_string(argv + offset, argc - offset, NULL);
2843 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2845 /* Truncate the reason to a length of TOPICLEN, as
2846 the ircd does; however, leave room for an ellipsis
2847 and the kicker's nick. */
2848 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2852 if((victim = GetUserH(argv[1])))
2854 victims = alloca(sizeof(victims[0]));
2855 victims[0] = GetUserMode(channel, victim);
2856 /* XXX: The comparison with ACTION_KICK is just because all
2857 * other actions can work on users outside the channel, and we
2858 * want to allow those (e.g. unbans) in that case. If we add
2859 * some other ejection action for in-channel users, change
2861 victimCount = victims[0] ? 1 : 0;
2863 if(IsService(victim))
2865 reply("MSG_SERVICE_IMMUNE", victim->nick);
2869 if((action == ACTION_KICK) && !victimCount)
2871 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2875 if(protect_user(victim, user, channel->channel_info))
2877 reply("CSMSG_USER_PROTECTED", victim->nick);
2881 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2882 name = victim->nick;
2886 if(!is_ircmask(argv[1]))
2888 reply("MSG_NICK_UNKNOWN", argv[1]);
2892 victims = alloca(sizeof(victims[0]) * channel->members.used);
2894 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2896 reply("CSMSG_MASK_PROTECTED", argv[1]);
2900 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2902 reply("CSMSG_LAME_MASK", argv[1]);
2906 if((action == ACTION_KICK) && (victimCount == 0))
2908 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2912 name = ban = strdup(argv[1]);
2915 /* Truncate the ban in place if necessary; we must ensure
2916 that 'ban' is a valid ban mask before sanitizing it. */
2917 sanitize_ircmask(ban);
2919 if(action & ACTION_ADD_BAN)
2921 struct banData *bData, *next;
2923 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2925 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2930 if(action & ACTION_ADD_TIMED_BAN)
2932 duration = ParseInterval(argv[2]);
2936 reply("CSMSG_DURATION_TOO_LOW");
2940 else if(duration > (86400 * 365 * 2))
2942 reply("CSMSG_DURATION_TOO_HIGH");
2948 for(bData = channel->channel_info->bans; bData; bData = next)
2950 if(match_ircglobs(bData->mask, ban))
2952 int exact = !irccasecmp(bData->mask, ban);
2954 /* The ban is redundant; there is already a ban
2955 with the same effect in place. */
2959 free(bData->reason);
2960 bData->reason = strdup(reason);
2961 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2963 reply("CSMSG_REASON_CHANGE", ban);
2967 if(exact && bData->expires)
2971 /* If the ban matches an existing one exactly,
2972 extend the expiration time if the provided
2973 duration is longer. */
2974 if(duration && ((time_t)(now + duration) > bData->expires))
2976 bData->expires = now + duration;
2987 /* Delete the expiration timeq entry and
2988 requeue if necessary. */
2989 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2992 timeq_add(bData->expires, expire_ban, bData);
2996 /* automated kickban */
2999 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3001 reply("CSMSG_BAN_ADDED", name, channel->name);
3007 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3014 if(match_ircglobs(ban, bData->mask))
3016 /* The ban we are adding makes previously existing
3017 bans redundant; silently remove them. */
3018 del_channel_ban(bData);
3022 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);
3024 name = ban = strdup(bData->mask);
3028 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3030 extern const char *hidden_host_suffix;
3031 const char *old_name = chanserv_conf.old_ban_names->list[n];
3033 unsigned int l1, l2;
3036 l2 = strlen(old_name);
3039 if(irccasecmp(ban + l1 - l2, old_name))
3041 new_mask = malloc(MAXLEN);
3042 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3044 name = ban = new_mask;
3049 if(action & ACTION_BAN)
3051 unsigned int exists;
3052 struct mod_chanmode *change;
3054 if(channel->banlist.used >= MAXBANS)
3057 reply("CSMSG_BANLIST_FULL", channel->name);
3062 exists = ChannelBanExists(channel, ban);
3063 change = mod_chanmode_alloc(victimCount + 1);
3064 for(n = 0; n < victimCount; ++n)
3066 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3067 change->args[n].u.member = victims[n];
3071 change->args[n].mode = MODE_BAN;
3072 change->args[n++].u.hostmask = ban;
3076 modcmd_chanmode_announce(change);
3078 mod_chanmode_announce(chanserv, channel, change);
3079 mod_chanmode_free(change);
3081 if(exists && (action == ACTION_BAN))
3084 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3090 if(action & ACTION_KICK)
3092 char kick_reason[MAXLEN];
3093 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3095 for(n = 0; n < victimCount; n++)
3096 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3101 /* No response, since it was automated. */
3103 else if(action & ACTION_ADD_BAN)
3106 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3108 reply("CSMSG_BAN_ADDED", name, channel->name);
3110 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3111 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3112 else if(action & ACTION_BAN)
3113 reply("CSMSG_BAN_DONE", name, channel->name);
3114 else if(action & ACTION_KICK && victimCount)
3115 reply("CSMSG_KICK_DONE", name, channel->name);
3121 static CHANSERV_FUNC(cmd_kickban)
3123 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3126 static CHANSERV_FUNC(cmd_kick)
3128 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3131 static CHANSERV_FUNC(cmd_ban)
3133 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3136 static CHANSERV_FUNC(cmd_addban)
3138 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3141 static CHANSERV_FUNC(cmd_addtimedban)
3143 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3146 static struct mod_chanmode *
3147 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3149 struct mod_chanmode *change;
3150 unsigned char *match;
3151 unsigned int ii, count;
3153 match = alloca(bans->used);
3156 for(ii = count = 0; ii < bans->used; ++ii)
3158 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3159 MATCH_USENICK | MATCH_VISIBLE);
3166 for(ii = count = 0; ii < bans->used; ++ii)
3168 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3175 change = mod_chanmode_alloc(count);
3176 for(ii = count = 0; ii < bans->used; ++ii)
3180 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3181 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3183 assert(count == change->argc);
3188 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3190 struct userNode *actee;
3196 /* may want to allow a comma delimited list of users... */
3197 if(!(actee = GetUserH(argv[1])))
3199 if(!is_ircmask(argv[1]))
3201 reply("MSG_NICK_UNKNOWN", argv[1]);
3205 mask = strdup(argv[1]);
3208 /* We don't sanitize the mask here because ircu
3210 if(action & ACTION_UNBAN)
3212 struct mod_chanmode *change;
3213 change = find_matching_bans(&channel->banlist, actee, mask);
3218 modcmd_chanmode_announce(change);
3219 for(ii = 0; ii < change->argc; ++ii)
3220 free((char*)change->args[ii].u.hostmask);
3221 mod_chanmode_free(change);
3226 if(action & ACTION_DEL_BAN)
3228 struct banData *ban, *next;
3230 ban = channel->channel_info->bans;
3234 for( ; ban && !user_matches_glob(actee, ban->mask,
3235 MATCH_USENICK | MATCH_VISIBLE);
3238 for( ; ban && !match_ircglobs(mask, ban->mask);
3243 del_channel_ban(ban);
3250 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3252 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3258 static CHANSERV_FUNC(cmd_unban)
3260 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3263 static CHANSERV_FUNC(cmd_delban)
3265 /* it doesn't necessarily have to remove the channel ban - may want
3266 to make that an option. */
3267 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3270 static CHANSERV_FUNC(cmd_unbanme)
3272 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3273 long flags = ACTION_UNBAN;
3275 /* remove permanent bans if the user has the proper access. */
3276 if(uData->access >= UL_MASTER)
3277 flags |= ACTION_DEL_BAN;
3279 argv[1] = user->nick;
3280 return unban_user(user, channel, 2, argv, cmd, flags);
3283 static CHANSERV_FUNC(cmd_unbanall)
3285 struct mod_chanmode *change;
3288 if(!channel->banlist.used)
3290 reply("CSMSG_NO_BANS", channel->name);
3294 change = mod_chanmode_alloc(channel->banlist.used);
3295 for(ii=0; ii<channel->banlist.used; ii++)
3297 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3298 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3300 modcmd_chanmode_announce(change);
3301 for(ii = 0; ii < change->argc; ++ii)
3302 free((char*)change->args[ii].u.hostmask);
3303 mod_chanmode_free(change);
3304 reply("CSMSG_BANS_REMOVED", channel->name);
3308 static CHANSERV_FUNC(cmd_open)
3310 struct mod_chanmode *change;
3313 change = find_matching_bans(&channel->banlist, user, NULL);
3315 change = mod_chanmode_alloc(0);
3316 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3317 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3318 && channel->channel_info->modes.modes_set)
3319 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3320 modcmd_chanmode_announce(change);
3321 reply("CSMSG_CHANNEL_OPENED", channel->name);
3322 for(ii = 0; ii < change->argc; ++ii)
3323 free((char*)change->args[ii].u.hostmask);
3324 mod_chanmode_free(change);
3328 static CHANSERV_FUNC(cmd_myaccess)
3330 static struct string_buffer sbuf;
3331 struct handle_info *target_handle;
3332 struct userData *uData;
3335 target_handle = user->handle_info;
3336 else if(!IsHelping(user))
3338 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3341 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3344 if(!target_handle->channels)
3346 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3350 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3351 for(uData = target_handle->channels; uData; uData = uData->u_next)
3353 struct chanData *cData = uData->channel;
3355 if(uData->access > UL_OWNER)
3357 if(IsProtected(cData)
3358 && (target_handle != user->handle_info)
3359 && !GetTrueChannelAccess(cData, user->handle_info))
3362 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3363 if(uData->flags != USER_AUTO_OP)
3364 string_buffer_append(&sbuf, ',');
3365 if(IsUserSuspended(uData))
3366 string_buffer_append(&sbuf, 's');
3367 if(IsUserAutoOp(uData))
3369 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3370 string_buffer_append(&sbuf, 'o');
3371 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3372 string_buffer_append(&sbuf, 'v');
3374 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3375 string_buffer_append(&sbuf, 'i');
3377 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3379 string_buffer_append_string(&sbuf, ")]");
3380 string_buffer_append(&sbuf, '\0');
3381 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3387 static CHANSERV_FUNC(cmd_access)
3389 struct userNode *target;
3390 struct handle_info *target_handle;
3391 struct userData *uData;
3393 char prefix[MAXLEN];
3398 target_handle = target->handle_info;
3400 else if((target = GetUserH(argv[1])))
3402 target_handle = target->handle_info;
3404 else if(argv[1][0] == '*')
3406 if(!(target_handle = get_handle_info(argv[1]+1)))
3408 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3414 reply("MSG_NICK_UNKNOWN", argv[1]);
3418 assert(target || target_handle);
3420 if(target == chanserv)
3422 reply("CSMSG_IS_CHANSERV");
3430 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3435 reply("MSG_USER_AUTHENTICATE", target->nick);
3438 reply("MSG_AUTHENTICATE");
3444 const char *epithet = NULL, *type = NULL;
3447 epithet = chanserv_conf.irc_operator_epithet;
3448 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3450 else if(IsNetworkHelper(target))
3452 epithet = chanserv_conf.network_helper_epithet;
3453 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3455 else if(IsSupportHelper(target))
3457 epithet = chanserv_conf.support_helper_epithet;
3458 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3462 if(target_handle->epithet)
3463 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3465 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3467 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3471 sprintf(prefix, "%s", target_handle->handle);
3474 if(!channel->channel_info)
3476 reply("CSMSG_NOT_REGISTERED", channel->name);
3480 helping = HANDLE_FLAGGED(target_handle, HELPING)
3481 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3482 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3484 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3485 /* To prevent possible information leaks, only show infolines
3486 * if the requestor is in the channel or it's their own
3488 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3490 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3492 /* Likewise, only say it's suspended if the user has active
3493 * access in that channel or it's their own entry. */
3494 if(IsUserSuspended(uData)
3495 && (GetChannelUser(channel->channel_info, user->handle_info)
3496 || (user->handle_info == uData->handle)))
3498 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3503 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3510 zoot_list(struct listData *list)
3512 struct userData *uData;
3513 unsigned int start, curr, highest, lowest;
3514 struct helpfile_table tmp_table;
3515 const char **temp, *msg;
3517 if(list->table.length == 1)
3520 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3522 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3523 msg = user_find_message(list->user, "MSG_NONE");
3524 send_message_type(4, list->user, list->bot, " %s", msg);
3526 tmp_table.width = list->table.width;
3527 tmp_table.flags = list->table.flags;
3528 list->table.contents[0][0] = " ";
3529 highest = list->highest;
3530 if(list->lowest != 0)
3531 lowest = list->lowest;
3532 else if(highest < 100)
3535 lowest = highest - 100;
3536 for(start = curr = 1; curr < list->table.length; )
3538 uData = list->users[curr-1];
3539 list->table.contents[curr++][0] = " ";
3540 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3543 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3545 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3546 temp = list->table.contents[--start];
3547 list->table.contents[start] = list->table.contents[0];
3548 tmp_table.contents = list->table.contents + start;
3549 tmp_table.length = curr - start;
3550 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3551 list->table.contents[start] = temp;
3553 highest = lowest - 1;
3554 lowest = (highest < 100) ? 0 : (highest - 99);
3560 def_list(struct listData *list)
3564 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3566 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3567 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3568 if(list->table.length == 1)
3570 msg = user_find_message(list->user, "MSG_NONE");
3571 send_message_type(4, list->user, list->bot, " %s", msg);
3576 userData_access_comp(const void *arg_a, const void *arg_b)
3578 const struct userData *a = *(struct userData**)arg_a;
3579 const struct userData *b = *(struct userData**)arg_b;
3581 if(a->access != b->access)
3582 res = b->access - a->access;
3584 res = irccasecmp(a->handle->handle, b->handle->handle);
3589 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3591 void (*send_list)(struct listData *);
3592 struct userData *uData;
3593 struct listData lData;
3594 unsigned int matches;
3598 lData.bot = cmd->parent->bot;
3599 lData.channel = channel;
3600 lData.lowest = lowest;
3601 lData.highest = highest;
3602 lData.search = (argc > 1) ? argv[1] : NULL;
3603 send_list = def_list;
3604 (void)zoot_list; /* since it doesn't show user levels */
3606 if(user->handle_info)
3608 switch(user->handle_info->userlist_style)
3610 case HI_STYLE_DEF: send_list = def_list; break;
3611 case HI_STYLE_ZOOT: send_list = def_list; break;
3615 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3617 for(uData = channel->channel_info->users; uData; uData = uData->next)
3619 if((uData->access < lowest)
3620 || (uData->access > highest)
3621 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3623 lData.users[matches++] = uData;
3625 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3627 lData.table.length = matches+1;
3628 lData.table.width = 4;
3629 lData.table.flags = TABLE_NO_FREE;
3630 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3631 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3632 lData.table.contents[0] = ary;
3635 ary[2] = "Last Seen";
3637 for(matches = 1; matches < lData.table.length; ++matches)
3639 struct userData *uData = lData.users[matches-1];
3640 char seen[INTERVALLEN];
3642 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3643 lData.table.contents[matches] = ary;
3644 ary[0] = strtab(uData->access);
3645 ary[1] = uData->handle->handle;
3648 else if(!uData->seen)
3651 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3652 ary[2] = strdup(ary[2]);
3653 if(IsUserSuspended(uData))
3654 ary[3] = "Suspended";
3655 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3656 ary[3] = "Vacation";
3661 for(matches = 1; matches < lData.table.length; ++matches)
3663 free((char*)lData.table.contents[matches][2]);
3664 free(lData.table.contents[matches]);
3666 free(lData.table.contents[0]);
3667 free(lData.table.contents);
3671 static CHANSERV_FUNC(cmd_users)
3673 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3676 static CHANSERV_FUNC(cmd_wlist)
3678 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3681 static CHANSERV_FUNC(cmd_clist)
3683 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3686 static CHANSERV_FUNC(cmd_mlist)
3688 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3691 static CHANSERV_FUNC(cmd_olist)
3693 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3696 static CHANSERV_FUNC(cmd_plist)
3698 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3701 static CHANSERV_FUNC(cmd_bans)
3703 struct userNode *search_u = NULL;
3704 struct helpfile_table tbl;
3705 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3706 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3707 const char *msg_never, *triggered, *expires;
3708 struct banData *ban, **bans;
3712 else if(strchr(search = argv[1], '!'))
3715 search_wilds = search[strcspn(search, "?*")];
3717 else if(!(search_u = GetUserH(search)))
3718 reply("MSG_NICK_UNKNOWN", search);
3720 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3722 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3726 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3731 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3734 bans[matches++] = ban;
3739 tbl.length = matches + 1;
3740 tbl.width = 4 + timed;
3742 tbl.flags = TABLE_NO_FREE;
3743 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3744 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3745 tbl.contents[0][0] = "Mask";
3746 tbl.contents[0][1] = "Set By";
3747 tbl.contents[0][2] = "Triggered";
3750 tbl.contents[0][3] = "Expires";
3751 tbl.contents[0][4] = "Reason";
3754 tbl.contents[0][3] = "Reason";
3757 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3759 free(tbl.contents[0]);
3764 msg_never = user_find_message(user, "MSG_NEVER");
3765 for(ii = 0; ii < matches; )
3771 else if(ban->expires)
3772 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3774 expires = msg_never;
3777 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3779 triggered = msg_never;
3781 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3782 tbl.contents[ii][0] = ban->mask;
3783 tbl.contents[ii][1] = ban->owner;
3784 tbl.contents[ii][2] = strdup(triggered);
3787 tbl.contents[ii][3] = strdup(expires);
3788 tbl.contents[ii][4] = ban->reason;
3791 tbl.contents[ii][3] = ban->reason;
3793 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3794 reply("MSG_MATCH_COUNT", matches);
3795 for(ii = 1; ii < tbl.length; ++ii)
3797 free((char*)tbl.contents[ii][2]);
3799 free((char*)tbl.contents[ii][3]);
3800 free(tbl.contents[ii]);
3802 free(tbl.contents[0]);
3808 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3810 struct chanData *cData = channel->channel_info;
3811 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3813 if(cData->topic_mask)
3814 return !match_ircglob(new_topic, cData->topic_mask);
3815 else if(cData->topic)
3816 return irccasecmp(new_topic, cData->topic);
3821 static CHANSERV_FUNC(cmd_topic)
3823 struct chanData *cData;
3826 cData = channel->channel_info;
3831 SetChannelTopic(channel, chanserv, cData->topic, 1);
3832 reply("CSMSG_TOPIC_SET", cData->topic);
3836 reply("CSMSG_NO_TOPIC", channel->name);
3840 topic = unsplit_string(argv + 1, argc - 1, NULL);
3841 /* If they say "!topic *", use an empty topic. */
3842 if((topic[0] == '*') && (topic[1] == 0))
3844 if(bad_topic(channel, user, topic))
3846 char *topic_mask = cData->topic_mask;
3849 char new_topic[TOPICLEN+1], tchar;
3850 int pos=0, starpos=-1, dpos=0, len;
3852 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3859 len = strlen(topic);
3860 if((dpos + len) > TOPICLEN)
3861 len = TOPICLEN + 1 - dpos;
3862 memcpy(new_topic+dpos, topic, len);
3866 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3867 default: new_topic[dpos++] = tchar; break;
3870 if((dpos > TOPICLEN) || tchar)
3873 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3874 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3877 new_topic[dpos] = 0;
3878 SetChannelTopic(channel, chanserv, new_topic, 1);
3880 reply("CSMSG_TOPIC_LOCKED", channel->name);
3885 SetChannelTopic(channel, chanserv, topic, 1);
3887 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3889 /* Grab the topic and save it as the default topic. */
3891 cData->topic = strdup(channel->topic);
3897 static CHANSERV_FUNC(cmd_mode)
3899 struct userData *uData;
3900 struct mod_chanmode *change;
3905 change = &channel->channel_info->modes;
3906 if(change->modes_set || change->modes_clear) {
3907 modcmd_chanmode_announce(change);
3908 reply("CSMSG_DEFAULTED_MODES", channel->name);
3910 reply("CSMSG_NO_MODES", channel->name);
3914 uData = GetChannelUser(channel->channel_info, user->handle_info);
3916 base_oplevel = MAXOPLEVEL;
3917 else if (uData->access >= UL_OWNER)
3920 base_oplevel = 1 + UL_OWNER - uData->access;
3921 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
3924 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3928 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3929 && mode_lock_violated(&channel->channel_info->modes, change))
3932 mod_chanmode_format(&channel->channel_info->modes, modes);
3933 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3937 modcmd_chanmode_announce(change);
3938 mod_chanmode_free(change);
3939 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3943 static CHANSERV_FUNC(cmd_invite)
3945 struct userData *uData;
3946 struct userNode *invite;
3948 uData = GetChannelUser(channel->channel_info, user->handle_info);
3952 if(!(invite = GetUserH(argv[1])))
3954 reply("MSG_NICK_UNKNOWN", argv[1]);
3961 if(GetUserMode(channel, invite))
3963 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3971 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3972 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3975 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3977 irc_invite(chanserv, invite, channel);
3979 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3984 static CHANSERV_FUNC(cmd_inviteme)
3986 if(GetUserMode(channel, user))
3988 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3991 if(channel->channel_info
3992 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3994 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3997 irc_invite(cmd->parent->bot, user, channel);
4002 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4005 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4007 /* We display things based on two dimensions:
4008 * - Issue time: present or absent
4009 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4010 * (in order of precedence, so something both expired and revoked
4011 * only counts as revoked)
4013 combo = (suspended->issued ? 4 : 0)
4014 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4016 case 0: /* no issue time, indefinite expiration */
4017 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4019 case 1: /* no issue time, expires in future */
4020 intervalString(buf1, suspended->expires-now, user->handle_info);
4021 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4023 case 2: /* no issue time, expired */
4024 intervalString(buf1, now-suspended->expires, user->handle_info);
4025 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4027 case 3: /* no issue time, revoked */
4028 intervalString(buf1, now-suspended->revoked, user->handle_info);
4029 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4031 case 4: /* issue time set, indefinite expiration */
4032 intervalString(buf1, now-suspended->issued, user->handle_info);
4033 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4035 case 5: /* issue time set, expires in future */
4036 intervalString(buf1, now-suspended->issued, user->handle_info);
4037 intervalString(buf2, suspended->expires-now, user->handle_info);
4038 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4040 case 6: /* issue time set, expired */
4041 intervalString(buf1, now-suspended->issued, user->handle_info);
4042 intervalString(buf2, now-suspended->expires, user->handle_info);
4043 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4045 case 7: /* issue time set, revoked */
4046 intervalString(buf1, now-suspended->issued, user->handle_info);
4047 intervalString(buf2, now-suspended->revoked, user->handle_info);
4048 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4051 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4056 static CHANSERV_FUNC(cmd_info)
4058 char modes[MAXLEN], buffer[INTERVALLEN];
4059 struct userData *uData, *owner;
4060 struct chanData *cData;
4061 struct do_not_register *dnr;
4066 cData = channel->channel_info;
4067 reply("CSMSG_CHANNEL_INFO", channel->name);
4069 uData = GetChannelUser(cData, user->handle_info);
4070 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4072 mod_chanmode_format(&cData->modes, modes);
4073 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4074 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4077 for(it = dict_first(cData->notes); it; it = iter_next(it))
4081 note = iter_data(it);
4082 if(!note_type_visible_to_user(cData, note->type, user))
4085 padding = PADLEN - 1 - strlen(iter_key(it));
4086 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4089 reply("CSMSG_CHANNEL_MAX", cData->max);
4090 for(owner = cData->users; owner; owner = owner->next)
4091 if(owner->access == UL_OWNER)
4092 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4093 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4094 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4095 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4097 privileged = IsStaff(user);
4099 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4100 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4101 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4103 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4104 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4106 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4108 struct suspended *suspended;
4109 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4110 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4111 show_suspension_info(cmd, user, suspended);
4113 else if(IsSuspended(cData))
4115 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4116 show_suspension_info(cmd, user, cData->suspended);
4121 static CHANSERV_FUNC(cmd_netinfo)
4123 extern time_t boot_time;
4124 extern unsigned long burst_length;
4125 char interval[INTERVALLEN];
4127 reply("CSMSG_NETWORK_INFO");
4128 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4129 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4130 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4131 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4132 reply("CSMSG_NETWORK_BANS", banCount);
4133 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4134 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4135 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4140 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4142 struct helpfile_table table;
4144 struct userNode *user;
4149 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4150 table.contents = alloca(list->used*sizeof(*table.contents));
4151 for(nn=0; nn<list->used; nn++)
4153 user = list->list[nn];
4154 if(user->modes & skip_flags)
4158 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4161 nick = alloca(strlen(user->nick)+3);
4162 sprintf(nick, "(%s)", user->nick);
4166 table.contents[table.length][0] = nick;
4169 table_send(chanserv, to->nick, 0, NULL, table);
4172 static CHANSERV_FUNC(cmd_ircops)
4174 reply("CSMSG_STAFF_OPERS");
4175 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4179 static CHANSERV_FUNC(cmd_helpers)
4181 reply("CSMSG_STAFF_HELPERS");
4182 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4186 static CHANSERV_FUNC(cmd_staff)
4188 reply("CSMSG_NETWORK_STAFF");
4189 cmd_ircops(CSFUNC_ARGS);
4190 cmd_helpers(CSFUNC_ARGS);
4194 static CHANSERV_FUNC(cmd_peek)
4196 struct modeNode *mn;
4197 char modes[MODELEN];
4199 struct helpfile_table table;
4201 irc_make_chanmode(channel, modes);
4203 reply("CSMSG_PEEK_INFO", channel->name);
4204 reply("CSMSG_PEEK_TOPIC", channel->topic);
4205 reply("CSMSG_PEEK_MODES", modes);
4206 reply("CSMSG_PEEK_USERS", channel->members.used);
4210 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4211 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4212 for(n = 0; n < channel->members.used; n++)
4214 mn = channel->members.list[n];
4215 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4217 table.contents[table.length] = alloca(sizeof(**table.contents));
4218 table.contents[table.length][0] = mn->user->nick;
4223 reply("CSMSG_PEEK_OPS");
4224 table_send(chanserv, user->nick, 0, NULL, table);
4227 reply("CSMSG_PEEK_NO_OPS");
4231 static MODCMD_FUNC(cmd_wipeinfo)
4233 struct handle_info *victim;
4234 struct userData *ud, *actor, *real_actor;
4235 unsigned int override = 0;
4238 actor = GetChannelUser(channel->channel_info, user->handle_info);
4239 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4240 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4242 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4244 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4247 if((ud->access >= actor->access) && (ud != actor))
4249 reply("MSG_USER_OUTRANKED", victim->handle);
4252 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4253 override = CMD_LOG_OVERRIDE;
4257 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4258 return 1 | override;
4261 static CHANSERV_FUNC(cmd_resync)
4263 struct mod_chanmode *changes;
4264 struct chanData *cData = channel->channel_info;
4265 unsigned int ii, used;
4267 changes = mod_chanmode_alloc(channel->members.used * 2);
4268 for(ii = used = 0; ii < channel->members.used; ++ii)
4270 struct modeNode *mn = channel->members.list[ii];
4271 struct userData *uData;
4273 if(IsService(mn->user))
4276 uData = GetChannelAccess(cData, mn->user->handle_info);
4277 if(!cData->lvlOpts[lvlGiveOps]
4278 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4280 if(!(mn->modes & MODE_CHANOP))
4282 changes->args[used].mode = MODE_CHANOP;
4283 changes->args[used++].u.member = mn;
4286 else if(!cData->lvlOpts[lvlGiveVoice]
4287 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4289 if(mn->modes & MODE_CHANOP)
4291 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4292 changes->args[used++].u.member = mn;
4294 if(!(mn->modes & MODE_VOICE))
4296 changes->args[used].mode = MODE_VOICE;
4297 changes->args[used++].u.member = mn;
4304 changes->args[used].mode = MODE_REMOVE | mn->modes;
4305 changes->args[used++].u.member = mn;
4309 changes->argc = used;
4310 modcmd_chanmode_announce(changes);
4311 mod_chanmode_free(changes);
4312 reply("CSMSG_RESYNCED_USERS", channel->name);
4316 static CHANSERV_FUNC(cmd_seen)
4318 struct userData *uData;
4319 struct handle_info *handle;
4320 char seen[INTERVALLEN];
4324 if(!irccasecmp(argv[1], chanserv->nick))
4326 reply("CSMSG_IS_CHANSERV");
4330 if(!(handle = get_handle_info(argv[1])))
4332 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4336 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4338 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4343 reply("CSMSG_USER_PRESENT", handle->handle);
4344 else if(uData->seen)
4345 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4347 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4349 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4350 reply("CSMSG_USER_VACATION", handle->handle);
4355 static MODCMD_FUNC(cmd_names)
4357 struct userNode *targ;
4358 struct userData *targData;
4359 unsigned int ii, pos;
4362 for(ii=pos=0; ii<channel->members.used; ++ii)
4364 targ = channel->members.list[ii]->user;
4365 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4368 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4371 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4375 if(IsUserSuspended(targData))
4377 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4380 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4381 reply("CSMSG_END_NAMES", channel->name);
4386 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4388 switch(ntype->visible_type)
4390 case NOTE_VIS_ALL: return 1;
4391 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4392 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4397 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4399 struct userData *uData;
4401 switch(ntype->set_access_type)
4403 case NOTE_SET_CHANNEL_ACCESS:
4404 if(!user->handle_info)
4406 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4408 return uData->access >= ntype->set_access.min_ulevel;
4409 case NOTE_SET_CHANNEL_SETTER:
4410 return check_user_level(channel, user, lvlSetters, 1, 0);
4411 case NOTE_SET_PRIVILEGED: default:
4412 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4416 static CHANSERV_FUNC(cmd_note)
4418 struct chanData *cData;
4420 struct note_type *ntype;
4422 cData = channel->channel_info;
4425 reply("CSMSG_NOT_REGISTERED", channel->name);
4429 /* If no arguments, show all visible notes for the channel. */
4435 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4437 note = iter_data(it);
4438 if(!note_type_visible_to_user(cData, note->type, user))
4441 reply("CSMSG_NOTELIST_HEADER", channel->name);
4442 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4445 reply("CSMSG_NOTELIST_END", channel->name);
4447 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4449 /* If one argument, show the named note. */
4452 if((note = dict_find(cData->notes, argv[1], NULL))
4453 && note_type_visible_to_user(cData, note->type, user))
4455 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4457 else if((ntype = dict_find(note_types, argv[1], NULL))
4458 && note_type_visible_to_user(NULL, ntype, user))
4460 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4465 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4469 /* Assume they're trying to set a note. */
4473 ntype = dict_find(note_types, argv[1], NULL);
4476 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4479 else if(note_type_settable_by_user(channel, ntype, user))
4481 note_text = unsplit_string(argv+2, argc-2, NULL);
4482 if((note = dict_find(cData->notes, argv[1], NULL)))
4483 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4484 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4485 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4487 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4489 /* The note is viewable to staff only, so return 0
4490 to keep the invocation from getting logged (or
4491 regular users can see it in !events). */
4497 reply("CSMSG_NO_ACCESS");
4504 static CHANSERV_FUNC(cmd_delnote)
4509 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4510 || !note_type_settable_by_user(channel, note->type, user))
4512 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4515 dict_remove(channel->channel_info->notes, note->type->name);
4516 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4520 static CHANSERV_FUNC(cmd_events)
4522 struct logSearch discrim;
4523 struct logReport report;
4524 unsigned int matches, limit;
4526 limit = (argc > 1) ? atoi(argv[1]) : 10;
4527 if(limit < 1 || limit > 200)
4530 memset(&discrim, 0, sizeof(discrim));
4531 discrim.masks.bot = chanserv;
4532 discrim.masks.channel_name = channel->name;
4534 discrim.masks.command = argv[2];
4535 discrim.limit = limit;
4536 discrim.max_time = INT_MAX;
4537 discrim.severities = 1 << LOG_COMMAND;
4538 report.reporter = chanserv;
4540 reply("CSMSG_EVENT_SEARCH_RESULTS");
4541 matches = log_entry_search(&discrim, log_report_entry, &report);
4543 reply("MSG_MATCH_COUNT", matches);
4545 reply("MSG_NO_MATCHES");
4549 static CHANSERV_FUNC(cmd_say)
4555 msg = unsplit_string(argv + 1, argc - 1, NULL);
4556 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4558 else if(GetUserH(argv[1]))
4561 msg = unsplit_string(argv + 2, argc - 2, NULL);
4562 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4566 reply("MSG_NOT_TARGET_NAME");
4572 static CHANSERV_FUNC(cmd_emote)
4578 /* CTCP is so annoying. */
4579 msg = unsplit_string(argv + 1, argc - 1, NULL);
4580 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4582 else if(GetUserH(argv[1]))
4584 msg = unsplit_string(argv + 2, argc - 2, NULL);
4585 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4589 reply("MSG_NOT_TARGET_NAME");
4595 struct channelList *
4596 chanserv_support_channels(void)
4598 return &chanserv_conf.support_channels;
4601 static CHANSERV_FUNC(cmd_expire)
4603 int channel_count = registered_channels;
4604 expire_channels(NULL);
4605 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4610 chanserv_expire_suspension(void *data)
4612 struct suspended *suspended = data;
4613 struct chanNode *channel;
4615 if(!suspended->expires || (now < suspended->expires))
4616 suspended->revoked = now;
4617 channel = suspended->cData->channel;
4618 suspended->cData->channel = channel;
4619 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4620 if(!IsOffChannel(suspended->cData))
4622 struct mod_chanmode change;
4623 mod_chanmode_init(&change);
4625 change.args[0].mode = MODE_CHANOP;
4626 change.args[0].u.member = AddChannelUser(chanserv, channel);
4627 mod_chanmode_announce(chanserv, channel, &change);
4631 static CHANSERV_FUNC(cmd_csuspend)
4633 struct suspended *suspended;
4634 char reason[MAXLEN];
4635 time_t expiry, duration;
4636 struct userData *uData;
4640 if(IsProtected(channel->channel_info))
4642 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4646 if(argv[1][0] == '!')
4648 else if(IsSuspended(channel->channel_info))
4650 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4651 show_suspension_info(cmd, user, channel->channel_info->suspended);
4655 if(!strcmp(argv[1], "0"))
4657 else if((duration = ParseInterval(argv[1])))
4658 expiry = now + duration;
4661 reply("MSG_INVALID_DURATION", argv[1]);
4665 unsplit_string(argv + 2, argc - 2, reason);
4667 suspended = calloc(1, sizeof(*suspended));
4668 suspended->revoked = 0;
4669 suspended->issued = now;
4670 suspended->suspender = strdup(user->handle_info->handle);
4671 suspended->expires = expiry;
4672 suspended->reason = strdup(reason);
4673 suspended->cData = channel->channel_info;
4674 suspended->previous = suspended->cData->suspended;
4675 suspended->cData->suspended = suspended;
4677 if(suspended->expires)
4678 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4680 if(IsSuspended(channel->channel_info))
4682 suspended->previous->revoked = now;
4683 if(suspended->previous->expires)
4684 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4685 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4686 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4690 /* Mark all users in channel as absent. */
4691 for(uData = channel->channel_info->users; uData; uData = uData->next)
4700 /* Mark the channel as suspended, then part. */
4701 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4702 DelChannelUser(chanserv, channel, suspended->reason, 0);
4703 reply("CSMSG_SUSPENDED", channel->name);
4704 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4705 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4710 static CHANSERV_FUNC(cmd_cunsuspend)
4712 struct suspended *suspended;
4713 char message[MAXLEN];
4715 if(!IsSuspended(channel->channel_info))
4717 reply("CSMSG_NOT_SUSPENDED", channel->name);
4721 suspended = channel->channel_info->suspended;
4723 /* Expire the suspension and join ChanServ to the channel. */
4724 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4725 chanserv_expire_suspension(suspended);
4726 reply("CSMSG_UNSUSPENDED", channel->name);
4727 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4728 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4732 typedef struct chanservSearch
4740 unsigned long flags;
4744 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4747 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4752 search = malloc(sizeof(struct chanservSearch));
4753 memset(search, 0, sizeof(*search));
4756 for(i = 0; i < argc; i++)
4758 /* Assume all criteria require arguments. */
4761 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4765 if(!irccasecmp(argv[i], "name"))
4766 search->name = argv[++i];
4767 else if(!irccasecmp(argv[i], "registrar"))
4768 search->registrar = argv[++i];
4769 else if(!irccasecmp(argv[i], "unvisited"))
4770 search->unvisited = ParseInterval(argv[++i]);
4771 else if(!irccasecmp(argv[i], "registered"))
4772 search->registered = ParseInterval(argv[++i]);
4773 else if(!irccasecmp(argv[i], "flags"))
4776 if(!irccasecmp(argv[i], "nodelete"))
4777 search->flags |= CHANNEL_NODELETE;
4778 else if(!irccasecmp(argv[i], "suspended"))
4779 search->flags |= CHANNEL_SUSPENDED;
4782 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4786 else if(!irccasecmp(argv[i], "limit"))
4787 search->limit = strtoul(argv[++i], NULL, 10);
4790 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4795 if(search->name && !strcmp(search->name, "*"))
4797 if(search->registrar && !strcmp(search->registrar, "*"))
4798 search->registrar = 0;
4807 chanserv_channel_match(struct chanData *channel, search_t search)
4809 const char *name = channel->channel->name;
4810 if((search->name && !match_ircglob(name, search->name)) ||
4811 (search->registrar && !channel->registrar) ||
4812 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4813 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4814 (search->registered && (now - channel->registered) > search->registered) ||
4815 (search->flags && ((search->flags & channel->flags) != search->flags)))
4822 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4824 struct chanData *channel;
4825 unsigned int matches = 0;
4827 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4829 if(!chanserv_channel_match(channel, search))
4839 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4844 search_print(struct chanData *channel, void *data)
4846 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4849 static CHANSERV_FUNC(cmd_search)
4852 unsigned int matches;
4853 channel_search_func action;
4857 if(!irccasecmp(argv[1], "count"))
4858 action = search_count;
4859 else if(!irccasecmp(argv[1], "print"))
4860 action = search_print;
4863 reply("CSMSG_ACTION_INVALID", argv[1]);
4867 search = chanserv_search_create(user, argc - 2, argv + 2);
4871 if(action == search_count)
4872 search->limit = INT_MAX;
4874 if(action == search_print)
4875 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4877 matches = chanserv_channel_search(search, action, user);
4880 reply("MSG_MATCH_COUNT", matches);
4882 reply("MSG_NO_MATCHES");
4888 static CHANSERV_FUNC(cmd_unvisited)
4890 struct chanData *cData;
4891 time_t interval = chanserv_conf.channel_expire_delay;
4892 char buffer[INTERVALLEN];
4893 unsigned int limit = 25, matches = 0;
4897 interval = ParseInterval(argv[1]);
4899 limit = atoi(argv[2]);
4902 intervalString(buffer, interval, user->handle_info);
4903 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4905 for(cData = channelList; cData && matches < limit; cData = cData->next)
4907 if((now - cData->visited) < interval)
4910 intervalString(buffer, now - cData->visited, user->handle_info);
4911 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4918 static MODCMD_FUNC(chan_opt_defaulttopic)
4924 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4926 reply("CSMSG_TOPIC_LOCKED", channel->name);
4930 topic = unsplit_string(argv+1, argc-1, NULL);
4932 free(channel->channel_info->topic);
4933 if(topic[0] == '*' && topic[1] == 0)
4935 topic = channel->channel_info->topic = NULL;
4939 topic = channel->channel_info->topic = strdup(topic);
4940 if(channel->channel_info->topic_mask
4941 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4942 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4944 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4947 if(channel->channel_info->topic)
4948 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4950 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4954 static MODCMD_FUNC(chan_opt_topicmask)
4958 struct chanData *cData = channel->channel_info;
4961 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4963 reply("CSMSG_TOPIC_LOCKED", channel->name);
4967 mask = unsplit_string(argv+1, argc-1, NULL);
4969 if(cData->topic_mask)
4970 free(cData->topic_mask);
4971 if(mask[0] == '*' && mask[1] == 0)
4973 cData->topic_mask = 0;
4977 cData->topic_mask = strdup(mask);
4979 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4980 else if(!match_ircglob(cData->topic, cData->topic_mask))
4981 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4985 if(channel->channel_info->topic_mask)
4986 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4988 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4992 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4996 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5000 if(greeting[0] == '*' && greeting[1] == 0)
5004 unsigned int length = strlen(greeting);
5005 if(length > chanserv_conf.greeting_length)
5007 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5010 *data = strdup(greeting);
5019 reply(name, user_find_message(user, "MSG_NONE"));
5023 static MODCMD_FUNC(chan_opt_greeting)
5025 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5028 static MODCMD_FUNC(chan_opt_usergreeting)
5030 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5033 static MODCMD_FUNC(chan_opt_modes)
5035 struct mod_chanmode *new_modes;
5036 char modes[MODELEN];
5040 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5042 reply("CSMSG_NO_ACCESS");
5045 if(argv[1][0] == '*' && argv[1][1] == 0)
5047 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5049 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5051 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5054 else if(new_modes->argc > 1)
5056 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5057 mod_chanmode_free(new_modes);
5062 channel->channel_info->modes = *new_modes;
5063 modcmd_chanmode_announce(new_modes);
5064 mod_chanmode_free(new_modes);
5068 mod_chanmode_format(&channel->channel_info->modes, modes);
5070 reply("CSMSG_SET_MODES", modes);
5072 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5076 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5078 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5080 struct chanData *cData = channel->channel_info;
5085 /* Set flag according to value. */
5086 if(enabled_string(argv[1]))
5088 cData->flags |= mask;
5091 else if(disabled_string(argv[1]))
5093 cData->flags &= ~mask;
5098 reply("MSG_INVALID_BINARY", argv[1]);
5104 /* Find current option value. */
5105 value = (cData->flags & mask) ? 1 : 0;
5109 reply(name, user_find_message(user, "MSG_ON"));
5111 reply(name, user_find_message(user, "MSG_OFF"));
5115 static MODCMD_FUNC(chan_opt_nodelete)
5117 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5119 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5123 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5126 static MODCMD_FUNC(chan_opt_dynlimit)
5128 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5131 static MODCMD_FUNC(chan_opt_offchannel)
5133 struct chanData *cData = channel->channel_info;
5138 /* Set flag according to value. */
5139 if(enabled_string(argv[1]))
5141 if(!IsOffChannel(cData))
5142 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5143 cData->flags |= CHANNEL_OFFCHANNEL;
5146 else if(disabled_string(argv[1]))
5148 if(IsOffChannel(cData))
5150 struct mod_chanmode change;
5151 mod_chanmode_init(&change);
5153 change.args[0].mode = MODE_CHANOP;
5154 change.args[0].u.member = AddChannelUser(chanserv, channel);
5155 mod_chanmode_announce(chanserv, channel, &change);
5157 cData->flags &= ~CHANNEL_OFFCHANNEL;
5162 reply("MSG_INVALID_BINARY", argv[1]);
5168 /* Find current option value. */
5169 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5173 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5175 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5179 static MODCMD_FUNC(chan_opt_defaults)
5181 struct userData *uData;
5182 struct chanData *cData;
5183 const char *confirm;
5184 enum levelOption lvlOpt;
5185 enum charOption chOpt;
5187 cData = channel->channel_info;
5188 uData = GetChannelUser(cData, user->handle_info);
5189 if(!uData || (uData->access < UL_OWNER))
5191 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5194 confirm = make_confirmation_string(uData);
5195 if((argc < 2) || strcmp(argv[1], confirm))
5197 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5200 cData->flags = CHANNEL_DEFAULT_FLAGS;
5201 cData->modes = chanserv_conf.default_modes;
5202 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5203 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5204 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5205 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5206 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5211 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5213 struct chanData *cData = channel->channel_info;
5214 struct userData *uData;
5215 unsigned short value;
5219 if(!check_user_level(channel, user, option, 1, 1))
5221 reply("CSMSG_CANNOT_SET");
5224 value = user_level_from_name(argv[1], UL_OWNER+1);
5225 if(!value && strcmp(argv[1], "0"))
5227 reply("CSMSG_INVALID_ACCESS", argv[1]);
5230 uData = GetChannelUser(cData, user->handle_info);
5231 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5233 reply("CSMSG_BAD_SETLEVEL");
5239 if(value > cData->lvlOpts[lvlGiveOps])
5241 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5246 if(value < cData->lvlOpts[lvlGiveVoice])
5248 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5253 /* This test only applies to owners, since non-owners
5254 * trying to set an option to above their level get caught
5255 * by the CSMSG_BAD_SETLEVEL test above.
5257 if(value > uData->access)
5259 reply("CSMSG_BAD_SETTERS");
5266 cData->lvlOpts[option] = value;
5268 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5272 static MODCMD_FUNC(chan_opt_enfops)
5274 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5277 static MODCMD_FUNC(chan_opt_giveops)
5279 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5282 static MODCMD_FUNC(chan_opt_enfmodes)
5284 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5287 static MODCMD_FUNC(chan_opt_enftopic)
5289 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5292 static MODCMD_FUNC(chan_opt_pubcmd)
5294 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5297 static MODCMD_FUNC(chan_opt_setters)
5299 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5302 static MODCMD_FUNC(chan_opt_ctcpusers)
5304 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5307 static MODCMD_FUNC(chan_opt_userinfo)
5309 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5312 static MODCMD_FUNC(chan_opt_givevoice)
5314 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5317 static MODCMD_FUNC(chan_opt_topicsnarf)
5319 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5322 static MODCMD_FUNC(chan_opt_inviteme)
5324 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5328 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5330 struct chanData *cData = channel->channel_info;
5331 int count = charOptions[option].count, index;
5335 index = atoi(argv[1]);
5337 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5339 reply("CSMSG_INVALID_NUMERIC", index);
5340 /* Show possible values. */
5341 for(index = 0; index < count; index++)
5342 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5346 cData->chOpts[option] = charOptions[option].values[index].value;
5350 /* Find current option value. */
5353 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5357 /* Somehow, the option value is corrupt; reset it to the default. */
5358 cData->chOpts[option] = charOptions[option].default_value;
5363 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5367 static MODCMD_FUNC(chan_opt_protect)
5369 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5372 static MODCMD_FUNC(chan_opt_toys)
5374 return channel_multiple_option(chToys, CSFUNC_ARGS);
5377 static MODCMD_FUNC(chan_opt_ctcpreaction)
5379 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5382 static MODCMD_FUNC(chan_opt_topicrefresh)
5384 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5387 static struct svccmd_list set_shows_list;
5390 handle_svccmd_unbind(struct svccmd *target) {
5392 for(ii=0; ii<set_shows_list.used; ++ii)
5393 if(target == set_shows_list.list[ii])
5394 set_shows_list.used = 0;
5397 static CHANSERV_FUNC(cmd_set)
5399 struct svccmd *subcmd;
5403 /* Check if we need to (re-)initialize set_shows_list. */
5404 if(!set_shows_list.used)
5406 if(!set_shows_list.size)
5408 set_shows_list.size = chanserv_conf.set_shows->used;
5409 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5411 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5413 const char *name = chanserv_conf.set_shows->list[ii];
5414 sprintf(buf, "%s %s", argv[0], name);
5415 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5418 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5421 svccmd_list_append(&set_shows_list, subcmd);
5427 reply("CSMSG_CHANNEL_OPTIONS");
5428 for(ii = 0; ii < set_shows_list.used; ii++)
5430 subcmd = set_shows_list.list[ii];
5431 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5436 sprintf(buf, "%s %s", argv[0], argv[1]);
5437 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5440 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5443 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5445 reply("CSMSG_NO_ACCESS");
5449 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5453 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5455 struct userData *uData;
5457 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5460 reply("CSMSG_NOT_USER", channel->name);
5466 /* Just show current option value. */
5468 else if(enabled_string(argv[1]))
5470 uData->flags |= mask;
5472 else if(disabled_string(argv[1]))
5474 uData->flags &= ~mask;
5478 reply("MSG_INVALID_BINARY", argv[1]);
5482 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5486 static MODCMD_FUNC(user_opt_noautoop)
5488 struct userData *uData;
5490 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5493 reply("CSMSG_NOT_USER", channel->name);
5496 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5497 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5499 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5502 static MODCMD_FUNC(user_opt_autoinvite)
5504 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5507 static MODCMD_FUNC(user_opt_info)
5509 struct userData *uData;
5512 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5516 /* If they got past the command restrictions (which require access)
5517 * but fail this test, we have some fool with security override on.
5519 reply("CSMSG_NOT_USER", channel->name);
5526 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5527 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5529 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5532 bp = strcspn(infoline, "\001");
5535 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5540 if(infoline[0] == '*' && infoline[1] == 0)
5543 uData->info = strdup(infoline);
5546 reply("CSMSG_USET_INFO", uData->info);
5548 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5552 struct svccmd_list uset_shows_list;
5554 static CHANSERV_FUNC(cmd_uset)
5556 struct svccmd *subcmd;
5560 /* Check if we need to (re-)initialize uset_shows_list. */
5561 if(!uset_shows_list.used)
5565 "NoAutoOp", "AutoInvite", "Info"
5568 if(!uset_shows_list.size)
5570 uset_shows_list.size = ArrayLength(options);
5571 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5573 for(ii = 0; ii < ArrayLength(options); ii++)
5575 const char *name = options[ii];
5576 sprintf(buf, "%s %s", argv[0], name);
5577 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5580 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5583 svccmd_list_append(&uset_shows_list, subcmd);
5589 /* Do this so options are presented in a consistent order. */
5590 reply("CSMSG_USER_OPTIONS");
5591 for(ii = 0; ii < uset_shows_list.used; ii++)
5592 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5596 sprintf(buf, "%s %s", argv[0], argv[1]);
5597 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5600 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5604 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5607 static CHANSERV_FUNC(cmd_giveownership)
5609 struct handle_info *new_owner_hi;
5610 struct userData *new_owner;
5611 struct userData *curr_user;
5612 struct userData *invoker;
5613 struct chanData *cData = channel->channel_info;
5614 struct do_not_register *dnr;
5615 const char *confirm;
5617 unsigned short co_access;
5618 char reason[MAXLEN];
5621 curr_user = GetChannelAccess(cData, user->handle_info);
5622 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5623 if(!curr_user || (curr_user->access != UL_OWNER))
5625 struct userData *owner = NULL;
5626 for(curr_user = channel->channel_info->users;
5628 curr_user = curr_user->next)
5630 if(curr_user->access != UL_OWNER)
5634 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5641 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5643 char delay[INTERVALLEN];
5644 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5645 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5648 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5650 if(new_owner_hi == user->handle_info)
5652 reply("CSMSG_NO_TRANSFER_SELF");
5655 new_owner = GetChannelAccess(cData, new_owner_hi);
5660 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5664 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5668 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5670 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5673 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5674 if(!IsHelping(user))
5675 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5677 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5680 invoker = GetChannelUser(cData, user->handle_info);
5681 if(invoker->access <= UL_OWNER)
5683 confirm = make_confirmation_string(curr_user);
5684 if((argc < 3) || strcmp(argv[2], confirm))
5686 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5690 if(new_owner->access >= UL_COOWNER)
5691 co_access = new_owner->access;
5693 co_access = UL_COOWNER;
5694 new_owner->access = UL_OWNER;
5696 curr_user->access = co_access;
5697 cData->ownerTransfer = now;
5698 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5699 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5700 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5704 static CHANSERV_FUNC(cmd_suspend)
5706 struct handle_info *hi;
5707 struct userData *self, *real_self, *target;
5708 unsigned int override = 0;
5711 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5712 self = GetChannelUser(channel->channel_info, user->handle_info);
5713 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5714 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5716 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5719 if(target->access >= self->access)
5721 reply("MSG_USER_OUTRANKED", hi->handle);
5724 if(target->flags & USER_SUSPENDED)
5726 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5731 target->present = 0;
5734 if(!real_self || target->access >= real_self->access)
5735 override = CMD_LOG_OVERRIDE;
5736 target->flags |= USER_SUSPENDED;
5737 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5738 return 1 | override;
5741 static CHANSERV_FUNC(cmd_unsuspend)
5743 struct handle_info *hi;
5744 struct userData *self, *real_self, *target;
5745 unsigned int override = 0;
5748 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5749 self = GetChannelUser(channel->channel_info, user->handle_info);
5750 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5751 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5753 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5756 if(target->access >= self->access)
5758 reply("MSG_USER_OUTRANKED", hi->handle);
5761 if(!(target->flags & USER_SUSPENDED))
5763 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5766 if(!real_self || target->access >= real_self->access)
5767 override = CMD_LOG_OVERRIDE;
5768 target->flags &= ~USER_SUSPENDED;
5769 scan_user_presence(target, NULL);
5770 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5771 return 1 | override;
5774 static MODCMD_FUNC(cmd_deleteme)
5776 struct handle_info *hi;
5777 struct userData *target;
5778 const char *confirm_string;
5779 unsigned short access;
5782 hi = user->handle_info;
5783 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5785 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5788 if(target->access == UL_OWNER)
5790 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5793 confirm_string = make_confirmation_string(target);
5794 if((argc < 2) || strcmp(argv[1], confirm_string))
5796 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5799 access = target->access;
5800 channel_name = strdup(channel->name);
5801 del_channel_user(target, 1);
5802 reply("CSMSG_DELETED_YOU", access, channel_name);
5808 chanserv_refresh_topics(UNUSED_ARG(void *data))
5810 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5811 struct chanData *cData;
5814 for(cData = channelList; cData; cData = cData->next)
5816 if(IsSuspended(cData))
5818 opt = cData->chOpts[chTopicRefresh];
5821 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5824 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5825 cData->last_refresh = refresh_num;
5827 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5830 static CHANSERV_FUNC(cmd_unf)
5834 char response[MAXLEN];
5835 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5836 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5837 irc_privmsg(cmd->parent->bot, channel->name, response);
5840 reply("CSMSG_UNF_RESPONSE");
5844 static CHANSERV_FUNC(cmd_ping)
5848 char response[MAXLEN];
5849 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5850 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5851 irc_privmsg(cmd->parent->bot, channel->name, response);
5854 reply("CSMSG_PING_RESPONSE");
5858 static CHANSERV_FUNC(cmd_wut)
5862 char response[MAXLEN];
5863 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5864 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5865 irc_privmsg(cmd->parent->bot, channel->name, response);
5868 reply("CSMSG_WUT_RESPONSE");
5872 static CHANSERV_FUNC(cmd_8ball)
5874 unsigned int i, j, accum;
5879 for(i=1; i<argc; i++)
5880 for(j=0; argv[i][j]; j++)
5881 accum = (accum << 5) - accum + toupper(argv[i][j]);
5882 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5885 char response[MAXLEN];
5886 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5887 irc_privmsg(cmd->parent->bot, channel->name, response);
5890 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5894 static CHANSERV_FUNC(cmd_d)
5896 unsigned long sides, count, modifier, ii, total;
5897 char response[MAXLEN], *sep;
5901 if((count = strtoul(argv[1], &sep, 10)) < 1)
5911 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5912 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5916 else if((sep[0] == '-') && isdigit(sep[1]))
5917 modifier = strtoul(sep, NULL, 10);
5918 else if((sep[0] == '+') && isdigit(sep[1]))
5919 modifier = strtoul(sep+1, NULL, 10);
5926 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5931 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5934 for(total = ii = 0; ii < count; ++ii)
5935 total += (rand() % sides) + 1;
5938 if((count > 1) || modifier)
5940 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5941 sprintf(response, fmt, total, count, sides, modifier);
5945 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5946 sprintf(response, fmt, total, sides);
5949 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5951 send_message_type(4, user, cmd->parent->bot, "%s", response);
5955 static CHANSERV_FUNC(cmd_huggle)
5957 /* CTCP must be via PRIVMSG, never notice */
5959 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5961 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5966 chanserv_adjust_limit(void *data)
5968 struct mod_chanmode change;
5969 struct chanData *cData = data;
5970 struct chanNode *channel = cData->channel;
5973 if(IsSuspended(cData))
5976 cData->limitAdjusted = now;
5977 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5978 if(cData->modes.modes_set & MODE_LIMIT)
5980 if(limit > cData->modes.new_limit)
5981 limit = cData->modes.new_limit;
5982 else if(limit == cData->modes.new_limit)
5986 mod_chanmode_init(&change);
5987 change.modes_set = MODE_LIMIT;
5988 change.new_limit = limit;
5989 mod_chanmode_announce(chanserv, channel, &change);
5993 handle_new_channel(struct chanNode *channel)
5995 struct chanData *cData;
5997 if(!(cData = channel->channel_info))
6000 if(cData->modes.modes_set || cData->modes.modes_clear)
6001 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6003 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6004 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6007 /* Welcome to my worst nightmare. Warning: Read (or modify)
6008 the code below at your own risk. */
6010 handle_join(struct modeNode *mNode)
6012 struct mod_chanmode change;
6013 struct userNode *user = mNode->user;
6014 struct chanNode *channel = mNode->channel;
6015 struct chanData *cData;
6016 struct userData *uData = NULL;
6017 struct banData *bData;
6018 struct handle_info *handle;
6019 unsigned int modes = 0, info = 0;
6022 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6025 cData = channel->channel_info;
6026 if(channel->members.used > cData->max)
6027 cData->max = channel->members.used;
6029 /* Check for bans. If they're joining through a ban, one of two
6031 * 1: Join during a netburst, by riding the break. Kick them
6032 * unless they have ops or voice in the channel.
6033 * 2: They're allowed to join through the ban (an invite in
6034 * ircu2.10, or a +e on Hybrid, or something).
6035 * If they're not joining through a ban, and the banlist is not
6036 * full, see if they're on the banlist for the channel. If so,
6039 if(user->uplink->burst && !mNode->modes)
6042 for(ii = 0; ii < channel->banlist.used; ii++)
6044 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6046 /* Riding a netburst. Naughty. */
6047 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6053 mod_chanmode_init(&change);
6055 if(channel->banlist.used < MAXBANS)
6057 /* Not joining through a ban. */
6058 for(bData = cData->bans;
6059 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6060 bData = bData->next);
6064 char kick_reason[MAXLEN];
6065 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6067 bData->triggered = now;
6068 if(bData != cData->bans)
6070 /* Shuffle the ban to the head of the list. */
6072 bData->next->prev = bData->prev;
6074 bData->prev->next = bData->next;
6077 bData->next = cData->bans;
6080 cData->bans->prev = bData;
6081 cData->bans = bData;
6084 change.args[0].mode = MODE_BAN;
6085 change.args[0].u.hostmask = bData->mask;
6086 mod_chanmode_announce(chanserv, channel, &change);
6087 KickChannelUser(user, channel, chanserv, kick_reason);
6092 /* ChanServ will not modify the limits in join-flooded channels.
6093 It will also skip DynLimit processing when the user (or srvx)
6094 is bursting in, because there are likely more incoming. */
6095 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6096 && !user->uplink->burst
6097 && !channel->join_flooded
6098 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6100 /* The user count has begun "bumping" into the channel limit,
6101 so set a timer to raise the limit a bit. Any previous
6102 timers are removed so three incoming users within the delay
6103 results in one limit change, not three. */
6105 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6106 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6109 if(channel->join_flooded)
6111 /* don't automatically give ops or voice during a join flood */
6113 else if(cData->lvlOpts[lvlGiveOps] == 0)
6114 modes |= MODE_CHANOP;
6115 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6116 modes |= MODE_VOICE;
6118 greeting = cData->greeting;
6119 if(user->handle_info)
6121 handle = user->handle_info;
6123 if(IsHelper(user) && !IsHelping(user))
6126 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6128 if(channel == chanserv_conf.support_channels.list[ii])
6130 HANDLE_SET_FLAG(user->handle_info, HELPING);
6136 uData = GetTrueChannelAccess(cData, handle);
6137 if(uData && !IsUserSuspended(uData))
6139 /* Ops and above were handled by the above case. */
6140 if(IsUserAutoOp(uData))
6142 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6143 modes |= MODE_CHANOP;
6144 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6145 modes |= MODE_VOICE;
6147 if(uData->access >= UL_PRESENT)
6148 cData->visited = now;
6149 if(cData->user_greeting)
6150 greeting = cData->user_greeting;
6152 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6153 && ((now - uData->seen) >= chanserv_conf.info_delay)
6161 /* If user joining normally (not during burst), apply op or voice,
6162 * and send greeting/userinfo as appropriate.
6164 if(!user->uplink->burst)
6168 if(modes & MODE_CHANOP)
6169 modes &= ~MODE_VOICE;
6170 change.args[0].mode = modes;
6171 change.args[0].u.member = mNode;
6172 mod_chanmode_announce(chanserv, channel, &change);
6175 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6177 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6183 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6185 struct mod_chanmode change;
6186 struct userData *channel;
6187 unsigned int ii, jj;
6189 if(!user->handle_info)
6192 mod_chanmode_init(&change);
6194 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6196 struct chanNode *cn;
6197 struct modeNode *mn;
6198 if(IsUserSuspended(channel)
6199 || IsSuspended(channel->channel)
6200 || !(cn = channel->channel->channel))
6203 mn = GetUserMode(cn, user);
6206 if(!IsUserSuspended(channel)
6207 && IsUserAutoInvite(channel)
6208 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6210 && !user->uplink->burst)
6211 irc_invite(chanserv, user, cn);
6215 if(channel->access >= UL_PRESENT)
6216 channel->channel->visited = now;
6218 if(IsUserAutoOp(channel))
6220 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6221 change.args[0].mode = MODE_CHANOP;
6222 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6223 change.args[0].mode = MODE_VOICE;
6225 change.args[0].mode = 0;
6226 change.args[0].u.member = mn;
6227 if(change.args[0].mode)
6228 mod_chanmode_announce(chanserv, cn, &change);
6231 channel->seen = now;
6232 channel->present = 1;
6235 for(ii = 0; ii < user->channels.used; ++ii)
6237 struct chanNode *channel = user->channels.list[ii]->channel;
6238 struct banData *ban;
6240 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6241 || !channel->channel_info
6242 || IsSuspended(channel->channel_info))
6244 for(jj = 0; jj < channel->banlist.used; ++jj)
6245 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6247 if(jj < channel->banlist.used)
6249 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6251 char kick_reason[MAXLEN];
6252 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6254 change.args[0].mode = MODE_BAN;
6255 change.args[0].u.hostmask = ban->mask;
6256 mod_chanmode_announce(chanserv, channel, &change);
6257 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6258 KickChannelUser(user, channel, chanserv, kick_reason);
6259 ban->triggered = now;
6264 if(IsSupportHelper(user))
6266 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6268 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6270 HANDLE_SET_FLAG(user->handle_info, HELPING);
6278 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6280 struct chanData *cData;
6281 struct userData *uData;
6283 cData = mn->channel->channel_info;
6284 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6287 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6289 /* Allow for a bit of padding so that the limit doesn't
6290 track the user count exactly, which could get annoying. */
6291 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6293 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6294 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6298 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6300 scan_user_presence(uData, mn->user);
6302 if (uData->access >= UL_PRESENT)
6303 cData->visited = now;
6306 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6308 unsigned int ii, jj;
6309 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6311 for(jj = 0; jj < mn->user->channels.used; ++jj)
6312 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6314 if(jj < mn->user->channels.used)
6317 if(ii == chanserv_conf.support_channels.used)
6318 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6323 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6325 struct userData *uData;
6327 if(!channel->channel_info || !kicker || IsService(kicker)
6328 || (kicker == victim) || IsSuspended(channel->channel_info)
6329 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6332 if(protect_user(victim, kicker, channel->channel_info))
6334 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6335 KickChannelUser(kicker, channel, chanserv, reason);
6338 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6343 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6345 struct chanData *cData;
6347 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6350 cData = channel->channel_info;
6351 if(bad_topic(channel, user, channel->topic))
6353 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6354 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6355 SetChannelTopic(channel, chanserv, old_topic, 1);
6356 else if(cData->topic)
6357 SetChannelTopic(channel, chanserv, cData->topic, 1);
6360 /* With topicsnarf, grab the topic and save it as the default topic. */
6361 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6364 cData->topic = strdup(channel->topic);
6370 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6372 struct mod_chanmode *bounce = NULL;
6373 unsigned int bnc, ii;
6376 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6379 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6380 && mode_lock_violated(&channel->channel_info->modes, change))
6382 char correct[MAXLEN];
6383 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6384 mod_chanmode_format(&channel->channel_info->modes, correct);
6385 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6387 for(ii = bnc = 0; ii < change->argc; ++ii)
6389 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6391 const struct userNode *victim = change->args[ii].u.member->user;
6392 if(!protect_user(victim, user, channel->channel_info))
6395 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6398 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6399 bounce->args[bnc].u.member = GetUserMode(channel, user);
6400 if(bounce->args[bnc].u.member)
6404 bounce->args[bnc].mode = MODE_CHANOP;
6405 bounce->args[bnc].u.member = change->args[ii].u.member;
6407 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6409 else if(change->args[ii].mode & MODE_CHANOP)
6411 const struct userNode *victim = change->args[ii].u.member->user;
6412 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6415 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6416 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6417 bounce->args[bnc].u.member = change->args[ii].u.member;
6420 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6422 const char *ban = change->args[ii].u.hostmask;
6423 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6426 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6427 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6428 bounce->args[bnc].u.hostmask = strdup(ban);
6430 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6435 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6436 mod_chanmode_announce(chanserv, channel, bounce);
6437 for(ii = 0; ii < change->argc; ++ii)
6438 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6439 free((char*)bounce->args[ii].u.hostmask);
6440 mod_chanmode_free(bounce);
6445 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6447 struct chanNode *channel;
6448 struct banData *bData;
6449 struct mod_chanmode change;
6450 unsigned int ii, jj;
6451 char kick_reason[MAXLEN];
6453 mod_chanmode_init(&change);
6455 change.args[0].mode = MODE_BAN;
6456 for(ii = 0; ii < user->channels.used; ++ii)
6458 channel = user->channels.list[ii]->channel;
6459 /* Need not check for bans if they're opped or voiced. */
6460 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6462 /* Need not check for bans unless channel registration is active. */
6463 if(!channel->channel_info || IsSuspended(channel->channel_info))
6465 /* Look for a matching ban already on the channel. */
6466 for(jj = 0; jj < channel->banlist.used; ++jj)
6467 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6469 /* Need not act if we found one. */
6470 if(jj < channel->banlist.used)
6472 /* Look for a matching ban in this channel. */
6473 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6475 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6477 change.args[0].u.hostmask = bData->mask;
6478 mod_chanmode_announce(chanserv, channel, &change);
6479 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6480 KickChannelUser(user, channel, chanserv, kick_reason);
6481 bData->triggered = now;
6482 break; /* we don't need to check any more bans in the channel */
6487 static void handle_rename(struct handle_info *handle, const char *old_handle)
6489 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6493 dict_remove2(handle_dnrs, old_handle, 1);
6494 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6495 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6500 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6502 struct userNode *h_user;
6504 if(handle->channels)
6506 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6507 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6509 while(handle->channels)
6510 del_channel_user(handle->channels, 1);
6515 handle_server_link(UNUSED_ARG(struct server *server))
6517 struct chanData *cData;
6519 for(cData = channelList; cData; cData = cData->next)
6521 if(!IsSuspended(cData))
6522 cData->may_opchan = 1;
6523 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6524 && !cData->channel->join_flooded
6525 && ((cData->channel->limit - cData->channel->members.used)
6526 < chanserv_conf.adjust_threshold))
6528 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6529 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6535 chanserv_conf_read(void)
6539 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6540 struct mod_chanmode *change;
6541 struct string_list *strlist;
6542 struct chanNode *chan;
6545 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6547 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6550 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6551 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6552 chanserv_conf.support_channels.used = 0;
6553 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6555 for(ii = 0; ii < strlist->used; ++ii)
6557 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6560 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6562 channelList_append(&chanserv_conf.support_channels, chan);
6565 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6568 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6571 chan = AddChannel(str, now, str2, NULL);
6573 channelList_append(&chanserv_conf.support_channels, chan);
6575 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6576 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6577 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6578 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6579 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6580 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6581 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6582 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6583 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6584 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6585 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6586 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6587 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6588 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6589 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6590 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6591 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6592 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6593 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6594 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6595 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6596 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6597 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6598 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6599 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6601 NickChange(chanserv, str, 0);
6602 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6603 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6604 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6605 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6606 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6607 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6608 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6609 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6610 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6611 chanserv_conf.max_owned = str ? atoi(str) : 5;
6612 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6613 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6614 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6615 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6616 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6617 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6618 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6621 safestrncpy(mode_line, str, sizeof(mode_line));
6622 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6623 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6624 && (change->argc < 2))
6626 chanserv_conf.default_modes = *change;
6627 mod_chanmode_free(change);
6629 free_string_list(chanserv_conf.set_shows);
6630 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6632 strlist = string_list_copy(strlist);
6635 static const char *list[] = {
6636 /* free form text */
6637 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6638 /* options based on user level */
6639 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6640 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6641 /* multiple choice options */
6642 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6643 /* binary options */
6644 "DynLimit", "NoDelete",
6649 strlist = alloc_string_list(ArrayLength(list)-1);
6650 for(ii=0; list[ii]; ii++)
6651 string_list_append(strlist, strdup(list[ii]));
6653 chanserv_conf.set_shows = strlist;
6654 /* We don't look things up now, in case the list refers to options
6655 * defined by modules initialized after this point. Just mark the
6656 * function list as invalid, so it will be initialized.
6658 set_shows_list.used = 0;
6659 free_string_list(chanserv_conf.eightball);
6660 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6663 strlist = string_list_copy(strlist);
6667 strlist = alloc_string_list(4);
6668 string_list_append(strlist, strdup("Yes."));
6669 string_list_append(strlist, strdup("No."));
6670 string_list_append(strlist, strdup("Maybe so."));
6672 chanserv_conf.eightball = strlist;
6673 free_string_list(chanserv_conf.old_ban_names);
6674 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6676 strlist = string_list_copy(strlist);
6678 strlist = alloc_string_list(2);
6679 chanserv_conf.old_ban_names = strlist;
6680 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6681 off_channel = str ? atoi(str) : 0;
6685 chanserv_note_type_read(const char *key, struct record_data *rd)
6688 struct note_type *ntype;
6691 if(!(obj = GET_RECORD_OBJECT(rd)))
6693 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6696 if(!(ntype = chanserv_create_note_type(key)))
6698 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6702 /* Figure out set access */
6703 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6705 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6706 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6708 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6710 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6711 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6713 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6715 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6719 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6720 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6721 ntype->set_access.min_opserv = 0;
6724 /* Figure out visibility */
6725 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6726 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6727 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6728 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6729 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6730 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6731 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6732 ntype->visible_type = NOTE_VIS_ALL;
6734 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6736 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6737 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6741 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6743 struct handle_info *handle;
6744 struct userData *uData;
6745 char *seen, *inf, *flags;
6747 unsigned short access;
6749 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6751 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6755 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6756 if(access > UL_OWNER)
6758 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6762 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6763 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6764 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6765 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6766 handle = get_handle_info(key);
6769 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6773 uData = add_channel_user(chan, handle, access, last_seen, inf);
6774 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6778 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6780 struct banData *bData;
6781 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6782 time_t set_time, triggered_time, expires_time;
6784 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6786 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6790 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6791 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6792 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6793 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6794 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6795 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6796 if (!reason || !owner)
6799 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6800 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6802 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6804 expires_time = set_time + atoi(s_duration);
6808 if(!reason || (expires_time && (expires_time < now)))
6811 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6814 static struct suspended *
6815 chanserv_read_suspended(dict_t obj)
6817 struct suspended *suspended = calloc(1, sizeof(*suspended));
6821 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6822 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6823 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6824 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6825 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6826 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6827 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6828 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6829 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6830 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6835 chanserv_channel_read(const char *key, struct record_data *hir)
6837 struct suspended *suspended;
6838 struct mod_chanmode *modes;
6839 struct chanNode *cNode;
6840 struct chanData *cData;
6841 struct dict *channel, *obj;
6842 char *str, *argv[10];
6846 channel = hir->d.object;
6848 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6851 cNode = AddChannel(key, now, NULL, NULL);
6854 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6857 cData = register_channel(cNode, str);
6860 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6864 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6866 enum levelOption lvlOpt;
6867 enum charOption chOpt;
6869 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6870 cData->flags = atoi(str);
6872 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6874 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6876 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6877 else if(levelOptions[lvlOpt].old_flag)
6879 if(cData->flags & levelOptions[lvlOpt].old_flag)
6880 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6882 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6886 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6888 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6890 cData->chOpts[chOpt] = str[0];
6893 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6895 enum levelOption lvlOpt;
6896 enum charOption chOpt;
6899 cData->flags = base64toint(str, 5);
6900 count = strlen(str += 5);
6901 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6904 if(levelOptions[lvlOpt].old_flag)
6906 if(cData->flags & levelOptions[lvlOpt].old_flag)
6907 lvl = levelOptions[lvlOpt].flag_value;
6909 lvl = levelOptions[lvlOpt].default_value;
6911 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6913 case 'c': lvl = UL_COOWNER; break;
6914 case 'm': lvl = UL_MASTER; break;
6915 case 'n': lvl = UL_OWNER+1; break;
6916 case 'o': lvl = UL_OP; break;
6917 case 'p': lvl = UL_PEON; break;
6918 case 'w': lvl = UL_OWNER; break;
6919 default: lvl = 0; break;
6921 cData->lvlOpts[lvlOpt] = lvl;
6923 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6924 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6927 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6929 suspended = chanserv_read_suspended(obj);
6930 cData->suspended = suspended;
6931 suspended->cData = cData;
6932 /* We could use suspended->expires and suspended->revoked to
6933 * set the CHANNEL_SUSPENDED flag, but we don't. */
6935 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6937 suspended = calloc(1, sizeof(*suspended));
6938 suspended->issued = 0;
6939 suspended->revoked = 0;
6940 suspended->suspender = strdup(str);
6941 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6942 suspended->expires = str ? atoi(str) : 0;
6943 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6944 suspended->reason = strdup(str ? str : "No reason");
6945 suspended->previous = NULL;
6946 cData->suspended = suspended;
6947 suspended->cData = cData;
6951 cData->flags &= ~CHANNEL_SUSPENDED;
6952 suspended = NULL; /* to squelch a warning */
6955 if(IsSuspended(cData)) {
6956 if(suspended->expires > now)
6957 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6958 else if(suspended->expires)
6959 cData->flags &= ~CHANNEL_SUSPENDED;
6962 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6963 struct mod_chanmode change;
6964 mod_chanmode_init(&change);
6966 change.args[0].mode = MODE_CHANOP;
6967 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6968 mod_chanmode_announce(chanserv, cNode, &change);
6971 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6972 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6973 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6974 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6975 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6976 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6977 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6978 cData->max = str ? atoi(str) : 0;
6979 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6980 cData->greeting = str ? strdup(str) : NULL;
6981 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6982 cData->user_greeting = str ? strdup(str) : NULL;
6983 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6984 cData->topic_mask = str ? strdup(str) : NULL;
6985 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6986 cData->topic = str ? strdup(str) : NULL;
6988 if(!IsSuspended(cData)
6989 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6990 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6991 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
6992 cData->modes = *modes;
6994 cData->modes.modes_set |= MODE_REGISTERED;
6995 if(cData->modes.argc > 1)
6996 cData->modes.argc = 1;
6997 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6998 mod_chanmode_free(modes);
7001 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7002 for(it = dict_first(obj); it; it = iter_next(it))
7003 user_read_helper(iter_key(it), iter_data(it), cData);
7005 if(!cData->users && !IsProtected(cData))
7007 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7008 unregister_channel(cData, "has empty user list.");
7012 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7013 for(it = dict_first(obj); it; it = iter_next(it))
7014 ban_read_helper(iter_key(it), iter_data(it), cData);
7016 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7017 for(it = dict_first(obj); it; it = iter_next(it))
7019 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7020 struct record_data *rd = iter_data(it);
7021 const char *note, *setter;
7023 if(rd->type != RECDB_OBJECT)
7025 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7029 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7031 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7033 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7037 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7038 if(!setter) setter = "<unknown>";
7039 chanserv_add_channel_note(cData, ntype, setter, note);
7047 chanserv_dnr_read(const char *key, struct record_data *hir)
7049 const char *setter, *reason, *str;
7050 struct do_not_register *dnr;
7053 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7056 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7059 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7062 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7065 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7066 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7067 if(expiry && expiry <= now)
7069 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7072 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7074 dnr->set = atoi(str);
7080 chanserv_saxdb_read(struct dict *database)
7082 struct dict *section;
7085 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7086 for(it = dict_first(section); it; it = iter_next(it))
7087 chanserv_note_type_read(iter_key(it), iter_data(it));
7089 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7090 for(it = dict_first(section); it; it = iter_next(it))
7091 chanserv_channel_read(iter_key(it), iter_data(it));
7093 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7094 for(it = dict_first(section); it; it = iter_next(it))
7095 chanserv_dnr_read(iter_key(it), iter_data(it));
7101 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7103 int high_present = 0;
7104 saxdb_start_record(ctx, KEY_USERS, 1);
7105 for(; uData; uData = uData->next)
7107 if((uData->access >= UL_PRESENT) && uData->present)
7109 saxdb_start_record(ctx, uData->handle->handle, 0);
7110 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7111 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7113 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7115 saxdb_write_string(ctx, KEY_INFO, uData->info);
7116 saxdb_end_record(ctx);
7118 saxdb_end_record(ctx);
7119 return high_present;
7123 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7127 saxdb_start_record(ctx, KEY_BANS, 1);
7128 for(; bData; bData = bData->next)
7130 saxdb_start_record(ctx, bData->mask, 0);
7131 saxdb_write_int(ctx, KEY_SET, bData->set);
7132 if(bData->triggered)
7133 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7135 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7137 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7139 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7140 saxdb_end_record(ctx);
7142 saxdb_end_record(ctx);
7146 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7148 saxdb_start_record(ctx, name, 0);
7149 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7150 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7152 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7154 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7156 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7158 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7159 saxdb_end_record(ctx);
7163 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7167 enum levelOption lvlOpt;
7168 enum charOption chOpt;
7170 saxdb_start_record(ctx, channel->channel->name, 1);
7172 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7173 saxdb_write_int(ctx, KEY_MAX, channel->max);
7175 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7176 if(channel->registrar)
7177 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7178 if(channel->greeting)
7179 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7180 if(channel->user_greeting)
7181 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7182 if(channel->topic_mask)
7183 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7184 if(channel->suspended)
7185 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7187 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7188 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7189 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7190 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7191 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7193 buf[0] = channel->chOpts[chOpt];
7195 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7197 saxdb_end_record(ctx);
7199 if(channel->modes.modes_set || channel->modes.modes_clear)
7201 mod_chanmode_format(&channel->modes, buf);
7202 saxdb_write_string(ctx, KEY_MODES, buf);
7205 high_present = chanserv_write_users(ctx, channel->users);
7206 chanserv_write_bans(ctx, channel->bans);
7208 if(dict_size(channel->notes))
7212 saxdb_start_record(ctx, KEY_NOTES, 1);
7213 for(it = dict_first(channel->notes); it; it = iter_next(it))
7215 struct note *note = iter_data(it);
7216 saxdb_start_record(ctx, iter_key(it), 0);
7217 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7218 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7219 saxdb_end_record(ctx);
7221 saxdb_end_record(ctx);
7224 if(channel->ownerTransfer)
7225 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7226 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7227 saxdb_end_record(ctx);
7231 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7235 saxdb_start_record(ctx, ntype->name, 0);
7236 switch(ntype->set_access_type)
7238 case NOTE_SET_CHANNEL_ACCESS:
7239 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7241 case NOTE_SET_CHANNEL_SETTER:
7242 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7244 case NOTE_SET_PRIVILEGED: default:
7245 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7248 switch(ntype->visible_type)
7250 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7251 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7252 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7254 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7255 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7256 saxdb_end_record(ctx);
7260 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7262 struct do_not_register *dnr;
7263 dict_iterator_t it, next;
7265 for(it = dict_first(dnrs); it; it = next)
7267 next = iter_next(it);
7268 dnr = iter_data(it);
7269 if(dnr->expires && dnr->expires <= now)
7271 dict_remove(dnrs, iter_key(it));
7274 saxdb_start_record(ctx, dnr->chan_name, 0);
7276 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7278 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7279 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7280 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7281 saxdb_end_record(ctx);
7286 chanserv_saxdb_write(struct saxdb_context *ctx)
7289 struct chanData *channel;
7292 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7293 for(it = dict_first(note_types); it; it = iter_next(it))
7294 chanserv_write_note_type(ctx, iter_data(it));
7295 saxdb_end_record(ctx);
7298 saxdb_start_record(ctx, KEY_DNR, 1);
7299 write_dnrs_helper(ctx, handle_dnrs);
7300 write_dnrs_helper(ctx, plain_dnrs);
7301 write_dnrs_helper(ctx, mask_dnrs);
7302 saxdb_end_record(ctx);
7305 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7306 for(channel = channelList; channel; channel = channel->next)
7307 chanserv_write_channel(ctx, channel);
7308 saxdb_end_record(ctx);
7314 chanserv_db_cleanup(void) {
7316 unreg_part_func(handle_part);
7318 unregister_channel(channelList, "terminating.");
7319 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7320 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7321 free(chanserv_conf.support_channels.list);
7322 dict_delete(handle_dnrs);
7323 dict_delete(plain_dnrs);
7324 dict_delete(mask_dnrs);
7325 dict_delete(note_types);
7326 free_string_list(chanserv_conf.eightball);
7327 free_string_list(chanserv_conf.old_ban_names);
7328 free_string_list(chanserv_conf.set_shows);
7329 free(set_shows_list.list);
7330 free(uset_shows_list.list);
7333 struct userData *helper = helperList;
7334 helperList = helperList->next;
7339 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7340 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7341 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7344 init_chanserv(const char *nick)
7346 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7347 conf_register_reload(chanserv_conf_read);
7351 reg_server_link_func(handle_server_link);
7352 reg_new_channel_func(handle_new_channel);
7353 reg_join_func(handle_join);
7354 reg_part_func(handle_part);
7355 reg_kick_func(handle_kick);
7356 reg_topic_func(handle_topic);
7357 reg_mode_change_func(handle_mode);
7358 reg_nick_change_func(handle_nick_change);
7359 reg_auth_func(handle_auth);
7362 reg_handle_rename_func(handle_rename);
7363 reg_unreg_func(handle_unreg);
7365 handle_dnrs = dict_new();
7366 dict_set_free_data(handle_dnrs, free);
7367 plain_dnrs = dict_new();
7368 dict_set_free_data(plain_dnrs, free);
7369 mask_dnrs = dict_new();
7370 dict_set_free_data(mask_dnrs, free);
7372 reg_svccmd_unbind_func(handle_svccmd_unbind);
7373 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7374 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7375 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7376 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7377 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7378 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7379 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7380 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7381 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7383 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7384 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7386 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7387 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7388 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7389 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7390 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7392 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7393 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7394 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7395 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7396 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7398 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7399 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7400 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7401 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7403 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7404 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7405 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7406 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7407 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7408 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7409 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7410 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7412 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7413 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7414 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7415 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7416 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7417 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7418 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7419 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7420 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7421 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7422 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7423 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7424 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7425 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7427 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7428 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7429 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7430 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7431 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7433 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7434 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7436 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7437 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7438 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7439 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7440 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7441 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7442 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7443 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7444 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7445 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7446 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7448 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7449 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7451 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7452 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7453 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7454 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7456 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7457 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7458 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7459 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7460 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7462 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7463 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7464 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7465 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7466 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7467 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7469 /* Channel options */
7470 DEFINE_CHANNEL_OPTION(defaulttopic);
7471 DEFINE_CHANNEL_OPTION(topicmask);
7472 DEFINE_CHANNEL_OPTION(greeting);
7473 DEFINE_CHANNEL_OPTION(usergreeting);
7474 DEFINE_CHANNEL_OPTION(modes);
7475 DEFINE_CHANNEL_OPTION(enfops);
7476 DEFINE_CHANNEL_OPTION(giveops);
7477 DEFINE_CHANNEL_OPTION(protect);
7478 DEFINE_CHANNEL_OPTION(enfmodes);
7479 DEFINE_CHANNEL_OPTION(enftopic);
7480 DEFINE_CHANNEL_OPTION(pubcmd);
7481 DEFINE_CHANNEL_OPTION(givevoice);
7482 DEFINE_CHANNEL_OPTION(userinfo);
7483 DEFINE_CHANNEL_OPTION(dynlimit);
7484 DEFINE_CHANNEL_OPTION(topicsnarf);
7485 DEFINE_CHANNEL_OPTION(nodelete);
7486 DEFINE_CHANNEL_OPTION(toys);
7487 DEFINE_CHANNEL_OPTION(setters);
7488 DEFINE_CHANNEL_OPTION(topicrefresh);
7489 DEFINE_CHANNEL_OPTION(ctcpusers);
7490 DEFINE_CHANNEL_OPTION(ctcpreaction);
7491 DEFINE_CHANNEL_OPTION(inviteme);
7493 DEFINE_CHANNEL_OPTION(offchannel);
7494 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7496 /* Alias set topic to set defaulttopic for compatibility. */
7497 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7500 DEFINE_USER_OPTION(noautoop);
7501 DEFINE_USER_OPTION(autoinvite);
7502 DEFINE_USER_OPTION(info);
7504 /* Alias uset autovoice to uset autoop. */
7505 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7507 note_types = dict_new();
7508 dict_set_free_data(note_types, chanserv_deref_note_type);
7511 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7512 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7513 service_register(chanserv)->trigger = '!';
7514 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7516 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7518 if(chanserv_conf.channel_expire_frequency)
7519 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7521 if(chanserv_conf.dnr_expire_frequency)
7522 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7524 if(chanserv_conf.refresh_period)
7526 time_t next_refresh;
7527 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7528 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7531 reg_exit_func(chanserv_db_cleanup);
7532 message_register_table(msgtab);