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);
1688 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1690 reply("CSMSG_MUST_BE_OPPED", channel->name);
1695 chan_name = channel->name;
1699 if((argc < 2) || !IsChannelName(argv[1]))
1701 reply("MSG_NOT_CHANNEL_NAME");
1705 if(opserv_bad_channel(argv[1]))
1707 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1712 chan_name = argv[1];
1715 if(argc >= (new_channel+2))
1717 if(!IsHelping(user))
1719 reply("CSMSG_PROXY_FORBIDDEN");
1723 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1725 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1726 dnr = chanserv_is_dnr(chan_name, handle);
1730 handle = user->handle_info;
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 if(!IsHelping(user))
1736 reply("CSMSG_DNR_CHANNEL", chan_name);
1738 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1742 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1744 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1749 channel = AddChannel(argv[1], now, NULL, NULL);
1751 cData = register_channel(channel, user->handle_info->handle);
1752 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1753 cData->modes = chanserv_conf.default_modes;
1755 cData->modes.modes_set |= MODE_REGISTERED;
1756 if (IsOffChannel(cData))
1758 mod_chanmode_announce(chanserv, channel, &cData->modes);
1762 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
1763 change->args[change->argc].mode = MODE_CHANOP;
1764 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1766 mod_chanmode_announce(chanserv, channel, change);
1767 mod_chanmode_free(change);
1770 /* Initialize the channel's max user record. */
1771 cData->max = channel->members.used;
1773 if(handle != user->handle_info)
1774 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1776 reply("CSMSG_REG_SUCCESS", channel->name);
1778 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1779 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1784 make_confirmation_string(struct userData *uData)
1786 static char strbuf[16];
1791 for(src = uData->handle->handle; *src; )
1792 accum = accum * 31 + toupper(*src++);
1794 for(src = uData->channel->channel->name; *src; )
1795 accum = accum * 31 + toupper(*src++);
1796 sprintf(strbuf, "%08x", accum);
1800 static CHANSERV_FUNC(cmd_unregister)
1803 char reason[MAXLEN];
1804 struct chanData *cData;
1805 struct userData *uData;
1807 cData = channel->channel_info;
1810 reply("CSMSG_NOT_REGISTERED", channel->name);
1814 uData = GetChannelUser(cData, user->handle_info);
1815 if(!uData || (uData->access < UL_OWNER))
1817 reply("CSMSG_NO_ACCESS");
1821 if(IsProtected(cData))
1823 reply("CSMSG_UNREG_NODELETE", channel->name);
1827 if(!IsHelping(user))
1829 const char *confirm_string;
1830 if(IsSuspended(cData))
1832 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1835 confirm_string = make_confirmation_string(uData);
1836 if((argc < 2) || strcmp(argv[1], confirm_string))
1838 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1843 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1844 name = strdup(channel->name);
1845 unregister_channel(cData, reason);
1846 reply("CSMSG_UNREG_SUCCESS", name);
1851 static CHANSERV_FUNC(cmd_move)
1853 struct mod_chanmode change;
1854 struct chanNode *target;
1855 struct modeNode *mn;
1856 struct userData *uData;
1857 char reason[MAXLEN];
1858 struct do_not_register *dnr;
1862 if(IsProtected(channel->channel_info))
1864 reply("CSMSG_MOVE_NODELETE", channel->name);
1868 if(!IsChannelName(argv[1]))
1870 reply("MSG_NOT_CHANNEL_NAME");
1874 if(opserv_bad_channel(argv[1]))
1876 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1880 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1882 for(uData = channel->channel_info->users; uData; uData = uData->next)
1884 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1886 if(!IsHelping(user))
1887 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1889 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1895 mod_chanmode_init(&change);
1896 if(!(target = GetChannel(argv[1])))
1898 target = AddChannel(argv[1], now, NULL, NULL);
1899 if(!IsSuspended(channel->channel_info))
1900 AddChannelUser(chanserv, target);
1902 else if(target->channel_info)
1904 reply("CSMSG_ALREADY_REGGED", target->name);
1907 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1908 && !IsHelping(user))
1910 reply("CSMSG_MUST_BE_OPPED", target->name);
1913 else if(!IsSuspended(channel->channel_info))
1916 change.args[0].mode = MODE_CHANOP;
1917 change.args[0].u.member = AddChannelUser(chanserv, target);
1918 mod_chanmode_announce(chanserv, target, &change);
1923 /* Clear MODE_REGISTERED from old channel, add it to new. */
1925 change.modes_clear = MODE_REGISTERED;
1926 mod_chanmode_announce(chanserv, channel, &change);
1927 change.modes_clear = 0;
1928 change.modes_set = MODE_REGISTERED;
1929 mod_chanmode_announce(chanserv, target, &change);
1932 /* Move the channel_info to the target channel; it
1933 shouldn't be necessary to clear timeq callbacks
1934 for the old channel. */
1935 target->channel_info = channel->channel_info;
1936 target->channel_info->channel = target;
1937 channel->channel_info = NULL;
1939 reply("CSMSG_MOVE_SUCCESS", target->name);
1941 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1942 if(!IsSuspended(target->channel_info))
1944 char reason2[MAXLEN];
1945 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1946 DelChannelUser(chanserv, channel, reason2, 0);
1948 UnlockChannel(channel);
1949 LockChannel(target);
1950 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1955 merge_users(struct chanData *source, struct chanData *target)
1957 struct userData *suData, *tuData, *next;
1963 /* Insert the source's users into the scratch area. */
1964 for(suData = source->users; suData; suData = suData->next)
1965 dict_insert(merge, suData->handle->handle, suData);
1967 /* Iterate through the target's users, looking for
1968 users common to both channels. The lower access is
1969 removed from either the scratch area or target user
1971 for(tuData = target->users; tuData; tuData = next)
1973 struct userData *choice;
1975 next = tuData->next;
1977 /* If a source user exists with the same handle as a target
1978 channel's user, resolve the conflict by removing one. */
1979 suData = dict_find(merge, tuData->handle->handle, NULL);
1983 /* Pick the data we want to keep. */
1984 /* If the access is the same, use the later seen time. */
1985 if(suData->access == tuData->access)
1986 choice = (suData->seen > tuData->seen) ? suData : tuData;
1987 else /* Otherwise, keep the higher access level. */
1988 choice = (suData->access > tuData->access) ? suData : tuData;
1990 /* Remove the user that wasn't picked. */
1991 if(choice == tuData)
1993 dict_remove(merge, suData->handle->handle);
1994 del_channel_user(suData, 0);
1997 del_channel_user(tuData, 0);
2000 /* Move the remaining users to the target channel. */
2001 for(it = dict_first(merge); it; it = iter_next(it))
2003 suData = iter_data(it);
2005 /* Insert the user into the target channel's linked list. */
2006 suData->prev = NULL;
2007 suData->next = target->users;
2008 suData->channel = target;
2011 target->users->prev = suData;
2012 target->users = suData;
2014 /* Update the user counts for the target channel; the
2015 source counts are left alone. */
2016 target->userCount++;
2019 /* Possible to assert (source->users == NULL) here. */
2020 source->users = NULL;
2025 merge_bans(struct chanData *source, struct chanData *target)
2027 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2029 /* Hold on to the original head of the target ban list
2030 to avoid comparing source bans with source bans. */
2031 tFront = target->bans;
2033 /* Perform a totally expensive O(n*m) merge, ick. */
2034 for(sbData = source->bans; sbData; sbData = sNext)
2036 /* Flag to track whether the ban's been moved
2037 to the destination yet. */
2040 /* Possible to assert (sbData->prev == NULL) here. */
2041 sNext = sbData->next;
2043 for(tbData = tFront; tbData; tbData = tNext)
2045 tNext = tbData->next;
2047 /* Perform two comparisons between each source
2048 and target ban, conflicts are resolved by
2049 keeping the broader ban and copying the later
2050 expiration and triggered time. */
2051 if(match_ircglobs(tbData->mask, sbData->mask))
2053 /* There is a broader ban in the target channel that
2054 overrides one in the source channel; remove the
2055 source ban and break. */
2056 if(sbData->expires > tbData->expires)
2057 tbData->expires = sbData->expires;
2058 if(sbData->triggered > tbData->triggered)
2059 tbData->triggered = sbData->triggered;
2060 del_channel_ban(sbData);
2063 else if(match_ircglobs(sbData->mask, tbData->mask))
2065 /* There is a broader ban in the source channel that
2066 overrides one in the target channel; remove the
2067 target ban, fall through and move the source over. */
2068 if(tbData->expires > sbData->expires)
2069 sbData->expires = tbData->expires;
2070 if(tbData->triggered > sbData->triggered)
2071 sbData->triggered = tbData->triggered;
2072 if(tbData == tFront)
2074 del_channel_ban(tbData);
2077 /* Source bans can override multiple target bans, so
2078 we allow a source to run through this loop multiple
2079 times, but we can only move it once. */
2084 /* Remove the source ban from the source ban list. */
2086 sbData->next->prev = sbData->prev;
2088 /* Modify the source ban's associated channel. */
2089 sbData->channel = target;
2091 /* Insert the ban into the target channel's linked list. */
2092 sbData->prev = NULL;
2093 sbData->next = target->bans;
2096 target->bans->prev = sbData;
2097 target->bans = sbData;
2099 /* Update the user counts for the target channel. */
2104 /* Possible to assert (source->bans == NULL) here. */
2105 source->bans = NULL;
2109 merge_data(struct chanData *source, struct chanData *target)
2111 if(source->visited > target->visited)
2112 target->visited = source->visited;
2116 merge_channel(struct chanData *source, struct chanData *target)
2118 merge_users(source, target);
2119 merge_bans(source, target);
2120 merge_data(source, target);
2123 static CHANSERV_FUNC(cmd_merge)
2125 struct userData *target_user;
2126 struct chanNode *target;
2127 char reason[MAXLEN];
2131 /* Make sure the target channel exists and is registered to the user
2132 performing the command. */
2133 if(!(target = GetChannel(argv[1])))
2135 reply("MSG_INVALID_CHANNEL");
2139 if(!target->channel_info)
2141 reply("CSMSG_NOT_REGISTERED", target->name);
2145 if(IsProtected(channel->channel_info))
2147 reply("CSMSG_MERGE_NODELETE");
2151 if(IsSuspended(target->channel_info))
2153 reply("CSMSG_MERGE_SUSPENDED");
2157 if(channel == target)
2159 reply("CSMSG_MERGE_SELF");
2163 target_user = GetChannelUser(target->channel_info, user->handle_info);
2164 if(!target_user || (target_user->access < UL_OWNER))
2166 reply("CSMSG_MERGE_NOT_OWNER");
2170 /* Merge the channel structures and associated data. */
2171 merge_channel(channel->channel_info, target->channel_info);
2172 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2173 unregister_channel(channel->channel_info, reason);
2174 reply("CSMSG_MERGE_SUCCESS", target->name);
2178 static CHANSERV_FUNC(cmd_opchan)
2180 struct mod_chanmode change;
2181 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2183 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2186 channel->channel_info->may_opchan = 0;
2187 mod_chanmode_init(&change);
2189 change.args[0].mode = MODE_CHANOP;
2190 change.args[0].u.member = GetUserMode(channel, chanserv);
2191 mod_chanmode_announce(chanserv, channel, &change);
2192 reply("CSMSG_OPCHAN_DONE", channel->name);
2196 static CHANSERV_FUNC(cmd_adduser)
2198 struct userData *actee;
2199 struct userData *actor;
2200 struct handle_info *handle;
2201 unsigned short access;
2205 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2207 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2211 access = user_level_from_name(argv[2], UL_OWNER);
2214 reply("CSMSG_INVALID_ACCESS", argv[2]);
2218 actor = GetChannelUser(channel->channel_info, user->handle_info);
2219 if(actor->access <= access)
2221 reply("CSMSG_NO_BUMP_ACCESS");
2225 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2228 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2230 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2234 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2235 scan_user_presence(actee, NULL);
2236 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2240 static CHANSERV_FUNC(cmd_clvl)
2242 struct handle_info *handle;
2243 struct userData *victim;
2244 struct userData *actor;
2245 unsigned short new_access;
2246 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2250 actor = GetChannelUser(channel->channel_info, user->handle_info);
2252 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2255 if(handle == user->handle_info && !privileged)
2257 reply("CSMSG_NO_SELF_CLVL");
2261 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2263 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2267 if(actor->access <= victim->access && !privileged)
2269 reply("MSG_USER_OUTRANKED", handle->handle);
2273 new_access = user_level_from_name(argv[2], UL_OWNER);
2277 reply("CSMSG_INVALID_ACCESS", argv[2]);
2281 if(new_access >= actor->access && !privileged)
2283 reply("CSMSG_NO_BUMP_ACCESS");
2287 victim->access = new_access;
2288 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2292 static CHANSERV_FUNC(cmd_deluser)
2294 struct handle_info *handle;
2295 struct userData *victim;
2296 struct userData *actor;
2297 unsigned short access;
2302 actor = GetChannelUser(channel->channel_info, user->handle_info);
2304 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2307 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2309 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2315 access = user_level_from_name(argv[1], UL_OWNER);
2318 reply("CSMSG_INVALID_ACCESS", argv[1]);
2321 if(access != victim->access)
2323 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2329 access = victim->access;
2332 if((actor->access <= victim->access) && !IsHelping(user))
2334 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2338 chan_name = strdup(channel->name);
2339 del_channel_user(victim, 1);
2340 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2346 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2348 struct userData *actor, *uData, *next;
2350 actor = GetChannelUser(channel->channel_info, user->handle_info);
2352 if(min_access > max_access)
2354 reply("CSMSG_BAD_RANGE", min_access, max_access);
2358 if((actor->access <= max_access) && !IsHelping(user))
2360 reply("CSMSG_NO_ACCESS");
2364 for(uData = channel->channel_info->users; uData; uData = next)
2368 if((uData->access >= min_access)
2369 && (uData->access <= max_access)
2370 && match_ircglob(uData->handle->handle, mask))
2371 del_channel_user(uData, 1);
2374 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2378 static CHANSERV_FUNC(cmd_mdelowner)
2380 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2383 static CHANSERV_FUNC(cmd_mdelcoowner)
2385 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2388 static CHANSERV_FUNC(cmd_mdelmaster)
2390 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2393 static CHANSERV_FUNC(cmd_mdelop)
2395 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2398 static CHANSERV_FUNC(cmd_mdelpeon)
2400 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2404 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2406 struct banData *bData, *next;
2407 char interval[INTERVALLEN];
2412 limit = now - duration;
2413 for(bData = channel->channel_info->bans; bData; bData = next)
2417 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2420 del_channel_ban(bData);
2424 intervalString(interval, duration, user->handle_info);
2425 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2430 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2432 struct userData *actor, *uData, *next;
2433 char interval[INTERVALLEN];
2437 actor = GetChannelUser(channel->channel_info, user->handle_info);
2438 if(min_access > max_access)
2440 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2444 if((actor->access <= max_access) && !IsHelping(user))
2446 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2451 limit = now - duration;
2452 for(uData = channel->channel_info->users; uData; uData = next)
2456 if((uData->seen > limit) || uData->present)
2459 if(((uData->access >= min_access) && (uData->access <= max_access))
2460 || (!max_access && (uData->access < actor->access)))
2462 del_channel_user(uData, 1);
2470 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2472 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2476 static CHANSERV_FUNC(cmd_trim)
2478 unsigned long duration;
2479 unsigned short min_level, max_level;
2483 duration = ParseInterval(argv[2]);
2486 reply("CSMSG_CANNOT_TRIM");
2490 if(!irccasecmp(argv[1], "bans"))
2492 cmd_trim_bans(user, channel, duration);
2495 else if(!irccasecmp(argv[1], "users"))
2497 cmd_trim_users(user, channel, 0, 0, duration);
2500 else if(parse_level_range(&min_level, &max_level, argv[1]))
2502 cmd_trim_users(user, channel, min_level, max_level, duration);
2505 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2507 cmd_trim_users(user, channel, min_level, min_level, duration);
2512 reply("CSMSG_INVALID_TRIM", argv[1]);
2517 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2518 to the user. cmd_all takes advantage of this. */
2519 static CHANSERV_FUNC(cmd_up)
2521 struct mod_chanmode change;
2522 struct userData *uData;
2525 mod_chanmode_init(&change);
2527 change.args[0].u.member = GetUserMode(channel, user);
2528 if(!change.args[0].u.member)
2531 reply("MSG_CHANNEL_ABSENT", channel->name);
2535 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2539 reply("CSMSG_GODMODE_UP", argv[0]);
2542 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2544 change.args[0].mode = MODE_CHANOP;
2545 errmsg = "CSMSG_ALREADY_OPPED";
2547 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2549 change.args[0].mode = MODE_VOICE;
2550 errmsg = "CSMSG_ALREADY_VOICED";
2555 reply("CSMSG_NO_ACCESS");
2558 change.args[0].mode &= ~change.args[0].u.member->modes;
2559 if(!change.args[0].mode)
2562 reply(errmsg, channel->name);
2565 modcmd_chanmode_announce(&change);
2569 static CHANSERV_FUNC(cmd_down)
2571 struct mod_chanmode change;
2573 mod_chanmode_init(&change);
2575 change.args[0].u.member = GetUserMode(channel, user);
2576 if(!change.args[0].u.member)
2579 reply("MSG_CHANNEL_ABSENT", channel->name);
2583 if(!change.args[0].u.member->modes)
2586 reply("CSMSG_ALREADY_DOWN", channel->name);
2590 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2591 modcmd_chanmode_announce(&change);
2595 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)
2597 struct userData *cList;
2599 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2601 if(IsSuspended(cList->channel)
2602 || IsUserSuspended(cList)
2603 || !GetUserMode(cList->channel->channel, user))
2606 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2612 static CHANSERV_FUNC(cmd_upall)
2614 return cmd_all(CSFUNC_ARGS, cmd_up);
2617 static CHANSERV_FUNC(cmd_downall)
2619 return cmd_all(CSFUNC_ARGS, cmd_down);
2622 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2623 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2626 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)
2628 unsigned int ii, valid;
2629 struct userNode *victim;
2630 struct mod_chanmode *change;
2632 change = mod_chanmode_alloc(argc - 1);
2634 for(ii=valid=0; ++ii < argc; )
2636 if(!(victim = GetUserH(argv[ii])))
2638 change->args[valid].mode = mode;
2639 change->args[valid].u.member = GetUserMode(channel, victim);
2640 if(!change->args[valid].u.member)
2642 if(validate && !validate(user, channel, victim))
2647 change->argc = valid;
2648 if(valid < (argc-1))
2649 reply("CSMSG_PROCESS_FAILED");
2652 modcmd_chanmode_announce(change);
2653 reply(action, channel->name);
2655 mod_chanmode_free(change);
2659 static CHANSERV_FUNC(cmd_op)
2661 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2664 static CHANSERV_FUNC(cmd_deop)
2666 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2669 static CHANSERV_FUNC(cmd_voice)
2671 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2674 static CHANSERV_FUNC(cmd_devoice)
2676 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2680 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
2686 for(ii=0; ii<channel->members.used; ii++)
2688 struct modeNode *mn = channel->members.list[ii];
2690 if(IsService(mn->user))
2693 if(!user_matches_glob(mn->user, ban, 1))
2696 if(protect_user(mn->user, user, channel->channel_info))
2700 victims[(*victimCount)++] = mn;
2706 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2708 struct userNode *victim;
2709 struct modeNode **victims;
2710 unsigned int offset, n, victimCount, duration = 0;
2711 char *reason = "Bye.", *ban, *name;
2712 char interval[INTERVALLEN];
2714 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2715 REQUIRE_PARAMS(offset);
2718 reason = unsplit_string(argv + offset, argc - offset, NULL);
2719 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2721 /* Truncate the reason to a length of TOPICLEN, as
2722 the ircd does; however, leave room for an ellipsis
2723 and the kicker's nick. */
2724 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2728 if((victim = GetUserH(argv[1])))
2730 victims = alloca(sizeof(victims[0]));
2731 victims[0] = GetUserMode(channel, victim);
2732 /* XXX: The comparison with ACTION_KICK is just because all
2733 * other actions can work on users outside the channel, and we
2734 * want to allow those (e.g. unbans) in that case. If we add
2735 * some other ejection action for in-channel users, change
2737 victimCount = victims[0] ? 1 : 0;
2739 if(IsService(victim))
2741 reply("MSG_SERVICE_IMMUNE", victim->nick);
2745 if((action == ACTION_KICK) && !victimCount)
2747 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2751 if(protect_user(victim, user, channel->channel_info))
2753 reply("CSMSG_USER_PROTECTED", victim->nick);
2757 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2758 name = victim->nick;
2762 if(!is_ircmask(argv[1]))
2764 reply("MSG_NICK_UNKNOWN", argv[1]);
2768 victims = alloca(sizeof(victims[0]) * channel->members.used);
2770 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2772 reply("CSMSG_MASK_PROTECTED", argv[1]);
2776 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2778 reply("CSMSG_LAME_MASK", argv[1]);
2782 if((action == ACTION_KICK) && (victimCount == 0))
2784 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2788 name = ban = strdup(argv[1]);
2791 /* Truncate the ban in place if necessary; we must ensure
2792 that 'ban' is a valid ban mask before sanitizing it. */
2793 sanitize_ircmask(ban);
2795 if(action & ACTION_ADD_BAN)
2797 struct banData *bData, *next;
2799 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2801 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2806 if(action & ACTION_ADD_TIMED_BAN)
2808 duration = ParseInterval(argv[2]);
2812 reply("CSMSG_DURATION_TOO_LOW");
2816 else if(duration > (86400 * 365 * 2))
2818 reply("CSMSG_DURATION_TOO_HIGH");
2824 for(bData = channel->channel_info->bans; bData; bData = next)
2826 if(match_ircglobs(bData->mask, ban))
2828 int exact = !irccasecmp(bData->mask, ban);
2830 /* The ban is redundant; there is already a ban
2831 with the same effect in place. */
2835 free(bData->reason);
2836 bData->reason = strdup(reason);
2837 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2839 reply("CSMSG_REASON_CHANGE", ban);
2843 if(exact && bData->expires)
2847 /* If the ban matches an existing one exactly,
2848 extend the expiration time if the provided
2849 duration is longer. */
2850 if(duration && ((time_t)(now + duration) > bData->expires))
2852 bData->expires = now + duration;
2863 /* Delete the expiration timeq entry and
2864 requeue if necessary. */
2865 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2868 timeq_add(bData->expires, expire_ban, bData);
2872 /* automated kickban */
2875 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2877 reply("CSMSG_BAN_ADDED", name, channel->name);
2883 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2890 if(match_ircglobs(ban, bData->mask))
2892 /* The ban we are adding makes previously existing
2893 bans redundant; silently remove them. */
2894 del_channel_ban(bData);
2898 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);
2900 name = ban = strdup(bData->mask);
2904 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2906 extern const char *hidden_host_suffix;
2907 const char *old_name = chanserv_conf.old_ban_names->list[n];
2909 unsigned int l1, l2;
2912 l2 = strlen(old_name);
2915 if(irccasecmp(ban + l1 - l2, old_name))
2917 new_mask = malloc(MAXLEN);
2918 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2920 name = ban = new_mask;
2925 if(action & ACTION_BAN)
2927 unsigned int exists;
2928 struct mod_chanmode *change;
2930 if(channel->banlist.used >= MAXBANS)
2933 reply("CSMSG_BANLIST_FULL", channel->name);
2938 exists = ChannelBanExists(channel, ban);
2939 change = mod_chanmode_alloc(victimCount + 1);
2940 for(n = 0; n < victimCount; ++n)
2942 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2943 change->args[n].u.member = victims[n];
2947 change->args[n].mode = MODE_BAN;
2948 change->args[n++].u.hostmask = ban;
2952 modcmd_chanmode_announce(change);
2954 mod_chanmode_announce(chanserv, channel, change);
2955 mod_chanmode_free(change);
2957 if(exists && (action == ACTION_BAN))
2960 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2966 if(action & ACTION_KICK)
2968 char kick_reason[MAXLEN];
2969 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2971 for(n = 0; n < victimCount; n++)
2972 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2977 /* No response, since it was automated. */
2979 else if(action & ACTION_ADD_BAN)
2982 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2984 reply("CSMSG_BAN_ADDED", name, channel->name);
2986 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2987 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2988 else if(action & ACTION_BAN)
2989 reply("CSMSG_BAN_DONE", name, channel->name);
2990 else if(action & ACTION_KICK && victimCount)
2991 reply("CSMSG_KICK_DONE", name, channel->name);
2997 static CHANSERV_FUNC(cmd_kickban)
2999 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3002 static CHANSERV_FUNC(cmd_kick)
3004 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3007 static CHANSERV_FUNC(cmd_ban)
3009 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3012 static CHANSERV_FUNC(cmd_addban)
3014 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3017 static CHANSERV_FUNC(cmd_addtimedban)
3019 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3022 static struct mod_chanmode *
3023 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3025 struct mod_chanmode *change;
3026 unsigned char *match;
3027 unsigned int ii, count;
3029 match = alloca(bans->used);
3032 for(ii = count = 0; ii < bans->used; ++ii)
3034 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3041 for(ii = count = 0; ii < bans->used; ++ii)
3043 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3050 change = mod_chanmode_alloc(count);
3051 for(ii = count = 0; ii < bans->used; ++ii)
3055 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3056 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3058 assert(count == change->argc);
3063 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3065 struct userNode *actee;
3071 /* may want to allow a comma delimited list of users... */
3072 if(!(actee = GetUserH(argv[1])))
3074 if(!is_ircmask(argv[1]))
3076 reply("MSG_NICK_UNKNOWN", argv[1]);
3080 mask = strdup(argv[1]);
3083 /* We don't sanitize the mask here because ircu
3085 if(action & ACTION_UNBAN)
3087 struct mod_chanmode *change;
3088 change = find_matching_bans(&channel->banlist, actee, mask);
3093 modcmd_chanmode_announce(change);
3094 for(ii = 0; ii < change->argc; ++ii)
3095 free((char*)change->args[ii].u.hostmask);
3096 mod_chanmode_free(change);
3101 if(action & ACTION_DEL_BAN)
3103 struct banData *ban, *next;
3105 ban = channel->channel_info->bans;
3109 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3112 for( ; ban && !match_ircglobs(mask, ban->mask);
3117 del_channel_ban(ban);
3124 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3126 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3132 static CHANSERV_FUNC(cmd_unban)
3134 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3137 static CHANSERV_FUNC(cmd_delban)
3139 /* it doesn't necessarily have to remove the channel ban - may want
3140 to make that an option. */
3141 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3144 static CHANSERV_FUNC(cmd_unbanme)
3146 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3147 long flags = ACTION_UNBAN;
3149 /* remove permanent bans if the user has the proper access. */
3150 if(uData->access >= UL_MASTER)
3151 flags |= ACTION_DEL_BAN;
3153 argv[1] = user->nick;
3154 return unban_user(user, channel, 2, argv, cmd, flags);
3157 static CHANSERV_FUNC(cmd_unbanall)
3159 struct mod_chanmode *change;
3162 if(!channel->banlist.used)
3164 reply("CSMSG_NO_BANS", channel->name);
3168 change = mod_chanmode_alloc(channel->banlist.used);
3169 for(ii=0; ii<channel->banlist.used; ii++)
3171 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3172 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3174 modcmd_chanmode_announce(change);
3175 for(ii = 0; ii < change->argc; ++ii)
3176 free((char*)change->args[ii].u.hostmask);
3177 mod_chanmode_free(change);
3178 reply("CSMSG_BANS_REMOVED", channel->name);
3182 static CHANSERV_FUNC(cmd_open)
3184 struct mod_chanmode *change;
3187 change = find_matching_bans(&channel->banlist, user, NULL);
3189 change = mod_chanmode_alloc(0);
3190 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3191 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3192 && channel->channel_info->modes.modes_set)
3193 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3194 modcmd_chanmode_announce(change);
3195 reply("CSMSG_CHANNEL_OPENED", channel->name);
3196 for(ii = 0; ii < change->argc; ++ii)
3197 free((char*)change->args[ii].u.hostmask);
3198 mod_chanmode_free(change);
3202 static CHANSERV_FUNC(cmd_myaccess)
3204 static struct string_buffer sbuf;
3205 struct handle_info *target_handle;
3206 struct userData *uData;
3209 target_handle = user->handle_info;
3210 else if(!IsHelping(user))
3212 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3215 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3218 if(!target_handle->channels)
3220 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3224 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3225 for(uData = target_handle->channels; uData; uData = uData->u_next)
3227 struct chanData *cData = uData->channel;
3229 if(uData->access > UL_OWNER)
3231 if(IsProtected(cData)
3232 && (target_handle != user->handle_info)
3233 && !GetTrueChannelAccess(cData, user->handle_info))
3236 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3237 if(uData->flags != USER_AUTO_OP)
3238 string_buffer_append(&sbuf, ',');
3239 if(IsUserSuspended(uData))
3240 string_buffer_append(&sbuf, 's');
3241 if(IsUserAutoOp(uData))
3243 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3244 string_buffer_append(&sbuf, 'o');
3245 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3246 string_buffer_append(&sbuf, 'v');
3248 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3249 string_buffer_append(&sbuf, 'i');
3251 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3253 string_buffer_append_string(&sbuf, ")]");
3254 string_buffer_append(&sbuf, '\0');
3255 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3261 static CHANSERV_FUNC(cmd_access)
3263 struct userNode *target;
3264 struct handle_info *target_handle;
3265 struct userData *uData;
3267 char prefix[MAXLEN];
3272 target_handle = target->handle_info;
3274 else if((target = GetUserH(argv[1])))
3276 target_handle = target->handle_info;
3278 else if(argv[1][0] == '*')
3280 if(!(target_handle = get_handle_info(argv[1]+1)))
3282 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3288 reply("MSG_NICK_UNKNOWN", argv[1]);
3292 assert(target || target_handle);
3294 if(target == chanserv)
3296 reply("CSMSG_IS_CHANSERV");
3304 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3309 reply("MSG_USER_AUTHENTICATE", target->nick);
3312 reply("MSG_AUTHENTICATE");
3318 const char *epithet = NULL, *type = NULL;
3321 epithet = chanserv_conf.irc_operator_epithet;
3324 else if(IsNetworkHelper(target))
3326 epithet = chanserv_conf.network_helper_epithet;
3327 type = "network helper";
3329 else if(IsSupportHelper(target))
3331 epithet = chanserv_conf.support_helper_epithet;
3332 type = "support helper";
3336 if(target_handle->epithet)
3337 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3339 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3341 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3345 sprintf(prefix, "%s", target_handle->handle);
3348 if(!channel->channel_info)
3350 reply("CSMSG_NOT_REGISTERED", channel->name);
3354 helping = HANDLE_FLAGGED(target_handle, HELPING)
3355 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3356 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3358 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3359 /* To prevent possible information leaks, only show infolines
3360 * if the requestor is in the channel or it's their own
3362 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3364 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3366 /* Likewise, only say it's suspended if the user has active
3367 * access in that channel or it's their own entry. */
3368 if(IsUserSuspended(uData)
3369 && (GetChannelUser(channel->channel_info, user->handle_info)
3370 || (user->handle_info == uData->handle)))
3372 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3377 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3384 zoot_list(struct listData *list)
3386 struct userData *uData;
3387 unsigned int start, curr, highest, lowest;
3388 struct helpfile_table tmp_table;
3389 const char **temp, *msg;
3391 if(list->table.length == 1)
3394 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3396 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3397 msg = user_find_message(list->user, "MSG_NONE");
3398 send_message_type(4, list->user, list->bot, " %s", msg);
3400 tmp_table.width = list->table.width;
3401 tmp_table.flags = list->table.flags;
3402 list->table.contents[0][0] = " ";
3403 highest = list->highest;
3404 if(list->lowest != 0)
3405 lowest = list->lowest;
3406 else if(highest < 100)
3409 lowest = highest - 100;
3410 for(start = curr = 1; curr < list->table.length; )
3412 uData = list->users[curr-1];
3413 list->table.contents[curr++][0] = " ";
3414 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3417 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3419 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3420 temp = list->table.contents[--start];
3421 list->table.contents[start] = list->table.contents[0];
3422 tmp_table.contents = list->table.contents + start;
3423 tmp_table.length = curr - start;
3424 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3425 list->table.contents[start] = temp;
3427 highest = lowest - 1;
3428 lowest = (highest < 100) ? 0 : (highest - 99);
3434 def_list(struct listData *list)
3438 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3440 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3441 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3442 if(list->table.length == 1)
3444 msg = user_find_message(list->user, "MSG_NONE");
3445 send_message_type(4, list->user, list->bot, " %s", msg);
3450 userData_access_comp(const void *arg_a, const void *arg_b)
3452 const struct userData *a = *(struct userData**)arg_a;
3453 const struct userData *b = *(struct userData**)arg_b;
3455 if(a->access != b->access)
3456 res = b->access - a->access;
3458 res = irccasecmp(a->handle->handle, b->handle->handle);
3463 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3465 void (*send_list)(struct listData *);
3466 struct userData *uData;
3467 struct listData lData;
3468 unsigned int matches;
3472 lData.bot = cmd->parent->bot;
3473 lData.channel = channel;
3474 lData.lowest = lowest;
3475 lData.highest = highest;
3476 lData.search = (argc > 1) ? argv[1] : NULL;
3477 send_list = def_list;
3478 (void)zoot_list; /* since it doesn't show user levels */
3480 if(user->handle_info)
3482 switch(user->handle_info->userlist_style)
3484 case HI_STYLE_DEF: send_list = def_list; break;
3485 case HI_STYLE_ZOOT: send_list = def_list; break;
3489 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3491 for(uData = channel->channel_info->users; uData; uData = uData->next)
3493 if((uData->access < lowest)
3494 || (uData->access > highest)
3495 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3497 lData.users[matches++] = uData;
3499 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3501 lData.table.length = matches+1;
3502 lData.table.width = 4;
3503 lData.table.flags = TABLE_NO_FREE;
3504 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3505 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3506 lData.table.contents[0] = ary;
3509 ary[2] = "Last Seen";
3511 for(matches = 1; matches < lData.table.length; ++matches)
3513 struct userData *uData = lData.users[matches-1];
3514 char seen[INTERVALLEN];
3516 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3517 lData.table.contents[matches] = ary;
3518 ary[0] = strtab(uData->access);
3519 ary[1] = uData->handle->handle;
3522 else if(!uData->seen)
3525 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3526 ary[2] = strdup(ary[2]);
3527 if(IsUserSuspended(uData))
3528 ary[3] = "Suspended";
3529 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3530 ary[3] = "Vacation";
3535 for(matches = 1; matches < lData.table.length; ++matches)
3537 free((char*)lData.table.contents[matches][2]);
3538 free(lData.table.contents[matches]);
3540 free(lData.table.contents[0]);
3541 free(lData.table.contents);
3545 static CHANSERV_FUNC(cmd_users)
3547 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3550 static CHANSERV_FUNC(cmd_wlist)
3552 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3555 static CHANSERV_FUNC(cmd_clist)
3557 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3560 static CHANSERV_FUNC(cmd_mlist)
3562 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3565 static CHANSERV_FUNC(cmd_olist)
3567 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3570 static CHANSERV_FUNC(cmd_plist)
3572 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3575 static CHANSERV_FUNC(cmd_bans)
3577 struct helpfile_table tbl;
3578 unsigned int matches = 0, timed = 0, ii;
3579 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3580 const char *msg_never, *triggered, *expires;
3581 struct banData *ban, **bans;
3588 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3590 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3592 if(search && !match_ircglobs(search, ban->mask))
3594 bans[matches++] = ban;
3599 tbl.length = matches + 1;
3600 tbl.width = 4 + timed;
3602 tbl.flags = TABLE_NO_FREE;
3603 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3604 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3605 tbl.contents[0][0] = "Mask";
3606 tbl.contents[0][1] = "Set By";
3607 tbl.contents[0][2] = "Triggered";
3610 tbl.contents[0][3] = "Expires";
3611 tbl.contents[0][4] = "Reason";
3614 tbl.contents[0][3] = "Reason";
3617 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3619 free(tbl.contents[0]);
3624 msg_never = user_find_message(user, "MSG_NEVER");
3625 for(ii = 0; ii < matches; )
3631 else if(ban->expires)
3632 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3634 expires = msg_never;
3637 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3639 triggered = msg_never;
3641 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3642 tbl.contents[ii][0] = ban->mask;
3643 tbl.contents[ii][1] = ban->owner;
3644 tbl.contents[ii][2] = strdup(triggered);
3647 tbl.contents[ii][3] = strdup(expires);
3648 tbl.contents[ii][4] = ban->reason;
3651 tbl.contents[ii][3] = ban->reason;
3653 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3654 reply("MSG_MATCH_COUNT", matches);
3655 for(ii = 1; ii < tbl.length; ++ii)
3657 free((char*)tbl.contents[ii][2]);
3659 free((char*)tbl.contents[ii][3]);
3660 free(tbl.contents[ii]);
3662 free(tbl.contents[0]);
3668 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3670 struct chanData *cData = channel->channel_info;
3671 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3673 if(cData->topic_mask)
3674 return !match_ircglob(new_topic, cData->topic_mask);
3675 else if(cData->topic)
3676 return irccasecmp(new_topic, cData->topic);
3681 static CHANSERV_FUNC(cmd_topic)
3683 struct chanData *cData;
3686 cData = channel->channel_info;
3691 SetChannelTopic(channel, chanserv, cData->topic, 1);
3692 reply("CSMSG_TOPIC_SET", cData->topic);
3696 reply("CSMSG_NO_TOPIC", channel->name);
3700 topic = unsplit_string(argv + 1, argc - 1, NULL);
3701 /* If they say "!topic *", use an empty topic. */
3702 if((topic[0] == '*') && (topic[1] == 0))
3704 if(bad_topic(channel, user, topic))
3706 char *topic_mask = cData->topic_mask;
3709 char new_topic[TOPICLEN+1], tchar;
3710 int pos=0, starpos=-1, dpos=0, len;
3712 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3719 len = strlen(topic);
3720 if((dpos + len) > TOPICLEN)
3721 len = TOPICLEN + 1 - dpos;
3722 memcpy(new_topic+dpos, topic, len);
3726 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3727 default: new_topic[dpos++] = tchar; break;
3730 if((dpos > TOPICLEN) || tchar)
3733 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3734 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3737 new_topic[dpos] = 0;
3738 SetChannelTopic(channel, chanserv, new_topic, 1);
3740 reply("CSMSG_TOPIC_LOCKED", channel->name);
3745 SetChannelTopic(channel, chanserv, topic, 1);
3747 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3749 /* Grab the topic and save it as the default topic. */
3751 cData->topic = strdup(channel->topic);
3757 static CHANSERV_FUNC(cmd_mode)
3759 struct mod_chanmode *change;
3763 change = &channel->channel_info->modes;
3764 if(change->modes_set || change->modes_clear) {
3765 modcmd_chanmode_announce(change);
3766 reply("CSMSG_DEFAULTED_MODES", channel->name);
3768 reply("CSMSG_NO_MODES", channel->name);
3772 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3775 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3779 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3780 && mode_lock_violated(&channel->channel_info->modes, change))
3783 mod_chanmode_format(&channel->channel_info->modes, modes);
3784 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3788 modcmd_chanmode_announce(change);
3789 mod_chanmode_free(change);
3790 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3794 static CHANSERV_FUNC(cmd_invite)
3796 struct userData *uData;
3797 struct userNode *invite;
3799 uData = GetChannelUser(channel->channel_info, user->handle_info);
3803 if(!(invite = GetUserH(argv[1])))
3805 reply("MSG_NICK_UNKNOWN", argv[1]);
3812 if(GetUserMode(channel, invite))
3814 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3822 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3823 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3826 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3828 irc_invite(chanserv, invite, channel);
3830 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3835 static CHANSERV_FUNC(cmd_inviteme)
3837 if(GetUserMode(channel, user))
3839 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3842 if(channel->channel_info
3843 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3845 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3848 irc_invite(cmd->parent->bot, user, channel);
3853 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3856 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3858 /* We display things based on two dimensions:
3859 * - Issue time: present or absent
3860 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3861 * (in order of precedence, so something both expired and revoked
3862 * only counts as revoked)
3864 combo = (suspended->issued ? 4 : 0)
3865 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3867 case 0: /* no issue time, indefinite expiration */
3868 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3870 case 1: /* no issue time, expires in future */
3871 intervalString(buf1, suspended->expires-now, user->handle_info);
3872 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3874 case 2: /* no issue time, expired */
3875 intervalString(buf1, now-suspended->expires, user->handle_info);
3876 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3878 case 3: /* no issue time, revoked */
3879 intervalString(buf1, now-suspended->revoked, user->handle_info);
3880 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3882 case 4: /* issue time set, indefinite expiration */
3883 intervalString(buf1, now-suspended->issued, user->handle_info);
3884 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3886 case 5: /* issue time set, expires in future */
3887 intervalString(buf1, now-suspended->issued, user->handle_info);
3888 intervalString(buf2, suspended->expires-now, user->handle_info);
3889 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3891 case 6: /* issue time set, expired */
3892 intervalString(buf1, now-suspended->issued, user->handle_info);
3893 intervalString(buf2, now-suspended->expires, user->handle_info);
3894 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3896 case 7: /* issue time set, revoked */
3897 intervalString(buf1, now-suspended->issued, user->handle_info);
3898 intervalString(buf2, now-suspended->revoked, user->handle_info);
3899 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3902 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3907 static CHANSERV_FUNC(cmd_info)
3909 char modes[MAXLEN], buffer[INTERVALLEN];
3910 struct userData *uData, *owner;
3911 struct chanData *cData;
3912 struct do_not_register *dnr;
3917 cData = channel->channel_info;
3918 reply("CSMSG_CHANNEL_INFO", channel->name);
3920 uData = GetChannelUser(cData, user->handle_info);
3921 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3923 mod_chanmode_format(&cData->modes, modes);
3924 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3925 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3928 for(it = dict_first(cData->notes); it; it = iter_next(it))
3932 note = iter_data(it);
3933 if(!note_type_visible_to_user(cData, note->type, user))
3936 padding = PADLEN - 1 - strlen(iter_key(it));
3937 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3940 reply("CSMSG_CHANNEL_MAX", cData->max);
3941 for(owner = cData->users; owner; owner = owner->next)
3942 if(owner->access == UL_OWNER)
3943 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3944 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3945 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3946 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3947 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3949 privileged = IsStaff(user);
3950 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3951 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3953 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3954 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3956 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3958 struct suspended *suspended;
3959 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3960 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3961 show_suspension_info(cmd, user, suspended);
3963 else if(IsSuspended(cData))
3965 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3966 show_suspension_info(cmd, user, cData->suspended);
3971 static CHANSERV_FUNC(cmd_netinfo)
3973 extern time_t boot_time;
3974 extern unsigned long burst_length;
3975 char interval[INTERVALLEN];
3977 reply("CSMSG_NETWORK_INFO");
3978 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3979 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3980 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3981 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3982 reply("CSMSG_NETWORK_BANS", banCount);
3983 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3984 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3985 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3990 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3992 struct helpfile_table table;
3994 struct userNode *user;
3999 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4000 table.contents = alloca(list->used*sizeof(*table.contents));
4001 for(nn=0; nn<list->used; nn++)
4003 user = list->list[nn];
4004 if(user->modes & skip_flags)
4008 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4011 nick = alloca(strlen(user->nick)+3);
4012 sprintf(nick, "(%s)", user->nick);
4016 table.contents[table.length][0] = nick;
4019 table_send(chanserv, to->nick, 0, NULL, table);
4022 static CHANSERV_FUNC(cmd_ircops)
4024 reply("CSMSG_STAFF_OPERS");
4025 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4029 static CHANSERV_FUNC(cmd_helpers)
4031 reply("CSMSG_STAFF_HELPERS");
4032 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4036 static CHANSERV_FUNC(cmd_staff)
4038 reply("CSMSG_NETWORK_STAFF");
4039 cmd_ircops(CSFUNC_ARGS);
4040 cmd_helpers(CSFUNC_ARGS);
4044 static CHANSERV_FUNC(cmd_peek)
4046 struct modeNode *mn;
4047 char modes[MODELEN];
4049 struct helpfile_table table;
4051 irc_make_chanmode(channel, modes);
4053 reply("CSMSG_PEEK_INFO", channel->name);
4054 reply("CSMSG_PEEK_TOPIC", channel->topic);
4055 reply("CSMSG_PEEK_MODES", modes);
4056 reply("CSMSG_PEEK_USERS", channel->members.used);
4060 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4061 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4062 for(n = 0; n < channel->members.used; n++)
4064 mn = channel->members.list[n];
4065 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4067 table.contents[table.length] = alloca(sizeof(**table.contents));
4068 table.contents[table.length][0] = mn->user->nick;
4073 reply("CSMSG_PEEK_OPS");
4074 table_send(chanserv, user->nick, 0, NULL, table);
4077 reply("CSMSG_PEEK_NO_OPS");
4081 static MODCMD_FUNC(cmd_wipeinfo)
4083 struct handle_info *victim;
4084 struct userData *ud, *actor;
4087 actor = GetChannelUser(channel->channel_info, user->handle_info);
4088 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4090 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4092 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4095 if((ud->access >= actor->access) && (ud != actor))
4097 reply("MSG_USER_OUTRANKED", victim->handle);
4103 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4107 static CHANSERV_FUNC(cmd_resync)
4109 struct mod_chanmode *changes;
4110 struct chanData *cData = channel->channel_info;
4111 unsigned int ii, used;
4113 changes = mod_chanmode_alloc(channel->members.used * 2);
4114 for(ii = used = 0; ii < channel->members.used; ++ii)
4116 struct modeNode *mn = channel->members.list[ii];
4117 struct userData *uData;
4119 if(IsService(mn->user))
4122 uData = GetChannelAccess(cData, mn->user->handle_info);
4123 if(!cData->lvlOpts[lvlGiveOps]
4124 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4126 if(!(mn->modes & MODE_CHANOP))
4128 changes->args[used].mode = MODE_CHANOP;
4129 changes->args[used++].u.member = mn;
4132 else if(!cData->lvlOpts[lvlGiveVoice]
4133 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4135 if(mn->modes & MODE_CHANOP)
4137 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4138 changes->args[used++].u.member = mn;
4140 if(!(mn->modes & MODE_VOICE))
4142 changes->args[used].mode = MODE_VOICE;
4143 changes->args[used++].u.member = mn;
4150 changes->args[used].mode = MODE_REMOVE | mn->modes;
4151 changes->args[used++].u.member = mn;
4155 changes->argc = used;
4156 modcmd_chanmode_announce(changes);
4157 mod_chanmode_free(changes);
4158 reply("CSMSG_RESYNCED_USERS", channel->name);
4162 static CHANSERV_FUNC(cmd_seen)
4164 struct userData *uData;
4165 struct handle_info *handle;
4166 char seen[INTERVALLEN];
4170 if(!irccasecmp(argv[1], chanserv->nick))
4172 reply("CSMSG_IS_CHANSERV");
4176 if(!(handle = get_handle_info(argv[1])))
4178 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4182 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4184 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4189 reply("CSMSG_USER_PRESENT", handle->handle);
4190 else if(uData->seen)
4191 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4193 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4195 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4196 reply("CSMSG_USER_VACATION", handle->handle);
4201 static MODCMD_FUNC(cmd_names)
4203 struct userNode *targ;
4204 struct userData *targData;
4205 unsigned int ii, pos;
4208 for(ii=pos=0; ii<channel->members.used; ++ii)
4210 targ = channel->members.list[ii]->user;
4211 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4214 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4217 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4221 if(IsUserSuspended(targData))
4223 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4226 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4227 reply("CSMSG_END_NAMES", channel->name);
4232 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4234 switch(ntype->visible_type)
4236 case NOTE_VIS_ALL: return 1;
4237 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4238 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4243 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4245 struct userData *uData;
4247 switch(ntype->set_access_type)
4249 case NOTE_SET_CHANNEL_ACCESS:
4250 if(!user->handle_info)
4252 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4254 return uData->access >= ntype->set_access.min_ulevel;
4255 case NOTE_SET_CHANNEL_SETTER:
4256 return check_user_level(channel, user, lvlSetters, 1, 0);
4257 case NOTE_SET_PRIVILEGED: default:
4258 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4262 static CHANSERV_FUNC(cmd_note)
4264 struct chanData *cData;
4266 struct note_type *ntype;
4268 cData = channel->channel_info;
4271 reply("CSMSG_NOT_REGISTERED", channel->name);
4275 /* If no arguments, show all visible notes for the channel. */
4281 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4283 note = iter_data(it);
4284 if(!note_type_visible_to_user(cData, note->type, user))
4287 reply("CSMSG_NOTELIST_HEADER", channel->name);
4288 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4291 reply("CSMSG_NOTELIST_END", channel->name);
4293 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4295 /* If one argument, show the named note. */
4298 if((note = dict_find(cData->notes, argv[1], NULL))
4299 && note_type_visible_to_user(cData, note->type, user))
4301 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4303 else if((ntype = dict_find(note_types, argv[1], NULL))
4304 && note_type_visible_to_user(NULL, ntype, user))
4306 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4311 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4315 /* Assume they're trying to set a note. */
4319 ntype = dict_find(note_types, argv[1], NULL);
4322 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4325 else if(note_type_settable_by_user(channel, ntype, user))
4327 note_text = unsplit_string(argv+2, argc-2, NULL);
4328 if((note = dict_find(cData->notes, argv[1], NULL)))
4329 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4330 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4331 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4333 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4335 /* The note is viewable to staff only, so return 0
4336 to keep the invocation from getting logged (or
4337 regular users can see it in !events). */
4343 reply("CSMSG_NO_ACCESS");
4350 static CHANSERV_FUNC(cmd_delnote)
4355 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4356 || !note_type_settable_by_user(channel, note->type, user))
4358 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4361 dict_remove(channel->channel_info->notes, note->type->name);
4362 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4366 static CHANSERV_FUNC(cmd_events)
4368 struct logSearch discrim;
4369 struct logReport report;
4370 unsigned int matches, limit;
4372 limit = (argc > 1) ? atoi(argv[1]) : 10;
4373 if(limit < 1 || limit > 200)
4376 memset(&discrim, 0, sizeof(discrim));
4377 discrim.masks.bot = chanserv;
4378 discrim.masks.channel_name = channel->name;
4380 discrim.masks.command = argv[2];
4381 discrim.limit = limit;
4382 discrim.max_time = INT_MAX;
4383 discrim.severities = 1 << LOG_COMMAND;
4384 report.reporter = chanserv;
4386 reply("CSMSG_EVENT_SEARCH_RESULTS");
4387 matches = log_entry_search(&discrim, log_report_entry, &report);
4389 reply("MSG_MATCH_COUNT", matches);
4391 reply("MSG_NO_MATCHES");
4395 static CHANSERV_FUNC(cmd_say)
4401 msg = unsplit_string(argv + 1, argc - 1, NULL);
4402 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4404 else if(GetUserH(argv[1]))
4407 msg = unsplit_string(argv + 2, argc - 2, NULL);
4408 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4412 reply("MSG_NOT_TARGET_NAME");
4418 static CHANSERV_FUNC(cmd_emote)
4424 /* CTCP is so annoying. */
4425 msg = unsplit_string(argv + 1, argc - 1, NULL);
4426 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4428 else if(GetUserH(argv[1]))
4430 msg = unsplit_string(argv + 2, argc - 2, NULL);
4431 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4435 reply("MSG_NOT_TARGET_NAME");
4441 struct channelList *
4442 chanserv_support_channels(void)
4444 return &chanserv_conf.support_channels;
4447 static CHANSERV_FUNC(cmd_expire)
4449 int channel_count = registered_channels;
4450 expire_channels(NULL);
4451 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4456 chanserv_expire_suspension(void *data)
4458 struct suspended *suspended = data;
4459 struct chanNode *channel;
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 if(!IsOffChannel(suspended->cData))
4468 struct mod_chanmode change;
4469 mod_chanmode_init(&change);
4471 change.args[0].mode = MODE_CHANOP;
4472 change.args[0].u.member = AddChannelUser(chanserv, channel);
4473 mod_chanmode_announce(chanserv, channel, &change);
4477 static CHANSERV_FUNC(cmd_csuspend)
4479 struct suspended *suspended;
4480 char reason[MAXLEN];
4481 time_t expiry, duration;
4482 struct userData *uData;
4486 if(IsProtected(channel->channel_info))
4488 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4492 if(argv[1][0] == '!')
4494 else if(IsSuspended(channel->channel_info))
4496 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4497 show_suspension_info(cmd, user, channel->channel_info->suspended);
4501 if(!strcmp(argv[1], "0"))
4503 else if((duration = ParseInterval(argv[1])))
4504 expiry = now + duration;
4507 reply("MSG_INVALID_DURATION", argv[1]);
4511 unsplit_string(argv + 2, argc - 2, reason);
4513 suspended = calloc(1, sizeof(*suspended));
4514 suspended->revoked = 0;
4515 suspended->issued = now;
4516 suspended->suspender = strdup(user->handle_info->handle);
4517 suspended->expires = expiry;
4518 suspended->reason = strdup(reason);
4519 suspended->cData = channel->channel_info;
4520 suspended->previous = suspended->cData->suspended;
4521 suspended->cData->suspended = suspended;
4523 if(suspended->expires)
4524 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4526 if(IsSuspended(channel->channel_info))
4528 suspended->previous->revoked = now;
4529 if(suspended->previous->expires)
4530 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4531 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4532 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4536 /* Mark all users in channel as absent. */
4537 for(uData = channel->channel_info->users; uData; uData = uData->next)
4546 /* Mark the channel as suspended, then part. */
4547 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4548 DelChannelUser(chanserv, channel, suspended->reason, 0);
4549 reply("CSMSG_SUSPENDED", channel->name);
4550 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4551 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4556 static CHANSERV_FUNC(cmd_cunsuspend)
4558 struct suspended *suspended;
4559 char message[MAXLEN];
4561 if(!IsSuspended(channel->channel_info))
4563 reply("CSMSG_NOT_SUSPENDED", channel->name);
4567 suspended = channel->channel_info->suspended;
4569 /* Expire the suspension and join ChanServ to the channel. */
4570 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4571 chanserv_expire_suspension(suspended);
4572 reply("CSMSG_UNSUSPENDED", channel->name);
4573 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4574 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4578 typedef struct chanservSearch
4586 unsigned long flags;
4590 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4593 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4598 search = malloc(sizeof(struct chanservSearch));
4599 memset(search, 0, sizeof(*search));
4602 for(i = 0; i < argc; i++)
4604 /* Assume all criteria require arguments. */
4607 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4611 if(!irccasecmp(argv[i], "name"))
4612 search->name = argv[++i];
4613 else if(!irccasecmp(argv[i], "registrar"))
4614 search->registrar = argv[++i];
4615 else if(!irccasecmp(argv[i], "unvisited"))
4616 search->unvisited = ParseInterval(argv[++i]);
4617 else if(!irccasecmp(argv[i], "registered"))
4618 search->registered = ParseInterval(argv[++i]);
4619 else if(!irccasecmp(argv[i], "flags"))
4622 if(!irccasecmp(argv[i], "nodelete"))
4623 search->flags |= CHANNEL_NODELETE;
4624 else if(!irccasecmp(argv[i], "suspended"))
4625 search->flags |= CHANNEL_SUSPENDED;
4628 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4632 else if(!irccasecmp(argv[i], "limit"))
4633 search->limit = strtoul(argv[++i], NULL, 10);
4636 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4641 if(search->name && !strcmp(search->name, "*"))
4643 if(search->registrar && !strcmp(search->registrar, "*"))
4644 search->registrar = 0;
4653 chanserv_channel_match(struct chanData *channel, search_t search)
4655 const char *name = channel->channel->name;
4656 if((search->name && !match_ircglob(name, search->name)) ||
4657 (search->registrar && !channel->registrar) ||
4658 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4659 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4660 (search->registered && (now - channel->registered) > search->registered) ||
4661 (search->flags && ((search->flags & channel->flags) != search->flags)))
4668 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4670 struct chanData *channel;
4671 unsigned int matches = 0;
4673 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4675 if(!chanserv_channel_match(channel, search))
4685 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4690 search_print(struct chanData *channel, void *data)
4692 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4695 static CHANSERV_FUNC(cmd_search)
4698 unsigned int matches;
4699 channel_search_func action;
4703 if(!irccasecmp(argv[1], "count"))
4704 action = search_count;
4705 else if(!irccasecmp(argv[1], "print"))
4706 action = search_print;
4709 reply("CSMSG_ACTION_INVALID", argv[1]);
4713 search = chanserv_search_create(user, argc - 2, argv + 2);
4717 if(action == search_count)
4718 search->limit = INT_MAX;
4720 if(action == search_print)
4721 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4723 matches = chanserv_channel_search(search, action, user);
4726 reply("MSG_MATCH_COUNT", matches);
4728 reply("MSG_NO_MATCHES");
4734 static CHANSERV_FUNC(cmd_unvisited)
4736 struct chanData *cData;
4737 time_t interval = chanserv_conf.channel_expire_delay;
4738 char buffer[INTERVALLEN];
4739 unsigned int limit = 25, matches = 0;
4743 interval = ParseInterval(argv[1]);
4745 limit = atoi(argv[2]);
4748 intervalString(buffer, interval, user->handle_info);
4749 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4751 for(cData = channelList; cData && matches < limit; cData = cData->next)
4753 if((now - cData->visited) < interval)
4756 intervalString(buffer, now - cData->visited, user->handle_info);
4757 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4764 static MODCMD_FUNC(chan_opt_defaulttopic)
4770 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4772 reply("CSMSG_TOPIC_LOCKED", channel->name);
4776 topic = unsplit_string(argv+1, argc-1, NULL);
4778 free(channel->channel_info->topic);
4779 if(topic[0] == '*' && topic[1] == 0)
4781 topic = channel->channel_info->topic = NULL;
4785 topic = channel->channel_info->topic = strdup(topic);
4786 if(channel->channel_info->topic_mask
4787 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4788 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4790 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4793 if(channel->channel_info->topic)
4794 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4796 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4800 static MODCMD_FUNC(chan_opt_topicmask)
4804 struct chanData *cData = channel->channel_info;
4807 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4809 reply("CSMSG_TOPIC_LOCKED", channel->name);
4813 mask = unsplit_string(argv+1, argc-1, NULL);
4815 if(cData->topic_mask)
4816 free(cData->topic_mask);
4817 if(mask[0] == '*' && mask[1] == 0)
4819 cData->topic_mask = 0;
4823 cData->topic_mask = strdup(mask);
4825 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4826 else if(!match_ircglob(cData->topic, cData->topic_mask))
4827 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4831 if(channel->channel_info->topic_mask)
4832 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4834 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4838 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4842 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4846 if(greeting[0] == '*' && greeting[1] == 0)
4850 unsigned int length = strlen(greeting);
4851 if(length > chanserv_conf.greeting_length)
4853 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4856 *data = strdup(greeting);
4865 reply(name, user_find_message(user, "MSG_NONE"));
4869 static MODCMD_FUNC(chan_opt_greeting)
4871 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4874 static MODCMD_FUNC(chan_opt_usergreeting)
4876 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4879 static MODCMD_FUNC(chan_opt_modes)
4881 struct mod_chanmode *new_modes;
4882 char modes[MODELEN];
4886 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4888 reply("CSMSG_NO_ACCESS");
4891 if(argv[1][0] == '*' && argv[1][1] == 0)
4893 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4895 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4897 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4900 else if(new_modes->argc > 1)
4902 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4903 mod_chanmode_free(new_modes);
4908 channel->channel_info->modes = *new_modes;
4909 modcmd_chanmode_announce(new_modes);
4910 mod_chanmode_free(new_modes);
4914 mod_chanmode_format(&channel->channel_info->modes, modes);
4916 reply("CSMSG_SET_MODES", modes);
4918 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4922 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4924 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4926 struct chanData *cData = channel->channel_info;
4931 /* Set flag according to value. */
4932 if(enabled_string(argv[1]))
4934 cData->flags |= mask;
4937 else if(disabled_string(argv[1]))
4939 cData->flags &= ~mask;
4944 reply("MSG_INVALID_BINARY", argv[1]);
4950 /* Find current option value. */
4951 value = (cData->flags & mask) ? 1 : 0;
4955 reply(name, user_find_message(user, "MSG_ON"));
4957 reply(name, user_find_message(user, "MSG_OFF"));
4961 static MODCMD_FUNC(chan_opt_nodelete)
4963 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4965 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4969 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4972 static MODCMD_FUNC(chan_opt_dynlimit)
4974 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4977 static MODCMD_FUNC(chan_opt_offchannel)
4979 struct chanData *cData = channel->channel_info;
4984 /* Set flag according to value. */
4985 if(enabled_string(argv[1]))
4987 if(!IsOffChannel(cData))
4988 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4989 cData->flags |= CHANNEL_OFFCHANNEL;
4992 else if(disabled_string(argv[1]))
4994 if(IsOffChannel(cData))
4996 struct mod_chanmode change;
4997 mod_chanmode_init(&change);
4999 change.args[0].mode = MODE_CHANOP;
5000 change.args[0].u.member = AddChannelUser(chanserv, channel);
5001 mod_chanmode_announce(chanserv, channel, &change);
5003 cData->flags &= ~CHANNEL_OFFCHANNEL;
5008 reply("MSG_INVALID_BINARY", argv[1]);
5014 /* Find current option value. */
5015 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5019 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5021 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5025 static MODCMD_FUNC(chan_opt_defaults)
5027 struct userData *uData;
5028 struct chanData *cData;
5029 const char *confirm;
5030 enum levelOption lvlOpt;
5031 enum charOption chOpt;
5033 cData = channel->channel_info;
5034 uData = GetChannelUser(cData, user->handle_info);
5035 if(!uData || (uData->access < UL_OWNER))
5037 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5040 confirm = make_confirmation_string(uData);
5041 if((argc < 2) || strcmp(argv[1], confirm))
5043 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5046 cData->flags = CHANNEL_DEFAULT_FLAGS;
5047 cData->modes = chanserv_conf.default_modes;
5048 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5049 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5050 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5051 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5052 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5057 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5059 struct chanData *cData = channel->channel_info;
5060 struct userData *uData;
5061 unsigned short value;
5065 if(!check_user_level(channel, user, option, 1, 1))
5067 reply("CSMSG_CANNOT_SET");
5070 value = user_level_from_name(argv[1], UL_OWNER+1);
5071 if(!value && strcmp(argv[1], "0"))
5073 reply("CSMSG_INVALID_ACCESS", argv[1]);
5076 uData = GetChannelUser(cData, user->handle_info);
5077 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5079 reply("CSMSG_BAD_SETLEVEL");
5085 if(value > cData->lvlOpts[lvlGiveOps])
5087 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5092 if(value < cData->lvlOpts[lvlGiveVoice])
5094 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5099 /* This test only applies to owners, since non-owners
5100 * trying to set an option to above their level get caught
5101 * by the CSMSG_BAD_SETLEVEL test above.
5103 if(value > uData->access)
5105 reply("CSMSG_BAD_SETTERS");
5112 cData->lvlOpts[option] = value;
5114 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5118 static MODCMD_FUNC(chan_opt_enfops)
5120 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5123 static MODCMD_FUNC(chan_opt_giveops)
5125 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5128 static MODCMD_FUNC(chan_opt_enfmodes)
5130 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5133 static MODCMD_FUNC(chan_opt_enftopic)
5135 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5138 static MODCMD_FUNC(chan_opt_pubcmd)
5140 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5143 static MODCMD_FUNC(chan_opt_setters)
5145 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5148 static MODCMD_FUNC(chan_opt_ctcpusers)
5150 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5153 static MODCMD_FUNC(chan_opt_userinfo)
5155 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5158 static MODCMD_FUNC(chan_opt_givevoice)
5160 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5163 static MODCMD_FUNC(chan_opt_topicsnarf)
5165 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5168 static MODCMD_FUNC(chan_opt_inviteme)
5170 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5174 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5176 struct chanData *cData = channel->channel_info;
5177 int count = charOptions[option].count, index;
5181 index = atoi(argv[1]);
5183 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5185 reply("CSMSG_INVALID_NUMERIC", index);
5186 /* Show possible values. */
5187 for(index = 0; index < count; index++)
5188 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5192 cData->chOpts[option] = charOptions[option].values[index].value;
5196 /* Find current option value. */
5199 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5203 /* Somehow, the option value is corrupt; reset it to the default. */
5204 cData->chOpts[option] = charOptions[option].default_value;
5209 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5213 static MODCMD_FUNC(chan_opt_protect)
5215 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5218 static MODCMD_FUNC(chan_opt_toys)
5220 return channel_multiple_option(chToys, CSFUNC_ARGS);
5223 static MODCMD_FUNC(chan_opt_ctcpreaction)
5225 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5228 static MODCMD_FUNC(chan_opt_topicrefresh)
5230 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5233 static struct svccmd_list set_shows_list;
5236 handle_svccmd_unbind(struct svccmd *target) {
5238 for(ii=0; ii<set_shows_list.used; ++ii)
5239 if(target == set_shows_list.list[ii])
5240 set_shows_list.used = 0;
5243 static CHANSERV_FUNC(cmd_set)
5245 struct svccmd *subcmd;
5249 /* Check if we need to (re-)initialize set_shows_list. */
5250 if(!set_shows_list.used)
5252 if(!set_shows_list.size)
5254 set_shows_list.size = chanserv_conf.set_shows->used;
5255 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5257 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5259 const char *name = chanserv_conf.set_shows->list[ii];
5260 sprintf(buf, "%s %s", argv[0], name);
5261 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5264 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5267 svccmd_list_append(&set_shows_list, subcmd);
5273 reply("CSMSG_CHANNEL_OPTIONS");
5274 for(ii = 0; ii < set_shows_list.used; ii++)
5276 subcmd = set_shows_list.list[ii];
5277 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5282 sprintf(buf, "%s %s", argv[0], argv[1]);
5283 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5286 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5289 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5291 reply("CSMSG_NO_ACCESS");
5295 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5299 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5301 struct userData *uData;
5303 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5306 reply("CSMSG_NOT_USER", channel->name);
5312 /* Just show current option value. */
5314 else if(enabled_string(argv[1]))
5316 uData->flags |= mask;
5318 else if(disabled_string(argv[1]))
5320 uData->flags &= ~mask;
5324 reply("MSG_INVALID_BINARY", argv[1]);
5328 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5332 static MODCMD_FUNC(user_opt_noautoop)
5334 struct userData *uData;
5336 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5339 reply("CSMSG_NOT_USER", channel->name);
5342 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5343 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5345 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5348 static MODCMD_FUNC(user_opt_autoinvite)
5350 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5353 static MODCMD_FUNC(user_opt_info)
5355 struct userData *uData;
5358 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5362 /* If they got past the command restrictions (which require access)
5363 * but fail this test, we have some fool with security override on.
5365 reply("CSMSG_NOT_USER", channel->name);
5372 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5373 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5375 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5378 bp = strcspn(infoline, "\001");
5381 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5386 if(infoline[0] == '*' && infoline[1] == 0)
5389 uData->info = strdup(infoline);
5392 reply("CSMSG_USET_INFO", uData->info);
5394 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5398 struct svccmd_list uset_shows_list;
5400 static CHANSERV_FUNC(cmd_uset)
5402 struct svccmd *subcmd;
5406 /* Check if we need to (re-)initialize uset_shows_list. */
5407 if(!uset_shows_list.used)
5411 "NoAutoOp", "AutoInvite", "Info"
5414 if(!uset_shows_list.size)
5416 uset_shows_list.size = ArrayLength(options);
5417 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5419 for(ii = 0; ii < ArrayLength(options); ii++)
5421 const char *name = options[ii];
5422 sprintf(buf, "%s %s", argv[0], name);
5423 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5426 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5429 svccmd_list_append(&uset_shows_list, subcmd);
5435 /* Do this so options are presented in a consistent order. */
5436 reply("CSMSG_USER_OPTIONS");
5437 for(ii = 0; ii < uset_shows_list.used; ii++)
5438 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5442 sprintf(buf, "%s %s", argv[0], argv[1]);
5443 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5446 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5450 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5453 static CHANSERV_FUNC(cmd_giveownership)
5455 struct handle_info *new_owner_hi;
5456 struct userData *new_owner, *curr_user;
5457 struct chanData *cData = channel->channel_info;
5458 struct do_not_register *dnr;
5460 unsigned short co_access;
5461 char reason[MAXLEN];
5464 curr_user = GetChannelAccess(cData, user->handle_info);
5465 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5466 if(!curr_user || (curr_user->access != UL_OWNER))
5468 struct userData *owner = NULL;
5469 for(curr_user = channel->channel_info->users;
5471 curr_user = curr_user->next)
5473 if(curr_user->access != UL_OWNER)
5477 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5484 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5486 char delay[INTERVALLEN];
5487 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5488 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5491 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5493 if(new_owner_hi == user->handle_info)
5495 reply("CSMSG_NO_TRANSFER_SELF");
5498 new_owner = GetChannelAccess(cData, new_owner_hi);
5503 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5507 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5511 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5513 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5516 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5517 if(!IsHelping(user))
5518 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5520 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5523 if(new_owner->access >= UL_COOWNER)
5524 co_access = new_owner->access;
5526 co_access = UL_COOWNER;
5527 new_owner->access = UL_OWNER;
5529 curr_user->access = co_access;
5530 cData->ownerTransfer = now;
5531 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5532 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5533 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5537 static CHANSERV_FUNC(cmd_suspend)
5539 struct handle_info *hi;
5540 struct userData *self, *target;
5543 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5544 self = GetChannelUser(channel->channel_info, user->handle_info);
5545 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5547 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5550 if(target->access >= self->access)
5552 reply("MSG_USER_OUTRANKED", hi->handle);
5555 if(target->flags & USER_SUSPENDED)
5557 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5562 target->present = 0;
5565 target->flags |= USER_SUSPENDED;
5566 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5570 static CHANSERV_FUNC(cmd_unsuspend)
5572 struct handle_info *hi;
5573 struct userData *self, *target;
5576 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5577 self = GetChannelUser(channel->channel_info, user->handle_info);
5578 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5580 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5583 if(target->access >= self->access)
5585 reply("MSG_USER_OUTRANKED", hi->handle);
5588 if(!(target->flags & USER_SUSPENDED))
5590 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5593 target->flags &= ~USER_SUSPENDED;
5594 scan_user_presence(target, NULL);
5595 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5599 static MODCMD_FUNC(cmd_deleteme)
5601 struct handle_info *hi;
5602 struct userData *target;
5603 const char *confirm_string;
5604 unsigned short access;
5607 hi = user->handle_info;
5608 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5610 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5613 if(target->access == UL_OWNER)
5615 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5618 confirm_string = make_confirmation_string(target);
5619 if((argc < 2) || strcmp(argv[1], confirm_string))
5621 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5624 access = target->access;
5625 channel_name = strdup(channel->name);
5626 del_channel_user(target, 1);
5627 reply("CSMSG_DELETED_YOU", access, channel_name);
5633 chanserv_refresh_topics(UNUSED_ARG(void *data))
5635 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5636 struct chanData *cData;
5639 for(cData = channelList; cData; cData = cData->next)
5641 if(IsSuspended(cData))
5643 opt = cData->chOpts[chTopicRefresh];
5646 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5649 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5650 cData->last_refresh = refresh_num;
5652 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5655 static CHANSERV_FUNC(cmd_unf)
5659 char response[MAXLEN];
5660 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5661 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5662 irc_privmsg(cmd->parent->bot, channel->name, response);
5665 reply("CSMSG_UNF_RESPONSE");
5669 static CHANSERV_FUNC(cmd_ping)
5673 char response[MAXLEN];
5674 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5675 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5676 irc_privmsg(cmd->parent->bot, channel->name, response);
5679 reply("CSMSG_PING_RESPONSE");
5683 static CHANSERV_FUNC(cmd_wut)
5687 char response[MAXLEN];
5688 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5689 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5690 irc_privmsg(cmd->parent->bot, channel->name, response);
5693 reply("CSMSG_WUT_RESPONSE");
5697 static CHANSERV_FUNC(cmd_8ball)
5699 unsigned int i, j, accum;
5704 for(i=1; i<argc; i++)
5705 for(j=0; argv[i][j]; j++)
5706 accum = (accum << 5) - accum + toupper(argv[i][j]);
5707 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5710 char response[MAXLEN];
5711 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5712 irc_privmsg(cmd->parent->bot, channel->name, response);
5715 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5719 static CHANSERV_FUNC(cmd_d)
5721 unsigned long sides, count, modifier, ii, total;
5722 char response[MAXLEN], *sep;
5726 if((count = strtoul(argv[1], &sep, 10)) < 1)
5736 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5737 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5741 else if((sep[0] == '-') && isdigit(sep[1]))
5742 modifier = strtoul(sep, NULL, 10);
5743 else if((sep[0] == '+') && isdigit(sep[1]))
5744 modifier = strtoul(sep+1, NULL, 10);
5751 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5756 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5759 for(total = ii = 0; ii < count; ++ii)
5760 total += (rand() % sides) + 1;
5763 if((count > 1) || modifier)
5765 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5766 sprintf(response, fmt, total, count, sides, modifier);
5770 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5771 sprintf(response, fmt, total, sides);
5774 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5776 send_message_type(4, user, cmd->parent->bot, "%s", response);
5780 static CHANSERV_FUNC(cmd_huggle)
5782 /* CTCP must be via PRIVMSG, never notice */
5784 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5786 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5791 chanserv_adjust_limit(void *data)
5793 struct mod_chanmode change;
5794 struct chanData *cData = data;
5795 struct chanNode *channel = cData->channel;
5798 if(IsSuspended(cData))
5801 cData->limitAdjusted = now;
5802 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5803 if(cData->modes.modes_set & MODE_LIMIT)
5805 if(limit > cData->modes.new_limit)
5806 limit = cData->modes.new_limit;
5807 else if(limit == cData->modes.new_limit)
5811 mod_chanmode_init(&change);
5812 change.modes_set = MODE_LIMIT;
5813 change.new_limit = limit;
5814 mod_chanmode_announce(chanserv, channel, &change);
5818 handle_new_channel(struct chanNode *channel)
5820 struct chanData *cData;
5822 if(!(cData = channel->channel_info))
5825 if(cData->modes.modes_set || cData->modes.modes_clear)
5826 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5828 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5829 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5832 /* Welcome to my worst nightmare. Warning: Read (or modify)
5833 the code below at your own risk. */
5835 handle_join(struct modeNode *mNode)
5837 struct mod_chanmode change;
5838 struct userNode *user = mNode->user;
5839 struct chanNode *channel = mNode->channel;
5840 struct chanData *cData;
5841 struct userData *uData = NULL;
5842 struct banData *bData;
5843 struct handle_info *handle;
5844 unsigned int modes = 0, info = 0;
5847 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5850 cData = channel->channel_info;
5851 if(channel->members.used > cData->max)
5852 cData->max = channel->members.used;
5854 /* Check for bans. If they're joining through a ban, one of two
5856 * 1: Join during a netburst, by riding the break. Kick them
5857 * unless they have ops or voice in the channel.
5858 * 2: They're allowed to join through the ban (an invite in
5859 * ircu2.10, or a +e on Hybrid, or something).
5860 * If they're not joining through a ban, and the banlist is not
5861 * full, see if they're on the banlist for the channel. If so,
5864 if(user->uplink->burst && !mNode->modes)
5867 for(ii = 0; ii < channel->banlist.used; ii++)
5869 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5871 /* Riding a netburst. Naughty. */
5872 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5878 mod_chanmode_init(&change);
5880 if(channel->banlist.used < MAXBANS)
5882 /* Not joining through a ban. */
5883 for(bData = cData->bans;
5884 bData && !user_matches_glob(user, bData->mask, 1);
5885 bData = bData->next);
5889 char kick_reason[MAXLEN];
5890 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5892 bData->triggered = now;
5893 if(bData != cData->bans)
5895 /* Shuffle the ban to the head of the list. */
5897 bData->next->prev = bData->prev;
5899 bData->prev->next = bData->next;
5902 bData->next = cData->bans;
5905 cData->bans->prev = bData;
5906 cData->bans = bData;
5909 change.args[0].mode = MODE_BAN;
5910 change.args[0].u.hostmask = bData->mask;
5911 mod_chanmode_announce(chanserv, channel, &change);
5912 KickChannelUser(user, channel, chanserv, kick_reason);
5917 /* ChanServ will not modify the limits in join-flooded channels.
5918 It will also skip DynLimit processing when the user (or srvx)
5919 is bursting in, because there are likely more incoming. */
5920 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5921 && !user->uplink->burst
5922 && !channel->join_flooded
5923 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5925 /* The user count has begun "bumping" into the channel limit,
5926 so set a timer to raise the limit a bit. Any previous
5927 timers are removed so three incoming users within the delay
5928 results in one limit change, not three. */
5930 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5931 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5934 if(channel->join_flooded)
5936 /* don't automatically give ops or voice during a join flood */
5938 else if(cData->lvlOpts[lvlGiveOps] == 0)
5939 modes |= MODE_CHANOP;
5940 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5941 modes |= MODE_VOICE;
5943 greeting = cData->greeting;
5944 if(user->handle_info)
5946 handle = user->handle_info;
5948 if(IsHelper(user) && !IsHelping(user))
5951 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5953 if(channel == chanserv_conf.support_channels.list[ii])
5955 HANDLE_SET_FLAG(user->handle_info, HELPING);
5961 uData = GetTrueChannelAccess(cData, handle);
5962 if(uData && !IsUserSuspended(uData))
5964 /* Ops and above were handled by the above case. */
5965 if(IsUserAutoOp(uData))
5967 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5968 modes |= MODE_CHANOP;
5969 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5970 modes |= MODE_VOICE;
5972 if(uData->access >= UL_PRESENT)
5973 cData->visited = now;
5974 if(cData->user_greeting)
5975 greeting = cData->user_greeting;
5977 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5978 && ((now - uData->seen) >= chanserv_conf.info_delay)
5985 if(!user->uplink->burst)
5989 if(modes & MODE_CHANOP)
5990 modes &= ~MODE_VOICE;
5991 change.args[0].mode = modes;
5992 change.args[0].u.member = mNode;
5993 mod_chanmode_announce(chanserv, channel, &change);
5995 if(greeting && !user->uplink->burst)
5996 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5998 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6004 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6006 struct mod_chanmode change;
6007 struct userData *channel;
6008 unsigned int ii, jj;
6010 if(!user->handle_info)
6013 mod_chanmode_init(&change);
6015 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6017 struct chanNode *cn;
6018 struct modeNode *mn;
6019 if(IsUserSuspended(channel)
6020 || IsSuspended(channel->channel)
6021 || !(cn = channel->channel->channel))
6024 mn = GetUserMode(cn, user);
6027 if(!IsUserSuspended(channel)
6028 && IsUserAutoInvite(channel)
6029 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6031 && !user->uplink->burst)
6032 irc_invite(chanserv, user, cn);
6036 if(channel->access >= UL_PRESENT)
6037 channel->channel->visited = now;
6039 if(IsUserAutoOp(channel))
6041 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6042 change.args[0].mode = MODE_CHANOP;
6043 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6044 change.args[0].mode = MODE_VOICE;
6046 change.args[0].mode = 0;
6047 change.args[0].u.member = mn;
6048 if(change.args[0].mode)
6049 mod_chanmode_announce(chanserv, cn, &change);
6052 channel->seen = now;
6053 channel->present = 1;
6056 for(ii = 0; ii < user->channels.used; ++ii)
6058 struct chanNode *channel = user->channels.list[ii]->channel;
6059 struct banData *ban;
6061 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6062 || !channel->channel_info
6063 || IsSuspended(channel->channel_info))
6065 for(jj = 0; jj < channel->banlist.used; ++jj)
6066 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6068 if(jj < channel->banlist.used)
6070 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6072 char kick_reason[MAXLEN];
6073 if(!user_matches_glob(user, ban->mask, 1))
6075 change.args[0].mode = MODE_BAN;
6076 change.args[0].u.hostmask = ban->mask;
6077 mod_chanmode_announce(chanserv, channel, &change);
6078 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6079 KickChannelUser(user, channel, chanserv, kick_reason);
6080 ban->triggered = now;
6085 if(IsSupportHelper(user))
6087 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6089 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6091 HANDLE_SET_FLAG(user->handle_info, HELPING);
6099 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6101 struct chanData *cData;
6102 struct userData *uData;
6104 cData = mn->channel->channel_info;
6105 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6108 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6110 /* Allow for a bit of padding so that the limit doesn't
6111 track the user count exactly, which could get annoying. */
6112 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6114 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6115 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6119 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6121 scan_user_presence(uData, mn->user);
6125 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6127 unsigned int ii, jj;
6128 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6130 for(jj = 0; jj < mn->user->channels.used; ++jj)
6131 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6133 if(jj < mn->user->channels.used)
6136 if(ii == chanserv_conf.support_channels.used)
6137 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6142 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6144 struct userData *uData;
6146 if(!channel->channel_info || !kicker || IsService(kicker)
6147 || (kicker == victim) || IsSuspended(channel->channel_info)
6148 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6151 if(protect_user(victim, kicker, channel->channel_info))
6153 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6154 KickChannelUser(kicker, channel, chanserv, reason);
6157 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6162 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6164 struct chanData *cData;
6166 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6169 cData = channel->channel_info;
6170 if(bad_topic(channel, user, channel->topic))
6172 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6173 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6174 SetChannelTopic(channel, chanserv, old_topic, 1);
6175 else if(cData->topic)
6176 SetChannelTopic(channel, chanserv, cData->topic, 1);
6179 /* With topicsnarf, grab the topic and save it as the default topic. */
6180 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6183 cData->topic = strdup(channel->topic);
6189 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6191 struct mod_chanmode *bounce = NULL;
6192 unsigned int bnc, ii;
6195 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6198 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6199 && mode_lock_violated(&channel->channel_info->modes, change))
6201 char correct[MAXLEN];
6202 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6203 mod_chanmode_format(&channel->channel_info->modes, correct);
6204 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6206 for(ii = bnc = 0; ii < change->argc; ++ii)
6208 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6210 const struct userNode *victim = change->args[ii].u.member->user;
6211 if(!protect_user(victim, user, channel->channel_info))
6214 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6217 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6218 bounce->args[bnc].u.member = GetUserMode(channel, user);
6219 if(bounce->args[bnc].u.member)
6223 bounce->args[bnc].mode = MODE_CHANOP;
6224 bounce->args[bnc].u.member = change->args[ii].u.member;
6226 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6228 else if(change->args[ii].mode & MODE_CHANOP)
6230 const struct userNode *victim = change->args[ii].u.member->user;
6231 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6234 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6235 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6236 bounce->args[bnc].u.member = change->args[ii].u.member;
6239 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6241 const char *ban = change->args[ii].u.hostmask;
6242 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6245 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6246 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6247 bounce->args[bnc].u.hostmask = strdup(ban);
6249 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6254 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6255 mod_chanmode_announce(chanserv, channel, bounce);
6256 for(ii = 0; ii < change->argc; ++ii)
6257 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6258 free((char*)bounce->args[ii].u.hostmask);
6259 mod_chanmode_free(bounce);
6264 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6266 struct chanNode *channel;
6267 struct banData *bData;
6268 struct mod_chanmode change;
6269 unsigned int ii, jj;
6270 char kick_reason[MAXLEN];
6272 mod_chanmode_init(&change);
6274 change.args[0].mode = MODE_BAN;
6275 for(ii = 0; ii < user->channels.used; ++ii)
6277 channel = user->channels.list[ii]->channel;
6278 /* Need not check for bans if they're opped or voiced. */
6279 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6281 /* Need not check for bans unless channel registration is active. */
6282 if(!channel->channel_info || IsSuspended(channel->channel_info))
6284 /* Look for a matching ban already on the channel. */
6285 for(jj = 0; jj < channel->banlist.used; ++jj)
6286 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6288 /* Need not act if we found one. */
6289 if(jj < channel->banlist.used)
6291 /* Look for a matching ban in this channel. */
6292 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6294 if(!user_matches_glob(user, bData->mask, 1))
6296 change.args[0].u.hostmask = bData->mask;
6297 mod_chanmode_announce(chanserv, channel, &change);
6298 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6299 KickChannelUser(user, channel, chanserv, kick_reason);
6300 bData->triggered = now;
6301 break; /* we don't need to check any more bans in the channel */
6306 static void handle_rename(struct handle_info *handle, const char *old_handle)
6308 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6312 dict_remove2(handle_dnrs, old_handle, 1);
6313 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6314 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6319 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6321 struct userNode *h_user;
6323 if(handle->channels)
6325 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6326 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6328 while(handle->channels)
6329 del_channel_user(handle->channels, 1);
6334 handle_server_link(UNUSED_ARG(struct server *server))
6336 struct chanData *cData;
6338 for(cData = channelList; cData; cData = cData->next)
6340 if(!IsSuspended(cData))
6341 cData->may_opchan = 1;
6342 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6343 && !cData->channel->join_flooded
6344 && ((cData->channel->limit - cData->channel->members.used)
6345 < chanserv_conf.adjust_threshold))
6347 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6348 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6354 chanserv_conf_read(void)
6358 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6359 struct mod_chanmode *change;
6360 struct string_list *strlist;
6361 struct chanNode *chan;
6364 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6366 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6369 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6370 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6371 chanserv_conf.support_channels.used = 0;
6372 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6374 for(ii = 0; ii < strlist->used; ++ii)
6376 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6379 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6381 channelList_append(&chanserv_conf.support_channels, chan);
6384 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6387 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6390 chan = AddChannel(str, now, str2, NULL);
6392 channelList_append(&chanserv_conf.support_channels, chan);
6394 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6395 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6396 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6397 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6398 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6399 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6400 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6401 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6402 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6403 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6404 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6405 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6406 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6407 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6408 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6409 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6410 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6411 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6412 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6413 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6414 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6415 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6416 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6418 NickChange(chanserv, str, 0);
6419 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6420 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6421 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6422 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6423 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6424 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6425 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6426 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6427 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6428 chanserv_conf.max_owned = str ? atoi(str) : 5;
6429 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6430 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6431 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6432 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6433 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6434 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6435 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6438 safestrncpy(mode_line, str, sizeof(mode_line));
6439 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6440 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6442 chanserv_conf.default_modes = *change;
6443 mod_chanmode_free(change);
6445 free_string_list(chanserv_conf.set_shows);
6446 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6448 strlist = string_list_copy(strlist);
6451 static const char *list[] = {
6452 /* free form text */
6453 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6454 /* options based on user level */
6455 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6456 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6457 /* multiple choice options */
6458 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6459 /* binary options */
6460 "DynLimit", "NoDelete",
6465 strlist = alloc_string_list(ArrayLength(list)-1);
6466 for(ii=0; list[ii]; ii++)
6467 string_list_append(strlist, strdup(list[ii]));
6469 chanserv_conf.set_shows = strlist;
6470 /* We don't look things up now, in case the list refers to options
6471 * defined by modules initialized after this point. Just mark the
6472 * function list as invalid, so it will be initialized.
6474 set_shows_list.used = 0;
6475 free_string_list(chanserv_conf.eightball);
6476 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6479 strlist = string_list_copy(strlist);
6483 strlist = alloc_string_list(4);
6484 string_list_append(strlist, strdup("Yes."));
6485 string_list_append(strlist, strdup("No."));
6486 string_list_append(strlist, strdup("Maybe so."));
6488 chanserv_conf.eightball = strlist;
6489 free_string_list(chanserv_conf.old_ban_names);
6490 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6492 strlist = string_list_copy(strlist);
6494 strlist = alloc_string_list(2);
6495 chanserv_conf.old_ban_names = strlist;
6496 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6497 off_channel = str ? atoi(str) : 0;
6501 chanserv_note_type_read(const char *key, struct record_data *rd)
6504 struct note_type *ntype;
6507 if(!(obj = GET_RECORD_OBJECT(rd)))
6509 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6512 if(!(ntype = chanserv_create_note_type(key)))
6514 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6518 /* Figure out set access */
6519 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6521 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6522 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6524 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6526 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6527 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6529 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6531 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6535 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6536 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6537 ntype->set_access.min_opserv = 0;
6540 /* Figure out visibility */
6541 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6542 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6543 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6544 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6545 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6546 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6547 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6548 ntype->visible_type = NOTE_VIS_ALL;
6550 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6552 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6553 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6557 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6559 struct handle_info *handle;
6560 struct userData *uData;
6561 char *seen, *inf, *flags;
6563 unsigned short access;
6565 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6567 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6571 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6572 if(access > UL_OWNER)
6574 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6578 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6579 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6580 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6581 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6582 handle = get_handle_info(key);
6585 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6589 uData = add_channel_user(chan, handle, access, last_seen, inf);
6590 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6594 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6596 struct banData *bData;
6597 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6598 time_t set_time, triggered_time, expires_time;
6600 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6602 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6606 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6607 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6608 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6609 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6610 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6611 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6612 if (!reason || !owner)
6615 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6616 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6618 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6620 expires_time = set_time + atoi(s_duration);
6624 if(!reason || (expires_time && (expires_time < now)))
6627 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6630 static struct suspended *
6631 chanserv_read_suspended(dict_t obj)
6633 struct suspended *suspended = calloc(1, sizeof(*suspended));
6637 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6638 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6639 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6640 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6641 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6642 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6643 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6644 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6645 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6646 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6651 chanserv_channel_read(const char *key, struct record_data *hir)
6653 struct suspended *suspended;
6654 struct mod_chanmode *modes;
6655 struct chanNode *cNode;
6656 struct chanData *cData;
6657 struct dict *channel, *obj;
6658 char *str, *argv[10];
6662 channel = hir->d.object;
6664 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6667 cNode = AddChannel(key, now, NULL, NULL);
6670 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6673 cData = register_channel(cNode, str);
6676 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6680 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6682 enum levelOption lvlOpt;
6683 enum charOption chOpt;
6685 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6686 cData->flags = atoi(str);
6688 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6690 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6692 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6693 else if(levelOptions[lvlOpt].old_flag)
6695 if(cData->flags & levelOptions[lvlOpt].old_flag)
6696 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6698 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6702 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6704 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6706 cData->chOpts[chOpt] = str[0];
6709 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6711 enum levelOption lvlOpt;
6712 enum charOption chOpt;
6715 cData->flags = base64toint(str, 5);
6716 count = strlen(str += 5);
6717 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6720 if(levelOptions[lvlOpt].old_flag)
6722 if(cData->flags & levelOptions[lvlOpt].old_flag)
6723 lvl = levelOptions[lvlOpt].flag_value;
6725 lvl = levelOptions[lvlOpt].default_value;
6727 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6729 case 'c': lvl = UL_COOWNER; break;
6730 case 'm': lvl = UL_MASTER; break;
6731 case 'n': lvl = UL_OWNER+1; break;
6732 case 'o': lvl = UL_OP; break;
6733 case 'p': lvl = UL_PEON; break;
6734 case 'w': lvl = UL_OWNER; break;
6735 default: lvl = 0; break;
6737 cData->lvlOpts[lvlOpt] = lvl;
6739 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6740 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6743 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6745 suspended = chanserv_read_suspended(obj);
6746 cData->suspended = suspended;
6747 suspended->cData = cData;
6748 /* We could use suspended->expires and suspended->revoked to
6749 * set the CHANNEL_SUSPENDED flag, but we don't. */
6751 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6753 suspended = calloc(1, sizeof(*suspended));
6754 suspended->issued = 0;
6755 suspended->revoked = 0;
6756 suspended->suspender = strdup(str);
6757 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6758 suspended->expires = str ? atoi(str) : 0;
6759 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6760 suspended->reason = strdup(str ? str : "No reason");
6761 suspended->previous = NULL;
6762 cData->suspended = suspended;
6763 suspended->cData = cData;
6767 cData->flags &= ~CHANNEL_SUSPENDED;
6768 suspended = NULL; /* to squelch a warning */
6771 if(IsSuspended(cData)) {
6772 if(suspended->expires > now)
6773 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6774 else if(suspended->expires)
6775 cData->flags &= ~CHANNEL_SUSPENDED;
6778 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6779 struct mod_chanmode change;
6780 mod_chanmode_init(&change);
6782 change.args[0].mode = MODE_CHANOP;
6783 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6784 mod_chanmode_announce(chanserv, cNode, &change);
6787 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6788 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6789 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6790 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6791 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6792 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6793 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6794 cData->max = str ? atoi(str) : 0;
6795 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6796 cData->greeting = str ? strdup(str) : NULL;
6797 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6798 cData->user_greeting = str ? strdup(str) : NULL;
6799 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6800 cData->topic_mask = str ? strdup(str) : NULL;
6801 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6802 cData->topic = str ? strdup(str) : NULL;
6804 if(!IsSuspended(cData)
6805 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6806 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6807 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6808 cData->modes = *modes;
6810 cData->modes.modes_set |= MODE_REGISTERED;
6811 if(cData->modes.argc > 1)
6812 cData->modes.argc = 1;
6813 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6814 mod_chanmode_free(modes);
6817 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6818 for(it = dict_first(obj); it; it = iter_next(it))
6819 user_read_helper(iter_key(it), iter_data(it), cData);
6821 if(!cData->users && !IsProtected(cData))
6823 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6824 unregister_channel(cData, "has empty user list.");
6828 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6829 for(it = dict_first(obj); it; it = iter_next(it))
6830 ban_read_helper(iter_key(it), iter_data(it), cData);
6832 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6833 for(it = dict_first(obj); it; it = iter_next(it))
6835 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6836 struct record_data *rd = iter_data(it);
6837 const char *note, *setter;
6839 if(rd->type != RECDB_OBJECT)
6841 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6845 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6847 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6849 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6853 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6854 if(!setter) setter = "<unknown>";
6855 chanserv_add_channel_note(cData, ntype, setter, note);
6863 chanserv_dnr_read(const char *key, struct record_data *hir)
6865 const char *setter, *reason, *str;
6866 struct do_not_register *dnr;
6868 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6871 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6874 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6877 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6880 dnr = chanserv_add_dnr(key, setter, reason);
6883 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6885 dnr->set = atoi(str);
6891 chanserv_saxdb_read(struct dict *database)
6893 struct dict *section;
6896 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6897 for(it = dict_first(section); it; it = iter_next(it))
6898 chanserv_note_type_read(iter_key(it), iter_data(it));
6900 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6901 for(it = dict_first(section); it; it = iter_next(it))
6902 chanserv_channel_read(iter_key(it), iter_data(it));
6904 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6905 for(it = dict_first(section); it; it = iter_next(it))
6906 chanserv_dnr_read(iter_key(it), iter_data(it));
6912 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6914 int high_present = 0;
6915 saxdb_start_record(ctx, KEY_USERS, 1);
6916 for(; uData; uData = uData->next)
6918 if((uData->access >= UL_PRESENT) && uData->present)
6920 saxdb_start_record(ctx, uData->handle->handle, 0);
6921 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6922 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6924 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6926 saxdb_write_string(ctx, KEY_INFO, uData->info);
6927 saxdb_end_record(ctx);
6929 saxdb_end_record(ctx);
6930 return high_present;
6934 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6938 saxdb_start_record(ctx, KEY_BANS, 1);
6939 for(; bData; bData = bData->next)
6941 saxdb_start_record(ctx, bData->mask, 0);
6942 saxdb_write_int(ctx, KEY_SET, bData->set);
6943 if(bData->triggered)
6944 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6946 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6948 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6950 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6951 saxdb_end_record(ctx);
6953 saxdb_end_record(ctx);
6957 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6959 saxdb_start_record(ctx, name, 0);
6960 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6961 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6963 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6965 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6967 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6969 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6970 saxdb_end_record(ctx);
6974 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6978 enum levelOption lvlOpt;
6979 enum charOption chOpt;
6981 saxdb_start_record(ctx, channel->channel->name, 1);
6983 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6984 saxdb_write_int(ctx, KEY_MAX, channel->max);
6986 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6987 if(channel->registrar)
6988 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6989 if(channel->greeting)
6990 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6991 if(channel->user_greeting)
6992 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6993 if(channel->topic_mask)
6994 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6995 if(channel->suspended)
6996 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6998 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6999 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7000 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7001 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7002 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7004 buf[0] = channel->chOpts[chOpt];
7006 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7008 saxdb_end_record(ctx);
7010 if(channel->modes.modes_set || channel->modes.modes_clear)
7012 mod_chanmode_format(&channel->modes, buf);
7013 saxdb_write_string(ctx, KEY_MODES, buf);
7016 high_present = chanserv_write_users(ctx, channel->users);
7017 chanserv_write_bans(ctx, channel->bans);
7019 if(dict_size(channel->notes))
7023 saxdb_start_record(ctx, KEY_NOTES, 1);
7024 for(it = dict_first(channel->notes); it; it = iter_next(it))
7026 struct note *note = iter_data(it);
7027 saxdb_start_record(ctx, iter_key(it), 0);
7028 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7029 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7030 saxdb_end_record(ctx);
7032 saxdb_end_record(ctx);
7035 if(channel->ownerTransfer)
7036 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7037 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7038 saxdb_end_record(ctx);
7042 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7046 saxdb_start_record(ctx, ntype->name, 0);
7047 switch(ntype->set_access_type)
7049 case NOTE_SET_CHANNEL_ACCESS:
7050 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7052 case NOTE_SET_CHANNEL_SETTER:
7053 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7055 case NOTE_SET_PRIVILEGED: default:
7056 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7059 switch(ntype->visible_type)
7061 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7062 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7063 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7065 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7066 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7067 saxdb_end_record(ctx);
7071 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7073 struct do_not_register *dnr;
7076 for(it = dict_first(dnrs); it; it = iter_next(it))
7078 dnr = iter_data(it);
7079 saxdb_start_record(ctx, dnr->chan_name, 0);
7081 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7082 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7083 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7084 saxdb_end_record(ctx);
7089 chanserv_saxdb_write(struct saxdb_context *ctx)
7092 struct chanData *channel;
7095 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7096 for(it = dict_first(note_types); it; it = iter_next(it))
7097 chanserv_write_note_type(ctx, iter_data(it));
7098 saxdb_end_record(ctx);
7101 saxdb_start_record(ctx, KEY_DNR, 1);
7102 write_dnrs_helper(ctx, handle_dnrs);
7103 write_dnrs_helper(ctx, plain_dnrs);
7104 write_dnrs_helper(ctx, mask_dnrs);
7105 saxdb_end_record(ctx);
7108 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7109 for(channel = channelList; channel; channel = channel->next)
7110 chanserv_write_channel(ctx, channel);
7111 saxdb_end_record(ctx);
7117 chanserv_db_cleanup(void) {
7119 unreg_part_func(handle_part);
7121 unregister_channel(channelList, "terminating.");
7122 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7123 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7124 free(chanserv_conf.support_channels.list);
7125 dict_delete(handle_dnrs);
7126 dict_delete(plain_dnrs);
7127 dict_delete(mask_dnrs);
7128 dict_delete(note_types);
7129 free_string_list(chanserv_conf.eightball);
7130 free_string_list(chanserv_conf.old_ban_names);
7131 free_string_list(chanserv_conf.set_shows);
7132 free(set_shows_list.list);
7133 free(uset_shows_list.list);
7136 struct userData *helper = helperList;
7137 helperList = helperList->next;
7142 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7143 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7144 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7147 init_chanserv(const char *nick)
7149 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7150 conf_register_reload(chanserv_conf_read);
7152 reg_server_link_func(handle_server_link);
7154 reg_new_channel_func(handle_new_channel);
7155 reg_join_func(handle_join);
7156 reg_part_func(handle_part);
7157 reg_kick_func(handle_kick);
7158 reg_topic_func(handle_topic);
7159 reg_mode_change_func(handle_mode);
7160 reg_nick_change_func(handle_nick_change);
7162 reg_auth_func(handle_auth);
7163 reg_handle_rename_func(handle_rename);
7164 reg_unreg_func(handle_unreg);
7166 handle_dnrs = dict_new();
7167 dict_set_free_data(handle_dnrs, free);
7168 plain_dnrs = dict_new();
7169 dict_set_free_data(plain_dnrs, free);
7170 mask_dnrs = dict_new();
7171 dict_set_free_data(mask_dnrs, free);
7173 reg_svccmd_unbind_func(handle_svccmd_unbind);
7174 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7175 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7176 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7177 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7178 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7179 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7180 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7181 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7182 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7184 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7185 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7187 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7188 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7189 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7190 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7191 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7193 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7194 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7195 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7196 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7197 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7199 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7200 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7201 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7202 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7204 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7205 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7206 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7207 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7208 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7209 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7210 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7211 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7213 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7214 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7215 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7216 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7217 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7218 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7219 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7220 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7221 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7222 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7223 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7224 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7225 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7226 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7228 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7229 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7230 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7231 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7232 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7234 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7235 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7237 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7238 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7239 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7240 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7241 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7242 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7243 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7244 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7245 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7246 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7247 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7249 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7250 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7252 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7253 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7254 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7255 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7257 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7258 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7259 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7260 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7261 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7263 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7264 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7265 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7266 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7267 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7268 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7270 /* Channel options */
7271 DEFINE_CHANNEL_OPTION(defaulttopic);
7272 DEFINE_CHANNEL_OPTION(topicmask);
7273 DEFINE_CHANNEL_OPTION(greeting);
7274 DEFINE_CHANNEL_OPTION(usergreeting);
7275 DEFINE_CHANNEL_OPTION(modes);
7276 DEFINE_CHANNEL_OPTION(enfops);
7277 DEFINE_CHANNEL_OPTION(giveops);
7278 DEFINE_CHANNEL_OPTION(protect);
7279 DEFINE_CHANNEL_OPTION(enfmodes);
7280 DEFINE_CHANNEL_OPTION(enftopic);
7281 DEFINE_CHANNEL_OPTION(pubcmd);
7282 DEFINE_CHANNEL_OPTION(givevoice);
7283 DEFINE_CHANNEL_OPTION(userinfo);
7284 DEFINE_CHANNEL_OPTION(dynlimit);
7285 DEFINE_CHANNEL_OPTION(topicsnarf);
7286 DEFINE_CHANNEL_OPTION(nodelete);
7287 DEFINE_CHANNEL_OPTION(toys);
7288 DEFINE_CHANNEL_OPTION(setters);
7289 DEFINE_CHANNEL_OPTION(topicrefresh);
7290 DEFINE_CHANNEL_OPTION(ctcpusers);
7291 DEFINE_CHANNEL_OPTION(ctcpreaction);
7292 DEFINE_CHANNEL_OPTION(inviteme);
7294 DEFINE_CHANNEL_OPTION(offchannel);
7295 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7297 /* Alias set topic to set defaulttopic for compatibility. */
7298 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7301 DEFINE_USER_OPTION(noautoop);
7302 DEFINE_USER_OPTION(autoinvite);
7303 DEFINE_USER_OPTION(info);
7305 /* Alias uset autovoice to uset autoop. */
7306 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7308 note_types = dict_new();
7309 dict_set_free_data(note_types, chanserv_deref_note_type);
7312 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7313 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7314 service_register(chanserv)->trigger = '!';
7315 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7317 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7319 if(chanserv_conf.channel_expire_frequency)
7320 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7322 if(chanserv_conf.refresh_period)
7324 time_t next_refresh;
7325 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7326 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7329 reg_exit_func(chanserv_db_cleanup);
7330 message_register_table(msgtab);