1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
424 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
427 { "CSMSG_USER_OPTIONS", "User Options:" },
428 { "CSMSG_USER_PROTECTED", "That user is protected." },
431 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
432 { "CSMSG_PING_RESPONSE", "Pong!" },
433 { "CSMSG_WUT_RESPONSE", "wut" },
434 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
435 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
436 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
437 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
438 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
439 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
440 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
443 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
447 /* eject_user and unban_user flags */
448 #define ACTION_KICK 0x0001
449 #define ACTION_BAN 0x0002
450 #define ACTION_ADD_BAN 0x0004
451 #define ACTION_ADD_TIMED_BAN 0x0008
452 #define ACTION_UNBAN 0x0010
453 #define ACTION_DEL_BAN 0x0020
455 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
456 #define MODELEN 40 + KEYLEN
460 #define CSFUNC_ARGS user, channel, argc, argv, cmd
462 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
463 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
464 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
465 reply("MSG_MISSING_PARAMS", argv[0]); \
469 DECLARE_LIST(dnrList, struct do_not_register *);
470 DEFINE_LIST(dnrList, struct do_not_register *);
472 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
474 struct userNode *chanserv;
477 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
478 static struct log_type *CS_LOG;
482 struct channelList support_channels;
483 struct mod_chanmode default_modes;
485 unsigned long db_backup_frequency;
486 unsigned long channel_expire_frequency;
489 unsigned int adjust_delay;
490 long channel_expire_delay;
491 unsigned int nodelete_level;
493 unsigned int adjust_threshold;
494 int join_flood_threshold;
496 unsigned int greeting_length;
497 unsigned int refresh_period;
498 unsigned int giveownership_period;
500 unsigned int max_owned;
501 unsigned int max_chan_users;
502 unsigned int max_chan_bans;
503 unsigned int max_userinfo_length;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->ownerTransfer = now;
1084 channel->flags = CHANNEL_DEFAULT_FLAGS;
1085 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1086 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1088 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1090 channel->prev = NULL;
1091 channel->next = channelList;
1094 channelList->prev = channel;
1095 channelList = channel;
1096 registered_channels++;
1098 channel->channel = cNode;
1100 cNode->channel_info = channel;
1105 static struct userData*
1106 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1108 struct userData *ud;
1110 if(access > UL_OWNER)
1113 ud = calloc(1, sizeof(*ud));
1114 ud->channel = channel;
1115 ud->handle = handle;
1117 ud->access = access;
1118 ud->info = info ? strdup(info) : NULL;
1121 ud->next = channel->users;
1123 channel->users->prev = ud;
1124 channel->users = ud;
1126 channel->userCount++;
1130 ud->u_next = ud->handle->channels;
1132 ud->u_next->u_prev = ud;
1133 ud->handle->channels = ud;
1138 static void unregister_channel(struct chanData *channel, const char *reason);
1141 del_channel_user(struct userData *user, int do_gc)
1143 struct chanData *channel = user->channel;
1145 channel->userCount--;
1149 user->prev->next = user->next;
1151 channel->users = user->next;
1153 user->next->prev = user->prev;
1156 user->u_prev->u_next = user->u_next;
1158 user->handle->channels = user->u_next;
1160 user->u_next->u_prev = user->u_prev;
1164 if(do_gc && !channel->users && !IsProtected(channel))
1165 unregister_channel(channel, "lost all users.");
1168 static void expire_ban(void *data);
1170 static struct banData*
1171 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1174 unsigned int ii, l1, l2;
1179 bd = malloc(sizeof(struct banData));
1181 bd->channel = channel;
1183 bd->triggered = triggered;
1184 bd->expires = expires;
1186 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1188 extern const char *hidden_host_suffix;
1189 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1193 l2 = strlen(old_name);
1196 if(irccasecmp(mask + l1 - l2, old_name))
1198 new_mask = alloca(MAXLEN);
1199 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1202 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1204 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1205 bd->reason = strdup(reason);
1208 timeq_add(expires, expire_ban, bd);
1211 bd->next = channel->bans;
1213 channel->bans->prev = bd;
1215 channel->banCount++;
1222 del_channel_ban(struct banData *ban)
1224 ban->channel->banCount--;
1228 ban->prev->next = ban->next;
1230 ban->channel->bans = ban->next;
1233 ban->next->prev = ban->prev;
1236 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1245 expire_ban(void *data)
1247 struct banData *bd = data;
1248 if(!IsSuspended(bd->channel))
1250 struct banList bans;
1251 struct mod_chanmode change;
1253 bans = bd->channel->channel->banlist;
1254 mod_chanmode_init(&change);
1255 for(ii=0; ii<bans.used; ii++)
1257 if(!strcmp(bans.list[ii]->ban, bd->mask))
1260 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1261 change.args[0].u.hostmask = bd->mask;
1262 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1268 del_channel_ban(bd);
1271 static void chanserv_expire_suspension(void *data);
1274 unregister_channel(struct chanData *channel, const char *reason)
1276 struct mod_chanmode change;
1277 char msgbuf[MAXLEN];
1279 /* After channel unregistration, the following must be cleaned
1281 - Channel information.
1284 - Channel suspension data.
1285 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1291 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 mod_chanmode_init(&change);
1296 change.modes_clear |= MODE_REGISTERED;
1297 mod_chanmode_announce(chanserv, channel->channel, &change);
1300 while(channel->users)
1301 del_channel_user(channel->users, 0);
1303 while(channel->bans)
1304 del_channel_ban(channel->bans);
1306 free(channel->topic);
1307 free(channel->registrar);
1308 free(channel->greeting);
1309 free(channel->user_greeting);
1310 free(channel->topic_mask);
1313 channel->prev->next = channel->next;
1315 channelList = channel->next;
1318 channel->next->prev = channel->prev;
1320 if(channel->suspended)
1322 struct chanNode *cNode = channel->channel;
1323 struct suspended *suspended, *next_suspended;
1325 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1327 next_suspended = suspended->previous;
1328 free(suspended->suspender);
1329 free(suspended->reason);
1330 if(suspended->expires)
1331 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1336 cNode->channel_info = NULL;
1338 channel->channel->channel_info = NULL;
1340 dict_delete(channel->notes);
1341 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1342 if(!IsSuspended(channel))
1343 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1345 UnlockChannel(channel->channel);
1347 registered_channels--;
1351 expire_channels(UNUSED_ARG(void *data))
1353 struct chanData *channel, *next;
1354 struct userData *user;
1355 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1357 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1358 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1360 for(channel = channelList; channel; channel = next)
1362 next = channel->next;
1364 /* See if the channel can be expired. */
1365 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1366 || IsProtected(channel))
1369 /* Make sure there are no high-ranking users still in the channel. */
1370 for(user=channel->users; user; user=user->next)
1371 if(user->present && (user->access >= UL_PRESENT))
1376 /* Unregister the channel */
1377 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1378 unregister_channel(channel, "registration expired.");
1381 if(chanserv_conf.channel_expire_frequency)
1382 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1386 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1388 char protect = channel->chOpts[chProtect];
1389 struct userData *cs_victim, *cs_aggressor;
1391 /* Don't protect if no one is to be protected, someone is attacking
1392 himself, or if the aggressor is an IRC Operator. */
1393 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1396 /* Don't protect if the victim isn't authenticated (because they
1397 can't be a channel user), unless we are to protect non-users
1399 cs_victim = GetChannelAccess(channel, victim->handle_info);
1400 if(protect != 'a' && !cs_victim)
1403 /* Protect if the aggressor isn't a user because at this point,
1404 the aggressor can only be less than or equal to the victim. */
1405 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1409 /* If the aggressor was a user, then the victim can't be helped. */
1416 if(cs_victim->access > cs_aggressor->access)
1421 if(cs_victim->access >= cs_aggressor->access)
1430 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1432 struct chanData *cData = channel->channel_info;
1433 struct userData *cs_victim;
1435 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1436 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1437 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1439 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1447 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1449 if(IsService(victim))
1451 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1455 if(protect_user(victim, user, channel->channel_info))
1457 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1464 static struct do_not_register *
1465 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1467 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1468 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1469 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1470 strcpy(dnr->reason, reason);
1472 if(dnr->chan_name[0] == '*')
1473 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1474 else if(strpbrk(dnr->chan_name, "*?"))
1475 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1477 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1481 static struct dnrList
1482 chanserv_find_dnrs(const char *chan_name, const char *handle)
1484 struct dnrList list;
1486 struct do_not_register *dnr;
1488 dnrList_init(&list);
1489 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1490 dnrList_append(&list, dnr);
1491 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1492 dnrList_append(&list, dnr);
1494 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1495 if(match_ircglob(chan_name, iter_key(it)))
1496 dnrList_append(&list, iter_data(it));
1501 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1503 struct dnrList list;
1504 struct do_not_register *dnr;
1506 char buf[INTERVALLEN];
1508 list = chanserv_find_dnrs(chan_name, handle);
1509 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1511 dnr = list.list[ii];
1514 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1515 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1518 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1521 reply("CSMSG_MORE_DNRS", list.used - ii);
1526 struct do_not_register *
1527 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1529 struct do_not_register *dnr;
1532 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1536 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1538 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1539 if(match_ircglob(chan_name, iter_key(it)))
1540 return iter_data(it);
1545 static CHANSERV_FUNC(cmd_noregister)
1548 struct do_not_register *dnr;
1549 char buf[INTERVALLEN];
1550 unsigned int matches;
1556 reply("CSMSG_DNR_SEARCH_RESULTS");
1558 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1560 dnr = iter_data(it);
1562 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1564 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1569 dnr = iter_data(it);
1571 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1573 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1576 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1578 dnr = iter_data(it);
1580 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1582 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587 reply("MSG_MATCH_COUNT", matches);
1589 reply("MSG_NO_MATCHES");
1595 if(!IsChannelName(target) && (*target != '*'))
1597 reply("CSMSG_NOT_DNR", target);
1603 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1604 if((*target == '*') && !get_handle_info(target + 1))
1606 reply("MSG_HANDLE_UNKNOWN", target + 1);
1609 chanserv_add_dnr(target, user->handle_info->handle, reason);
1610 reply("CSMSG_NOREGISTER_CHANNEL", target);
1614 reply("CSMSG_DNR_SEARCH_RESULTS");
1616 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1618 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1620 reply("MSG_NO_MATCHES");
1624 static CHANSERV_FUNC(cmd_allowregister)
1626 const char *chan_name = argv[1];
1628 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1630 dict_remove(handle_dnrs, chan_name+1);
1631 reply("CSMSG_DNR_REMOVED", chan_name);
1633 else if(dict_find(plain_dnrs, chan_name, NULL))
1635 dict_remove(plain_dnrs, chan_name);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(mask_dnrs, chan_name, NULL))
1640 dict_remove(mask_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1645 reply("CSMSG_NO_SUCH_DNR", chan_name);
1652 chanserv_get_owned_count(struct handle_info *hi)
1654 struct userData *cList;
1657 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1658 if(cList->access == UL_OWNER)
1663 static CHANSERV_FUNC(cmd_register)
1665 struct handle_info *handle;
1666 struct chanData *cData;
1667 struct modeNode *mn;
1668 char reason[MAXLEN];
1670 unsigned int new_channel, force=0;
1671 struct do_not_register *dnr;
1675 if(channel->channel_info)
1677 reply("CSMSG_ALREADY_REGGED", channel->name);
1681 if(channel->bad_channel)
1683 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1687 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1689 reply("CSMSG_MUST_BE_OPPED", channel->name);
1694 chan_name = channel->name;
1698 if((argc < 2) || !IsChannelName(argv[1]))
1700 reply("MSG_NOT_CHANNEL_NAME");
1704 if(opserv_bad_channel(argv[1]))
1706 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1711 chan_name = argv[1];
1714 if(argc >= (new_channel+2))
1716 if(!IsHelping(user))
1718 reply("CSMSG_PROXY_FORBIDDEN");
1722 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1724 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1725 dnr = chanserv_is_dnr(chan_name, handle);
1729 handle = user->handle_info;
1730 dnr = chanserv_is_dnr(chan_name, handle);
1734 if(!IsHelping(user))
1735 reply("CSMSG_DNR_CHANNEL", chan_name);
1737 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1741 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1743 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1748 channel = AddChannel(argv[1], now, NULL, NULL);
1750 cData = register_channel(channel, user->handle_info->handle);
1751 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1752 cData->modes = chanserv_conf.default_modes;
1754 cData->modes.modes_set |= MODE_REGISTERED;
1755 if (IsOffChannel(cData))
1757 mod_chanmode_announce(chanserv, channel, &cData->modes);
1761 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1762 change->args[change->argc].mode = MODE_CHANOP;
1763 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1765 mod_chanmode_announce(chanserv, channel, change);
1766 mod_chanmode_free(change);
1769 /* Initialize the channel's max user record. */
1770 cData->max = channel->members.used;
1772 if(handle != user->handle_info)
1773 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1775 reply("CSMSG_REG_SUCCESS", channel->name);
1777 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1778 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1783 make_confirmation_string(struct userData *uData)
1785 static char strbuf[16];
1790 for(src = uData->handle->handle; *src; )
1791 accum = accum * 31 + toupper(*src++);
1793 for(src = uData->channel->channel->name; *src; )
1794 accum = accum * 31 + toupper(*src++);
1795 sprintf(strbuf, "%08x", accum);
1799 static CHANSERV_FUNC(cmd_unregister)
1802 char reason[MAXLEN];
1803 struct chanData *cData;
1804 struct userData *uData;
1806 cData = channel->channel_info;
1809 reply("CSMSG_NOT_REGISTERED", channel->name);
1813 uData = GetChannelUser(cData, user->handle_info);
1814 if(!uData || (uData->access < UL_OWNER))
1816 reply("CSMSG_NO_ACCESS");
1820 if(IsProtected(cData))
1822 reply("CSMSG_UNREG_NODELETE", channel->name);
1826 if(!IsHelping(user))
1828 const char *confirm_string;
1829 if(IsSuspended(cData))
1831 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1834 confirm_string = make_confirmation_string(uData);
1835 if((argc < 2) || strcmp(argv[1], confirm_string))
1837 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1842 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1843 name = strdup(channel->name);
1844 unregister_channel(cData, reason);
1845 reply("CSMSG_UNREG_SUCCESS", name);
1850 static CHANSERV_FUNC(cmd_move)
1852 struct mod_chanmode change;
1853 struct chanNode *target;
1854 struct modeNode *mn;
1855 struct userData *uData;
1856 char reason[MAXLEN];
1857 struct do_not_register *dnr;
1861 if(IsProtected(channel->channel_info))
1863 reply("CSMSG_MOVE_NODELETE", channel->name);
1867 if(!IsChannelName(argv[1]))
1869 reply("MSG_NOT_CHANNEL_NAME");
1873 if(opserv_bad_channel(argv[1]))
1875 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1879 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1881 for(uData = channel->channel_info->users; uData; uData = uData->next)
1883 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1885 if(!IsHelping(user))
1886 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1888 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1894 mod_chanmode_init(&change);
1895 if(!(target = GetChannel(argv[1])))
1897 target = AddChannel(argv[1], now, NULL, NULL);
1898 if(!IsSuspended(channel->channel_info))
1899 AddChannelUser(chanserv, target);
1901 else if(target->channel_info)
1903 reply("CSMSG_ALREADY_REGGED", target->name);
1906 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1907 && !IsHelping(user))
1909 reply("CSMSG_MUST_BE_OPPED", target->name);
1912 else if(!IsSuspended(channel->channel_info))
1915 change.args[0].mode = MODE_CHANOP;
1916 change.args[0].u.member = AddChannelUser(chanserv, target);
1917 mod_chanmode_announce(chanserv, target, &change);
1922 /* Clear MODE_REGISTERED from old channel, add it to new. */
1924 change.modes_clear = MODE_REGISTERED;
1925 mod_chanmode_announce(chanserv, channel, &change);
1926 change.modes_clear = 0;
1927 change.modes_set = MODE_REGISTERED;
1928 mod_chanmode_announce(chanserv, target, &change);
1931 /* Move the channel_info to the target channel; it
1932 shouldn't be necessary to clear timeq callbacks
1933 for the old channel. */
1934 target->channel_info = channel->channel_info;
1935 target->channel_info->channel = target;
1936 channel->channel_info = NULL;
1938 reply("CSMSG_MOVE_SUCCESS", target->name);
1940 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1941 if(!IsSuspended(target->channel_info))
1943 char reason2[MAXLEN];
1944 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1945 DelChannelUser(chanserv, channel, reason2, 0);
1947 UnlockChannel(channel);
1948 LockChannel(target);
1949 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1954 merge_users(struct chanData *source, struct chanData *target)
1956 struct userData *suData, *tuData, *next;
1962 /* Insert the source's users into the scratch area. */
1963 for(suData = source->users; suData; suData = suData->next)
1964 dict_insert(merge, suData->handle->handle, suData);
1966 /* Iterate through the target's users, looking for
1967 users common to both channels. The lower access is
1968 removed from either the scratch area or target user
1970 for(tuData = target->users; tuData; tuData = next)
1972 struct userData *choice;
1974 next = tuData->next;
1976 /* If a source user exists with the same handle as a target
1977 channel's user, resolve the conflict by removing one. */
1978 suData = dict_find(merge, tuData->handle->handle, NULL);
1982 /* Pick the data we want to keep. */
1983 /* If the access is the same, use the later seen time. */
1984 if(suData->access == tuData->access)
1985 choice = (suData->seen > tuData->seen) ? suData : tuData;
1986 else /* Otherwise, keep the higher access level. */
1987 choice = (suData->access > tuData->access) ? suData : tuData;
1989 /* Remove the user that wasn't picked. */
1990 if(choice == tuData)
1992 dict_remove(merge, suData->handle->handle);
1993 del_channel_user(suData, 0);
1996 del_channel_user(tuData, 0);
1999 /* Move the remaining users to the target channel. */
2000 for(it = dict_first(merge); it; it = iter_next(it))
2002 suData = iter_data(it);
2004 /* Insert the user into the target channel's linked list. */
2005 suData->prev = NULL;
2006 suData->next = target->users;
2007 suData->channel = target;
2010 target->users->prev = suData;
2011 target->users = suData;
2013 /* Update the user counts for the target channel; the
2014 source counts are left alone. */
2015 target->userCount++;
2018 /* Possible to assert (source->users == NULL) here. */
2019 source->users = NULL;
2024 merge_bans(struct chanData *source, struct chanData *target)
2026 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2028 /* Hold on to the original head of the target ban list
2029 to avoid comparing source bans with source bans. */
2030 tFront = target->bans;
2032 /* Perform a totally expensive O(n*m) merge, ick. */
2033 for(sbData = source->bans; sbData; sbData = sNext)
2035 /* Flag to track whether the ban's been moved
2036 to the destination yet. */
2039 /* Possible to assert (sbData->prev == NULL) here. */
2040 sNext = sbData->next;
2042 for(tbData = tFront; tbData; tbData = tNext)
2044 tNext = tbData->next;
2046 /* Perform two comparisons between each source
2047 and target ban, conflicts are resolved by
2048 keeping the broader ban and copying the later
2049 expiration and triggered time. */
2050 if(match_ircglobs(tbData->mask, sbData->mask))
2052 /* There is a broader ban in the target channel that
2053 overrides one in the source channel; remove the
2054 source ban and break. */
2055 if(sbData->expires > tbData->expires)
2056 tbData->expires = sbData->expires;
2057 if(sbData->triggered > tbData->triggered)
2058 tbData->triggered = sbData->triggered;
2059 del_channel_ban(sbData);
2062 else if(match_ircglobs(sbData->mask, tbData->mask))
2064 /* There is a broader ban in the source channel that
2065 overrides one in the target channel; remove the
2066 target ban, fall through and move the source over. */
2067 if(tbData->expires > sbData->expires)
2068 sbData->expires = tbData->expires;
2069 if(tbData->triggered > sbData->triggered)
2070 sbData->triggered = tbData->triggered;
2071 if(tbData == tFront)
2073 del_channel_ban(tbData);
2076 /* Source bans can override multiple target bans, so
2077 we allow a source to run through this loop multiple
2078 times, but we can only move it once. */
2083 /* Remove the source ban from the source ban list. */
2085 sbData->next->prev = sbData->prev;
2087 /* Modify the source ban's associated channel. */
2088 sbData->channel = target;
2090 /* Insert the ban into the target channel's linked list. */
2091 sbData->prev = NULL;
2092 sbData->next = target->bans;
2095 target->bans->prev = sbData;
2096 target->bans = sbData;
2098 /* Update the user counts for the target channel. */
2103 /* Possible to assert (source->bans == NULL) here. */
2104 source->bans = NULL;
2108 merge_data(struct chanData *source, struct chanData *target)
2110 if(source->visited > target->visited)
2111 target->visited = source->visited;
2115 merge_channel(struct chanData *source, struct chanData *target)
2117 merge_users(source, target);
2118 merge_bans(source, target);
2119 merge_data(source, target);
2122 static CHANSERV_FUNC(cmd_merge)
2124 struct userData *target_user;
2125 struct chanNode *target;
2126 char reason[MAXLEN];
2130 /* Make sure the target channel exists and is registered to the user
2131 performing the command. */
2132 if(!(target = GetChannel(argv[1])))
2134 reply("MSG_INVALID_CHANNEL");
2138 if(!target->channel_info)
2140 reply("CSMSG_NOT_REGISTERED", target->name);
2144 if(IsProtected(channel->channel_info))
2146 reply("CSMSG_MERGE_NODELETE");
2150 if(IsSuspended(target->channel_info))
2152 reply("CSMSG_MERGE_SUSPENDED");
2156 if(channel == target)
2158 reply("CSMSG_MERGE_SELF");
2162 target_user = GetChannelUser(target->channel_info, user->handle_info);
2163 if(!target_user || (target_user->access < UL_OWNER))
2165 reply("CSMSG_MERGE_NOT_OWNER");
2169 /* Merge the channel structures and associated data. */
2170 merge_channel(channel->channel_info, target->channel_info);
2171 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2172 unregister_channel(channel->channel_info, reason);
2173 reply("CSMSG_MERGE_SUCCESS", target->name);
2177 static CHANSERV_FUNC(cmd_opchan)
2179 struct mod_chanmode change;
2180 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2182 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2185 channel->channel_info->may_opchan = 0;
2186 mod_chanmode_init(&change);
2188 change.args[0].mode = MODE_CHANOP;
2189 change.args[0].u.member = GetUserMode(channel, chanserv);
2190 mod_chanmode_announce(chanserv, channel, &change);
2191 reply("CSMSG_OPCHAN_DONE", channel->name);
2195 static CHANSERV_FUNC(cmd_adduser)
2197 struct userData *actee;
2198 struct userData *actor;
2199 struct handle_info *handle;
2200 unsigned short access;
2204 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2206 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2210 access = user_level_from_name(argv[2], UL_OWNER);
2213 reply("CSMSG_INVALID_ACCESS", argv[2]);
2217 actor = GetChannelUser(channel->channel_info, user->handle_info);
2218 if(actor->access <= access)
2220 reply("CSMSG_NO_BUMP_ACCESS");
2224 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2227 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2229 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2233 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2234 scan_user_presence(actee, NULL);
2235 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2239 static CHANSERV_FUNC(cmd_clvl)
2241 struct handle_info *handle;
2242 struct userData *victim;
2243 struct userData *actor;
2244 unsigned short new_access;
2245 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2249 actor = GetChannelUser(channel->channel_info, user->handle_info);
2251 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2254 if(handle == user->handle_info && !privileged)
2256 reply("CSMSG_NO_SELF_CLVL");
2260 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2262 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2266 if(actor->access <= victim->access && !privileged)
2268 reply("MSG_USER_OUTRANKED", handle->handle);
2272 new_access = user_level_from_name(argv[2], UL_OWNER);
2276 reply("CSMSG_INVALID_ACCESS", argv[2]);
2280 if(new_access >= actor->access && !privileged)
2282 reply("CSMSG_NO_BUMP_ACCESS");
2286 victim->access = new_access;
2287 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2291 static CHANSERV_FUNC(cmd_deluser)
2293 struct handle_info *handle;
2294 struct userData *victim;
2295 struct userData *actor;
2296 unsigned short access;
2301 actor = GetChannelUser(channel->channel_info, user->handle_info);
2303 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2306 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2308 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2314 access = user_level_from_name(argv[1], UL_OWNER);
2317 reply("CSMSG_INVALID_ACCESS", argv[1]);
2320 if(access != victim->access)
2322 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2328 access = victim->access;
2331 if((actor->access <= victim->access) && !IsHelping(user))
2333 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2337 chan_name = strdup(channel->name);
2338 del_channel_user(victim, 1);
2339 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2345 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2347 struct userData *actor, *uData, *next;
2349 actor = GetChannelUser(channel->channel_info, user->handle_info);
2351 if(min_access > max_access)
2353 reply("CSMSG_BAD_RANGE", min_access, max_access);
2357 if((actor->access <= max_access) && !IsHelping(user))
2359 reply("CSMSG_NO_ACCESS");
2363 for(uData = channel->channel_info->users; uData; uData = next)
2367 if((uData->access >= min_access)
2368 && (uData->access <= max_access)
2369 && match_ircglob(uData->handle->handle, mask))
2370 del_channel_user(uData, 1);
2373 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2377 static CHANSERV_FUNC(cmd_mdelowner)
2379 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2382 static CHANSERV_FUNC(cmd_mdelcoowner)
2384 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2387 static CHANSERV_FUNC(cmd_mdelmaster)
2389 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2392 static CHANSERV_FUNC(cmd_mdelop)
2394 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2397 static CHANSERV_FUNC(cmd_mdelpeon)
2399 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2403 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2405 struct banData *bData, *next;
2406 char interval[INTERVALLEN];
2411 limit = now - duration;
2412 for(bData = channel->channel_info->bans; bData; bData = next)
2416 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2419 del_channel_ban(bData);
2423 intervalString(interval, duration, user->handle_info);
2424 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2429 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2431 struct userData *actor, *uData, *next;
2432 char interval[INTERVALLEN];
2436 actor = GetChannelUser(channel->channel_info, user->handle_info);
2437 if(min_access > max_access)
2439 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2443 if((actor->access <= max_access) && !IsHelping(user))
2445 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2450 limit = now - duration;
2451 for(uData = channel->channel_info->users; uData; uData = next)
2455 if((uData->seen > limit) || uData->present)
2458 if(((uData->access >= min_access) && (uData->access <= max_access))
2459 || (!max_access && (uData->access < actor->access)))
2461 del_channel_user(uData, 1);
2469 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2471 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2475 static CHANSERV_FUNC(cmd_trim)
2477 unsigned long duration;
2478 unsigned short min_level, max_level;
2482 duration = ParseInterval(argv[2]);
2485 reply("CSMSG_CANNOT_TRIM");
2489 if(!irccasecmp(argv[1], "bans"))
2491 cmd_trim_bans(user, channel, duration);
2494 else if(!irccasecmp(argv[1], "users"))
2496 cmd_trim_users(user, channel, 0, 0, duration);
2499 else if(parse_level_range(&min_level, &max_level, argv[1]))
2501 cmd_trim_users(user, channel, min_level, max_level, duration);
2504 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2506 cmd_trim_users(user, channel, min_level, min_level, duration);
2511 reply("CSMSG_INVALID_TRIM", argv[1]);
2516 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2517 to the user. cmd_all takes advantage of this. */
2518 static CHANSERV_FUNC(cmd_up)
2520 struct mod_chanmode change;
2521 struct userData *uData;
2524 mod_chanmode_init(&change);
2526 change.args[0].u.member = GetUserMode(channel, user);
2527 if(!change.args[0].u.member)
2530 reply("MSG_CHANNEL_ABSENT", channel->name);
2534 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2538 reply("CSMSG_GODMODE_UP", argv[0]);
2541 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2543 change.args[0].mode = MODE_CHANOP;
2544 errmsg = "CSMSG_ALREADY_OPPED";
2546 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2548 change.args[0].mode = MODE_VOICE;
2549 errmsg = "CSMSG_ALREADY_VOICED";
2554 reply("CSMSG_NO_ACCESS");
2557 change.args[0].mode &= ~change.args[0].u.member->modes;
2558 if(!change.args[0].mode)
2561 reply(errmsg, channel->name);
2564 modcmd_chanmode_announce(&change);
2568 static CHANSERV_FUNC(cmd_down)
2570 struct mod_chanmode change;
2572 mod_chanmode_init(&change);
2574 change.args[0].u.member = GetUserMode(channel, user);
2575 if(!change.args[0].u.member)
2578 reply("MSG_CHANNEL_ABSENT", channel->name);
2582 if(!change.args[0].u.member->modes)
2585 reply("CSMSG_ALREADY_DOWN", channel->name);
2589 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2590 modcmd_chanmode_announce(&change);
2594 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)
2596 struct userData *cList;
2598 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2600 if(IsSuspended(cList->channel)
2601 || IsUserSuspended(cList)
2602 || !GetUserMode(cList->channel->channel, user))
2605 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2611 static CHANSERV_FUNC(cmd_upall)
2613 return cmd_all(CSFUNC_ARGS, cmd_up);
2616 static CHANSERV_FUNC(cmd_downall)
2618 return cmd_all(CSFUNC_ARGS, cmd_down);
2621 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2622 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2625 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)
2627 unsigned int ii, valid;
2628 struct userNode *victim;
2629 struct mod_chanmode *change;
2631 change = mod_chanmode_alloc(argc - 1);
2633 for(ii=valid=0; ++ii < argc; )
2635 if(!(victim = GetUserH(argv[ii])))
2637 change->args[valid].mode = mode;
2638 change->args[valid].u.member = GetUserMode(channel, victim);
2639 if(!change->args[valid].u.member)
2641 if(validate && !validate(user, channel, victim))
2646 change->argc = valid;
2647 if(valid < (argc-1))
2648 reply("CSMSG_PROCESS_FAILED");
2651 modcmd_chanmode_announce(change);
2652 reply(action, channel->name);
2654 mod_chanmode_free(change);
2658 static CHANSERV_FUNC(cmd_op)
2660 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2663 static CHANSERV_FUNC(cmd_deop)
2665 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2668 static CHANSERV_FUNC(cmd_voice)
2670 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2673 static CHANSERV_FUNC(cmd_devoice)
2675 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2679 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2685 for(ii=0; ii<channel->members.used; ii++)
2687 struct modeNode *mn = channel->members.list[ii];
2689 if(IsService(mn->user))
2692 if(!user_matches_glob(mn->user, ban, 1))
2695 if(protect_user(mn->user, user, channel->channel_info))
2699 victims[(*victimCount)++] = mn;
2705 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2707 struct userNode *victim;
2708 struct modeNode **victims;
2709 unsigned int offset, n, victimCount, duration = 0;
2710 char *reason = "Bye.", *ban, *name;
2711 char interval[INTERVALLEN];
2713 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2714 REQUIRE_PARAMS(offset);
2717 reason = unsplit_string(argv + offset, argc - offset, NULL);
2718 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2720 /* Truncate the reason to a length of TOPICLEN, as
2721 the ircd does; however, leave room for an ellipsis
2722 and the kicker's nick. */
2723 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2727 if((victim = GetUserH(argv[1])))
2729 victims = alloca(sizeof(victims[0]));
2730 victims[0] = GetUserMode(channel, victim);
2731 /* XXX: The comparison with ACTION_KICK is just because all
2732 * other actions can work on users outside the channel, and we
2733 * want to allow those (e.g. unbans) in that case. If we add
2734 * some other ejection action for in-channel users, change
2736 victimCount = victims[0] ? 1 : 0;
2738 if(IsService(victim))
2740 reply("MSG_SERVICE_IMMUNE", victim->nick);
2744 if((action == ACTION_KICK) && !victimCount)
2746 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2750 if(protect_user(victim, user, channel->channel_info))
2752 reply("CSMSG_USER_PROTECTED", victim->nick);
2756 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2757 name = victim->nick;
2761 if(!is_ircmask(argv[1]))
2763 reply("MSG_NICK_UNKNOWN", argv[1]);
2767 victims = alloca(sizeof(victims[0]) * channel->members.used);
2769 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2771 reply("CSMSG_MASK_PROTECTED", argv[1]);
2775 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2777 reply("CSMSG_LAME_MASK", argv[1]);
2781 if((action == ACTION_KICK) && (victimCount == 0))
2783 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2787 name = ban = strdup(argv[1]);
2790 /* Truncate the ban in place if necessary; we must ensure
2791 that 'ban' is a valid ban mask before sanitizing it. */
2792 sanitize_ircmask(ban);
2794 if(action & ACTION_ADD_BAN)
2796 struct banData *bData, *next;
2798 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2800 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2805 if(action & ACTION_ADD_TIMED_BAN)
2807 duration = ParseInterval(argv[2]);
2811 reply("CSMSG_DURATION_TOO_LOW");
2815 else if(duration > (86400 * 365 * 2))
2817 reply("CSMSG_DURATION_TOO_HIGH");
2823 for(bData = channel->channel_info->bans; bData; bData = next)
2825 if(match_ircglobs(bData->mask, ban))
2827 int exact = !irccasecmp(bData->mask, ban);
2829 /* The ban is redundant; there is already a ban
2830 with the same effect in place. */
2834 free(bData->reason);
2835 bData->reason = strdup(reason);
2836 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2838 reply("CSMSG_REASON_CHANGE", ban);
2842 if(exact && bData->expires)
2846 /* If the ban matches an existing one exactly,
2847 extend the expiration time if the provided
2848 duration is longer. */
2849 if(duration && ((time_t)(now + duration) > bData->expires))
2851 bData->expires = now + duration;
2862 /* Delete the expiration timeq entry and
2863 requeue if necessary. */
2864 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2867 timeq_add(bData->expires, expire_ban, bData);
2871 /* automated kickban */
2874 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2876 reply("CSMSG_BAN_ADDED", name, channel->name);
2882 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2889 if(match_ircglobs(ban, bData->mask))
2891 /* The ban we are adding makes previously existing
2892 bans redundant; silently remove them. */
2893 del_channel_ban(bData);
2897 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);
2899 name = ban = strdup(bData->mask);
2903 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2905 extern const char *hidden_host_suffix;
2906 const char *old_name = chanserv_conf.old_ban_names->list[n];
2908 unsigned int l1, l2;
2911 l2 = strlen(old_name);
2914 if(irccasecmp(ban + l1 - l2, old_name))
2916 new_mask = malloc(MAXLEN);
2917 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2919 name = ban = new_mask;
2924 if(action & ACTION_BAN)
2926 unsigned int exists;
2927 struct mod_chanmode *change;
2929 if(channel->banlist.used >= MAXBANS)
2932 reply("CSMSG_BANLIST_FULL", channel->name);
2937 exists = ChannelBanExists(channel, ban);
2938 change = mod_chanmode_alloc(victimCount + 1);
2939 for(n = 0; n < victimCount; ++n)
2941 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2942 change->args[n].u.member = victims[n];
2946 change->args[n].mode = MODE_BAN;
2947 change->args[n++].u.hostmask = ban;
2951 modcmd_chanmode_announce(change);
2953 mod_chanmode_announce(chanserv, channel, change);
2954 mod_chanmode_free(change);
2956 if(exists && (action == ACTION_BAN))
2959 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2965 if(action & ACTION_KICK)
2967 char kick_reason[MAXLEN];
2968 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2970 for(n = 0; n < victimCount; n++)
2971 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2976 /* No response, since it was automated. */
2978 else if(action & ACTION_ADD_BAN)
2981 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2983 reply("CSMSG_BAN_ADDED", name, channel->name);
2985 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2986 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2987 else if(action & ACTION_BAN)
2988 reply("CSMSG_BAN_DONE", name, channel->name);
2989 else if(action & ACTION_KICK && victimCount)
2990 reply("CSMSG_KICK_DONE", name, channel->name);
2996 static CHANSERV_FUNC(cmd_kickban)
2998 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3001 static CHANSERV_FUNC(cmd_kick)
3003 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3006 static CHANSERV_FUNC(cmd_ban)
3008 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3011 static CHANSERV_FUNC(cmd_addban)
3013 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3016 static CHANSERV_FUNC(cmd_addtimedban)
3018 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3021 static struct mod_chanmode *
3022 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3024 struct mod_chanmode *change;
3025 unsigned char *match;
3026 unsigned int ii, count;
3028 match = alloca(bans->used);
3031 for(ii = count = 0; ii < bans->used; ++ii)
3033 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3040 for(ii = count = 0; ii < bans->used; ++ii)
3042 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3049 change = mod_chanmode_alloc(count);
3050 for(ii = count = 0; ii < bans->used; ++ii)
3054 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3055 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3057 assert(count == change->argc);
3062 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3064 struct userNode *actee;
3070 /* may want to allow a comma delimited list of users... */
3071 if(!(actee = GetUserH(argv[1])))
3073 if(!is_ircmask(argv[1]))
3075 reply("MSG_NICK_UNKNOWN", argv[1]);
3079 mask = strdup(argv[1]);
3082 /* We don't sanitize the mask here because ircu
3084 if(action & ACTION_UNBAN)
3086 struct mod_chanmode *change;
3087 change = find_matching_bans(&channel->banlist, actee, mask);
3092 modcmd_chanmode_announce(change);
3093 for(ii = 0; ii < change->argc; ++ii)
3094 free((char*)change->args[ii].u.hostmask);
3095 mod_chanmode_free(change);
3100 if(action & ACTION_DEL_BAN)
3102 struct banData *ban, *next;
3104 ban = channel->channel_info->bans;
3108 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3111 for( ; ban && !match_ircglobs(mask, ban->mask);
3116 del_channel_ban(ban);
3123 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3125 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3131 static CHANSERV_FUNC(cmd_unban)
3133 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3136 static CHANSERV_FUNC(cmd_delban)
3138 /* it doesn't necessarily have to remove the channel ban - may want
3139 to make that an option. */
3140 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3143 static CHANSERV_FUNC(cmd_unbanme)
3145 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3146 long flags = ACTION_UNBAN;
3148 /* remove permanent bans if the user has the proper access. */
3149 if(uData->access >= UL_MASTER)
3150 flags |= ACTION_DEL_BAN;
3152 argv[1] = user->nick;
3153 return unban_user(user, channel, 2, argv, cmd, flags);
3156 static CHANSERV_FUNC(cmd_unbanall)
3158 struct mod_chanmode *change;
3161 if(!channel->banlist.used)
3163 reply("CSMSG_NO_BANS", channel->name);
3167 change = mod_chanmode_alloc(channel->banlist.used);
3168 for(ii=0; ii<channel->banlist.used; ii++)
3170 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3171 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3173 modcmd_chanmode_announce(change);
3174 for(ii = 0; ii < change->argc; ++ii)
3175 free((char*)change->args[ii].u.hostmask);
3176 mod_chanmode_free(change);
3177 reply("CSMSG_BANS_REMOVED", channel->name);
3181 static CHANSERV_FUNC(cmd_open)
3183 struct mod_chanmode *change;
3186 change = find_matching_bans(&channel->banlist, user, NULL);
3188 change = mod_chanmode_alloc(0);
3189 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3190 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3191 && channel->channel_info->modes.modes_set)
3192 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3193 modcmd_chanmode_announce(change);
3194 reply("CSMSG_CHANNEL_OPENED", channel->name);
3195 for(ii = 0; ii < change->argc; ++ii)
3196 free((char*)change->args[ii].u.hostmask);
3197 mod_chanmode_free(change);
3201 static CHANSERV_FUNC(cmd_myaccess)
3203 static struct string_buffer sbuf;
3204 struct handle_info *target_handle;
3205 struct userData *uData;
3208 target_handle = user->handle_info;
3209 else if(!IsHelping(user))
3211 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3214 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3217 if(!target_handle->channels)
3219 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3223 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3224 for(uData = target_handle->channels; uData; uData = uData->u_next)
3226 struct chanData *cData = uData->channel;
3228 if(uData->access > UL_OWNER)
3230 if(IsProtected(cData)
3231 && (target_handle != user->handle_info)
3232 && !GetTrueChannelAccess(cData, user->handle_info))
3235 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3236 if(uData->flags != USER_AUTO_OP)
3237 string_buffer_append(&sbuf, ',');
3238 if(IsUserSuspended(uData))
3239 string_buffer_append(&sbuf, 's');
3240 if(IsUserAutoOp(uData))
3242 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3243 string_buffer_append(&sbuf, 'o');
3244 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3245 string_buffer_append(&sbuf, 'v');
3247 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3248 string_buffer_append(&sbuf, 'i');
3250 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3252 string_buffer_append_string(&sbuf, ")]");
3253 string_buffer_append(&sbuf, '\0');
3254 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3260 static CHANSERV_FUNC(cmd_access)
3262 struct userNode *target;
3263 struct handle_info *target_handle;
3264 struct userData *uData;
3266 char prefix[MAXLEN];
3271 target_handle = target->handle_info;
3273 else if((target = GetUserH(argv[1])))
3275 target_handle = target->handle_info;
3277 else if(argv[1][0] == '*')
3279 if(!(target_handle = get_handle_info(argv[1]+1)))
3281 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3287 reply("MSG_NICK_UNKNOWN", argv[1]);
3291 assert(target || target_handle);
3293 if(target == chanserv)
3295 reply("CSMSG_IS_CHANSERV");
3303 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3308 reply("MSG_USER_AUTHENTICATE", target->nick);
3311 reply("MSG_AUTHENTICATE");
3317 const char *epithet = NULL, *type = NULL;
3320 epithet = chanserv_conf.irc_operator_epithet;
3323 else if(IsNetworkHelper(target))
3325 epithet = chanserv_conf.network_helper_epithet;
3326 type = "network helper";
3328 else if(IsSupportHelper(target))
3330 epithet = chanserv_conf.support_helper_epithet;
3331 type = "support helper";
3335 if(target_handle->epithet)
3336 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3338 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3340 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3344 sprintf(prefix, "%s", target_handle->handle);
3347 if(!channel->channel_info)
3349 reply("CSMSG_NOT_REGISTERED", channel->name);
3353 helping = HANDLE_FLAGGED(target_handle, HELPING)
3354 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3355 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3357 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3358 /* To prevent possible information leaks, only show infolines
3359 * if the requestor is in the channel or it's their own
3361 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3363 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3365 /* Likewise, only say it's suspended if the user has active
3366 * access in that channel or it's their own entry. */
3367 if(IsUserSuspended(uData)
3368 && (GetChannelUser(channel->channel_info, user->handle_info)
3369 || (user->handle_info == uData->handle)))
3371 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3376 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3383 zoot_list(struct listData *list)
3385 struct userData *uData;
3386 unsigned int start, curr, highest, lowest;
3387 struct helpfile_table tmp_table;
3388 const char **temp, *msg;
3390 if(list->table.length == 1)
3393 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3395 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3396 msg = user_find_message(list->user, "MSG_NONE");
3397 send_message_type(4, list->user, list->bot, " %s", msg);
3399 tmp_table.width = list->table.width;
3400 tmp_table.flags = list->table.flags;
3401 list->table.contents[0][0] = " ";
3402 highest = list->highest;
3403 if(list->lowest != 0)
3404 lowest = list->lowest;
3405 else if(highest < 100)
3408 lowest = highest - 100;
3409 for(start = curr = 1; curr < list->table.length; )
3411 uData = list->users[curr-1];
3412 list->table.contents[curr++][0] = " ";
3413 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3416 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3418 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3419 temp = list->table.contents[--start];
3420 list->table.contents[start] = list->table.contents[0];
3421 tmp_table.contents = list->table.contents + start;
3422 tmp_table.length = curr - start;
3423 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3424 list->table.contents[start] = temp;
3426 highest = lowest - 1;
3427 lowest = (highest < 100) ? 0 : (highest - 99);
3433 def_list(struct listData *list)
3437 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3439 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3440 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3441 if(list->table.length == 1)
3443 msg = user_find_message(list->user, "MSG_NONE");
3444 send_message_type(4, list->user, list->bot, " %s", msg);
3449 userData_access_comp(const void *arg_a, const void *arg_b)
3451 const struct userData *a = *(struct userData**)arg_a;
3452 const struct userData *b = *(struct userData**)arg_b;
3454 if(a->access != b->access)
3455 res = b->access - a->access;
3457 res = irccasecmp(a->handle->handle, b->handle->handle);
3462 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3464 void (*send_list)(struct listData *);
3465 struct userData *uData;
3466 struct listData lData;
3467 unsigned int matches;
3471 lData.bot = cmd->parent->bot;
3472 lData.channel = channel;
3473 lData.lowest = lowest;
3474 lData.highest = highest;
3475 lData.search = (argc > 1) ? argv[1] : NULL;
3476 send_list = def_list;
3477 (void)zoot_list; /* since it doesn't show user levels */
3479 if(user->handle_info)
3481 switch(user->handle_info->userlist_style)
3483 case HI_STYLE_DEF: send_list = def_list; break;
3484 case HI_STYLE_ZOOT: send_list = def_list; break;
3488 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3490 for(uData = channel->channel_info->users; uData; uData = uData->next)
3492 if((uData->access < lowest)
3493 || (uData->access > highest)
3494 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3496 lData.users[matches++] = uData;
3498 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3500 lData.table.length = matches+1;
3501 lData.table.width = 4;
3502 lData.table.flags = TABLE_NO_FREE;
3503 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3504 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3505 lData.table.contents[0] = ary;
3508 ary[2] = "Last Seen";
3510 for(matches = 1; matches < lData.table.length; ++matches)
3512 struct userData *uData = lData.users[matches-1];
3513 char seen[INTERVALLEN];
3515 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3516 lData.table.contents[matches] = ary;
3517 ary[0] = strtab(uData->access);
3518 ary[1] = uData->handle->handle;
3521 else if(!uData->seen)
3524 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3525 ary[2] = strdup(ary[2]);
3526 if(IsUserSuspended(uData))
3527 ary[3] = "Suspended";
3528 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3529 ary[3] = "Vacation";
3534 for(matches = 1; matches < lData.table.length; ++matches)
3536 free((char*)lData.table.contents[matches][2]);
3537 free(lData.table.contents[matches]);
3539 free(lData.table.contents[0]);
3540 free(lData.table.contents);
3544 static CHANSERV_FUNC(cmd_users)
3546 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3549 static CHANSERV_FUNC(cmd_wlist)
3551 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3554 static CHANSERV_FUNC(cmd_clist)
3556 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3559 static CHANSERV_FUNC(cmd_mlist)
3561 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3564 static CHANSERV_FUNC(cmd_olist)
3566 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3569 static CHANSERV_FUNC(cmd_plist)
3571 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3574 static CHANSERV_FUNC(cmd_bans)
3576 struct helpfile_table tbl;
3577 unsigned int matches = 0, timed = 0, ii;
3578 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3579 const char *msg_never, *triggered, *expires;
3580 struct banData *ban, **bans;
3587 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3589 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3591 if(search && !match_ircglobs(search, ban->mask))
3593 bans[matches++] = ban;
3598 tbl.length = matches + 1;
3599 tbl.width = 4 + timed;
3601 tbl.flags = TABLE_NO_FREE;
3602 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3603 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3604 tbl.contents[0][0] = "Mask";
3605 tbl.contents[0][1] = "Set By";
3606 tbl.contents[0][2] = "Triggered";
3609 tbl.contents[0][3] = "Expires";
3610 tbl.contents[0][4] = "Reason";
3613 tbl.contents[0][3] = "Reason";
3616 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3618 free(tbl.contents[0]);
3623 msg_never = user_find_message(user, "MSG_NEVER");
3624 for(ii = 0; ii < matches; )
3630 else if(ban->expires)
3631 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3633 expires = msg_never;
3636 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3638 triggered = msg_never;
3640 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3641 tbl.contents[ii][0] = ban->mask;
3642 tbl.contents[ii][1] = ban->owner;
3643 tbl.contents[ii][2] = strdup(triggered);
3646 tbl.contents[ii][3] = strdup(expires);
3647 tbl.contents[ii][4] = ban->reason;
3650 tbl.contents[ii][3] = ban->reason;
3652 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3653 reply("MSG_MATCH_COUNT", matches);
3654 for(ii = 1; ii < tbl.length; ++ii)
3656 free((char*)tbl.contents[ii][2]);
3658 free((char*)tbl.contents[ii][3]);
3659 free(tbl.contents[ii]);
3661 free(tbl.contents[0]);
3667 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3669 struct chanData *cData = channel->channel_info;
3670 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3672 if(cData->topic_mask)
3673 return !match_ircglob(new_topic, cData->topic_mask);
3674 else if(cData->topic)
3675 return irccasecmp(new_topic, cData->topic);
3680 static CHANSERV_FUNC(cmd_topic)
3682 struct chanData *cData;
3685 cData = channel->channel_info;
3690 SetChannelTopic(channel, chanserv, cData->topic, 1);
3691 reply("CSMSG_TOPIC_SET", cData->topic);
3695 reply("CSMSG_NO_TOPIC", channel->name);
3699 topic = unsplit_string(argv + 1, argc - 1, NULL);
3700 /* If they say "!topic *", use an empty topic. */
3701 if((topic[0] == '*') && (topic[1] == 0))
3703 if(bad_topic(channel, user, topic))
3705 char *topic_mask = cData->topic_mask;
3708 char new_topic[TOPICLEN+1], tchar;
3709 int pos=0, starpos=-1, dpos=0, len;
3711 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3718 len = strlen(topic);
3719 if((dpos + len) > TOPICLEN)
3720 len = TOPICLEN + 1 - dpos;
3721 memcpy(new_topic+dpos, topic, len);
3725 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3726 default: new_topic[dpos++] = tchar; break;
3729 if((dpos > TOPICLEN) || tchar)
3732 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3733 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3736 new_topic[dpos] = 0;
3737 SetChannelTopic(channel, chanserv, new_topic, 1);
3739 reply("CSMSG_TOPIC_LOCKED", channel->name);
3744 SetChannelTopic(channel, chanserv, topic, 1);
3746 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3748 /* Grab the topic and save it as the default topic. */
3750 cData->topic = strdup(channel->topic);
3756 static CHANSERV_FUNC(cmd_mode)
3758 struct mod_chanmode *change;
3762 change = &channel->channel_info->modes;
3763 if(change->modes_set || change->modes_clear) {
3764 modcmd_chanmode_announce(change);
3765 reply("CSMSG_DEFAULTED_MODES", channel->name);
3767 reply("CSMSG_NO_MODES", channel->name);
3771 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3774 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3778 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3779 && mode_lock_violated(&channel->channel_info->modes, change))
3782 mod_chanmode_format(&channel->channel_info->modes, modes);
3783 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3787 modcmd_chanmode_announce(change);
3788 mod_chanmode_free(change);
3789 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3793 static CHANSERV_FUNC(cmd_invite)
3795 struct userData *uData;
3796 struct userNode *invite;
3798 uData = GetChannelUser(channel->channel_info, user->handle_info);
3802 if(!(invite = GetUserH(argv[1])))
3804 reply("MSG_NICK_UNKNOWN", argv[1]);
3811 if(GetUserMode(channel, invite))
3813 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3821 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3822 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3825 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3827 irc_invite(chanserv, invite, channel);
3829 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3834 static CHANSERV_FUNC(cmd_inviteme)
3836 if(GetUserMode(channel, user))
3838 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3841 if(channel->channel_info
3842 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3844 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3847 irc_invite(cmd->parent->bot, user, channel);
3852 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3855 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3857 /* We display things based on two dimensions:
3858 * - Issue time: present or absent
3859 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3860 * (in order of precedence, so something both expired and revoked
3861 * only counts as revoked)
3863 combo = (suspended->issued ? 4 : 0)
3864 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3866 case 0: /* no issue time, indefinite expiration */
3867 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3869 case 1: /* no issue time, expires in future */
3870 intervalString(buf1, suspended->expires-now, user->handle_info);
3871 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3873 case 2: /* no issue time, expired */
3874 intervalString(buf1, now-suspended->expires, user->handle_info);
3875 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3877 case 3: /* no issue time, revoked */
3878 intervalString(buf1, now-suspended->revoked, user->handle_info);
3879 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3881 case 4: /* issue time set, indefinite expiration */
3882 intervalString(buf1, now-suspended->issued, user->handle_info);
3883 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3885 case 5: /* issue time set, expires in future */
3886 intervalString(buf1, now-suspended->issued, user->handle_info);
3887 intervalString(buf2, suspended->expires-now, user->handle_info);
3888 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3890 case 6: /* issue time set, expired */
3891 intervalString(buf1, now-suspended->issued, user->handle_info);
3892 intervalString(buf2, now-suspended->expires, user->handle_info);
3893 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3895 case 7: /* issue time set, revoked */
3896 intervalString(buf1, now-suspended->issued, user->handle_info);
3897 intervalString(buf2, now-suspended->revoked, user->handle_info);
3898 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3901 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3906 static CHANSERV_FUNC(cmd_info)
3908 char modes[MAXLEN], buffer[INTERVALLEN];
3909 struct userData *uData, *owner;
3910 struct chanData *cData;
3911 struct do_not_register *dnr;
3916 cData = channel->channel_info;
3917 reply("CSMSG_CHANNEL_INFO", channel->name);
3919 uData = GetChannelUser(cData, user->handle_info);
3920 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3922 mod_chanmode_format(&cData->modes, modes);
3923 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3924 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3927 for(it = dict_first(cData->notes); it; it = iter_next(it))
3931 note = iter_data(it);
3932 if(!note_type_visible_to_user(cData, note->type, user))
3935 padding = PADLEN - 1 - strlen(iter_key(it));
3936 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3939 reply("CSMSG_CHANNEL_MAX", cData->max);
3940 for(owner = cData->users; owner; owner = owner->next)
3941 if(owner->access == UL_OWNER)
3942 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3943 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3944 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3945 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3946 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3948 privileged = IsStaff(user);
3949 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3950 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3952 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3953 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3955 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3957 struct suspended *suspended;
3958 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3959 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3960 show_suspension_info(cmd, user, suspended);
3962 else if(IsSuspended(cData))
3964 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3965 show_suspension_info(cmd, user, cData->suspended);
3970 static CHANSERV_FUNC(cmd_netinfo)
3972 extern time_t boot_time;
3973 extern unsigned long burst_length;
3974 char interval[INTERVALLEN];
3976 reply("CSMSG_NETWORK_INFO");
3977 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3978 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3979 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3980 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3981 reply("CSMSG_NETWORK_BANS", banCount);
3982 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3983 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3984 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3989 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3991 struct helpfile_table table;
3993 struct userNode *user;
3998 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3999 table.contents = alloca(list->used*sizeof(*table.contents));
4000 for(nn=0; nn<list->used; nn++)
4002 user = list->list[nn];
4003 if(user->modes & skip_flags)
4007 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4010 nick = alloca(strlen(user->nick)+3);
4011 sprintf(nick, "(%s)", user->nick);
4015 table.contents[table.length][0] = nick;
4018 table_send(chanserv, to->nick, 0, NULL, table);
4021 static CHANSERV_FUNC(cmd_ircops)
4023 reply("CSMSG_STAFF_OPERS");
4024 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4028 static CHANSERV_FUNC(cmd_helpers)
4030 reply("CSMSG_STAFF_HELPERS");
4031 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4035 static CHANSERV_FUNC(cmd_staff)
4037 reply("CSMSG_NETWORK_STAFF");
4038 cmd_ircops(CSFUNC_ARGS);
4039 cmd_helpers(CSFUNC_ARGS);
4043 static CHANSERV_FUNC(cmd_peek)
4045 struct modeNode *mn;
4046 char modes[MODELEN];
4048 struct helpfile_table table;
4050 irc_make_chanmode(channel, modes);
4052 reply("CSMSG_PEEK_INFO", channel->name);
4053 reply("CSMSG_PEEK_TOPIC", channel->topic);
4054 reply("CSMSG_PEEK_MODES", modes);
4055 reply("CSMSG_PEEK_USERS", channel->members.used);
4059 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4060 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4061 for(n = 0; n < channel->members.used; n++)
4063 mn = channel->members.list[n];
4064 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4066 table.contents[table.length] = alloca(sizeof(**table.contents));
4067 table.contents[table.length][0] = mn->user->nick;
4072 reply("CSMSG_PEEK_OPS");
4073 table_send(chanserv, user->nick, 0, NULL, table);
4076 reply("CSMSG_PEEK_NO_OPS");
4080 static MODCMD_FUNC(cmd_wipeinfo)
4082 struct handle_info *victim;
4083 struct userData *ud, *actor;
4086 actor = GetChannelUser(channel->channel_info, user->handle_info);
4087 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4089 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4091 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4094 if((ud->access >= actor->access) && (ud != actor))
4096 reply("MSG_USER_OUTRANKED", victim->handle);
4102 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4106 static CHANSERV_FUNC(cmd_resync)
4108 struct mod_chanmode *changes;
4109 struct chanData *cData = channel->channel_info;
4110 unsigned int ii, used;
4112 changes = mod_chanmode_alloc(channel->members.used * 2);
4113 for(ii = used = 0; ii < channel->members.used; ++ii)
4115 struct modeNode *mn = channel->members.list[ii];
4116 struct userData *uData;
4118 if(IsService(mn->user))
4121 uData = GetChannelAccess(cData, mn->user->handle_info);
4122 if(!cData->lvlOpts[lvlGiveOps]
4123 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4125 if(!(mn->modes & MODE_CHANOP))
4127 changes->args[used].mode = MODE_CHANOP;
4128 changes->args[used++].u.member = mn;
4131 else if(!cData->lvlOpts[lvlGiveVoice]
4132 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4134 if(mn->modes & MODE_CHANOP)
4136 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4137 changes->args[used++].u.member = mn;
4139 if(!(mn->modes & MODE_VOICE))
4141 changes->args[used].mode = MODE_VOICE;
4142 changes->args[used++].u.member = mn;
4149 changes->args[used].mode = MODE_REMOVE | mn->modes;
4150 changes->args[used++].u.member = mn;
4154 changes->argc = used;
4155 modcmd_chanmode_announce(changes);
4156 mod_chanmode_free(changes);
4157 reply("CSMSG_RESYNCED_USERS", channel->name);
4161 static CHANSERV_FUNC(cmd_seen)
4163 struct userData *uData;
4164 struct handle_info *handle;
4165 char seen[INTERVALLEN];
4169 if(!irccasecmp(argv[1], chanserv->nick))
4171 reply("CSMSG_IS_CHANSERV");
4175 if(!(handle = get_handle_info(argv[1])))
4177 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4181 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4183 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4188 reply("CSMSG_USER_PRESENT", handle->handle);
4189 else if(uData->seen)
4190 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4192 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4194 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4195 reply("CSMSG_USER_VACATION", handle->handle);
4200 static MODCMD_FUNC(cmd_names)
4202 struct userNode *targ;
4203 struct userData *targData;
4204 unsigned int ii, pos;
4207 for(ii=pos=0; ii<channel->members.used; ++ii)
4209 targ = channel->members.list[ii]->user;
4210 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4213 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4216 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4220 if(IsUserSuspended(targData))
4222 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4225 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4226 reply("CSMSG_END_NAMES", channel->name);
4231 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4233 switch(ntype->visible_type)
4235 case NOTE_VIS_ALL: return 1;
4236 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4237 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4242 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4244 struct userData *uData;
4246 switch(ntype->set_access_type)
4248 case NOTE_SET_CHANNEL_ACCESS:
4249 if(!user->handle_info)
4251 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4253 return uData->access >= ntype->set_access.min_ulevel;
4254 case NOTE_SET_CHANNEL_SETTER:
4255 return check_user_level(channel, user, lvlSetters, 1, 0);
4256 case NOTE_SET_PRIVILEGED: default:
4257 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4261 static CHANSERV_FUNC(cmd_note)
4263 struct chanData *cData;
4265 struct note_type *ntype;
4267 cData = channel->channel_info;
4270 reply("CSMSG_NOT_REGISTERED", channel->name);
4274 /* If no arguments, show all visible notes for the channel. */
4280 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4282 note = iter_data(it);
4283 if(!note_type_visible_to_user(cData, note->type, user))
4286 reply("CSMSG_NOTELIST_HEADER", channel->name);
4287 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4290 reply("CSMSG_NOTELIST_END", channel->name);
4292 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4294 /* If one argument, show the named note. */
4297 if((note = dict_find(cData->notes, argv[1], NULL))
4298 && note_type_visible_to_user(cData, note->type, user))
4300 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4302 else if((ntype = dict_find(note_types, argv[1], NULL))
4303 && note_type_visible_to_user(NULL, ntype, user))
4305 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4310 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4314 /* Assume they're trying to set a note. */
4318 ntype = dict_find(note_types, argv[1], NULL);
4321 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4324 else if(note_type_settable_by_user(channel, ntype, user))
4326 note_text = unsplit_string(argv+2, argc-2, NULL);
4327 if((note = dict_find(cData->notes, argv[1], NULL)))
4328 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4329 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4330 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4332 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4334 /* The note is viewable to staff only, so return 0
4335 to keep the invocation from getting logged (or
4336 regular users can see it in !events). */
4342 reply("CSMSG_NO_ACCESS");
4349 static CHANSERV_FUNC(cmd_delnote)
4354 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4355 || !note_type_settable_by_user(channel, note->type, user))
4357 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4360 dict_remove(channel->channel_info->notes, note->type->name);
4361 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4365 static CHANSERV_FUNC(cmd_events)
4367 struct logSearch discrim;
4368 struct logReport report;
4369 unsigned int matches, limit;
4371 limit = (argc > 1) ? atoi(argv[1]) : 10;
4372 if(limit < 1 || limit > 200)
4375 memset(&discrim, 0, sizeof(discrim));
4376 discrim.masks.bot = chanserv;
4377 discrim.masks.channel_name = channel->name;
4379 discrim.masks.command = argv[2];
4380 discrim.limit = limit;
4381 discrim.max_time = INT_MAX;
4382 discrim.severities = 1 << LOG_COMMAND;
4383 report.reporter = chanserv;
4385 reply("CSMSG_EVENT_SEARCH_RESULTS");
4386 matches = log_entry_search(&discrim, log_report_entry, &report);
4388 reply("MSG_MATCH_COUNT", matches);
4390 reply("MSG_NO_MATCHES");
4394 static CHANSERV_FUNC(cmd_say)
4400 msg = unsplit_string(argv + 1, argc - 1, NULL);
4401 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4403 else if(GetUserH(argv[1]))
4406 msg = unsplit_string(argv + 2, argc - 2, NULL);
4407 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4411 reply("MSG_NOT_TARGET_NAME");
4417 static CHANSERV_FUNC(cmd_emote)
4423 /* CTCP is so annoying. */
4424 msg = unsplit_string(argv + 1, argc - 1, NULL);
4425 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4427 else if(GetUserH(argv[1]))
4429 msg = unsplit_string(argv + 2, argc - 2, NULL);
4430 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4434 reply("MSG_NOT_TARGET_NAME");
4440 struct channelList *
4441 chanserv_support_channels(void)
4443 return &chanserv_conf.support_channels;
4446 static CHANSERV_FUNC(cmd_expire)
4448 int channel_count = registered_channels;
4449 expire_channels(NULL);
4450 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4455 chanserv_expire_suspension(void *data)
4457 struct suspended *suspended = data;
4458 struct chanNode *channel;
4459 struct mod_chanmode change;
4461 if(!suspended->expires || (now < suspended->expires))
4462 suspended->revoked = now;
4463 channel = suspended->cData->channel;
4464 suspended->cData->channel = channel;
4465 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4466 mod_chanmode_init(&change);
4468 change.args[0].mode = MODE_CHANOP;
4469 change.args[0].u.member = AddChannelUser(chanserv, channel);
4470 mod_chanmode_announce(chanserv, channel, &change);
4473 static CHANSERV_FUNC(cmd_csuspend)
4475 struct suspended *suspended;
4476 char reason[MAXLEN];
4477 time_t expiry, duration;
4478 struct userData *uData;
4482 if(IsProtected(channel->channel_info))
4484 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4488 if(argv[1][0] == '!')
4490 else if(IsSuspended(channel->channel_info))
4492 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4493 show_suspension_info(cmd, user, channel->channel_info->suspended);
4497 if(!strcmp(argv[1], "0"))
4499 else if((duration = ParseInterval(argv[1])))
4500 expiry = now + duration;
4503 reply("MSG_INVALID_DURATION", argv[1]);
4507 unsplit_string(argv + 2, argc - 2, reason);
4509 suspended = calloc(1, sizeof(*suspended));
4510 suspended->revoked = 0;
4511 suspended->issued = now;
4512 suspended->suspender = strdup(user->handle_info->handle);
4513 suspended->expires = expiry;
4514 suspended->reason = strdup(reason);
4515 suspended->cData = channel->channel_info;
4516 suspended->previous = suspended->cData->suspended;
4517 suspended->cData->suspended = suspended;
4519 if(suspended->expires)
4520 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4522 if(IsSuspended(channel->channel_info))
4524 suspended->previous->revoked = now;
4525 if(suspended->previous->expires)
4526 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4527 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4528 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4532 /* Mark all users in channel as absent. */
4533 for(uData = channel->channel_info->users; uData; uData = uData->next)
4542 /* Mark the channel as suspended, then part. */
4543 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4544 DelChannelUser(chanserv, channel, suspended->reason, 0);
4545 reply("CSMSG_SUSPENDED", channel->name);
4546 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4547 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4552 static CHANSERV_FUNC(cmd_cunsuspend)
4554 struct suspended *suspended;
4555 char message[MAXLEN];
4557 if(!IsSuspended(channel->channel_info))
4559 reply("CSMSG_NOT_SUSPENDED", channel->name);
4563 suspended = channel->channel_info->suspended;
4565 /* Expire the suspension and join ChanServ to the channel. */
4566 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4567 chanserv_expire_suspension(suspended);
4568 reply("CSMSG_UNSUSPENDED", channel->name);
4569 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4570 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4574 typedef struct chanservSearch
4582 unsigned long flags;
4586 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4589 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4594 search = malloc(sizeof(struct chanservSearch));
4595 memset(search, 0, sizeof(*search));
4598 for(i = 0; i < argc; i++)
4600 /* Assume all criteria require arguments. */
4603 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4607 if(!irccasecmp(argv[i], "name"))
4608 search->name = argv[++i];
4609 else if(!irccasecmp(argv[i], "registrar"))
4610 search->registrar = argv[++i];
4611 else if(!irccasecmp(argv[i], "unvisited"))
4612 search->unvisited = ParseInterval(argv[++i]);
4613 else if(!irccasecmp(argv[i], "registered"))
4614 search->registered = ParseInterval(argv[++i]);
4615 else if(!irccasecmp(argv[i], "flags"))
4618 if(!irccasecmp(argv[i], "nodelete"))
4619 search->flags |= CHANNEL_NODELETE;
4620 else if(!irccasecmp(argv[i], "suspended"))
4621 search->flags |= CHANNEL_SUSPENDED;
4624 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4628 else if(!irccasecmp(argv[i], "limit"))
4629 search->limit = strtoul(argv[++i], NULL, 10);
4632 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4637 if(search->name && !strcmp(search->name, "*"))
4639 if(search->registrar && !strcmp(search->registrar, "*"))
4640 search->registrar = 0;
4649 chanserv_channel_match(struct chanData *channel, search_t search)
4651 const char *name = channel->channel->name;
4652 if((search->name && !match_ircglob(name, search->name)) ||
4653 (search->registrar && !channel->registrar) ||
4654 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4655 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4656 (search->registered && (now - channel->registered) > search->registered) ||
4657 (search->flags && ((search->flags & channel->flags) != search->flags)))
4664 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4666 struct chanData *channel;
4667 unsigned int matches = 0;
4669 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4671 if(!chanserv_channel_match(channel, search))
4681 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4686 search_print(struct chanData *channel, void *data)
4688 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4691 static CHANSERV_FUNC(cmd_search)
4694 unsigned int matches;
4695 channel_search_func action;
4699 if(!irccasecmp(argv[1], "count"))
4700 action = search_count;
4701 else if(!irccasecmp(argv[1], "print"))
4702 action = search_print;
4705 reply("CSMSG_ACTION_INVALID", argv[1]);
4709 search = chanserv_search_create(user, argc - 2, argv + 2);
4713 if(action == search_count)
4714 search->limit = INT_MAX;
4716 if(action == search_print)
4717 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4719 matches = chanserv_channel_search(search, action, user);
4722 reply("MSG_MATCH_COUNT", matches);
4724 reply("MSG_NO_MATCHES");
4730 static CHANSERV_FUNC(cmd_unvisited)
4732 struct chanData *cData;
4733 time_t interval = chanserv_conf.channel_expire_delay;
4734 char buffer[INTERVALLEN];
4735 unsigned int limit = 25, matches = 0;
4739 interval = ParseInterval(argv[1]);
4741 limit = atoi(argv[2]);
4744 intervalString(buffer, interval, user->handle_info);
4745 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4747 for(cData = channelList; cData && matches < limit; cData = cData->next)
4749 if((now - cData->visited) < interval)
4752 intervalString(buffer, now - cData->visited, user->handle_info);
4753 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4760 static MODCMD_FUNC(chan_opt_defaulttopic)
4766 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4768 reply("CSMSG_TOPIC_LOCKED", channel->name);
4772 topic = unsplit_string(argv+1, argc-1, NULL);
4774 free(channel->channel_info->topic);
4775 if(topic[0] == '*' && topic[1] == 0)
4777 topic = channel->channel_info->topic = NULL;
4781 topic = channel->channel_info->topic = strdup(topic);
4782 if(channel->channel_info->topic_mask
4783 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4784 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4786 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4789 if(channel->channel_info->topic)
4790 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4792 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4796 static MODCMD_FUNC(chan_opt_topicmask)
4800 struct chanData *cData = channel->channel_info;
4803 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4805 reply("CSMSG_TOPIC_LOCKED", channel->name);
4809 mask = unsplit_string(argv+1, argc-1, NULL);
4811 if(cData->topic_mask)
4812 free(cData->topic_mask);
4813 if(mask[0] == '*' && mask[1] == 0)
4815 cData->topic_mask = 0;
4819 cData->topic_mask = strdup(mask);
4821 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4822 else if(!match_ircglob(cData->topic, cData->topic_mask))
4823 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4827 if(channel->channel_info->topic_mask)
4828 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4830 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4834 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4838 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4842 if(greeting[0] == '*' && greeting[1] == 0)
4846 unsigned int length = strlen(greeting);
4847 if(length > chanserv_conf.greeting_length)
4849 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4852 *data = strdup(greeting);
4861 reply(name, user_find_message(user, "MSG_NONE"));
4865 static MODCMD_FUNC(chan_opt_greeting)
4867 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4870 static MODCMD_FUNC(chan_opt_usergreeting)
4872 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4875 static MODCMD_FUNC(chan_opt_modes)
4877 struct mod_chanmode *new_modes;
4878 char modes[MODELEN];
4882 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4884 reply("CSMSG_NO_ACCESS");
4887 if(argv[1][0] == '*' && argv[1][1] == 0)
4889 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4891 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4893 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4896 else if(new_modes->argc > 1)
4898 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4899 mod_chanmode_free(new_modes);
4904 channel->channel_info->modes = *new_modes;
4905 modcmd_chanmode_announce(new_modes);
4906 mod_chanmode_free(new_modes);
4910 mod_chanmode_format(&channel->channel_info->modes, modes);
4912 reply("CSMSG_SET_MODES", modes);
4914 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4918 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4920 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4922 struct chanData *cData = channel->channel_info;
4927 /* Set flag according to value. */
4928 if(enabled_string(argv[1]))
4930 cData->flags |= mask;
4933 else if(disabled_string(argv[1]))
4935 cData->flags &= ~mask;
4940 reply("MSG_INVALID_BINARY", argv[1]);
4946 /* Find current option value. */
4947 value = (cData->flags & mask) ? 1 : 0;
4951 reply(name, user_find_message(user, "MSG_ON"));
4953 reply(name, user_find_message(user, "MSG_OFF"));
4957 static MODCMD_FUNC(chan_opt_nodelete)
4959 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4961 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4965 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4968 static MODCMD_FUNC(chan_opt_dynlimit)
4970 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4973 static MODCMD_FUNC(chan_opt_offchannel)
4975 struct chanData *cData = channel->channel_info;
4980 /* Set flag according to value. */
4981 if(enabled_string(argv[1]))
4983 if(!IsOffChannel(cData))
4984 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4985 cData->flags |= CHANNEL_OFFCHANNEL;
4988 else if(disabled_string(argv[1]))
4990 if(IsOffChannel(cData))
4992 struct mod_chanmode change;
4993 mod_chanmode_init(&change);
4995 change.args[0].mode = MODE_CHANOP;
4996 change.args[0].u.member = AddChannelUser(chanserv, channel);
4997 mod_chanmode_announce(chanserv, channel, &change);
4999 cData->flags &= ~CHANNEL_OFFCHANNEL;
5004 reply("MSG_INVALID_BINARY", argv[1]);
5010 /* Find current option value. */
5011 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5015 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5017 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5021 static MODCMD_FUNC(chan_opt_defaults)
5023 struct userData *uData;
5024 struct chanData *cData;
5025 const char *confirm;
5026 enum levelOption lvlOpt;
5027 enum charOption chOpt;
5029 cData = channel->channel_info;
5030 uData = GetChannelUser(cData, user->handle_info);
5031 if(!uData || (uData->access < UL_OWNER))
5033 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5036 confirm = make_confirmation_string(uData);
5037 if((argc < 2) || strcmp(argv[1], confirm))
5039 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5042 cData->flags = CHANNEL_DEFAULT_FLAGS;
5043 cData->modes = chanserv_conf.default_modes;
5044 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5045 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5046 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5047 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5048 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5053 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5055 struct chanData *cData = channel->channel_info;
5056 struct userData *uData;
5057 unsigned short value;
5061 if(!check_user_level(channel, user, option, 1, 1))
5063 reply("CSMSG_CANNOT_SET");
5066 value = user_level_from_name(argv[1], UL_OWNER+1);
5067 if(!value && strcmp(argv[1], "0"))
5069 reply("CSMSG_INVALID_ACCESS", argv[1]);
5072 uData = GetChannelUser(cData, user->handle_info);
5073 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5075 reply("CSMSG_BAD_SETLEVEL");
5081 if(value > cData->lvlOpts[lvlGiveOps])
5083 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5088 if(value < cData->lvlOpts[lvlGiveVoice])
5090 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5095 /* This test only applies to owners, since non-owners
5096 * trying to set an option to above their level get caught
5097 * by the CSMSG_BAD_SETLEVEL test above.
5099 if(value > uData->access)
5101 reply("CSMSG_BAD_SETTERS");
5108 cData->lvlOpts[option] = value;
5110 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5114 static MODCMD_FUNC(chan_opt_enfops)
5116 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5119 static MODCMD_FUNC(chan_opt_giveops)
5121 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5124 static MODCMD_FUNC(chan_opt_enfmodes)
5126 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5129 static MODCMD_FUNC(chan_opt_enftopic)
5131 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5134 static MODCMD_FUNC(chan_opt_pubcmd)
5136 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5139 static MODCMD_FUNC(chan_opt_setters)
5141 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5144 static MODCMD_FUNC(chan_opt_ctcpusers)
5146 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5149 static MODCMD_FUNC(chan_opt_userinfo)
5151 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5154 static MODCMD_FUNC(chan_opt_givevoice)
5156 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5159 static MODCMD_FUNC(chan_opt_topicsnarf)
5161 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5164 static MODCMD_FUNC(chan_opt_inviteme)
5166 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5170 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5172 struct chanData *cData = channel->channel_info;
5173 int count = charOptions[option].count, index;
5177 index = atoi(argv[1]);
5179 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5181 reply("CSMSG_INVALID_NUMERIC", index);
5182 /* Show possible values. */
5183 for(index = 0; index < count; index++)
5184 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5188 cData->chOpts[option] = charOptions[option].values[index].value;
5192 /* Find current option value. */
5195 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5199 /* Somehow, the option value is corrupt; reset it to the default. */
5200 cData->chOpts[option] = charOptions[option].default_value;
5205 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5209 static MODCMD_FUNC(chan_opt_protect)
5211 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5214 static MODCMD_FUNC(chan_opt_toys)
5216 return channel_multiple_option(chToys, CSFUNC_ARGS);
5219 static MODCMD_FUNC(chan_opt_ctcpreaction)
5221 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5224 static MODCMD_FUNC(chan_opt_topicrefresh)
5226 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5229 static struct svccmd_list set_shows_list;
5232 handle_svccmd_unbind(struct svccmd *target) {
5234 for(ii=0; ii<set_shows_list.used; ++ii)
5235 if(target == set_shows_list.list[ii])
5236 set_shows_list.used = 0;
5239 static CHANSERV_FUNC(cmd_set)
5241 struct svccmd *subcmd;
5245 /* Check if we need to (re-)initialize set_shows_list. */
5246 if(!set_shows_list.used)
5248 if(!set_shows_list.size)
5250 set_shows_list.size = chanserv_conf.set_shows->used;
5251 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5253 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5255 const char *name = chanserv_conf.set_shows->list[ii];
5256 sprintf(buf, "%s %s", argv[0], name);
5257 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5260 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5263 svccmd_list_append(&set_shows_list, subcmd);
5269 reply("CSMSG_CHANNEL_OPTIONS");
5270 for(ii = 0; ii < set_shows_list.used; ii++)
5272 subcmd = set_shows_list.list[ii];
5273 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5278 sprintf(buf, "%s %s", argv[0], argv[1]);
5279 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5282 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5285 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5287 reply("CSMSG_NO_ACCESS");
5291 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5295 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5297 struct userData *uData;
5299 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5302 reply("CSMSG_NOT_USER", channel->name);
5308 /* Just show current option value. */
5310 else if(enabled_string(argv[1]))
5312 uData->flags |= mask;
5314 else if(disabled_string(argv[1]))
5316 uData->flags &= ~mask;
5320 reply("MSG_INVALID_BINARY", argv[1]);
5324 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5328 static MODCMD_FUNC(user_opt_noautoop)
5330 struct userData *uData;
5332 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5335 reply("CSMSG_NOT_USER", channel->name);
5338 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5339 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5341 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5344 static MODCMD_FUNC(user_opt_autoinvite)
5346 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5349 static MODCMD_FUNC(user_opt_info)
5351 struct userData *uData;
5354 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5358 /* If they got past the command restrictions (which require access)
5359 * but fail this test, we have some fool with security override on.
5361 reply("CSMSG_NOT_USER", channel->name);
5368 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5369 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5371 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5374 bp = strcspn(infoline, "\001");
5377 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5382 if(infoline[0] == '*' && infoline[1] == 0)
5385 uData->info = strdup(infoline);
5388 reply("CSMSG_USET_INFO", uData->info);
5390 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5394 struct svccmd_list uset_shows_list;
5396 static CHANSERV_FUNC(cmd_uset)
5398 struct svccmd *subcmd;
5402 /* Check if we need to (re-)initialize uset_shows_list. */
5403 if(!uset_shows_list.used)
5407 "NoAutoOp", "AutoInvite", "Info"
5410 if(!uset_shows_list.size)
5412 uset_shows_list.size = ArrayLength(options);
5413 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5415 for(ii = 0; ii < ArrayLength(options); ii++)
5417 const char *name = options[ii];
5418 sprintf(buf, "%s %s", argv[0], name);
5419 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5422 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5425 svccmd_list_append(&uset_shows_list, subcmd);
5431 /* Do this so options are presented in a consistent order. */
5432 reply("CSMSG_USER_OPTIONS");
5433 for(ii = 0; ii < uset_shows_list.used; ii++)
5434 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5438 sprintf(buf, "%s %s", argv[0], argv[1]);
5439 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5442 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5446 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5449 static CHANSERV_FUNC(cmd_giveownership)
5451 struct handle_info *new_owner_hi;
5452 struct userData *new_owner, *curr_user;
5453 struct chanData *cData = channel->channel_info;
5454 struct do_not_register *dnr;
5456 unsigned short co_access;
5457 char reason[MAXLEN];
5460 curr_user = GetChannelAccess(cData, user->handle_info);
5461 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5462 if(!curr_user || (curr_user->access != UL_OWNER))
5464 struct userData *owner = NULL;
5465 for(curr_user = channel->channel_info->users;
5467 curr_user = curr_user->next)
5469 if(curr_user->access != UL_OWNER)
5473 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5480 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5482 char delay[INTERVALLEN];
5483 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5484 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5487 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5489 if(new_owner_hi == user->handle_info)
5491 reply("CSMSG_NO_TRANSFER_SELF");
5494 new_owner = GetChannelAccess(cData, new_owner_hi);
5497 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5500 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5502 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5505 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5506 if(!IsHelping(user))
5507 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5509 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5512 if(new_owner->access >= UL_COOWNER)
5513 co_access = new_owner->access;
5515 co_access = UL_COOWNER;
5516 new_owner->access = UL_OWNER;
5518 curr_user->access = co_access;
5519 cData->ownerTransfer = now;
5520 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5521 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5522 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5526 static CHANSERV_FUNC(cmd_suspend)
5528 struct handle_info *hi;
5529 struct userData *self, *target;
5532 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5533 self = GetChannelUser(channel->channel_info, user->handle_info);
5534 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5536 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5539 if(target->access >= self->access)
5541 reply("MSG_USER_OUTRANKED", hi->handle);
5544 if(target->flags & USER_SUSPENDED)
5546 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5551 target->present = 0;
5554 target->flags |= USER_SUSPENDED;
5555 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5559 static CHANSERV_FUNC(cmd_unsuspend)
5561 struct handle_info *hi;
5562 struct userData *self, *target;
5565 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5566 self = GetChannelUser(channel->channel_info, user->handle_info);
5567 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5569 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5572 if(target->access >= self->access)
5574 reply("MSG_USER_OUTRANKED", hi->handle);
5577 if(!(target->flags & USER_SUSPENDED))
5579 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5582 target->flags &= ~USER_SUSPENDED;
5583 scan_user_presence(target, NULL);
5584 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5588 static MODCMD_FUNC(cmd_deleteme)
5590 struct handle_info *hi;
5591 struct userData *target;
5592 const char *confirm_string;
5593 unsigned short access;
5596 hi = user->handle_info;
5597 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5599 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5602 if(target->access == UL_OWNER)
5604 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5607 confirm_string = make_confirmation_string(target);
5608 if((argc < 2) || strcmp(argv[1], confirm_string))
5610 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5613 access = target->access;
5614 channel_name = strdup(channel->name);
5615 del_channel_user(target, 1);
5616 reply("CSMSG_DELETED_YOU", access, channel_name);
5622 chanserv_refresh_topics(UNUSED_ARG(void *data))
5624 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5625 struct chanData *cData;
5628 for(cData = channelList; cData; cData = cData->next)
5630 if(IsSuspended(cData))
5632 opt = cData->chOpts[chTopicRefresh];
5635 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5638 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5639 cData->last_refresh = refresh_num;
5641 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5644 static CHANSERV_FUNC(cmd_unf)
5648 char response[MAXLEN];
5649 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5650 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5651 irc_privmsg(cmd->parent->bot, channel->name, response);
5654 reply("CSMSG_UNF_RESPONSE");
5658 static CHANSERV_FUNC(cmd_ping)
5662 char response[MAXLEN];
5663 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5664 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5665 irc_privmsg(cmd->parent->bot, channel->name, response);
5668 reply("CSMSG_PING_RESPONSE");
5672 static CHANSERV_FUNC(cmd_wut)
5676 char response[MAXLEN];
5677 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5678 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5679 irc_privmsg(cmd->parent->bot, channel->name, response);
5682 reply("CSMSG_WUT_RESPONSE");
5686 static CHANSERV_FUNC(cmd_8ball)
5688 unsigned int i, j, accum;
5693 for(i=1; i<argc; i++)
5694 for(j=0; argv[i][j]; j++)
5695 accum = (accum << 5) - accum + toupper(argv[i][j]);
5696 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5699 char response[MAXLEN];
5700 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5701 irc_privmsg(cmd->parent->bot, channel->name, response);
5704 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5708 static CHANSERV_FUNC(cmd_d)
5710 unsigned long sides, count, modifier, ii, total;
5711 char response[MAXLEN], *sep;
5715 if((count = strtoul(argv[1], &sep, 10)) < 1)
5725 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5726 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5730 else if((sep[0] == '-') && isdigit(sep[1]))
5731 modifier = strtoul(sep, NULL, 10);
5732 else if((sep[0] == '+') && isdigit(sep[1]))
5733 modifier = strtoul(sep+1, NULL, 10);
5740 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5745 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5748 for(total = ii = 0; ii < count; ++ii)
5749 total += (rand() % sides) + 1;
5752 if((count > 1) || modifier)
5754 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5755 sprintf(response, fmt, total, count, sides, modifier);
5759 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5760 sprintf(response, fmt, total, sides);
5763 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5765 send_message_type(4, user, cmd->parent->bot, "%s", response);
5769 static CHANSERV_FUNC(cmd_huggle)
5771 /* CTCP must be via PRIVMSG, never notice */
5773 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5775 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5780 chanserv_adjust_limit(void *data)
5782 struct mod_chanmode change;
5783 struct chanData *cData = data;
5784 struct chanNode *channel = cData->channel;
5787 if(IsSuspended(cData))
5790 cData->limitAdjusted = now;
5791 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5792 if(cData->modes.modes_set & MODE_LIMIT)
5794 if(limit > cData->modes.new_limit)
5795 limit = cData->modes.new_limit;
5796 else if(limit == cData->modes.new_limit)
5800 mod_chanmode_init(&change);
5801 change.modes_set = MODE_LIMIT;
5802 change.new_limit = limit;
5803 mod_chanmode_announce(chanserv, channel, &change);
5807 handle_new_channel(struct chanNode *channel)
5809 struct chanData *cData;
5811 if(!(cData = channel->channel_info))
5814 if(cData->modes.modes_set || cData->modes.modes_clear)
5815 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5817 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5818 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5821 /* Welcome to my worst nightmare. Warning: Read (or modify)
5822 the code below at your own risk. */
5824 handle_join(struct modeNode *mNode)
5826 struct mod_chanmode change;
5827 struct userNode *user = mNode->user;
5828 struct chanNode *channel = mNode->channel;
5829 struct chanData *cData;
5830 struct userData *uData = NULL;
5831 struct banData *bData;
5832 struct handle_info *handle;
5833 unsigned int modes = 0, info = 0;
5836 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5839 cData = channel->channel_info;
5840 if(channel->members.used > cData->max)
5841 cData->max = channel->members.used;
5843 /* Check for bans. If they're joining through a ban, one of two
5845 * 1: Join during a netburst, by riding the break. Kick them
5846 * unless they have ops or voice in the channel.
5847 * 2: They're allowed to join through the ban (an invite in
5848 * ircu2.10, or a +e on Hybrid, or something).
5849 * If they're not joining through a ban, and the banlist is not
5850 * full, see if they're on the banlist for the channel. If so,
5853 if(user->uplink->burst && !mNode->modes)
5856 for(ii = 0; ii < channel->banlist.used; ii++)
5858 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5860 /* Riding a netburst. Naughty. */
5861 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5867 mod_chanmode_init(&change);
5869 if(channel->banlist.used < MAXBANS)
5871 /* Not joining through a ban. */
5872 for(bData = cData->bans;
5873 bData && !user_matches_glob(user, bData->mask, 1);
5874 bData = bData->next);
5878 char kick_reason[MAXLEN];
5879 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5881 bData->triggered = now;
5882 if(bData != cData->bans)
5884 /* Shuffle the ban to the head of the list. */
5886 bData->next->prev = bData->prev;
5888 bData->prev->next = bData->next;
5891 bData->next = cData->bans;
5894 cData->bans->prev = bData;
5895 cData->bans = bData;
5898 change.args[0].mode = MODE_BAN;
5899 change.args[0].u.hostmask = bData->mask;
5900 mod_chanmode_announce(chanserv, channel, &change);
5901 KickChannelUser(user, channel, chanserv, kick_reason);
5906 /* ChanServ will not modify the limits in join-flooded channels.
5907 It will also skip DynLimit processing when the user (or srvx)
5908 is bursting in, because there are likely more incoming. */
5909 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5910 && !user->uplink->burst
5911 && !channel->join_flooded
5912 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5914 /* The user count has begun "bumping" into the channel limit,
5915 so set a timer to raise the limit a bit. Any previous
5916 timers are removed so three incoming users within the delay
5917 results in one limit change, not three. */
5919 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5920 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5923 if(channel->join_flooded)
5925 /* don't automatically give ops or voice during a join flood */
5927 else if(cData->lvlOpts[lvlGiveOps] == 0)
5928 modes |= MODE_CHANOP;
5929 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5930 modes |= MODE_VOICE;
5932 greeting = cData->greeting;
5933 if(user->handle_info)
5935 handle = user->handle_info;
5937 if(IsHelper(user) && !IsHelping(user))
5940 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5942 if(channel == chanserv_conf.support_channels.list[ii])
5944 HANDLE_SET_FLAG(user->handle_info, HELPING);
5950 uData = GetTrueChannelAccess(cData, handle);
5951 if(uData && !IsUserSuspended(uData))
5953 /* Ops and above were handled by the above case. */
5954 if(IsUserAutoOp(uData))
5956 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5957 modes |= MODE_CHANOP;
5958 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5959 modes |= MODE_VOICE;
5961 if(uData->access >= UL_PRESENT)
5962 cData->visited = now;
5963 if(cData->user_greeting)
5964 greeting = cData->user_greeting;
5966 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5967 && ((now - uData->seen) >= chanserv_conf.info_delay)
5974 if(!user->uplink->burst)
5978 if(modes & MODE_CHANOP)
5979 modes &= ~MODE_VOICE;
5980 change.args[0].mode = modes;
5981 change.args[0].u.member = mNode;
5982 mod_chanmode_announce(chanserv, channel, &change);
5984 if(greeting && !user->uplink->burst)
5985 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5987 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5993 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5995 struct mod_chanmode change;
5996 struct userData *channel;
5997 unsigned int ii, jj;
5999 if(!user->handle_info)
6002 mod_chanmode_init(&change);
6004 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6006 struct chanNode *cn;
6007 struct modeNode *mn;
6008 if(IsUserSuspended(channel)
6009 || IsSuspended(channel->channel)
6010 || !(cn = channel->channel->channel))
6013 mn = GetUserMode(cn, user);
6016 if(!IsUserSuspended(channel)
6017 && IsUserAutoInvite(channel)
6018 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6020 && !user->uplink->burst)
6021 irc_invite(chanserv, user, cn);
6025 if(channel->access >= UL_PRESENT)
6026 channel->channel->visited = now;
6028 if(IsUserAutoOp(channel))
6030 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6031 change.args[0].mode = MODE_CHANOP;
6032 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6033 change.args[0].mode = MODE_VOICE;
6035 change.args[0].mode = 0;
6036 change.args[0].u.member = mn;
6037 if(change.args[0].mode)
6038 mod_chanmode_announce(chanserv, cn, &change);
6041 channel->seen = now;
6042 channel->present = 1;
6045 for(ii = 0; ii < user->channels.used; ++ii)
6047 struct chanNode *channel = user->channels.list[ii]->channel;
6048 struct banData *ban;
6050 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6051 || !channel->channel_info
6052 || IsSuspended(channel->channel_info))
6054 for(jj = 0; jj < channel->banlist.used; ++jj)
6055 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6057 if(jj < channel->banlist.used)
6059 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6061 char kick_reason[MAXLEN];
6062 if(!user_matches_glob(user, ban->mask, 1))
6064 change.args[0].mode = MODE_BAN;
6065 change.args[0].u.hostmask = ban->mask;
6066 mod_chanmode_announce(chanserv, channel, &change);
6067 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6068 KickChannelUser(user, channel, chanserv, kick_reason);
6069 ban->triggered = now;
6074 if(IsSupportHelper(user))
6076 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6078 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6080 HANDLE_SET_FLAG(user->handle_info, HELPING);
6088 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6090 struct chanData *cData;
6091 struct userData *uData;
6093 cData = mn->channel->channel_info;
6094 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6097 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6099 /* Allow for a bit of padding so that the limit doesn't
6100 track the user count exactly, which could get annoying. */
6101 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6103 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6104 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6108 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6110 scan_user_presence(uData, mn->user);
6114 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6116 unsigned int ii, jj;
6117 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6119 for(jj = 0; jj < mn->user->channels.used; ++jj)
6120 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6122 if(jj < mn->user->channels.used)
6125 if(ii == chanserv_conf.support_channels.used)
6126 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6131 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6133 struct userData *uData;
6135 if(!channel->channel_info || !kicker || IsService(kicker)
6136 || (kicker == victim) || IsSuspended(channel->channel_info)
6137 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6140 if(protect_user(victim, kicker, channel->channel_info))
6142 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6143 KickChannelUser(kicker, channel, chanserv, reason);
6146 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6151 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6153 struct chanData *cData;
6155 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6158 cData = channel->channel_info;
6159 if(bad_topic(channel, user, channel->topic))
6161 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6162 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6163 SetChannelTopic(channel, chanserv, old_topic, 1);
6164 else if(cData->topic)
6165 SetChannelTopic(channel, chanserv, cData->topic, 1);
6168 /* With topicsnarf, grab the topic and save it as the default topic. */
6169 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6172 cData->topic = strdup(channel->topic);
6178 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6180 struct mod_chanmode *bounce = NULL;
6181 unsigned int bnc, ii;
6184 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6187 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6188 && mode_lock_violated(&channel->channel_info->modes, change))
6190 char correct[MAXLEN];
6191 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6192 mod_chanmode_format(&channel->channel_info->modes, correct);
6193 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6195 for(ii = bnc = 0; ii < change->argc; ++ii)
6197 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6199 const struct userNode *victim = change->args[ii].u.member->user;
6200 if(!protect_user(victim, user, channel->channel_info))
6203 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6206 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6207 bounce->args[bnc].u.member = GetUserMode(channel, user);
6208 if(bounce->args[bnc].u.member)
6212 bounce->args[bnc].mode = MODE_CHANOP;
6213 bounce->args[bnc].u.member = change->args[ii].u.member;
6215 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6217 else if(change->args[ii].mode & MODE_CHANOP)
6219 const struct userNode *victim = change->args[ii].u.member->user;
6220 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6223 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6224 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6225 bounce->args[bnc].u.member = change->args[ii].u.member;
6228 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6230 const char *ban = change->args[ii].u.hostmask;
6231 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6234 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6235 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6236 bounce->args[bnc].u.hostmask = strdup(ban);
6238 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6243 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6244 mod_chanmode_announce(chanserv, channel, bounce);
6245 for(ii = 0; ii < change->argc; ++ii)
6246 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6247 free((char*)bounce->args[ii].u.hostmask);
6248 mod_chanmode_free(bounce);
6253 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6255 struct chanNode *channel;
6256 struct banData *bData;
6257 struct mod_chanmode change;
6258 unsigned int ii, jj;
6259 char kick_reason[MAXLEN];
6261 mod_chanmode_init(&change);
6263 change.args[0].mode = MODE_BAN;
6264 for(ii = 0; ii < user->channels.used; ++ii)
6266 channel = user->channels.list[ii]->channel;
6267 /* Need not check for bans if they're opped or voiced. */
6268 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6270 /* Need not check for bans unless channel registration is active. */
6271 if(!channel->channel_info || IsSuspended(channel->channel_info))
6273 /* Look for a matching ban already on the channel. */
6274 for(jj = 0; jj < channel->banlist.used; ++jj)
6275 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6277 /* Need not act if we found one. */
6278 if(jj < channel->banlist.used)
6280 /* Look for a matching ban in this channel. */
6281 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6283 if(!user_matches_glob(user, bData->mask, 1))
6285 change.args[0].u.hostmask = bData->mask;
6286 mod_chanmode_announce(chanserv, channel, &change);
6287 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6288 KickChannelUser(user, channel, chanserv, kick_reason);
6289 bData->triggered = now;
6290 break; /* we don't need to check any more bans in the channel */
6295 static void handle_rename(struct handle_info *handle, const char *old_handle)
6297 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6301 dict_remove2(handle_dnrs, old_handle, 1);
6302 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6303 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6308 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6310 struct userNode *h_user;
6312 if(handle->channels)
6314 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6315 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6317 while(handle->channels)
6318 del_channel_user(handle->channels, 1);
6323 handle_server_link(UNUSED_ARG(struct server *server))
6325 struct chanData *cData;
6327 for(cData = channelList; cData; cData = cData->next)
6329 if(!IsSuspended(cData))
6330 cData->may_opchan = 1;
6331 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6332 && !cData->channel->join_flooded
6333 && ((cData->channel->limit - cData->channel->members.used)
6334 < chanserv_conf.adjust_threshold))
6336 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6337 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6343 chanserv_conf_read(void)
6347 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6348 struct mod_chanmode *change;
6349 struct string_list *strlist;
6350 struct chanNode *chan;
6353 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6355 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6358 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6359 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6360 chanserv_conf.support_channels.used = 0;
6361 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6363 for(ii = 0; ii < strlist->used; ++ii)
6365 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6368 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6370 channelList_append(&chanserv_conf.support_channels, chan);
6373 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6376 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6379 chan = AddChannel(str, now, str2, NULL);
6381 channelList_append(&chanserv_conf.support_channels, chan);
6383 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6384 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6385 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6386 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6387 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6388 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6389 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6390 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6391 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6392 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6393 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6394 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6395 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6396 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6397 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6398 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6399 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6400 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6401 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6402 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6403 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6404 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6405 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6407 NickChange(chanserv, str, 0);
6408 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6409 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6410 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6411 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6412 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6413 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6414 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6415 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6416 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6417 chanserv_conf.max_owned = str ? atoi(str) : 5;
6418 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6419 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6420 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6421 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6422 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6423 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6424 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6427 safestrncpy(mode_line, str, sizeof(mode_line));
6428 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6429 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6431 chanserv_conf.default_modes = *change;
6432 mod_chanmode_free(change);
6434 free_string_list(chanserv_conf.set_shows);
6435 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6437 strlist = string_list_copy(strlist);
6440 static const char *list[] = {
6441 /* free form text */
6442 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6443 /* options based on user level */
6444 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6445 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6446 /* multiple choice options */
6447 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6448 /* binary options */
6449 "DynLimit", "NoDelete",
6454 strlist = alloc_string_list(ArrayLength(list)-1);
6455 for(ii=0; list[ii]; ii++)
6456 string_list_append(strlist, strdup(list[ii]));
6458 chanserv_conf.set_shows = strlist;
6459 /* We don't look things up now, in case the list refers to options
6460 * defined by modules initialized after this point. Just mark the
6461 * function list as invalid, so it will be initialized.
6463 set_shows_list.used = 0;
6464 free_string_list(chanserv_conf.eightball);
6465 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6468 strlist = string_list_copy(strlist);
6472 strlist = alloc_string_list(4);
6473 string_list_append(strlist, strdup("Yes."));
6474 string_list_append(strlist, strdup("No."));
6475 string_list_append(strlist, strdup("Maybe so."));
6477 chanserv_conf.eightball = strlist;
6478 free_string_list(chanserv_conf.old_ban_names);
6479 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6481 strlist = string_list_copy(strlist);
6483 strlist = alloc_string_list(2);
6484 chanserv_conf.old_ban_names = strlist;
6485 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6486 off_channel = str ? atoi(str) : 0;
6490 chanserv_note_type_read(const char *key, struct record_data *rd)
6493 struct note_type *ntype;
6496 if(!(obj = GET_RECORD_OBJECT(rd)))
6498 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6501 if(!(ntype = chanserv_create_note_type(key)))
6503 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6507 /* Figure out set access */
6508 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6510 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6511 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6513 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6515 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6516 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6518 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6520 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6524 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6525 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6526 ntype->set_access.min_opserv = 0;
6529 /* Figure out visibility */
6530 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6531 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6532 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6533 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6534 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6535 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6536 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6537 ntype->visible_type = NOTE_VIS_ALL;
6539 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6541 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6542 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6546 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6548 struct handle_info *handle;
6549 struct userData *uData;
6550 char *seen, *inf, *flags;
6552 unsigned short access;
6554 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6556 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6560 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6561 if(access > UL_OWNER)
6563 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6567 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6568 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6569 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6570 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6571 handle = get_handle_info(key);
6574 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6578 uData = add_channel_user(chan, handle, access, last_seen, inf);
6579 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6583 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6585 struct banData *bData;
6586 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6587 time_t set_time, triggered_time, expires_time;
6589 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6591 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6595 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6596 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6597 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6598 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6599 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6600 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6602 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6603 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6605 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6607 expires_time = set_time + atoi(s_duration);
6611 if(!reason || (expires_time && (expires_time < now)))
6614 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6617 static struct suspended *
6618 chanserv_read_suspended(dict_t obj)
6620 struct suspended *suspended = calloc(1, sizeof(*suspended));
6624 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6625 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6626 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6627 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6628 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6629 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6630 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6631 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6632 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6633 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6638 chanserv_channel_read(const char *key, struct record_data *hir)
6640 struct suspended *suspended;
6641 struct mod_chanmode *modes;
6642 struct chanNode *cNode;
6643 struct chanData *cData;
6644 struct dict *channel, *obj;
6645 char *str, *argv[10];
6649 channel = hir->d.object;
6651 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6654 cNode = AddChannel(key, now, NULL, NULL);
6657 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6660 cData = register_channel(cNode, str);
6663 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6667 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6669 enum levelOption lvlOpt;
6670 enum charOption chOpt;
6672 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6673 cData->flags = atoi(str);
6675 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6677 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6679 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6680 else if(levelOptions[lvlOpt].old_flag)
6682 if(cData->flags & levelOptions[lvlOpt].old_flag)
6683 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6685 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6689 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6691 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6693 cData->chOpts[chOpt] = str[0];
6696 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6698 enum levelOption lvlOpt;
6699 enum charOption chOpt;
6702 cData->flags = base64toint(str, 5);
6703 count = strlen(str += 5);
6704 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6707 if(levelOptions[lvlOpt].old_flag)
6709 if(cData->flags & levelOptions[lvlOpt].old_flag)
6710 lvl = levelOptions[lvlOpt].flag_value;
6712 lvl = levelOptions[lvlOpt].default_value;
6714 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6716 case 'c': lvl = UL_COOWNER; break;
6717 case 'm': lvl = UL_MASTER; break;
6718 case 'n': lvl = UL_OWNER+1; break;
6719 case 'o': lvl = UL_OP; break;
6720 case 'p': lvl = UL_PEON; break;
6721 case 'w': lvl = UL_OWNER; break;
6722 default: lvl = 0; break;
6724 cData->lvlOpts[lvlOpt] = lvl;
6726 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6727 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6730 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6732 suspended = chanserv_read_suspended(obj);
6733 cData->suspended = suspended;
6734 suspended->cData = cData;
6735 /* We could use suspended->expires and suspended->revoked to
6736 * set the CHANNEL_SUSPENDED flag, but we don't. */
6738 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6740 suspended = calloc(1, sizeof(*suspended));
6741 suspended->issued = 0;
6742 suspended->revoked = 0;
6743 suspended->suspender = strdup(str);
6744 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6745 suspended->expires = str ? atoi(str) : 0;
6746 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6747 suspended->reason = strdup(str ? str : "No reason");
6748 suspended->previous = NULL;
6749 cData->suspended = suspended;
6750 suspended->cData = cData;
6754 cData->flags &= ~CHANNEL_SUSPENDED;
6755 suspended = NULL; /* to squelch a warning */
6758 if(IsSuspended(cData)) {
6759 if(suspended->expires > now)
6760 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6761 else if(suspended->expires)
6762 cData->flags &= ~CHANNEL_SUSPENDED;
6765 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6766 struct mod_chanmode change;
6767 mod_chanmode_init(&change);
6769 change.args[0].mode = MODE_CHANOP;
6770 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6771 mod_chanmode_announce(chanserv, cNode, &change);
6774 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6775 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6776 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6777 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6778 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6779 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6780 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6781 cData->max = str ? atoi(str) : 0;
6782 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6783 cData->greeting = str ? strdup(str) : NULL;
6784 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6785 cData->user_greeting = str ? strdup(str) : NULL;
6786 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6787 cData->topic_mask = str ? strdup(str) : NULL;
6788 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6789 cData->topic = str ? strdup(str) : NULL;
6791 if(!IsSuspended(cData)
6792 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6793 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6794 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6795 cData->modes = *modes;
6797 cData->modes.modes_set |= MODE_REGISTERED;
6798 if(cData->modes.argc > 1)
6799 cData->modes.argc = 1;
6800 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6801 mod_chanmode_free(modes);
6804 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6805 for(it = dict_first(obj); it; it = iter_next(it))
6806 user_read_helper(iter_key(it), iter_data(it), cData);
6808 if(!cData->users && !IsProtected(cData))
6810 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6811 unregister_channel(cData, "has empty user list.");
6815 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6816 for(it = dict_first(obj); it; it = iter_next(it))
6817 ban_read_helper(iter_key(it), iter_data(it), cData);
6819 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6820 for(it = dict_first(obj); it; it = iter_next(it))
6822 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6823 struct record_data *rd = iter_data(it);
6824 const char *note, *setter;
6826 if(rd->type != RECDB_OBJECT)
6828 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6832 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6834 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6836 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6840 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6841 if(!setter) setter = "<unknown>";
6842 chanserv_add_channel_note(cData, ntype, setter, note);
6850 chanserv_dnr_read(const char *key, struct record_data *hir)
6852 const char *setter, *reason, *str;
6853 struct do_not_register *dnr;
6855 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6858 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6861 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6864 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6867 dnr = chanserv_add_dnr(key, setter, reason);
6870 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6872 dnr->set = atoi(str);
6878 chanserv_saxdb_read(struct dict *database)
6880 struct dict *section;
6883 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6884 for(it = dict_first(section); it; it = iter_next(it))
6885 chanserv_note_type_read(iter_key(it), iter_data(it));
6887 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6888 for(it = dict_first(section); it; it = iter_next(it))
6889 chanserv_channel_read(iter_key(it), iter_data(it));
6891 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6892 for(it = dict_first(section); it; it = iter_next(it))
6893 chanserv_dnr_read(iter_key(it), iter_data(it));
6899 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6901 int high_present = 0;
6902 saxdb_start_record(ctx, KEY_USERS, 1);
6903 for(; uData; uData = uData->next)
6905 if((uData->access >= UL_PRESENT) && uData->present)
6907 saxdb_start_record(ctx, uData->handle->handle, 0);
6908 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6909 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6911 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6913 saxdb_write_string(ctx, KEY_INFO, uData->info);
6914 saxdb_end_record(ctx);
6916 saxdb_end_record(ctx);
6917 return high_present;
6921 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6925 saxdb_start_record(ctx, KEY_BANS, 1);
6926 for(; bData; bData = bData->next)
6928 saxdb_start_record(ctx, bData->mask, 0);
6929 saxdb_write_int(ctx, KEY_SET, bData->set);
6930 if(bData->triggered)
6931 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6933 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6935 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6937 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6938 saxdb_end_record(ctx);
6940 saxdb_end_record(ctx);
6944 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6946 saxdb_start_record(ctx, name, 0);
6947 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6948 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6950 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6952 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6954 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6956 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6957 saxdb_end_record(ctx);
6961 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6965 enum levelOption lvlOpt;
6966 enum charOption chOpt;
6968 saxdb_start_record(ctx, channel->channel->name, 1);
6970 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6971 saxdb_write_int(ctx, KEY_MAX, channel->max);
6973 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6974 if(channel->registrar)
6975 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6976 if(channel->greeting)
6977 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6978 if(channel->user_greeting)
6979 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6980 if(channel->topic_mask)
6981 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6982 if(channel->suspended)
6983 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6985 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6986 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6987 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6988 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6989 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6991 buf[0] = channel->chOpts[chOpt];
6993 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6995 saxdb_end_record(ctx);
6997 if(channel->modes.modes_set || channel->modes.modes_clear)
6999 mod_chanmode_format(&channel->modes, buf);
7000 saxdb_write_string(ctx, KEY_MODES, buf);
7003 high_present = chanserv_write_users(ctx, channel->users);
7004 chanserv_write_bans(ctx, channel->bans);
7006 if(dict_size(channel->notes))
7010 saxdb_start_record(ctx, KEY_NOTES, 1);
7011 for(it = dict_first(channel->notes); it; it = iter_next(it))
7013 struct note *note = iter_data(it);
7014 saxdb_start_record(ctx, iter_key(it), 0);
7015 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7016 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7017 saxdb_end_record(ctx);
7019 saxdb_end_record(ctx);
7022 if(channel->ownerTransfer)
7023 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7024 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7025 saxdb_end_record(ctx);
7029 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7033 saxdb_start_record(ctx, ntype->name, 0);
7034 switch(ntype->set_access_type)
7036 case NOTE_SET_CHANNEL_ACCESS:
7037 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7039 case NOTE_SET_CHANNEL_SETTER:
7040 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7042 case NOTE_SET_PRIVILEGED: default:
7043 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7046 switch(ntype->visible_type)
7048 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7049 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7050 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7052 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7053 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7054 saxdb_end_record(ctx);
7058 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7060 struct do_not_register *dnr;
7063 for(it = dict_first(dnrs); it; it = iter_next(it))
7065 dnr = iter_data(it);
7066 saxdb_start_record(ctx, dnr->chan_name, 0);
7068 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7069 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7070 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7071 saxdb_end_record(ctx);
7076 chanserv_saxdb_write(struct saxdb_context *ctx)
7079 struct chanData *channel;
7082 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7083 for(it = dict_first(note_types); it; it = iter_next(it))
7084 chanserv_write_note_type(ctx, iter_data(it));
7085 saxdb_end_record(ctx);
7088 saxdb_start_record(ctx, KEY_DNR, 1);
7089 write_dnrs_helper(ctx, handle_dnrs);
7090 write_dnrs_helper(ctx, plain_dnrs);
7091 write_dnrs_helper(ctx, mask_dnrs);
7092 saxdb_end_record(ctx);
7095 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7096 for(channel = channelList; channel; channel = channel->next)
7097 chanserv_write_channel(ctx, channel);
7098 saxdb_end_record(ctx);
7104 chanserv_db_cleanup(void) {
7106 unreg_part_func(handle_part);
7108 unregister_channel(channelList, "terminating.");
7109 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7110 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7111 free(chanserv_conf.support_channels.list);
7112 dict_delete(handle_dnrs);
7113 dict_delete(plain_dnrs);
7114 dict_delete(mask_dnrs);
7115 dict_delete(note_types);
7116 free_string_list(chanserv_conf.eightball);
7117 free_string_list(chanserv_conf.old_ban_names);
7118 free_string_list(chanserv_conf.set_shows);
7119 free(set_shows_list.list);
7120 free(uset_shows_list.list);
7123 struct userData *helper = helperList;
7124 helperList = helperList->next;
7129 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7130 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7131 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7134 init_chanserv(const char *nick)
7136 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7137 conf_register_reload(chanserv_conf_read);
7139 reg_server_link_func(handle_server_link);
7141 reg_new_channel_func(handle_new_channel);
7142 reg_join_func(handle_join);
7143 reg_part_func(handle_part);
7144 reg_kick_func(handle_kick);
7145 reg_topic_func(handle_topic);
7146 reg_mode_change_func(handle_mode);
7147 reg_nick_change_func(handle_nick_change);
7149 reg_auth_func(handle_auth);
7150 reg_handle_rename_func(handle_rename);
7151 reg_unreg_func(handle_unreg);
7153 handle_dnrs = dict_new();
7154 dict_set_free_data(handle_dnrs, free);
7155 plain_dnrs = dict_new();
7156 dict_set_free_data(plain_dnrs, free);
7157 mask_dnrs = dict_new();
7158 dict_set_free_data(mask_dnrs, free);
7160 reg_svccmd_unbind_func(handle_svccmd_unbind);
7161 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7162 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7163 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7164 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7165 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7166 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7167 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7168 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7169 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7171 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7172 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7174 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7175 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7176 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7177 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7178 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7180 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7181 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7182 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7183 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7184 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7186 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7187 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7188 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7189 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7191 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7192 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7193 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7194 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7195 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7196 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7197 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7198 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7200 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7201 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7202 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7203 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7204 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7205 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7206 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7207 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7208 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7209 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7210 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7211 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7212 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7213 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7215 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7216 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7217 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7218 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7219 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7221 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7222 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7224 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7225 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7226 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7227 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7228 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7229 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7230 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7231 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7232 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7233 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7234 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7236 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7237 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7239 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7240 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7241 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7242 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7244 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7245 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7246 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7247 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7248 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7250 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7251 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7252 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7253 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7254 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7255 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7257 /* Channel options */
7258 DEFINE_CHANNEL_OPTION(defaulttopic);
7259 DEFINE_CHANNEL_OPTION(topicmask);
7260 DEFINE_CHANNEL_OPTION(greeting);
7261 DEFINE_CHANNEL_OPTION(usergreeting);
7262 DEFINE_CHANNEL_OPTION(modes);
7263 DEFINE_CHANNEL_OPTION(enfops);
7264 DEFINE_CHANNEL_OPTION(giveops);
7265 DEFINE_CHANNEL_OPTION(protect);
7266 DEFINE_CHANNEL_OPTION(enfmodes);
7267 DEFINE_CHANNEL_OPTION(enftopic);
7268 DEFINE_CHANNEL_OPTION(pubcmd);
7269 DEFINE_CHANNEL_OPTION(givevoice);
7270 DEFINE_CHANNEL_OPTION(userinfo);
7271 DEFINE_CHANNEL_OPTION(dynlimit);
7272 DEFINE_CHANNEL_OPTION(topicsnarf);
7273 DEFINE_CHANNEL_OPTION(nodelete);
7274 DEFINE_CHANNEL_OPTION(toys);
7275 DEFINE_CHANNEL_OPTION(setters);
7276 DEFINE_CHANNEL_OPTION(topicrefresh);
7277 DEFINE_CHANNEL_OPTION(ctcpusers);
7278 DEFINE_CHANNEL_OPTION(ctcpreaction);
7279 DEFINE_CHANNEL_OPTION(inviteme);
7281 DEFINE_CHANNEL_OPTION(offchannel);
7282 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7284 /* Alias set topic to set defaulttopic for compatibility. */
7285 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7288 DEFINE_USER_OPTION(noautoop);
7289 DEFINE_USER_OPTION(autoinvite);
7290 DEFINE_USER_OPTION(info);
7292 /* Alias uset autovoice to uset autoop. */
7293 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7295 note_types = dict_new();
7296 dict_set_free_data(note_types, chanserv_deref_note_type);
7299 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7300 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7301 service_register(chanserv)->trigger = '!';
7302 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7304 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7306 if(chanserv_conf.channel_expire_frequency)
7307 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7309 if(chanserv_conf.refresh_period)
7311 time_t next_refresh;
7312 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7313 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7316 reg_exit_func(chanserv_db_cleanup);
7317 message_register_table(msgtab);