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 unsigned int use_registered_mode;
507 struct string_list *set_shows;
508 struct string_list *eightball;
509 struct string_list *old_ban_names;
511 const char *ctcp_short_ban_duration;
512 const char *ctcp_long_ban_duration;
514 const char *irc_operator_epithet;
515 const char *network_helper_epithet;
516 const char *support_helper_epithet;
521 struct userNode *user;
522 struct userNode *bot;
523 struct chanNode *channel;
525 unsigned short lowest;
526 unsigned short highest;
527 struct userData **users;
528 struct helpfile_table table;
531 enum note_access_type
533 NOTE_SET_CHANNEL_ACCESS,
534 NOTE_SET_CHANNEL_SETTER,
538 enum note_visible_type
541 NOTE_VIS_CHANNEL_USERS,
547 enum note_access_type set_access_type;
549 unsigned int min_opserv;
550 unsigned short min_ulevel;
552 enum note_visible_type visible_type;
553 unsigned int max_length;
560 struct note_type *type;
561 char setter[NICKSERV_HANDLE_LEN+1];
565 static unsigned int registered_channels;
566 static unsigned int banCount;
568 static const struct {
571 unsigned short level;
574 { "peon", "Peon", UL_PEON, '+' },
575 { "op", "Op", UL_OP, '@' },
576 { "master", "Master", UL_MASTER, '%' },
577 { "coowner", "Coowner", UL_COOWNER, '*' },
578 { "owner", "Owner", UL_OWNER, '!' },
579 { "helper", "BUG:", UL_HELPER, 'X' }
582 static const struct {
585 unsigned short default_value;
586 unsigned int old_idx;
587 unsigned int old_flag;
588 unsigned short flag_value;
590 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
591 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
592 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
593 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
594 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
595 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
596 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
597 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
598 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
599 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
600 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
603 struct charOptionValues {
606 } protectValues[] = {
607 { 'a', "CSMSG_PROTECT_ALL" },
608 { 'e', "CSMSG_PROTECT_EQUAL" },
609 { 'l', "CSMSG_PROTECT_LOWER" },
610 { 'n', "CSMSG_PROTECT_NONE" }
612 { 'd', "CSMSG_TOYS_DISABLED" },
613 { 'n', "CSMSG_TOYS_PRIVATE" },
614 { 'p', "CSMSG_TOYS_PUBLIC" }
615 }, topicRefreshValues[] = {
616 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
617 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
618 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
619 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
620 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
621 }, ctcpReactionValues[] = {
622 { 'k', "CSMSG_CTCPREACTION_KICK" },
623 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
624 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
625 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
628 static const struct {
632 unsigned int old_idx;
634 struct charOptionValues *values;
636 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
637 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
638 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
639 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
642 struct userData *helperList;
643 struct chanData *channelList;
644 static struct module *chanserv_module;
645 static unsigned int userCount;
647 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
648 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
649 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
652 user_level_from_name(const char *name, unsigned short clamp_level)
654 unsigned int level = 0, ii;
656 level = strtoul(name, NULL, 10);
657 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
658 if(!irccasecmp(name, accessLevels[ii].name))
659 level = accessLevels[ii].level;
660 if(level > clamp_level)
666 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
669 *minl = strtoul(arg, &sep, 10);
677 *maxl = strtoul(sep+1, &sep, 10);
685 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
687 struct userData *uData, **head;
689 if(!channel || !handle)
692 if(override && HANDLE_FLAGGED(handle, HELPING)
693 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
695 for(uData = helperList;
696 uData && uData->handle != handle;
697 uData = uData->next);
701 uData = calloc(1, sizeof(struct userData));
702 uData->handle = handle;
704 uData->access = UL_HELPER;
710 uData->next = helperList;
712 helperList->prev = uData;
720 for(uData = channel->users; uData; uData = uData->next)
721 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
724 head = &(channel->users);
727 if(uData && (uData != *head))
729 /* Shuffle the user to the head of whatever list he was in. */
731 uData->next->prev = uData->prev;
733 uData->prev->next = uData->next;
739 (**head).prev = uData;
746 /* Returns non-zero if user has at least the minimum access.
747 * exempt_owner is set when handling !set, so the owner can set things
750 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
752 struct userData *uData;
753 struct chanData *cData = channel->channel_info;
754 unsigned short minimum = cData->lvlOpts[opt];
757 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
760 if(minimum <= uData->access)
762 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
767 /* Scan for other users authenticated to the same handle
768 still in the channel. If so, keep them listed as present.
770 user is optional, if not null, it skips checking that userNode
771 (for the handle_part function) */
773 scan_user_presence(struct userData *uData, struct userNode *user)
777 if(IsSuspended(uData->channel)
778 || IsUserSuspended(uData)
779 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
791 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
793 unsigned int eflags, argc;
795 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
797 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
798 if(!channel->channel_info
799 || IsSuspended(channel->channel_info)
801 || !ircncasecmp(text, "ACTION ", 7))
803 /* Figure out the minimum level needed to CTCP the channel */
804 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
806 /* We need to enforce against them; do so. */
809 argv[1] = user->nick;
811 if(GetUserMode(channel, user))
812 eflags |= ACTION_KICK;
813 switch(channel->channel_info->chOpts[chCTCPReaction]) {
814 default: case 'k': /* just do the kick */ break;
816 eflags |= ACTION_BAN;
819 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
820 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
823 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
824 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
827 argv[argc++] = bad_ctcp_reason;
828 eject_user(chanserv, channel, argc, argv, NULL, eflags);
832 chanserv_create_note_type(const char *name)
834 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
835 strcpy(ntype->name, name);
837 dict_insert(note_types, ntype->name, ntype);
842 chanserv_deref_note_type(void *data)
844 struct note_type *ntype = data;
846 if(--ntype->refs > 0)
852 chanserv_flush_note_type(struct note_type *ntype)
854 struct chanData *cData;
855 for(cData = channelList; cData; cData = cData->next)
856 dict_remove(cData->notes, ntype->name);
860 chanserv_truncate_notes(struct note_type *ntype)
862 struct chanData *cData;
864 unsigned int size = sizeof(*note) + ntype->max_length;
866 for(cData = channelList; cData; cData = cData->next) {
867 note = dict_find(cData->notes, ntype->name, NULL);
870 if(strlen(note->note) <= ntype->max_length)
872 dict_remove2(cData->notes, ntype->name, 1);
873 note = realloc(note, size);
874 note->note[ntype->max_length] = 0;
875 dict_insert(cData->notes, ntype->name, note);
879 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
882 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
885 unsigned int len = strlen(text);
887 if(len > type->max_length) len = type->max_length;
888 note = calloc(1, sizeof(*note) + len);
890 strncpy(note->setter, setter, sizeof(note->setter)-1);
891 memcpy(note->note, text, len);
893 dict_insert(channel->notes, type->name, note);
899 chanserv_free_note(void *data)
901 struct note *note = data;
903 chanserv_deref_note_type(note->type);
904 assert(note->type->refs > 0); /* must use delnote to remove the type */
908 static MODCMD_FUNC(cmd_createnote) {
909 struct note_type *ntype;
910 unsigned int arg = 1, existed = 0, max_length;
912 if((ntype = dict_find(note_types, argv[1], NULL)))
915 ntype = chanserv_create_note_type(argv[arg]);
916 if(!irccasecmp(argv[++arg], "privileged"))
919 ntype->set_access_type = NOTE_SET_PRIVILEGED;
920 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
922 else if(!irccasecmp(argv[arg], "channel"))
924 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
927 reply("CSMSG_INVALID_ACCESS", argv[arg]);
930 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
931 ntype->set_access.min_ulevel = ulvl;
933 else if(!irccasecmp(argv[arg], "setter"))
935 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
939 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
943 if(!irccasecmp(argv[++arg], "privileged"))
944 ntype->visible_type = NOTE_VIS_PRIVILEGED;
945 else if(!irccasecmp(argv[arg], "channel_users"))
946 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
947 else if(!irccasecmp(argv[arg], "all"))
948 ntype->visible_type = NOTE_VIS_ALL;
950 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
954 if((arg+1) >= argc) {
955 reply("MSG_MISSING_PARAMS", argv[0]);
958 max_length = strtoul(argv[++arg], NULL, 0);
959 if(max_length < 20 || max_length > 450)
961 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
964 if(existed && (max_length < ntype->max_length))
966 ntype->max_length = max_length;
967 chanserv_truncate_notes(ntype);
969 ntype->max_length = max_length;
972 reply("CSMSG_NOTE_MODIFIED", ntype->name);
974 reply("CSMSG_NOTE_CREATED", ntype->name);
979 dict_remove(note_types, ntype->name);
983 static MODCMD_FUNC(cmd_removenote) {
984 struct note_type *ntype;
987 ntype = dict_find(note_types, argv[1], NULL);
988 force = (argc > 2) && !irccasecmp(argv[2], "force");
991 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
998 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1001 chanserv_flush_note_type(ntype);
1003 dict_remove(note_types, argv[1]);
1004 reply("CSMSG_NOTE_DELETED", argv[1]);
1009 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1013 if(orig->modes_set & change->modes_clear)
1015 if(orig->modes_clear & change->modes_set)
1017 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1018 && strcmp(orig->new_key, change->new_key))
1020 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1021 && (orig->new_limit != change->new_limit))
1026 static char max_length_text[MAXLEN+1][16];
1028 static struct helpfile_expansion
1029 chanserv_expand_variable(const char *variable)
1031 struct helpfile_expansion exp;
1033 if(!irccasecmp(variable, "notes"))
1036 exp.type = HF_TABLE;
1037 exp.value.table.length = 1;
1038 exp.value.table.width = 3;
1039 exp.value.table.flags = 0;
1040 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1041 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1042 exp.value.table.contents[0][0] = "Note Type";
1043 exp.value.table.contents[0][1] = "Visibility";
1044 exp.value.table.contents[0][2] = "Max Length";
1045 for(it=dict_first(note_types); it; it=iter_next(it))
1047 struct note_type *ntype = iter_data(it);
1050 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1051 row = exp.value.table.length++;
1052 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1053 exp.value.table.contents[row][0] = ntype->name;
1054 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1055 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1057 if(!max_length_text[ntype->max_length][0])
1058 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1059 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1064 exp.type = HF_STRING;
1065 exp.value.str = NULL;
1069 static struct chanData*
1070 register_channel(struct chanNode *cNode, char *registrar)
1072 struct chanData *channel;
1073 enum levelOption lvlOpt;
1074 enum charOption chOpt;
1076 channel = calloc(1, sizeof(struct chanData));
1078 channel->notes = dict_new();
1079 dict_set_free_data(channel->notes, chanserv_free_note);
1081 channel->registrar = strdup(registrar);
1082 channel->registered = now;
1083 channel->visited = now;
1084 channel->limitAdjusted = now;
1085 channel->ownerTransfer = now;
1086 channel->flags = CHANNEL_DEFAULT_FLAGS;
1087 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1088 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1089 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1090 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1092 channel->prev = NULL;
1093 channel->next = channelList;
1096 channelList->prev = channel;
1097 channelList = channel;
1098 registered_channels++;
1100 channel->channel = cNode;
1102 cNode->channel_info = channel;
1107 static struct userData*
1108 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1110 struct userData *ud;
1112 if(access > UL_OWNER)
1115 ud = calloc(1, sizeof(*ud));
1116 ud->channel = channel;
1117 ud->handle = handle;
1119 ud->access = access;
1120 ud->info = info ? strdup(info) : NULL;
1123 ud->next = channel->users;
1125 channel->users->prev = ud;
1126 channel->users = ud;
1128 channel->userCount++;
1132 ud->u_next = ud->handle->channels;
1134 ud->u_next->u_prev = ud;
1135 ud->handle->channels = ud;
1140 static void unregister_channel(struct chanData *channel, const char *reason);
1143 del_channel_user(struct userData *user, int do_gc)
1145 struct chanData *channel = user->channel;
1147 channel->userCount--;
1151 user->prev->next = user->next;
1153 channel->users = user->next;
1155 user->next->prev = user->prev;
1158 user->u_prev->u_next = user->u_next;
1160 user->handle->channels = user->u_next;
1162 user->u_next->u_prev = user->u_prev;
1166 if(do_gc && !channel->users && !IsProtected(channel))
1167 unregister_channel(channel, "lost all users.");
1170 static void expire_ban(void *data);
1172 static struct banData*
1173 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1176 unsigned int ii, l1, l2;
1181 bd = malloc(sizeof(struct banData));
1183 bd->channel = channel;
1185 bd->triggered = triggered;
1186 bd->expires = expires;
1188 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1190 extern const char *hidden_host_suffix;
1191 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1195 l2 = strlen(old_name);
1198 if(irccasecmp(mask + l1 - l2, old_name))
1200 new_mask = alloca(MAXLEN);
1201 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1204 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1206 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1207 bd->reason = reason ? strdup(reason) : NULL;
1210 timeq_add(expires, expire_ban, bd);
1213 bd->next = channel->bans;
1215 channel->bans->prev = bd;
1217 channel->banCount++;
1224 del_channel_ban(struct banData *ban)
1226 ban->channel->banCount--;
1230 ban->prev->next = ban->next;
1232 ban->channel->bans = ban->next;
1235 ban->next->prev = ban->prev;
1238 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1247 expire_ban(void *data)
1249 struct banData *bd = data;
1250 if(!IsSuspended(bd->channel))
1252 struct banList bans;
1253 struct mod_chanmode change;
1255 bans = bd->channel->channel->banlist;
1256 mod_chanmode_init(&change);
1257 for(ii=0; ii<bans.used; ii++)
1259 if(!strcmp(bans.list[ii]->ban, bd->mask))
1262 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1263 change.args[0].u.hostmask = bd->mask;
1264 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1270 del_channel_ban(bd);
1273 static void chanserv_expire_suspension(void *data);
1276 unregister_channel(struct chanData *channel, const char *reason)
1278 struct mod_chanmode change;
1279 char msgbuf[MAXLEN];
1281 /* After channel unregistration, the following must be cleaned
1283 - Channel information.
1286 - Channel suspension data.
1287 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1293 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 if(chanserv_conf.use_registered_mode)
1297 mod_chanmode_init(&change);
1298 change.modes_clear |= MODE_REGISTERED;
1299 mod_chanmode_announce(chanserv, channel->channel, &change);
1302 while(channel->users)
1303 del_channel_user(channel->users, 0);
1305 while(channel->bans)
1306 del_channel_ban(channel->bans);
1308 free(channel->topic);
1309 free(channel->registrar);
1310 free(channel->greeting);
1311 free(channel->user_greeting);
1312 free(channel->topic_mask);
1315 channel->prev->next = channel->next;
1317 channelList = channel->next;
1320 channel->next->prev = channel->prev;
1322 if(channel->suspended)
1324 struct chanNode *cNode = channel->channel;
1325 struct suspended *suspended, *next_suspended;
1327 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1329 next_suspended = suspended->previous;
1330 free(suspended->suspender);
1331 free(suspended->reason);
1332 if(suspended->expires)
1333 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1338 cNode->channel_info = NULL;
1340 channel->channel->channel_info = NULL;
1342 dict_delete(channel->notes);
1343 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1344 if(!IsSuspended(channel))
1345 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1346 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1347 UnlockChannel(channel->channel);
1349 registered_channels--;
1353 expire_channels(UNUSED_ARG(void *data))
1355 struct chanData *channel, *next;
1356 struct userData *user;
1357 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1359 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1360 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1362 for(channel = channelList; channel; channel = next)
1364 next = channel->next;
1366 /* See if the channel can be expired. */
1367 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1368 || IsProtected(channel))
1371 /* Make sure there are no high-ranking users still in the channel. */
1372 for(user=channel->users; user; user=user->next)
1373 if(user->present && (user->access >= UL_PRESENT))
1378 /* Unregister the channel */
1379 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1380 unregister_channel(channel, "registration expired.");
1383 if(chanserv_conf.channel_expire_frequency)
1384 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1388 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1390 char protect = channel->chOpts[chProtect];
1391 struct userData *cs_victim, *cs_aggressor;
1393 /* Don't protect if no one is to be protected, someone is attacking
1394 himself, or if the aggressor is an IRC Operator. */
1395 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1398 /* Don't protect if the victim isn't authenticated (because they
1399 can't be a channel user), unless we are to protect non-users
1401 cs_victim = GetChannelAccess(channel, victim->handle_info);
1402 if(protect != 'a' && !cs_victim)
1405 /* Protect if the aggressor isn't a user because at this point,
1406 the aggressor can only be less than or equal to the victim. */
1407 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1411 /* If the aggressor was a user, then the victim can't be helped. */
1418 if(cs_victim->access > cs_aggressor->access)
1423 if(cs_victim->access >= cs_aggressor->access)
1432 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1434 struct chanData *cData = channel->channel_info;
1435 struct userData *cs_victim;
1437 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1438 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1439 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1441 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1449 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1451 if(IsService(victim))
1453 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1457 if(protect_user(victim, user, channel->channel_info))
1459 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1466 static struct do_not_register *
1467 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1469 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1470 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1471 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1472 strcpy(dnr->reason, reason);
1474 if(dnr->chan_name[0] == '*')
1475 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1476 else if(strpbrk(dnr->chan_name, "*?"))
1477 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1479 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1483 static struct dnrList
1484 chanserv_find_dnrs(const char *chan_name, struct handle_info *handle)
1486 struct dnrList list;
1488 struct do_not_register *dnr;
1490 dnrList_init(&list);
1491 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1492 dnrList_append(&list, dnr);
1493 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1494 dnrList_append(&list, dnr);
1496 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1497 if(match_ircglob(chan_name, iter_key(it)))
1498 dnrList_append(&list, iter_data(it));
1503 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, struct handle_info *handle)
1505 struct dnrList list;
1506 struct do_not_register *dnr;
1508 char buf[INTERVALLEN];
1510 list = chanserv_find_dnrs(chan_name, handle);
1511 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1513 dnr = list.list[ii];
1516 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1517 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1520 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1523 reply("CSMSG_MORE_DNRS", list.used - ii);
1528 struct do_not_register *
1529 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1531 struct do_not_register *dnr;
1534 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1538 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1540 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1541 if(match_ircglob(chan_name, iter_key(it)))
1542 return iter_data(it);
1547 static CHANSERV_FUNC(cmd_noregister)
1550 struct do_not_register *dnr;
1551 char buf[INTERVALLEN];
1552 unsigned int matches;
1558 reply("CSMSG_DNR_SEARCH_RESULTS");
1560 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1562 dnr = iter_data(it);
1564 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1566 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1569 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1571 dnr = iter_data(it);
1573 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1575 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1578 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1580 dnr = iter_data(it);
1582 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1584 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1589 reply("MSG_MATCH_COUNT", matches);
1591 reply("MSG_NO_MATCHES");
1597 if(!IsChannelName(target) && (*target != '*'))
1599 reply("CSMSG_NOT_DNR", target);
1605 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1606 if((*target == '*') && !get_handle_info(target + 1))
1608 reply("MSG_HANDLE_UNKNOWN", target + 1);
1611 chanserv_add_dnr(target, user->handle_info->handle, reason);
1612 reply("CSMSG_NOREGISTER_CHANNEL", target);
1616 reply("CSMSG_DNR_SEARCH_RESULTS");
1618 matches = chanserv_show_dnrs(user, cmd, NULL, get_handle_info(target + 1));
1620 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1622 reply("MSG_NO_MATCHES");
1626 static CHANSERV_FUNC(cmd_allowregister)
1628 const char *chan_name = argv[1];
1630 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1632 dict_remove(handle_dnrs, chan_name+1);
1633 reply("CSMSG_DNR_REMOVED", chan_name);
1635 else if(dict_find(plain_dnrs, chan_name, NULL))
1637 dict_remove(plain_dnrs, chan_name);
1638 reply("CSMSG_DNR_REMOVED", chan_name);
1640 else if(dict_find(mask_dnrs, chan_name, NULL))
1642 dict_remove(mask_dnrs, chan_name);
1643 reply("CSMSG_DNR_REMOVED", chan_name);
1647 reply("CSMSG_NO_SUCH_DNR", chan_name);
1654 chanserv_get_owned_count(struct handle_info *hi)
1656 struct userData *cList;
1659 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1660 if(cList->access == UL_OWNER)
1665 static CHANSERV_FUNC(cmd_register)
1667 struct mod_chanmode *change;
1668 struct handle_info *handle;
1669 struct chanData *cData;
1670 struct modeNode *mn;
1671 char reason[MAXLEN];
1673 unsigned int new_channel, force=0;
1674 struct do_not_register *dnr;
1678 if(channel->channel_info)
1680 reply("CSMSG_ALREADY_REGGED", channel->name);
1684 if(channel->bad_channel)
1686 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1690 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1692 reply("CSMSG_MUST_BE_OPPED", channel->name);
1697 chan_name = channel->name;
1701 if((argc < 2) || !IsChannelName(argv[1]))
1703 reply("MSG_NOT_CHANNEL_NAME");
1707 if(opserv_bad_channel(argv[1]))
1709 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1714 chan_name = argv[1];
1717 if(argc >= (new_channel+2))
1719 if(!IsHelping(user))
1721 reply("CSMSG_PROXY_FORBIDDEN");
1725 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1727 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1728 dnr = chanserv_is_dnr(chan_name, handle);
1732 handle = user->handle_info;
1733 dnr = chanserv_is_dnr(chan_name, handle);
1737 if(!IsHelping(user))
1738 reply("CSMSG_DNR_CHANNEL", chan_name);
1740 chanserv_show_dnrs(user, cmd, chan_name, handle);
1744 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1746 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1751 channel = AddChannel(argv[1], now, NULL, NULL);
1753 cData = register_channel(channel, user->handle_info->handle);
1754 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1755 cData->modes = chanserv_conf.default_modes;
1756 if(chanserv_conf.use_registered_mode)
1757 cData->modes.modes_set |= MODE_REGISTERED;
1758 change = mod_chanmode_dup(&cData->modes, 1);
1759 change->args[change->argc].mode = MODE_CHANOP;
1760 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1762 mod_chanmode_announce(chanserv, channel, change);
1763 mod_chanmode_free(change);
1765 /* Initialize the channel's max user record. */
1766 cData->max = channel->members.used;
1768 if(handle != user->handle_info)
1769 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1771 reply("CSMSG_REG_SUCCESS", channel->name);
1773 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1774 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1779 make_confirmation_string(struct userData *uData)
1781 static char strbuf[16];
1786 for(src = uData->handle->handle; *src; )
1787 accum = accum * 31 + toupper(*src++);
1789 for(src = uData->channel->channel->name; *src; )
1790 accum = accum * 31 + toupper(*src++);
1791 sprintf(strbuf, "%08x", accum);
1795 static CHANSERV_FUNC(cmd_unregister)
1798 char reason[MAXLEN];
1799 struct chanData *cData;
1800 struct userData *uData;
1802 cData = channel->channel_info;
1805 reply("CSMSG_NOT_REGISTERED", channel->name);
1809 uData = GetChannelUser(cData, user->handle_info);
1810 if(!uData || (uData->access < UL_OWNER))
1812 reply("CSMSG_NO_ACCESS");
1816 if(IsProtected(cData))
1818 reply("CSMSG_UNREG_NODELETE", channel->name);
1822 if(!IsHelping(user))
1824 const char *confirm_string;
1825 if(IsSuspended(cData))
1827 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1830 confirm_string = make_confirmation_string(uData);
1831 if((argc < 2) || strcmp(argv[1], confirm_string))
1833 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1838 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1839 name = strdup(channel->name);
1840 unregister_channel(cData, reason);
1841 reply("CSMSG_UNREG_SUCCESS", name);
1846 static CHANSERV_FUNC(cmd_move)
1848 struct chanNode *target;
1849 struct modeNode *mn;
1850 struct userData *uData;
1851 char reason[MAXLEN];
1852 struct do_not_register *dnr;
1856 if(IsProtected(channel->channel_info))
1858 reply("CSMSG_MOVE_NODELETE", channel->name);
1862 if(!IsChannelName(argv[1]))
1864 reply("MSG_NOT_CHANNEL_NAME");
1868 if(opserv_bad_channel(argv[1]))
1870 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1874 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1876 for(uData = channel->channel_info->users; uData; uData = uData->next)
1878 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1880 if(!IsHelping(user))
1881 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1883 chanserv_show_dnrs(user, cmd, argv[1], uData->handle);
1889 if(!(target = GetChannel(argv[1])))
1891 target = AddChannel(argv[1], now, NULL, NULL);
1892 if(!IsSuspended(channel->channel_info))
1893 AddChannelUser(chanserv, target);
1895 else if(target->channel_info)
1897 reply("CSMSG_ALREADY_REGGED", target->name);
1900 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
1901 && !IsHelping(user))
1903 reply("CSMSG_MUST_BE_OPPED", target->name);
1906 else if(!IsSuspended(channel->channel_info))
1908 struct mod_chanmode change;
1909 mod_chanmode_init(&change);
1911 change.args[0].mode = MODE_CHANOP;
1912 change.args[0].u.member = AddChannelUser(chanserv, target);
1913 mod_chanmode_announce(chanserv, target, &change);
1916 /* Move the channel_info to the target channel; it
1917 shouldn't be necessary to clear timeq callbacks
1918 for the old channel. */
1919 target->channel_info = channel->channel_info;
1920 target->channel_info->channel = target;
1921 channel->channel_info = NULL;
1923 reply("CSMSG_MOVE_SUCCESS", target->name);
1925 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1926 if(!IsSuspended(target->channel_info))
1928 char reason2[MAXLEN];
1929 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1930 DelChannelUser(chanserv, channel, reason2, 0);
1932 UnlockChannel(channel);
1933 LockChannel(target);
1934 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1939 merge_users(struct chanData *source, struct chanData *target)
1941 struct userData *suData, *tuData, *next;
1947 /* Insert the source's users into the scratch area. */
1948 for(suData = source->users; suData; suData = suData->next)
1949 dict_insert(merge, suData->handle->handle, suData);
1951 /* Iterate through the target's users, looking for
1952 users common to both channels. The lower access is
1953 removed from either the scratch area or target user
1955 for(tuData = target->users; tuData; tuData = next)
1957 struct userData *choice;
1959 next = tuData->next;
1961 /* If a source user exists with the same handle as a target
1962 channel's user, resolve the conflict by removing one. */
1963 suData = dict_find(merge, tuData->handle->handle, NULL);
1967 /* Pick the data we want to keep. */
1968 /* If the access is the same, use the later seen time. */
1969 if(suData->access == tuData->access)
1970 choice = (suData->seen > tuData->seen) ? suData : tuData;
1971 else /* Otherwise, keep the higher access level. */
1972 choice = (suData->access > tuData->access) ? suData : tuData;
1974 /* Remove the user that wasn't picked. */
1975 if(choice == tuData)
1977 dict_remove(merge, suData->handle->handle);
1978 del_channel_user(suData, 0);
1981 del_channel_user(tuData, 0);
1984 /* Move the remaining users to the target channel. */
1985 for(it = dict_first(merge); it; it = iter_next(it))
1987 suData = iter_data(it);
1989 /* Insert the user into the target channel's linked list. */
1990 suData->prev = NULL;
1991 suData->next = target->users;
1992 suData->channel = target;
1995 target->users->prev = suData;
1996 target->users = suData;
1998 /* Update the user counts for the target channel; the
1999 source counts are left alone. */
2000 target->userCount++;
2003 /* Possible to assert (source->users == NULL) here. */
2004 source->users = NULL;
2009 merge_bans(struct chanData *source, struct chanData *target)
2011 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2013 /* Hold on to the original head of the target ban list
2014 to avoid comparing source bans with source bans. */
2015 tFront = target->bans;
2017 /* Perform a totally expensive O(n*m) merge, ick. */
2018 for(sbData = source->bans; sbData; sbData = sNext)
2020 /* Flag to track whether the ban's been moved
2021 to the destination yet. */
2024 /* Possible to assert (sbData->prev == NULL) here. */
2025 sNext = sbData->next;
2027 for(tbData = tFront; tbData; tbData = tNext)
2029 tNext = tbData->next;
2031 /* Perform two comparisons between each source
2032 and target ban, conflicts are resolved by
2033 keeping the broader ban and copying the later
2034 expiration and triggered time. */
2035 if(match_ircglobs(tbData->mask, sbData->mask))
2037 /* There is a broader ban in the target channel that
2038 overrides one in the source channel; remove the
2039 source ban and break. */
2040 if(sbData->expires > tbData->expires)
2041 tbData->expires = sbData->expires;
2042 if(sbData->triggered > tbData->triggered)
2043 tbData->triggered = sbData->triggered;
2044 del_channel_ban(sbData);
2047 else if(match_ircglobs(sbData->mask, tbData->mask))
2049 /* There is a broader ban in the source channel that
2050 overrides one in the target channel; remove the
2051 target ban, fall through and move the source over. */
2052 if(tbData->expires > sbData->expires)
2053 sbData->expires = tbData->expires;
2054 if(tbData->triggered > sbData->triggered)
2055 sbData->triggered = tbData->triggered;
2056 if(tbData == tFront)
2058 del_channel_ban(tbData);
2061 /* Source bans can override multiple target bans, so
2062 we allow a source to run through this loop multiple
2063 times, but we can only move it once. */
2068 /* Remove the source ban from the source ban list. */
2070 sbData->next->prev = sbData->prev;
2072 /* Modify the source ban's associated channel. */
2073 sbData->channel = target;
2075 /* Insert the ban into the target channel's linked list. */
2076 sbData->prev = NULL;
2077 sbData->next = target->bans;
2080 target->bans->prev = sbData;
2081 target->bans = sbData;
2083 /* Update the user counts for the target channel. */
2088 /* Possible to assert (source->bans == NULL) here. */
2089 source->bans = NULL;
2093 merge_data(struct chanData *source, struct chanData *target)
2095 if(source->visited > target->visited)
2096 target->visited = source->visited;
2100 merge_channel(struct chanData *source, struct chanData *target)
2102 merge_users(source, target);
2103 merge_bans(source, target);
2104 merge_data(source, target);
2107 static CHANSERV_FUNC(cmd_merge)
2109 struct userData *target_user;
2110 struct chanNode *target;
2111 char reason[MAXLEN];
2115 /* Make sure the target channel exists and is registered to the user
2116 performing the command. */
2117 if(!(target = GetChannel(argv[1])))
2119 reply("MSG_INVALID_CHANNEL");
2123 if(!target->channel_info)
2125 reply("CSMSG_NOT_REGISTERED", target->name);
2129 if(IsProtected(channel->channel_info))
2131 reply("CSMSG_MERGE_NODELETE");
2135 if(IsSuspended(target->channel_info))
2137 reply("CSMSG_MERGE_SUSPENDED");
2141 if(channel == target)
2143 reply("CSMSG_MERGE_SELF");
2147 target_user = GetChannelUser(target->channel_info, user->handle_info);
2148 if(!target_user || (target_user->access < UL_OWNER))
2150 reply("CSMSG_MERGE_NOT_OWNER");
2154 /* Merge the channel structures and associated data. */
2155 merge_channel(channel->channel_info, target->channel_info);
2156 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2157 unregister_channel(channel->channel_info, reason);
2158 reply("CSMSG_MERGE_SUCCESS", target->name);
2162 static CHANSERV_FUNC(cmd_opchan)
2164 struct mod_chanmode change;
2165 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2167 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2170 channel->channel_info->may_opchan = 0;
2171 mod_chanmode_init(&change);
2173 change.args[0].mode = MODE_CHANOP;
2174 change.args[0].u.member = GetUserMode(channel, chanserv);
2175 mod_chanmode_announce(chanserv, channel, &change);
2176 reply("CSMSG_OPCHAN_DONE", channel->name);
2180 static CHANSERV_FUNC(cmd_adduser)
2182 struct userData *actee;
2183 struct userData *actor;
2184 struct handle_info *handle;
2185 unsigned short access;
2189 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2191 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2195 access = user_level_from_name(argv[2], UL_OWNER);
2198 reply("CSMSG_INVALID_ACCESS", argv[2]);
2202 actor = GetChannelUser(channel->channel_info, user->handle_info);
2203 if(actor->access <= access)
2205 reply("CSMSG_NO_BUMP_ACCESS");
2209 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2212 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2214 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2218 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2219 scan_user_presence(actee, NULL);
2220 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2224 static CHANSERV_FUNC(cmd_clvl)
2226 struct handle_info *handle;
2227 struct userData *victim;
2228 struct userData *actor;
2229 unsigned short new_access;
2230 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2234 actor = GetChannelUser(channel->channel_info, user->handle_info);
2236 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2239 if(handle == user->handle_info && !privileged)
2241 reply("CSMSG_NO_SELF_CLVL");
2245 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2247 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2251 if(actor->access <= victim->access && !privileged)
2253 reply("MSG_USER_OUTRANKED", handle->handle);
2257 new_access = user_level_from_name(argv[2], UL_OWNER);
2261 reply("CSMSG_INVALID_ACCESS", argv[2]);
2265 if(new_access >= actor->access && !privileged)
2267 reply("CSMSG_NO_BUMP_ACCESS");
2271 victim->access = new_access;
2272 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2276 static CHANSERV_FUNC(cmd_deluser)
2278 struct handle_info *handle;
2279 struct userData *victim;
2280 struct userData *actor;
2281 unsigned short access;
2286 actor = GetChannelUser(channel->channel_info, user->handle_info);
2288 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2291 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2293 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2299 access = user_level_from_name(argv[1], UL_OWNER);
2302 reply("CSMSG_INVALID_ACCESS", argv[1]);
2305 if(access != victim->access)
2307 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2313 access = victim->access;
2316 if((actor->access <= victim->access) && !IsHelping(user))
2318 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2322 chan_name = strdup(channel->name);
2323 del_channel_user(victim, 1);
2324 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2330 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2332 struct userData *actor, *uData, *next;
2334 actor = GetChannelUser(channel->channel_info, user->handle_info);
2336 if(min_access > max_access)
2338 reply("CSMSG_BAD_RANGE", min_access, max_access);
2342 if((actor->access <= max_access) && !IsHelping(user))
2344 reply("CSMSG_NO_ACCESS");
2348 for(uData = channel->channel_info->users; uData; uData = next)
2352 if((uData->access >= min_access)
2353 && (uData->access <= max_access)
2354 && match_ircglob(uData->handle->handle, mask))
2355 del_channel_user(uData, 1);
2358 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2362 static CHANSERV_FUNC(cmd_mdelowner)
2364 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2367 static CHANSERV_FUNC(cmd_mdelcoowner)
2369 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2372 static CHANSERV_FUNC(cmd_mdelmaster)
2374 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2377 static CHANSERV_FUNC(cmd_mdelop)
2379 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2382 static CHANSERV_FUNC(cmd_mdelpeon)
2384 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2388 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2390 struct banData *bData, *next;
2391 char interval[INTERVALLEN];
2396 limit = now - duration;
2397 for(bData = channel->channel_info->bans; bData; bData = next)
2401 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2404 del_channel_ban(bData);
2408 intervalString(interval, duration, user->handle_info);
2409 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2414 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2416 struct userData *actor, *uData, *next;
2417 char interval[INTERVALLEN];
2421 actor = GetChannelUser(channel->channel_info, user->handle_info);
2422 if(min_access > max_access)
2424 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2428 if((actor->access <= max_access) && !IsHelping(user))
2430 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2435 limit = now - duration;
2436 for(uData = channel->channel_info->users; uData; uData = next)
2440 if((uData->seen > limit) || uData->present)
2443 if(((uData->access >= min_access) && (uData->access <= max_access))
2444 || (!max_access && (uData->access < actor->access)))
2446 del_channel_user(uData, 1);
2454 max_access = (uData->access >= UL_OWNER) ? UL_OWNER : (uData->access - 1);
2456 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2460 static CHANSERV_FUNC(cmd_trim)
2462 unsigned long duration;
2463 unsigned short min_level, max_level;
2467 duration = ParseInterval(argv[2]);
2470 reply("CSMSG_CANNOT_TRIM");
2474 if(!irccasecmp(argv[1], "bans"))
2476 cmd_trim_bans(user, channel, duration);
2479 else if(!irccasecmp(argv[1], "users"))
2481 cmd_trim_users(user, channel, 0, 0, duration);
2484 else if(parse_level_range(&min_level, &max_level, argv[1]))
2486 cmd_trim_users(user, channel, min_level, max_level, duration);
2489 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2491 cmd_trim_users(user, channel, min_level, min_level, duration);
2496 reply("CSMSG_INVALID_TRIM", argv[1]);
2501 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2502 to the user. cmd_all takes advantage of this. */
2503 static CHANSERV_FUNC(cmd_up)
2505 struct mod_chanmode change;
2506 struct userData *uData;
2509 mod_chanmode_init(&change);
2511 change.args[0].u.member = GetUserMode(channel, user);
2512 if(!change.args[0].u.member)
2515 reply("MSG_CHANNEL_ABSENT", channel->name);
2519 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2523 reply("CSMSG_GODMODE_UP", argv[0]);
2526 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2528 change.args[0].mode = MODE_CHANOP;
2529 errmsg = "CSMSG_ALREADY_OPPED";
2531 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2533 change.args[0].mode = MODE_VOICE;
2534 errmsg = "CSMSG_ALREADY_VOICED";
2539 reply("CSMSG_NO_ACCESS");
2542 change.args[0].mode &= ~change.args[0].u.member->modes;
2543 if(!change.args[0].mode)
2546 reply(errmsg, channel->name);
2549 modcmd_chanmode_announce(&change);
2553 static CHANSERV_FUNC(cmd_down)
2555 struct mod_chanmode change;
2557 mod_chanmode_init(&change);
2559 change.args[0].u.member = GetUserMode(channel, user);
2560 if(!change.args[0].u.member)
2563 reply("MSG_CHANNEL_ABSENT", channel->name);
2567 if(!change.args[0].u.member->modes)
2570 reply("CSMSG_ALREADY_DOWN", channel->name);
2574 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2575 modcmd_chanmode_announce(&change);
2579 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)
2581 struct userData *cList;
2583 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2585 if(IsSuspended(cList->channel)
2586 || IsUserSuspended(cList)
2587 || !GetUserMode(cList->channel->channel, user))
2590 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2596 static CHANSERV_FUNC(cmd_upall)
2598 return cmd_all(CSFUNC_ARGS, cmd_up);
2601 static CHANSERV_FUNC(cmd_downall)
2603 return cmd_all(CSFUNC_ARGS, cmd_down);
2606 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2607 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2610 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)
2612 unsigned int ii, valid;
2613 struct userNode *victim;
2614 struct mod_chanmode *change;
2616 change = mod_chanmode_alloc(argc - 1);
2618 for(ii=valid=0; ++ii < argc; )
2620 if(!(victim = GetUserH(argv[ii])))
2622 change->args[valid].mode = mode;
2623 change->args[valid].u.member = GetUserMode(channel, victim);
2624 if(!change->args[valid].u.member)
2626 if(validate && !validate(user, channel, victim))
2631 change->argc = valid;
2632 if(valid < (argc-1))
2633 reply("CSMSG_PROCESS_FAILED");
2636 modcmd_chanmode_announce(change);
2637 reply(action, channel->name);
2639 mod_chanmode_free(change);
2643 static CHANSERV_FUNC(cmd_op)
2645 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2648 static CHANSERV_FUNC(cmd_deop)
2650 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2653 static CHANSERV_FUNC(cmd_voice)
2655 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2658 static CHANSERV_FUNC(cmd_devoice)
2660 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2664 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2670 for(ii=0; ii<channel->members.used; ii++)
2672 struct modeNode *mn = channel->members.list[ii];
2674 if(IsService(mn->user))
2677 if(!user_matches_glob(mn->user, ban, 1))
2680 if(protect_user(mn->user, user, channel->channel_info))
2684 victims[(*victimCount)++] = mn;
2690 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2692 struct userNode *victim;
2693 struct modeNode **victims;
2694 unsigned int offset, n, victimCount, duration = 0;
2695 char *reason = "Bye.", *ban, *name;
2696 char interval[INTERVALLEN];
2698 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2699 REQUIRE_PARAMS(offset);
2702 reason = unsplit_string(argv + offset, argc - offset, NULL);
2703 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2705 /* Truncate the reason to a length of TOPICLEN, as
2706 the ircd does; however, leave room for an ellipsis
2707 and the kicker's nick. */
2708 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2712 if((victim = GetUserH(argv[1])))
2714 victims = alloca(sizeof(victims[0]));
2715 victims[0] = GetUserMode(channel, victim);
2716 /* XXX: The comparison with ACTION_KICK is just because all
2717 * other actions can work on users outside the channel, and we
2718 * want to allow those (e.g. unbans) in that case. If we add
2719 * some other ejection action for in-channel users, change
2721 victimCount = victims[0] ? 1 : 0;
2723 if(IsService(victim))
2725 reply("MSG_SERVICE_IMMUNE", victim->nick);
2729 if((action == ACTION_KICK) && !victimCount)
2731 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2735 if(protect_user(victim, user, channel->channel_info))
2737 reply("CSMSG_USER_PROTECTED", victim->nick);
2741 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2742 name = victim->nick;
2746 if(!is_ircmask(argv[1]))
2748 reply("MSG_NICK_UNKNOWN", argv[1]);
2752 victims = alloca(sizeof(victims[0]) * channel->members.used);
2754 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2756 reply("CSMSG_MASK_PROTECTED", argv[1]);
2760 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2762 reply("CSMSG_LAME_MASK", argv[1]);
2766 if((action == ACTION_KICK) && (victimCount == 0))
2768 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2772 name = ban = strdup(argv[1]);
2775 /* Truncate the ban in place if necessary; we must ensure
2776 that 'ban' is a valid ban mask before sanitizing it. */
2777 sanitize_ircmask(ban);
2779 if(action & ACTION_ADD_BAN)
2781 struct banData *bData, *next;
2783 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2785 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2790 if(action & ACTION_ADD_TIMED_BAN)
2792 duration = ParseInterval(argv[2]);
2796 reply("CSMSG_DURATION_TOO_LOW");
2800 else if(duration > (86400 * 365 * 2))
2802 reply("CSMSG_DURATION_TOO_HIGH");
2808 for(bData = channel->channel_info->bans; bData; bData = next)
2810 if(match_ircglobs(bData->mask, ban))
2812 int exact = !irccasecmp(bData->mask, ban);
2814 /* The ban is redundant; there is already a ban
2815 with the same effect in place. */
2819 free(bData->reason);
2820 bData->reason = strdup(reason);
2821 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2823 reply("CSMSG_REASON_CHANGE", ban);
2827 if(exact && bData->expires)
2831 /* If the ban matches an existing one exactly,
2832 extend the expiration time if the provided
2833 duration is longer. */
2834 if(duration && ((time_t)(now + duration) > bData->expires))
2836 bData->expires = now + duration;
2847 /* Delete the expiration timeq entry and
2848 requeue if necessary. */
2849 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2852 timeq_add(bData->expires, expire_ban, bData);
2856 /* automated kickban */
2859 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2861 reply("CSMSG_BAN_ADDED", name, channel->name);
2867 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2874 if(match_ircglobs(ban, bData->mask))
2876 /* The ban we are adding makes previously existing
2877 bans redundant; silently remove them. */
2878 del_channel_ban(bData);
2882 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);
2884 name = ban = strdup(bData->mask);
2888 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2890 extern const char *hidden_host_suffix;
2891 const char *old_name = chanserv_conf.old_ban_names->list[n];
2893 unsigned int l1, l2;
2896 l2 = strlen(old_name);
2899 if(irccasecmp(ban + l1 - l2, old_name))
2901 new_mask = malloc(MAXLEN);
2902 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2904 name = ban = new_mask;
2909 if(action & ACTION_BAN)
2911 unsigned int exists;
2912 struct mod_chanmode *change;
2914 if(channel->banlist.used >= MAXBANS)
2917 reply("CSMSG_BANLIST_FULL", channel->name);
2922 exists = ChannelBanExists(channel, ban);
2923 change = mod_chanmode_alloc(victimCount + 1);
2924 for(n = 0; n < victimCount; ++n)
2926 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2927 change->args[n].u.member = victims[n];
2931 change->args[n].mode = MODE_BAN;
2932 change->args[n++].u.hostmask = ban;
2936 modcmd_chanmode_announce(change);
2938 mod_chanmode_announce(chanserv, channel, change);
2939 mod_chanmode_free(change);
2941 if(exists && (action == ACTION_BAN))
2944 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2950 if(action & ACTION_KICK)
2952 char kick_reason[MAXLEN];
2953 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2955 for(n = 0; n < victimCount; n++)
2956 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2961 /* No response, since it was automated. */
2963 else if(action & ACTION_ADD_BAN)
2966 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2968 reply("CSMSG_BAN_ADDED", name, channel->name);
2970 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2971 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2972 else if(action & ACTION_BAN)
2973 reply("CSMSG_BAN_DONE", name, channel->name);
2974 else if(action & ACTION_KICK && victimCount)
2975 reply("CSMSG_KICK_DONE", name, channel->name);
2981 static CHANSERV_FUNC(cmd_kickban)
2983 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2986 static CHANSERV_FUNC(cmd_kick)
2988 return eject_user(CSFUNC_ARGS, ACTION_KICK);
2991 static CHANSERV_FUNC(cmd_ban)
2993 return eject_user(CSFUNC_ARGS, ACTION_BAN);
2996 static CHANSERV_FUNC(cmd_addban)
2998 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3001 static CHANSERV_FUNC(cmd_addtimedban)
3003 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3006 static struct mod_chanmode *
3007 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3009 struct mod_chanmode *change;
3010 unsigned char *match;
3011 unsigned int ii, count;
3013 match = alloca(bans->used);
3016 for(ii = count = 0; ii < bans->used; ++ii)
3018 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3027 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3034 change = mod_chanmode_alloc(count);
3035 for(ii = count = 0; ii < bans->used; ++ii)
3039 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3040 change->args[count++].u.hostmask = bans->list[ii]->ban;
3046 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3048 struct userNode *actee;
3054 /* may want to allow a comma delimited list of users... */
3055 if(!(actee = GetUserH(argv[1])))
3057 if(!is_ircmask(argv[1]))
3059 reply("MSG_NICK_UNKNOWN", argv[1]);
3063 mask = strdup(argv[1]);
3066 /* We don't sanitize the mask here because ircu
3068 if(action & ACTION_UNBAN)
3070 struct mod_chanmode *change;
3071 change = find_matching_bans(&channel->banlist, actee, mask);
3074 modcmd_chanmode_announce(change);
3075 mod_chanmode_free(change);
3080 if(action & ACTION_DEL_BAN)
3082 struct banData *ban, *next;
3084 ban = channel->channel_info->bans;
3088 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3091 for( ; ban && !match_ircglobs(mask, ban->mask);
3096 del_channel_ban(ban);
3103 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3105 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3111 static CHANSERV_FUNC(cmd_unban)
3113 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3116 static CHANSERV_FUNC(cmd_delban)
3118 /* it doesn't necessarily have to remove the channel ban - may want
3119 to make that an option. */
3120 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3123 static CHANSERV_FUNC(cmd_unbanme)
3125 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3126 long flags = ACTION_UNBAN;
3128 /* remove permanent bans if the user has the proper access. */
3129 if(uData->access >= UL_MASTER)
3130 flags |= ACTION_DEL_BAN;
3132 argv[1] = user->nick;
3133 return unban_user(user, channel, 2, argv, cmd, flags);
3136 static CHANSERV_FUNC(cmd_unbanall)
3138 struct mod_chanmode *change;
3141 if(!channel->banlist.used)
3143 reply("CSMSG_NO_BANS", channel->name);
3147 change = mod_chanmode_alloc(channel->banlist.used);
3148 for(ii=0; ii<channel->banlist.used; ii++)
3150 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3151 change->args[ii].u.hostmask = channel->banlist.list[ii]->ban;
3153 modcmd_chanmode_announce(change);
3154 mod_chanmode_free(change);
3155 reply("CSMSG_BANS_REMOVED", channel->name);
3159 static CHANSERV_FUNC(cmd_open)
3161 struct mod_chanmode *change;
3163 change = find_matching_bans(&channel->banlist, user, NULL);
3165 change = mod_chanmode_alloc(0);
3166 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3167 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3168 && channel->channel_info->modes.modes_set)
3169 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3170 modcmd_chanmode_announce(change);
3171 reply("CSMSG_CHANNEL_OPENED", channel->name);
3172 mod_chanmode_free(change);
3176 static CHANSERV_FUNC(cmd_myaccess)
3178 static struct string_buffer sbuf;
3179 struct handle_info *target_handle;
3180 struct userData *uData;
3183 target_handle = user->handle_info;
3184 else if(!IsHelping(user))
3186 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3189 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3192 if(!target_handle->channels)
3194 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3198 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3199 for(uData = target_handle->channels; uData; uData = uData->u_next)
3201 struct chanData *cData = uData->channel;
3203 if(uData->access > UL_OWNER)
3205 if(IsProtected(cData)
3206 && (target_handle != user->handle_info)
3207 && !GetTrueChannelAccess(cData, user->handle_info))
3210 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3211 if(uData->flags != USER_AUTO_OP)
3212 string_buffer_append(&sbuf, ',');
3213 if(IsUserSuspended(uData))
3214 string_buffer_append(&sbuf, 's');
3215 if(IsUserAutoOp(uData))
3217 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3218 string_buffer_append(&sbuf, 'o');
3219 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3220 string_buffer_append(&sbuf, 'v');
3222 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3223 string_buffer_append(&sbuf, 'i');
3225 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3227 string_buffer_append_string(&sbuf, ")]");
3228 string_buffer_append(&sbuf, '\0');
3229 send_message_type(4, user, cmd->parent->bot, sbuf.list);
3235 static CHANSERV_FUNC(cmd_access)
3237 struct userNode *target;
3238 struct handle_info *target_handle;
3239 struct userData *uData;
3241 char prefix[MAXLEN];
3246 target_handle = target->handle_info;
3248 else if((target = GetUserH(argv[1])))
3250 target_handle = target->handle_info;
3252 else if(argv[1][0] == '*')
3254 if(!(target_handle = get_handle_info(argv[1]+1)))
3256 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3262 reply("MSG_NICK_UNKNOWN", argv[1]);
3266 assert(target || target_handle);
3268 if(target == chanserv)
3270 reply("CSMSG_IS_CHANSERV");
3278 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3283 reply("MSG_USER_AUTHENTICATE", target->nick);
3286 reply("MSG_AUTHENTICATE");
3292 const char *epithet = NULL, *type = NULL;
3295 epithet = chanserv_conf.irc_operator_epithet;
3298 else if(IsNetworkHelper(target))
3300 epithet = chanserv_conf.network_helper_epithet;
3301 type = "network helper";
3303 else if(IsSupportHelper(target))
3305 epithet = chanserv_conf.support_helper_epithet;
3306 type = "support helper";
3310 if(target_handle->epithet)
3311 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3313 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3315 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3319 sprintf(prefix, "%s", target_handle->handle);
3322 if(!channel->channel_info)
3324 reply("CSMSG_NOT_REGISTERED", channel->name);
3328 helping = HANDLE_FLAGGED(target_handle, HELPING)
3329 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3330 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3332 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3333 /* To prevent possible information leaks, only show infolines
3334 * if the requestor is in the channel or it's their own
3336 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3338 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3340 /* Likewise, only say it's suspended if the user has active
3341 * access in that channel or it's their own entry. */
3342 if(IsUserSuspended(uData)
3343 && (GetChannelUser(channel->channel_info, user->handle_info)
3344 || (user->handle_info == uData->handle)))
3346 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3351 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3358 zoot_list(struct listData *list)
3360 struct userData *uData;
3361 unsigned int start, curr, highest, lowest;
3362 struct helpfile_table tmp_table;
3363 const char **temp, *msg;
3365 if(list->table.length == 1)
3368 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3370 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3371 msg = user_find_message(list->user, "MSG_NONE");
3372 send_message_type(4, list->user, list->bot, " %s", msg);
3374 tmp_table.width = list->table.width;
3375 tmp_table.flags = list->table.flags;
3376 list->table.contents[0][0] = " ";
3377 highest = list->highest;
3378 if(list->lowest != 0)
3379 lowest = list->lowest;
3380 else if(highest < 100)
3383 lowest = highest - 100;
3384 for(start = curr = 1; curr < list->table.length; )
3386 uData = list->users[curr-1];
3387 list->table.contents[curr++][0] = " ";
3388 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3391 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3393 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3394 temp = list->table.contents[--start];
3395 list->table.contents[start] = list->table.contents[0];
3396 tmp_table.contents = list->table.contents + start;
3397 tmp_table.length = curr - start;
3398 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3399 list->table.contents[start] = temp;
3401 highest = lowest - 1;
3402 lowest = (highest < 100) ? 0 : (highest - 99);
3408 def_list(struct listData *list)
3412 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3414 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3415 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3416 if(list->table.length == 1)
3418 msg = user_find_message(list->user, "MSG_NONE");
3419 send_message_type(4, list->user, list->bot, " %s", msg);
3424 userData_access_comp(const void *arg_a, const void *arg_b)
3426 const struct userData *a = *(struct userData**)arg_a;
3427 const struct userData *b = *(struct userData**)arg_b;
3429 if(a->access != b->access)
3430 res = b->access - a->access;
3432 res = irccasecmp(a->handle->handle, b->handle->handle);
3437 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3439 void (*send_list)(struct listData *);
3440 struct userData *uData;
3441 struct listData lData;
3442 unsigned int matches;
3446 lData.bot = cmd->parent->bot;
3447 lData.channel = channel;
3448 lData.lowest = lowest;
3449 lData.highest = highest;
3450 lData.search = (argc > 1) ? argv[1] : NULL;
3451 send_list = def_list;
3452 (void)zoot_list; /* since it doesn't show user levels */
3454 if(user->handle_info)
3456 switch(user->handle_info->userlist_style)
3458 case HI_STYLE_DEF: send_list = def_list; break;
3459 case HI_STYLE_ZOOT: send_list = def_list; break;
3463 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3465 for(uData = channel->channel_info->users; uData; uData = uData->next)
3467 if((uData->access < lowest)
3468 || (uData->access > highest)
3469 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3471 lData.users[matches++] = uData;
3473 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3475 lData.table.length = matches+1;
3476 lData.table.width = 4;
3477 lData.table.flags = TABLE_NO_FREE;
3478 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3479 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3480 lData.table.contents[0] = ary;
3483 ary[2] = "Last Seen";
3485 for(matches = 1; matches < lData.table.length; ++matches)
3487 struct userData *uData = lData.users[matches-1];
3488 char seen[INTERVALLEN];
3490 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3491 lData.table.contents[matches] = ary;
3492 ary[0] = strtab(uData->access);
3493 ary[1] = uData->handle->handle;
3496 else if(!uData->seen)
3499 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3500 ary[2] = strdup(ary[2]);
3501 if(IsUserSuspended(uData))
3502 ary[3] = "Suspended";
3503 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3504 ary[3] = "Vacation";
3509 for(matches = 1; matches < lData.table.length; ++matches)
3511 free((char*)lData.table.contents[matches][2]);
3512 free(lData.table.contents[matches]);
3514 free(lData.table.contents[0]);
3515 free(lData.table.contents);
3519 static CHANSERV_FUNC(cmd_users)
3521 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3524 static CHANSERV_FUNC(cmd_wlist)
3526 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3529 static CHANSERV_FUNC(cmd_clist)
3531 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3534 static CHANSERV_FUNC(cmd_mlist)
3536 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3539 static CHANSERV_FUNC(cmd_olist)
3541 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3544 static CHANSERV_FUNC(cmd_plist)
3546 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3549 static CHANSERV_FUNC(cmd_bans)
3551 struct helpfile_table tbl;
3552 unsigned int matches = 0, timed = 0, ii;
3553 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3554 const char *msg_never, *triggered, *expires;
3555 struct banData *ban, **bans;
3562 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3564 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3566 if(search && !match_ircglobs(search, ban->mask))
3568 bans[matches++] = ban;
3573 tbl.length = matches + 1;
3574 tbl.width = 4 + timed;
3576 tbl.flags = TABLE_NO_FREE;
3577 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3578 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3579 tbl.contents[0][0] = "Mask";
3580 tbl.contents[0][1] = "Set By";
3581 tbl.contents[0][2] = "Triggered";
3584 tbl.contents[0][3] = "Expires";
3585 tbl.contents[0][4] = "Reason";
3588 tbl.contents[0][3] = "Reason";
3591 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3593 free(tbl.contents[0]);
3598 msg_never = user_find_message(user, "MSG_NEVER");
3599 for(ii = 0; ii < matches; )
3605 else if(ban->expires)
3606 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3608 expires = msg_never;
3611 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3613 triggered = msg_never;
3615 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3616 tbl.contents[ii][0] = ban->mask;
3617 tbl.contents[ii][1] = ban->owner;
3618 tbl.contents[ii][2] = strdup(triggered);
3621 tbl.contents[ii][3] = strdup(expires);
3622 tbl.contents[ii][4] = ban->reason;
3625 tbl.contents[ii][3] = ban->reason;
3627 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3628 reply("MSG_MATCH_COUNT", matches);
3629 for(ii = 1; ii < tbl.length; ++ii)
3631 free((char*)tbl.contents[ii][2]);
3633 free((char*)tbl.contents[ii][3]);
3634 free(tbl.contents[ii]);
3636 free(tbl.contents[0]);
3642 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3644 struct chanData *cData = channel->channel_info;
3645 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3647 if(cData->topic_mask)
3648 return !match_ircglob(new_topic, cData->topic_mask);
3649 else if(cData->topic)
3650 return irccasecmp(new_topic, cData->topic);
3655 static CHANSERV_FUNC(cmd_topic)
3657 struct chanData *cData;
3660 cData = channel->channel_info;
3665 SetChannelTopic(channel, chanserv, cData->topic, 1);
3666 reply("CSMSG_TOPIC_SET", cData->topic);
3670 reply("CSMSG_NO_TOPIC", channel->name);
3674 topic = unsplit_string(argv + 1, argc - 1, NULL);
3675 /* If they say "!topic *", use an empty topic. */
3676 if((topic[0] == '*') && (topic[1] == 0))
3678 if(bad_topic(channel, user, topic))
3680 char *topic_mask = cData->topic_mask;
3683 char new_topic[TOPICLEN+1], tchar;
3684 int pos=0, starpos=-1, dpos=0, len;
3686 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3693 len = strlen(topic);
3694 if((dpos + len) > TOPICLEN)
3695 len = TOPICLEN + 1 - dpos;
3696 memcpy(new_topic+dpos, topic, len);
3700 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3701 default: new_topic[dpos++] = tchar; break;
3704 if((dpos > TOPICLEN) || tchar)
3707 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3708 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3711 new_topic[dpos] = 0;
3712 SetChannelTopic(channel, chanserv, new_topic, 1);
3714 reply("CSMSG_TOPIC_LOCKED", channel->name);
3719 SetChannelTopic(channel, chanserv, topic, 1);
3721 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3723 /* Grab the topic and save it as the default topic. */
3725 cData->topic = strdup(channel->topic);
3731 static CHANSERV_FUNC(cmd_mode)
3733 struct mod_chanmode *change;
3737 change = &channel->channel_info->modes;
3738 if(change->modes_set || change->modes_clear) {
3739 modcmd_chanmode_announce(change);
3740 reply("CSMSG_DEFAULTED_MODES", channel->name);
3742 reply("CSMSG_NO_MODES", channel->name);
3746 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3749 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3753 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3754 && mode_lock_violated(&channel->channel_info->modes, change))
3757 mod_chanmode_format(&channel->channel_info->modes, modes);
3758 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3762 modcmd_chanmode_announce(change);
3763 mod_chanmode_free(change);
3764 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3768 static CHANSERV_FUNC(cmd_invite)
3770 struct userData *uData;
3771 struct userNode *invite;
3773 uData = GetChannelUser(channel->channel_info, user->handle_info);
3777 if(!(invite = GetUserH(argv[1])))
3779 reply("MSG_NICK_UNKNOWN", argv[1]);
3786 if(GetUserMode(channel, invite))
3788 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3796 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3797 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3800 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3802 irc_invite(chanserv, invite, channel);
3804 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3809 static CHANSERV_FUNC(cmd_inviteme)
3811 if(GetUserMode(channel, user))
3813 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3816 if(channel->channel_info
3817 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3819 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3822 irc_invite(cmd->parent->bot, user, channel);
3827 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3830 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3832 /* We display things based on two dimensions:
3833 * - Issue time: present or absent
3834 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3835 * (in order of precedence, so something both expired and revoked
3836 * only counts as revoked)
3838 combo = (suspended->issued ? 4 : 0)
3839 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3841 case 0: /* no issue time, indefinite expiration */
3842 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3844 case 1: /* no issue time, expires in future */
3845 intervalString(buf1, suspended->expires-now, user->handle_info);
3846 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3848 case 2: /* no issue time, expired */
3849 intervalString(buf1, now-suspended->expires, user->handle_info);
3850 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3852 case 3: /* no issue time, revoked */
3853 intervalString(buf1, now-suspended->revoked, user->handle_info);
3854 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3856 case 4: /* issue time set, indefinite expiration */
3857 intervalString(buf1, now-suspended->issued, user->handle_info);
3858 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3860 case 5: /* issue time set, expires in future */
3861 intervalString(buf1, now-suspended->issued, user->handle_info);
3862 intervalString(buf2, suspended->expires-now, user->handle_info);
3863 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3865 case 6: /* issue time set, expired */
3866 intervalString(buf1, now-suspended->issued, user->handle_info);
3867 intervalString(buf2, now-suspended->expires, user->handle_info);
3868 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3870 case 7: /* issue time set, revoked */
3871 intervalString(buf1, now-suspended->issued, user->handle_info);
3872 intervalString(buf2, now-suspended->revoked, user->handle_info);
3873 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3876 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3881 static CHANSERV_FUNC(cmd_info)
3883 char modes[MAXLEN], buffer[INTERVALLEN];
3884 struct userData *uData, *owner;
3885 struct chanData *cData;
3886 struct do_not_register *dnr;
3891 cData = channel->channel_info;
3892 reply("CSMSG_CHANNEL_INFO", channel->name);
3894 uData = GetChannelUser(cData, user->handle_info);
3895 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3897 mod_chanmode_format(&cData->modes, modes);
3898 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3899 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3902 for(it = dict_first(cData->notes); it; it = iter_next(it))
3906 note = iter_data(it);
3907 if(!note_type_visible_to_user(cData, note->type, user))
3910 padding = PADLEN - 1 - strlen(iter_key(it));
3911 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3914 reply("CSMSG_CHANNEL_MAX", cData->max);
3915 for(owner = cData->users; owner; owner = owner->next)
3916 if(owner->access == UL_OWNER)
3917 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3918 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3919 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3920 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3921 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3923 privileged = IsStaff(user);
3924 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3925 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3927 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3928 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3930 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3932 struct suspended *suspended;
3933 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3934 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3935 show_suspension_info(cmd, user, suspended);
3937 else if(IsSuspended(cData))
3939 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3940 show_suspension_info(cmd, user, cData->suspended);
3945 static CHANSERV_FUNC(cmd_netinfo)
3947 extern time_t boot_time;
3948 extern unsigned long burst_length;
3949 char interval[INTERVALLEN];
3951 reply("CSMSG_NETWORK_INFO");
3952 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3953 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3954 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3955 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3956 reply("CSMSG_NETWORK_BANS", banCount);
3957 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3958 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3959 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3964 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3966 struct helpfile_table table;
3968 struct userNode *user;
3973 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3974 table.contents = alloca(list->used*sizeof(*table.contents));
3975 for(nn=0; nn<list->used; nn++)
3977 user = list->list[nn];
3978 if(user->modes & skip_flags)
3982 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
3985 nick = alloca(strlen(user->nick)+3);
3986 sprintf(nick, "(%s)", user->nick);
3990 table.contents[table.length][0] = nick;
3993 table_send(chanserv, to->nick, 0, NULL, table);
3996 static CHANSERV_FUNC(cmd_ircops)
3998 reply("CSMSG_STAFF_OPERS");
3999 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4003 static CHANSERV_FUNC(cmd_helpers)
4005 reply("CSMSG_STAFF_HELPERS");
4006 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4010 static CHANSERV_FUNC(cmd_staff)
4012 reply("CSMSG_NETWORK_STAFF");
4013 cmd_ircops(CSFUNC_ARGS);
4014 cmd_helpers(CSFUNC_ARGS);
4018 static CHANSERV_FUNC(cmd_peek)
4020 struct modeNode *mn;
4021 char modes[MODELEN];
4023 struct helpfile_table table;
4025 irc_make_chanmode(channel, modes);
4027 reply("CSMSG_PEEK_INFO", channel->name);
4028 reply("CSMSG_PEEK_TOPIC", channel->topic);
4029 reply("CSMSG_PEEK_MODES", modes);
4030 reply("CSMSG_PEEK_USERS", channel->members.used);
4034 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4035 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4036 for(n = 0; n < channel->members.used; n++)
4038 mn = channel->members.list[n];
4039 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4041 table.contents[table.length] = alloca(sizeof(**table.contents));
4042 table.contents[table.length][0] = mn->user->nick;
4047 reply("CSMSG_PEEK_OPS");
4048 table_send(chanserv, user->nick, 0, NULL, table);
4051 reply("CSMSG_PEEK_NO_OPS");
4055 static MODCMD_FUNC(cmd_wipeinfo)
4057 struct handle_info *victim;
4058 struct userData *ud, *actor;
4061 actor = GetChannelUser(channel->channel_info, user->handle_info);
4062 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4064 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4066 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4069 if((ud->access >= actor->access) && (ud != actor))
4071 reply("MSG_USER_OUTRANKED", victim->handle);
4077 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4081 static CHANSERV_FUNC(cmd_resync)
4083 struct mod_chanmode *changes;
4084 struct chanData *cData = channel->channel_info;
4085 unsigned int ii, used;
4087 changes = mod_chanmode_alloc(channel->members.used * 2);
4088 for(ii = used = 0; ii < channel->members.used; ++ii)
4090 struct modeNode *mn = channel->members.list[ii];
4091 struct userData *uData;
4093 if(IsService(mn->user))
4096 uData = GetChannelAccess(cData, mn->user->handle_info);
4097 if(!cData->lvlOpts[lvlGiveOps]
4098 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4100 if(!(mn->modes & MODE_CHANOP))
4102 changes->args[used].mode = MODE_CHANOP;
4103 changes->args[used++].u.member = mn;
4106 else if(!cData->lvlOpts[lvlGiveVoice]
4107 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4109 if(mn->modes & MODE_CHANOP)
4111 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4112 changes->args[used++].u.member = mn;
4114 if(!(mn->modes & MODE_VOICE))
4116 changes->args[used].mode = MODE_VOICE;
4117 changes->args[used++].u.member = mn;
4124 changes->args[used].mode = MODE_REMOVE | mn->modes;
4125 changes->args[used++].u.member = mn;
4129 changes->argc = used;
4130 modcmd_chanmode_announce(changes);
4131 mod_chanmode_free(changes);
4132 reply("CSMSG_RESYNCED_USERS", channel->name);
4136 static CHANSERV_FUNC(cmd_seen)
4138 struct userData *uData;
4139 struct handle_info *handle;
4140 char seen[INTERVALLEN];
4144 if(!irccasecmp(argv[1], chanserv->nick))
4146 reply("CSMSG_IS_CHANSERV");
4150 if(!(handle = get_handle_info(argv[1])))
4152 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4156 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4158 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4163 reply("CSMSG_USER_PRESENT", handle->handle);
4164 else if(uData->seen)
4165 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4167 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4169 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4170 reply("CSMSG_USER_VACATION", handle->handle);
4175 static MODCMD_FUNC(cmd_names)
4177 struct userNode *targ;
4178 struct userData *targData;
4179 unsigned int ii, pos;
4182 for(ii=pos=0; ii<channel->members.used; ++ii)
4184 targ = channel->members.list[ii]->user;
4185 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4188 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4191 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4195 if(IsUserSuspended(targData))
4197 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4200 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4201 reply("CSMSG_END_NAMES", channel->name);
4206 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4208 switch(ntype->visible_type)
4210 case NOTE_VIS_ALL: return 1;
4211 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4212 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4217 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4219 struct userData *uData;
4221 switch(ntype->set_access_type)
4223 case NOTE_SET_CHANNEL_ACCESS:
4224 if(!user->handle_info)
4226 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4228 return uData->access >= ntype->set_access.min_ulevel;
4229 case NOTE_SET_CHANNEL_SETTER:
4230 return check_user_level(channel, user, lvlSetters, 1, 0);
4231 case NOTE_SET_PRIVILEGED: default:
4232 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4236 static CHANSERV_FUNC(cmd_note)
4238 struct chanData *cData;
4240 struct note_type *ntype;
4242 cData = channel->channel_info;
4245 reply("CSMSG_NOT_REGISTERED", channel->name);
4249 /* If no arguments, show all visible notes for the channel. */
4255 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4257 note = iter_data(it);
4258 if(!note_type_visible_to_user(cData, note->type, user))
4261 reply("CSMSG_NOTELIST_HEADER", channel->name);
4262 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4265 reply("CSMSG_NOTELIST_END", channel->name);
4267 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4269 /* If one argument, show the named note. */
4272 if((note = dict_find(cData->notes, argv[1], NULL))
4273 && note_type_visible_to_user(cData, note->type, user))
4275 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4277 else if((ntype = dict_find(note_types, argv[1], NULL))
4278 && note_type_visible_to_user(NULL, ntype, user))
4280 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4285 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4289 /* Assume they're trying to set a note. */
4293 ntype = dict_find(note_types, argv[1], NULL);
4296 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4299 else if(note_type_settable_by_user(channel, ntype, user))
4301 note_text = unsplit_string(argv+2, argc-2, NULL);
4302 if((note = dict_find(cData->notes, argv[1], NULL)))
4303 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4304 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4305 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4307 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4309 /* The note is viewable to staff only, so return 0
4310 to keep the invocation from getting logged (or
4311 regular users can see it in !events). */
4317 reply("CSMSG_NO_ACCESS");
4324 static CHANSERV_FUNC(cmd_delnote)
4329 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4330 || !note_type_settable_by_user(channel, note->type, user))
4332 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4335 dict_remove(channel->channel_info->notes, note->type->name);
4336 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4340 static CHANSERV_FUNC(cmd_events)
4342 struct logSearch discrim;
4343 struct logReport report;
4344 unsigned int matches, limit;
4346 limit = (argc > 1) ? atoi(argv[1]) : 10;
4347 if(limit < 1 || limit > 200)
4350 memset(&discrim, 0, sizeof(discrim));
4351 discrim.masks.bot = chanserv;
4352 discrim.masks.channel_name = channel->name;
4354 discrim.masks.command = argv[2];
4355 discrim.limit = limit;
4356 discrim.max_time = INT_MAX;
4357 discrim.severities = 1 << LOG_COMMAND;
4358 report.reporter = chanserv;
4360 reply("CSMSG_EVENT_SEARCH_RESULTS");
4361 matches = log_entry_search(&discrim, log_report_entry, &report);
4363 reply("MSG_MATCH_COUNT", matches);
4365 reply("MSG_NO_MATCHES");
4369 static CHANSERV_FUNC(cmd_say)
4375 msg = unsplit_string(argv + 1, argc - 1, NULL);
4376 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4378 else if(GetUserH(argv[1]))
4381 msg = unsplit_string(argv + 2, argc - 2, NULL);
4382 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4386 reply("MSG_NOT_TARGET_NAME");
4392 static CHANSERV_FUNC(cmd_emote)
4398 /* CTCP is so annoying. */
4399 msg = unsplit_string(argv + 1, argc - 1, NULL);
4400 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4402 else if(GetUserH(argv[1]))
4404 msg = unsplit_string(argv + 2, argc - 2, NULL);
4405 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4409 reply("MSG_NOT_TARGET_NAME");
4415 struct channelList *
4416 chanserv_support_channels(void)
4418 return &chanserv_conf.support_channels;
4421 static CHANSERV_FUNC(cmd_expire)
4423 int channel_count = registered_channels;
4424 expire_channels(NULL);
4425 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4430 chanserv_expire_suspension(void *data)
4432 struct suspended *suspended = data;
4433 struct chanNode *channel;
4434 struct mod_chanmode change;
4436 if(!suspended->expires || (now < suspended->expires))
4437 suspended->revoked = now;
4438 channel = suspended->cData->channel;
4439 suspended->cData->channel = channel;
4440 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4441 mod_chanmode_init(&change);
4443 change.args[0].mode = MODE_CHANOP;
4444 change.args[0].u.member = AddChannelUser(chanserv, channel);
4445 mod_chanmode_announce(chanserv, channel, &change);
4448 static CHANSERV_FUNC(cmd_csuspend)
4450 struct suspended *suspended;
4451 char reason[MAXLEN];
4452 time_t expiry, duration;
4453 struct userData *uData;
4457 if(IsProtected(channel->channel_info))
4459 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4463 if(argv[1][0] == '!')
4465 else if(IsSuspended(channel->channel_info))
4467 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4468 show_suspension_info(cmd, user, channel->channel_info->suspended);
4472 if(!strcmp(argv[1], "0"))
4474 else if((duration = ParseInterval(argv[1])))
4475 expiry = now + duration;
4478 reply("MSG_INVALID_DURATION", argv[1]);
4482 unsplit_string(argv + 2, argc - 2, reason);
4484 suspended = calloc(1, sizeof(*suspended));
4485 suspended->revoked = 0;
4486 suspended->issued = now;
4487 suspended->suspender = strdup(user->handle_info->handle);
4488 suspended->expires = expiry;
4489 suspended->reason = strdup(reason);
4490 suspended->cData = channel->channel_info;
4491 suspended->previous = suspended->cData->suspended;
4492 suspended->cData->suspended = suspended;
4494 if(suspended->expires)
4495 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4497 if(IsSuspended(channel->channel_info))
4499 suspended->previous->revoked = now;
4500 if(suspended->previous->expires)
4501 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4502 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4503 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4507 /* Mark all users in channel as absent. */
4508 for(uData = channel->channel_info->users; uData; uData = uData->next)
4517 /* Mark the channel as suspended, then part. */
4518 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4519 DelChannelUser(chanserv, channel, suspended->reason, 0);
4520 reply("CSMSG_SUSPENDED", channel->name);
4521 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4522 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4527 static CHANSERV_FUNC(cmd_cunsuspend)
4529 struct suspended *suspended;
4530 char message[MAXLEN];
4532 if(!IsSuspended(channel->channel_info))
4534 reply("CSMSG_NOT_SUSPENDED", channel->name);
4538 suspended = channel->channel_info->suspended;
4540 /* Expire the suspension and join ChanServ to the channel. */
4541 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4542 chanserv_expire_suspension(suspended);
4543 reply("CSMSG_UNSUSPENDED", channel->name);
4544 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4545 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4549 typedef struct chanservSearch
4557 unsigned long flags;
4561 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4564 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4569 search = malloc(sizeof(struct chanservSearch));
4570 memset(search, 0, sizeof(*search));
4573 for(i = 0; i < argc; i++)
4575 /* Assume all criteria require arguments. */
4578 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4582 if(!irccasecmp(argv[i], "name"))
4583 search->name = argv[++i];
4584 else if(!irccasecmp(argv[i], "registrar"))
4585 search->registrar = argv[++i];
4586 else if(!irccasecmp(argv[i], "unvisited"))
4587 search->unvisited = ParseInterval(argv[++i]);
4588 else if(!irccasecmp(argv[i], "registered"))
4589 search->registered = ParseInterval(argv[++i]);
4590 else if(!irccasecmp(argv[i], "flags"))
4593 if(!irccasecmp(argv[i], "nodelete"))
4594 search->flags |= CHANNEL_NODELETE;
4595 else if(!irccasecmp(argv[i], "suspended"))
4596 search->flags |= CHANNEL_SUSPENDED;
4599 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4603 else if(!irccasecmp(argv[i], "limit"))
4604 search->limit = strtoul(argv[++i], NULL, 10);
4607 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4612 if(search->name && !strcmp(search->name, "*"))
4614 if(search->registrar && !strcmp(search->registrar, "*"))
4615 search->registrar = 0;
4624 chanserv_channel_match(struct chanData *channel, search_t search)
4626 const char *name = channel->channel->name;
4627 if((search->name && !match_ircglob(name, search->name)) ||
4628 (search->registrar && !channel->registrar) ||
4629 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4630 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4631 (search->registered && (now - channel->registered) > search->registered) ||
4632 (search->flags && ((search->flags & channel->flags) != search->flags)))
4639 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4641 struct chanData *channel;
4642 unsigned int matches = 0;
4644 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4646 if(!chanserv_channel_match(channel, search))
4656 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4661 search_print(struct chanData *channel, void *data)
4663 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4666 static CHANSERV_FUNC(cmd_search)
4669 unsigned int matches;
4670 channel_search_func action;
4674 if(!irccasecmp(argv[1], "count"))
4675 action = search_count;
4676 else if(!irccasecmp(argv[1], "print"))
4677 action = search_print;
4680 reply("CSMSG_ACTION_INVALID", argv[1]);
4684 search = chanserv_search_create(user, argc - 2, argv + 2);
4688 if(action == search_count)
4689 search->limit = INT_MAX;
4691 if(action == search_print)
4692 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4694 matches = chanserv_channel_search(search, action, user);
4697 reply("MSG_MATCH_COUNT", matches);
4699 reply("MSG_NO_MATCHES");
4705 static CHANSERV_FUNC(cmd_unvisited)
4707 struct chanData *cData;
4708 time_t interval = chanserv_conf.channel_expire_delay;
4709 char buffer[INTERVALLEN];
4710 unsigned int limit = 25, matches = 0;
4714 interval = ParseInterval(argv[1]);
4716 limit = atoi(argv[2]);
4719 intervalString(buffer, interval, user->handle_info);
4720 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4722 for(cData = channelList; cData && matches < limit; cData = cData->next)
4724 if((now - cData->visited) < interval)
4727 intervalString(buffer, now - cData->visited, user->handle_info);
4728 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4735 static MODCMD_FUNC(chan_opt_defaulttopic)
4741 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4743 reply("CSMSG_TOPIC_LOCKED", channel->name);
4747 topic = unsplit_string(argv+1, argc-1, NULL);
4749 free(channel->channel_info->topic);
4750 if(topic[0] == '*' && topic[1] == 0)
4752 topic = channel->channel_info->topic = NULL;
4756 topic = channel->channel_info->topic = strdup(topic);
4757 if(channel->channel_info->topic_mask
4758 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4759 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4761 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4764 if(channel->channel_info->topic)
4765 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4767 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4771 static MODCMD_FUNC(chan_opt_topicmask)
4775 struct chanData *cData = channel->channel_info;
4778 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4780 reply("CSMSG_TOPIC_LOCKED", channel->name);
4784 mask = unsplit_string(argv+1, argc-1, NULL);
4786 if(cData->topic_mask)
4787 free(cData->topic_mask);
4788 if(mask[0] == '*' && mask[1] == 0)
4790 cData->topic_mask = 0;
4794 cData->topic_mask = strdup(mask);
4796 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4797 else if(!match_ircglob(cData->topic, cData->topic_mask))
4798 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4802 if(channel->channel_info->topic_mask)
4803 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4805 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4809 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4813 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4817 if(greeting[0] == '*' && greeting[1] == 0)
4821 unsigned int length = strlen(greeting);
4822 if(length > chanserv_conf.greeting_length)
4824 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4827 *data = strdup(greeting);
4836 reply(name, user_find_message(user, "MSG_NONE"));
4840 static MODCMD_FUNC(chan_opt_greeting)
4842 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4845 static MODCMD_FUNC(chan_opt_usergreeting)
4847 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4850 static MODCMD_FUNC(chan_opt_modes)
4852 struct mod_chanmode *new_modes;
4853 char modes[MODELEN];
4857 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4859 reply("CSMSG_NO_ACCESS");
4862 if(argv[1][0] == '*' && argv[1][1] == 0)
4864 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4866 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4868 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4871 else if(new_modes->argc > 1)
4873 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4874 mod_chanmode_free(new_modes);
4879 channel->channel_info->modes = *new_modes;
4880 modcmd_chanmode_announce(new_modes);
4881 mod_chanmode_free(new_modes);
4885 mod_chanmode_format(&channel->channel_info->modes, modes);
4887 reply("CSMSG_SET_MODES", modes);
4889 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4893 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4895 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4897 struct chanData *cData = channel->channel_info;
4902 /* Set flag according to value. */
4903 if(enabled_string(argv[1]))
4905 cData->flags |= mask;
4908 else if(disabled_string(argv[1]))
4910 cData->flags &= ~mask;
4915 reply("MSG_INVALID_BINARY", argv[1]);
4921 /* Find current option value. */
4922 value = (cData->flags & mask) ? 1 : 0;
4926 reply(name, user_find_message(user, "MSG_ON"));
4928 reply(name, user_find_message(user, "MSG_OFF"));
4932 static MODCMD_FUNC(chan_opt_nodelete)
4934 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4936 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4940 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4943 static MODCMD_FUNC(chan_opt_dynlimit)
4945 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4948 static MODCMD_FUNC(chan_opt_offchannel)
4950 struct chanData *cData = channel->channel_info;
4955 /* Set flag according to value. */
4956 if(enabled_string(argv[1]))
4958 if(!IsOffChannel(cData))
4959 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4960 cData->flags |= CHANNEL_OFFCHANNEL;
4963 else if(disabled_string(argv[1]))
4965 if(IsOffChannel(cData))
4967 struct mod_chanmode change;
4968 mod_chanmode_init(&change);
4970 change.args[0].mode = MODE_CHANOP;
4971 change.args[0].u.member = AddChannelUser(chanserv, channel);
4972 mod_chanmode_announce(chanserv, channel, &change);
4974 cData->flags &= ~CHANNEL_OFFCHANNEL;
4979 reply("MSG_INVALID_BINARY", argv[1]);
4985 /* Find current option value. */
4986 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
4990 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
4992 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
4996 static MODCMD_FUNC(chan_opt_defaults)
4998 struct userData *uData;
4999 struct chanData *cData;
5000 const char *confirm;
5001 enum levelOption lvlOpt;
5002 enum charOption chOpt;
5004 cData = channel->channel_info;
5005 uData = GetChannelUser(cData, user->handle_info);
5006 if(!uData || (uData->access < UL_OWNER))
5008 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5011 confirm = make_confirmation_string(uData);
5012 if((argc < 2) || strcmp(argv[1], confirm))
5014 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5017 cData->flags = CHANNEL_DEFAULT_FLAGS;
5018 cData->modes = chanserv_conf.default_modes;
5019 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5020 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5021 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5022 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5023 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5028 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5030 struct chanData *cData = channel->channel_info;
5031 struct userData *uData;
5032 unsigned short value;
5036 if(!check_user_level(channel, user, option, 1, 1))
5038 reply("CSMSG_CANNOT_SET");
5041 value = user_level_from_name(argv[1], UL_OWNER+1);
5042 if(!value && strcmp(argv[1], "0"))
5044 reply("CSMSG_INVALID_ACCESS", argv[1]);
5047 uData = GetChannelUser(cData, user->handle_info);
5048 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5050 reply("CSMSG_BAD_SETLEVEL");
5056 if(value > cData->lvlOpts[lvlGiveOps])
5058 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5063 if(value < cData->lvlOpts[lvlGiveVoice])
5065 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5070 /* This test only applies to owners, since non-owners
5071 * trying to set an option to above their level get caught
5072 * by the CSMSG_BAD_SETLEVEL test above.
5074 if(value > uData->access)
5076 reply("CSMSG_BAD_SETTERS");
5083 cData->lvlOpts[option] = value;
5085 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5089 static MODCMD_FUNC(chan_opt_enfops)
5091 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5094 static MODCMD_FUNC(chan_opt_giveops)
5096 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5099 static MODCMD_FUNC(chan_opt_enfmodes)
5101 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5104 static MODCMD_FUNC(chan_opt_enftopic)
5106 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5109 static MODCMD_FUNC(chan_opt_pubcmd)
5111 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5114 static MODCMD_FUNC(chan_opt_setters)
5116 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5119 static MODCMD_FUNC(chan_opt_ctcpusers)
5121 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5124 static MODCMD_FUNC(chan_opt_userinfo)
5126 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5129 static MODCMD_FUNC(chan_opt_givevoice)
5131 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5134 static MODCMD_FUNC(chan_opt_topicsnarf)
5136 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5139 static MODCMD_FUNC(chan_opt_inviteme)
5141 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5145 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5147 struct chanData *cData = channel->channel_info;
5148 int count = charOptions[option].count, index;
5152 index = atoi(argv[1]);
5154 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5156 reply("CSMSG_INVALID_NUMERIC", index);
5157 /* Show possible values. */
5158 for(index = 0; index < count; index++)
5159 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5163 cData->chOpts[option] = charOptions[option].values[index].value;
5167 /* Find current option value. */
5170 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5174 /* Somehow, the option value is corrupt; reset it to the default. */
5175 cData->chOpts[option] = charOptions[option].default_value;
5180 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5184 static MODCMD_FUNC(chan_opt_protect)
5186 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5189 static MODCMD_FUNC(chan_opt_toys)
5191 return channel_multiple_option(chToys, CSFUNC_ARGS);
5194 static MODCMD_FUNC(chan_opt_ctcpreaction)
5196 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5199 static MODCMD_FUNC(chan_opt_topicrefresh)
5201 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5204 static struct svccmd_list set_shows_list;
5207 handle_svccmd_unbind(struct svccmd *target) {
5209 for(ii=0; ii<set_shows_list.used; ++ii)
5210 if(target == set_shows_list.list[ii])
5211 set_shows_list.used = 0;
5214 static CHANSERV_FUNC(cmd_set)
5216 struct svccmd *subcmd;
5220 /* Check if we need to (re-)initialize set_shows_list. */
5221 if(!set_shows_list.used)
5223 if(!set_shows_list.size)
5225 set_shows_list.size = chanserv_conf.set_shows->used;
5226 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5228 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5230 const char *name = chanserv_conf.set_shows->list[ii];
5231 sprintf(buf, "%s %s", argv[0], name);
5232 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5235 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5238 svccmd_list_append(&set_shows_list, subcmd);
5244 reply("CSMSG_CHANNEL_OPTIONS");
5245 for(ii = 0; ii < set_shows_list.used; ii++)
5247 subcmd = set_shows_list.list[ii];
5248 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5253 sprintf(buf, "%s %s", argv[0], argv[1]);
5254 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5257 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5260 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5262 reply("CSMSG_NO_ACCESS");
5266 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5270 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5272 struct userData *uData;
5274 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5277 reply("CSMSG_NOT_USER", channel->name);
5283 /* Just show current option value. */
5285 else if(enabled_string(argv[1]))
5287 uData->flags |= mask;
5289 else if(disabled_string(argv[1]))
5291 uData->flags &= ~mask;
5295 reply("MSG_INVALID_BINARY", argv[1]);
5299 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5303 static MODCMD_FUNC(user_opt_noautoop)
5305 struct userData *uData;
5307 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5310 reply("CSMSG_NOT_USER", channel->name);
5313 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5314 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5316 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5319 static MODCMD_FUNC(user_opt_autoinvite)
5321 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5324 static MODCMD_FUNC(user_opt_info)
5326 struct userData *uData;
5329 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5333 /* If they got past the command restrictions (which require access)
5334 * but fail this test, we have some fool with security override on.
5336 reply("CSMSG_NOT_USER", channel->name);
5343 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5344 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5346 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5349 bp = strcspn(infoline, "\001");
5352 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5357 if(infoline[0] == '*' && infoline[1] == 0)
5360 uData->info = strdup(infoline);
5363 reply("CSMSG_USET_INFO", uData->info);
5365 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5369 struct svccmd_list uset_shows_list;
5371 static CHANSERV_FUNC(cmd_uset)
5373 struct svccmd *subcmd;
5377 /* Check if we need to (re-)initialize uset_shows_list. */
5378 if(!uset_shows_list.used)
5382 "NoAutoOp", "AutoInvite", "Info"
5385 if(!uset_shows_list.size)
5387 uset_shows_list.size = ArrayLength(options);
5388 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5390 for(ii = 0; ii < ArrayLength(options); ii++)
5392 const char *name = options[ii];
5393 sprintf(buf, "%s %s", argv[0], name);
5394 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5397 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5400 svccmd_list_append(&uset_shows_list, subcmd);
5406 /* Do this so options are presented in a consistent order. */
5407 reply("CSMSG_USER_OPTIONS");
5408 for(ii = 0; ii < uset_shows_list.used; ii++)
5409 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5413 sprintf(buf, "%s %s", argv[0], argv[1]);
5414 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5417 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5421 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5424 static CHANSERV_FUNC(cmd_giveownership)
5426 struct handle_info *new_owner_hi;
5427 struct userData *new_owner, *curr_user;
5428 struct chanData *cData = channel->channel_info;
5429 struct do_not_register *dnr;
5431 unsigned short co_access;
5432 char reason[MAXLEN];
5435 curr_user = GetChannelAccess(cData, user->handle_info);
5436 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5437 if(!curr_user || (curr_user->access != UL_OWNER))
5439 struct userData *owner = NULL;
5440 for(curr_user = channel->channel_info->users;
5442 curr_user = curr_user->next)
5444 if(curr_user->access != UL_OWNER)
5448 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5455 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5457 char delay[INTERVALLEN];
5458 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5459 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5462 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5464 if(new_owner_hi == user->handle_info)
5466 reply("CSMSG_NO_TRANSFER_SELF");
5469 new_owner = GetChannelAccess(cData, new_owner_hi);
5472 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5475 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5477 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5480 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5481 if(!IsHelping(user))
5482 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5484 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi);
5487 if(new_owner->access >= UL_COOWNER)
5488 co_access = new_owner->access;
5490 co_access = UL_COOWNER;
5491 new_owner->access = UL_OWNER;
5493 curr_user->access = co_access;
5494 cData->ownerTransfer = now;
5495 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5496 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5497 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5501 static CHANSERV_FUNC(cmd_suspend)
5503 struct handle_info *hi;
5504 struct userData *self, *target;
5507 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5508 self = GetChannelUser(channel->channel_info, user->handle_info);
5509 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5511 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5514 if(target->access >= self->access)
5516 reply("MSG_USER_OUTRANKED", hi->handle);
5519 if(target->flags & USER_SUSPENDED)
5521 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5526 target->present = 0;
5529 target->flags |= USER_SUSPENDED;
5530 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5534 static CHANSERV_FUNC(cmd_unsuspend)
5536 struct handle_info *hi;
5537 struct userData *self, *target;
5540 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5541 self = GetChannelUser(channel->channel_info, user->handle_info);
5542 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5544 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5547 if(target->access >= self->access)
5549 reply("MSG_USER_OUTRANKED", hi->handle);
5552 if(!(target->flags & USER_SUSPENDED))
5554 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5557 target->flags &= ~USER_SUSPENDED;
5558 scan_user_presence(target, NULL);
5559 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5563 static MODCMD_FUNC(cmd_deleteme)
5565 struct handle_info *hi;
5566 struct userData *target;
5567 const char *confirm_string;
5568 unsigned short access;
5571 hi = user->handle_info;
5572 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5574 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5577 if(target->access == UL_OWNER)
5579 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5582 confirm_string = make_confirmation_string(target);
5583 if((argc < 2) || strcmp(argv[1], confirm_string))
5585 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5588 access = target->access;
5589 channel_name = strdup(channel->name);
5590 del_channel_user(target, 1);
5591 reply("CSMSG_DELETED_YOU", access, channel_name);
5597 chanserv_refresh_topics(UNUSED_ARG(void *data))
5599 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5600 struct chanData *cData;
5603 for(cData = channelList; cData; cData = cData->next)
5605 if(IsSuspended(cData))
5607 opt = cData->chOpts[chTopicRefresh];
5610 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5613 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5614 cData->last_refresh = refresh_num;
5616 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5619 static CHANSERV_FUNC(cmd_unf)
5623 char response[MAXLEN];
5624 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5625 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5626 irc_privmsg(cmd->parent->bot, channel->name, response);
5629 reply("CSMSG_UNF_RESPONSE");
5633 static CHANSERV_FUNC(cmd_ping)
5637 char response[MAXLEN];
5638 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5639 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5640 irc_privmsg(cmd->parent->bot, channel->name, response);
5643 reply("CSMSG_PING_RESPONSE");
5647 static CHANSERV_FUNC(cmd_wut)
5651 char response[MAXLEN];
5652 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5653 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5654 irc_privmsg(cmd->parent->bot, channel->name, response);
5657 reply("CSMSG_WUT_RESPONSE");
5661 static CHANSERV_FUNC(cmd_8ball)
5663 unsigned int i, j, accum;
5668 for(i=1; i<argc; i++)
5669 for(j=0; argv[i][j]; j++)
5670 accum = (accum << 5) - accum + toupper(argv[i][j]);
5671 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5674 char response[MAXLEN];
5675 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5676 irc_privmsg(cmd->parent->bot, channel->name, response);
5679 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5683 static CHANSERV_FUNC(cmd_d)
5685 unsigned long sides, count, modifier, ii, total;
5686 char response[MAXLEN], *sep;
5690 if((count = strtoul(argv[1], &sep, 10)) < 1)
5700 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5701 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5705 else if((sep[0] == '-') && isdigit(sep[1]))
5706 modifier = strtoul(sep, NULL, 10);
5707 else if((sep[0] == '+') && isdigit(sep[1]))
5708 modifier = strtoul(sep+1, NULL, 10);
5715 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5720 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5723 for(total = ii = 0; ii < count; ++ii)
5724 total += (rand() % sides) + 1;
5727 if((count > 1) || modifier)
5729 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5730 sprintf(response, fmt, total, count, sides, modifier);
5734 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5735 sprintf(response, fmt, total, sides);
5738 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5740 send_message_type(4, user, cmd->parent->bot, "%s", response);
5744 static CHANSERV_FUNC(cmd_huggle)
5746 /* CTCP must be via PRIVMSG, never notice */
5748 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5750 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5755 chanserv_adjust_limit(void *data)
5757 struct mod_chanmode change;
5758 struct chanData *cData = data;
5759 struct chanNode *channel = cData->channel;
5762 if(IsSuspended(cData))
5765 cData->limitAdjusted = now;
5766 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5767 if(cData->modes.modes_set & MODE_LIMIT)
5769 if(limit > cData->modes.new_limit)
5770 limit = cData->modes.new_limit;
5771 else if(limit == cData->modes.new_limit)
5775 mod_chanmode_init(&change);
5776 change.modes_set = MODE_LIMIT;
5777 change.new_limit = limit;
5778 mod_chanmode_announce(chanserv, channel, &change);
5782 handle_new_channel(struct chanNode *channel)
5784 struct chanData *cData;
5786 if(!(cData = channel->channel_info))
5789 if(cData->modes.modes_set || cData->modes.modes_clear)
5790 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5792 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5793 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5796 /* Welcome to my worst nightmare. Warning: Read (or modify)
5797 the code below at your own risk. */
5799 handle_join(struct modeNode *mNode)
5801 struct mod_chanmode change;
5802 struct userNode *user = mNode->user;
5803 struct chanNode *channel = mNode->channel;
5804 struct chanData *cData;
5805 struct userData *uData = NULL;
5806 struct banData *bData;
5807 struct handle_info *handle;
5808 unsigned int modes = 0, info = 0;
5811 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5814 cData = channel->channel_info;
5815 if(channel->members.used > cData->max)
5816 cData->max = channel->members.used;
5818 /* Check for bans. If they're joining through a ban, one of two
5820 * 1: Join during a netburst, by riding the break. Kick them
5821 * unless they have ops or voice in the channel.
5822 * 2: They're allowed to join through the ban (an invite in
5823 * ircu2.10, or a +e on Hybrid, or something).
5824 * If they're not joining through a ban, and the banlist is not
5825 * full, see if they're on the banlist for the channel. If so,
5828 if(user->uplink->burst && !mNode->modes)
5831 for(ii = 0; ii < channel->banlist.used; ii++)
5833 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5835 /* Riding a netburst. Naughty. */
5836 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5842 mod_chanmode_init(&change);
5844 if(channel->banlist.used < MAXBANS)
5846 /* Not joining through a ban. */
5847 for(bData = cData->bans;
5848 bData && !user_matches_glob(user, bData->mask, 1);
5849 bData = bData->next);
5853 char kick_reason[MAXLEN];
5854 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5856 bData->triggered = now;
5857 if(bData != cData->bans)
5859 /* Shuffle the ban to the head of the list. */
5861 bData->next->prev = bData->prev;
5863 bData->prev->next = bData->next;
5866 bData->next = cData->bans;
5869 cData->bans->prev = bData;
5870 cData->bans = bData;
5873 change.args[0].mode = MODE_BAN;
5874 change.args[0].u.hostmask = bData->mask;
5875 mod_chanmode_announce(chanserv, channel, &change);
5876 KickChannelUser(user, channel, chanserv, kick_reason);
5881 /* ChanServ will not modify the limits in join-flooded channels.
5882 It will also skip DynLimit processing when the user (or srvx)
5883 is bursting in, because there are likely more incoming. */
5884 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5885 && !user->uplink->burst
5886 && !channel->join_flooded
5887 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5889 /* The user count has begun "bumping" into the channel limit,
5890 so set a timer to raise the limit a bit. Any previous
5891 timers are removed so three incoming users within the delay
5892 results in one limit change, not three. */
5894 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5895 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5898 if(channel->join_flooded)
5900 /* don't automatically give ops or voice during a join flood */
5902 else if(cData->lvlOpts[lvlGiveOps] == 0)
5903 modes |= MODE_CHANOP;
5904 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5905 modes |= MODE_VOICE;
5907 greeting = cData->greeting;
5908 if(user->handle_info)
5910 handle = user->handle_info;
5912 if(IsHelper(user) && !IsHelping(user))
5915 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5917 if(channel == chanserv_conf.support_channels.list[ii])
5919 HANDLE_SET_FLAG(user->handle_info, HELPING);
5925 uData = GetTrueChannelAccess(cData, handle);
5926 if(uData && !IsUserSuspended(uData))
5928 /* Ops and above were handled by the above case. */
5929 if(IsUserAutoOp(uData))
5931 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5932 modes |= MODE_CHANOP;
5933 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5934 modes |= MODE_VOICE;
5936 if(uData->access >= UL_PRESENT)
5937 cData->visited = now;
5938 if(cData->user_greeting)
5939 greeting = cData->user_greeting;
5941 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5942 && ((now - uData->seen) >= chanserv_conf.info_delay)
5949 if(!user->uplink->burst)
5953 if(modes & MODE_CHANOP)
5954 modes &= ~MODE_VOICE;
5955 change.args[0].mode = modes;
5956 change.args[0].u.member = mNode;
5957 mod_chanmode_announce(chanserv, channel, &change);
5959 if(greeting && !user->uplink->burst)
5960 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5962 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5968 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5970 struct mod_chanmode change;
5971 struct userData *channel;
5972 unsigned int ii, jj;
5974 if(!user->handle_info)
5977 mod_chanmode_init(&change);
5979 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
5981 struct chanNode *cn;
5982 struct modeNode *mn;
5983 if(IsUserSuspended(channel)
5984 || IsSuspended(channel->channel)
5985 || !(cn = channel->channel->channel))
5988 mn = GetUserMode(cn, user);
5991 if(!IsUserSuspended(channel)
5992 && IsUserAutoInvite(channel)
5993 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
5995 && !user->uplink->burst)
5996 irc_invite(chanserv, user, cn);
6000 if(channel->access >= UL_PRESENT)
6001 channel->channel->visited = now;
6003 if(IsUserAutoOp(channel))
6005 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6006 change.args[0].mode = MODE_CHANOP;
6007 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6008 change.args[0].mode = MODE_VOICE;
6010 change.args[0].mode = 0;
6011 change.args[0].u.member = mn;
6012 if(change.args[0].mode)
6013 mod_chanmode_announce(chanserv, cn, &change);
6016 channel->seen = now;
6017 channel->present = 1;
6020 for(ii = 0; ii < user->channels.used; ++ii)
6022 struct chanNode *channel = user->channels.list[ii]->channel;
6023 struct banData *ban;
6025 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6026 || !channel->channel_info)
6028 for(jj = 0; jj < channel->banlist.used; ++jj)
6029 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6031 if(jj < channel->banlist.used)
6033 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6035 char kick_reason[MAXLEN];
6036 if(!user_matches_glob(user, ban->mask, 1))
6038 change.args[0].mode = MODE_BAN;
6039 change.args[0].u.hostmask = ban->mask;
6040 mod_chanmode_announce(chanserv, channel, &change);
6041 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6042 KickChannelUser(user, channel, chanserv, kick_reason);
6043 ban->triggered = now;
6048 if(IsSupportHelper(user))
6050 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6052 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6054 HANDLE_SET_FLAG(user->handle_info, HELPING);
6062 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6064 struct chanData *cData;
6065 struct userData *uData;
6067 cData = mn->channel->channel_info;
6068 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6071 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6073 /* Allow for a bit of padding so that the limit doesn't
6074 track the user count exactly, which could get annoying. */
6075 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6077 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6078 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6082 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6084 scan_user_presence(uData, mn->user);
6088 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6090 unsigned int ii, jj;
6091 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6093 for(jj = 0; jj < mn->user->channels.used; ++jj)
6094 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6096 if(jj < mn->user->channels.used)
6099 if(ii == chanserv_conf.support_channels.used)
6100 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6105 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6107 struct userData *uData;
6109 if(!channel->channel_info || !kicker || IsService(kicker)
6110 || (kicker == victim) || IsSuspended(channel->channel_info)
6111 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6114 if(protect_user(victim, kicker, channel->channel_info))
6116 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6117 KickChannelUser(kicker, channel, chanserv, reason);
6120 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6125 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6127 struct chanData *cData;
6129 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6132 cData = channel->channel_info;
6133 if(bad_topic(channel, user, channel->topic))
6135 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6136 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6137 SetChannelTopic(channel, chanserv, old_topic, 1);
6138 else if(cData->topic)
6139 SetChannelTopic(channel, chanserv, cData->topic, 1);
6142 /* With topicsnarf, grab the topic and save it as the default topic. */
6143 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6146 cData->topic = strdup(channel->topic);
6152 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6154 struct mod_chanmode *bounce = NULL;
6155 unsigned int bnc, ii;
6158 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6161 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6162 && mode_lock_violated(&channel->channel_info->modes, change))
6164 char correct[MAXLEN];
6165 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6166 mod_chanmode_format(&channel->channel_info->modes, correct);
6167 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6169 for(ii = bnc = 0; ii < change->argc; ++ii)
6171 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6173 const struct userNode *victim = change->args[ii].u.member->user;
6174 if(!protect_user(victim, user, channel->channel_info))
6177 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6180 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6181 bounce->args[bnc].u.member = GetUserMode(channel, user);
6182 if(bounce->args[bnc].u.member)
6186 bounce->args[bnc].mode = MODE_CHANOP;
6187 bounce->args[bnc].u.member = change->args[ii].u.member;
6189 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6191 else if(change->args[ii].mode & MODE_CHANOP)
6193 const struct userNode *victim = change->args[ii].u.member->user;
6194 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6197 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6198 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6199 bounce->args[bnc].u.member = change->args[ii].u.member;
6202 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6204 const char *ban = change->args[ii].u.hostmask;
6205 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6208 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6209 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6210 bounce->args[bnc].u.hostmask = ban;
6212 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6217 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6218 mod_chanmode_announce(chanserv, channel, bounce);
6219 mod_chanmode_free(bounce);
6224 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6226 struct chanNode *channel;
6227 struct banData *bData;
6228 struct mod_chanmode change;
6229 unsigned int ii, jj;
6230 char kick_reason[MAXLEN];
6232 mod_chanmode_init(&change);
6234 change.args[0].mode = MODE_BAN;
6235 for(ii = 0; ii < user->channels.used; ++ii)
6237 channel = user->channels.list[ii]->channel;
6238 /* Need not check for bans if they're opped or voiced. */
6239 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6241 /* Need not check for bans unless channel registration is active. */
6242 if(!channel->channel_info || IsSuspended(channel->channel_info))
6244 /* Look for a matching ban already on the channel. */
6245 for(jj = 0; jj < channel->banlist.used; ++jj)
6246 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6248 /* Need not act if we found one. */
6249 if(jj < channel->banlist.used)
6251 /* Look for a matching ban in this channel. */
6252 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6254 if(!user_matches_glob(user, bData->mask, 1))
6256 change.args[0].u.hostmask = bData->mask;
6257 mod_chanmode_announce(chanserv, channel, &change);
6258 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6259 KickChannelUser(user, channel, chanserv, kick_reason);
6260 bData->triggered = now;
6261 break; /* we don't need to check any more bans in the channel */
6266 static void handle_rename(struct handle_info *handle, const char *old_handle)
6268 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6272 dict_remove2(handle_dnrs, old_handle, 1);
6273 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6274 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6279 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6281 struct userNode *h_user;
6283 if(handle->channels)
6285 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6286 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6288 while(handle->channels)
6289 del_channel_user(handle->channels, 1);
6294 handle_server_link(UNUSED_ARG(struct server *server))
6296 struct chanData *cData;
6298 for(cData = channelList; cData; cData = cData->next)
6300 if(!IsSuspended(cData))
6301 cData->may_opchan = 1;
6302 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6303 && !cData->channel->join_flooded
6304 && ((cData->channel->limit - cData->channel->members.used)
6305 < chanserv_conf.adjust_threshold))
6307 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6308 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6314 chanserv_conf_read(void)
6318 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6319 struct mod_chanmode *change;
6320 struct string_list *strlist;
6321 struct chanNode *chan;
6324 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6326 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6329 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6330 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6331 chanserv_conf.support_channels.used = 0;
6332 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6334 for(ii = 0; ii < strlist->used; ++ii)
6336 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6339 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6341 channelList_append(&chanserv_conf.support_channels, chan);
6344 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6347 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6350 chan = AddChannel(str, now, str2, NULL);
6352 channelList_append(&chanserv_conf.support_channels, chan);
6354 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6355 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6356 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6357 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6358 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6359 chanserv_conf.greeting_length = str ? atoi(str) : 120;
6360 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6361 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6362 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6363 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6364 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6365 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6366 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6367 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6368 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6369 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6370 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6371 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6372 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6373 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6374 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6375 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6376 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6378 NickChange(chanserv, str, 0);
6379 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6380 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6381 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6382 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6383 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6384 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6385 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6386 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6387 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6388 chanserv_conf.max_owned = str ? atoi(str) : 5;
6389 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6390 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6391 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6392 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6393 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6394 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6395 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6398 safestrncpy(mode_line, str, sizeof(mode_line));
6399 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6400 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6402 chanserv_conf.default_modes = *change;
6403 mod_chanmode_free(change);
6405 free_string_list(chanserv_conf.set_shows);
6406 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6408 strlist = string_list_copy(strlist);
6411 static const char *list[] = {
6412 /* free form text */
6413 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6414 /* options based on user level */
6415 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6416 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6417 /* multiple choice options */
6418 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6419 /* binary options */
6420 "DynLimit", "NoDelete",
6425 strlist = alloc_string_list(ArrayLength(list)-1);
6426 for(ii=0; list[ii]; ii++)
6427 string_list_append(strlist, strdup(list[ii]));
6429 chanserv_conf.set_shows = strlist;
6430 /* We don't look things up now, in case the list refers to options
6431 * defined by modules initialized after this point. Just mark the
6432 * function list as invalid, so it will be initialized.
6434 set_shows_list.used = 0;
6435 free_string_list(chanserv_conf.eightball);
6436 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6439 strlist = string_list_copy(strlist);
6443 strlist = alloc_string_list(4);
6444 string_list_append(strlist, strdup("Yes."));
6445 string_list_append(strlist, strdup("No."));
6446 string_list_append(strlist, strdup("Maybe so."));
6448 chanserv_conf.eightball = strlist;
6449 free_string_list(chanserv_conf.old_ban_names);
6450 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6452 strlist = string_list_copy(strlist);
6454 strlist = alloc_string_list(2);
6455 chanserv_conf.old_ban_names = strlist;
6456 /* the variable itself is actually declared in proto-common.c; this is equally
6458 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6459 off_channel = (str && enabled_string(str)) ? 1 : 0;
6460 str = database_get_data(conf_node, "use_registered_mode", RECDB_QSTRING);
6461 chanserv_conf.use_registered_mode = (str && enabled_string(str)) ? 1 : 0;
6465 chanserv_note_type_read(const char *key, struct record_data *rd)
6468 struct note_type *ntype;
6471 if(!(obj = GET_RECORD_OBJECT(rd)))
6473 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6476 if(!(ntype = chanserv_create_note_type(key)))
6478 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6482 /* Figure out set access */
6483 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6485 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6486 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6488 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6490 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6491 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6493 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6495 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6499 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6500 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6501 ntype->set_access.min_opserv = 0;
6504 /* Figure out visibility */
6505 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6506 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6507 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6508 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6509 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6510 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6511 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6512 ntype->visible_type = NOTE_VIS_ALL;
6514 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6516 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6517 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6521 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6523 struct handle_info *handle;
6524 struct userData *uData;
6525 char *seen, *inf, *flags;
6527 unsigned short access;
6529 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6531 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6535 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6536 if(access > UL_OWNER)
6538 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6542 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6543 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6544 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6545 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6546 handle = get_handle_info(key);
6549 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6553 uData = add_channel_user(chan, handle, access, last_seen, inf);
6554 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6558 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6560 struct banData *bData;
6561 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6562 time_t set_time, triggered_time, expires_time;
6564 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6566 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6570 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6571 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6572 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6573 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6574 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6575 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6577 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6578 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6580 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6582 expires_time = set_time + atoi(s_duration);
6586 if(expires_time && (expires_time < now))
6589 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6592 static struct suspended *
6593 chanserv_read_suspended(dict_t obj)
6595 struct suspended *suspended = calloc(1, sizeof(*suspended));
6599 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6600 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6601 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6602 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6603 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6604 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6605 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6606 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6607 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6608 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6613 chanserv_channel_read(const char *key, struct record_data *hir)
6615 struct suspended *suspended;
6616 struct mod_chanmode *modes;
6617 struct chanNode *cNode;
6618 struct chanData *cData;
6619 struct dict *channel, *obj;
6620 char *str, *argv[10];
6624 channel = hir->d.object;
6626 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6629 cNode = AddChannel(key, now, NULL, NULL);
6632 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6635 cData = register_channel(cNode, str);
6638 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6642 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6644 enum levelOption lvlOpt;
6645 enum charOption chOpt;
6647 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6648 cData->flags = atoi(str);
6650 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6652 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6654 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6655 else if(levelOptions[lvlOpt].old_flag)
6657 if(cData->flags & levelOptions[lvlOpt].old_flag)
6658 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6660 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6664 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6666 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6668 cData->chOpts[chOpt] = str[0];
6671 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6673 enum levelOption lvlOpt;
6674 enum charOption chOpt;
6677 cData->flags = base64toint(str, 5);
6678 count = strlen(str += 5);
6679 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6682 if(levelOptions[lvlOpt].old_flag)
6684 if(cData->flags & levelOptions[lvlOpt].old_flag)
6685 lvl = levelOptions[lvlOpt].flag_value;
6687 lvl = levelOptions[lvlOpt].default_value;
6689 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6691 case 'c': lvl = UL_COOWNER; break;
6692 case 'm': lvl = UL_MASTER; break;
6693 case 'n': lvl = UL_OWNER+1; break;
6694 case 'o': lvl = UL_OP; break;
6695 case 'p': lvl = UL_PEON; break;
6696 case 'w': lvl = UL_OWNER; break;
6697 default: lvl = 0; break;
6699 cData->lvlOpts[lvlOpt] = lvl;
6701 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6702 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6705 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6707 suspended = chanserv_read_suspended(obj);
6708 cData->suspended = suspended;
6709 suspended->cData = cData;
6710 /* We could use suspended->expires and suspended->revoked to
6711 * set the CHANNEL_SUSPENDED flag, but we don't. */
6713 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6715 suspended = calloc(1, sizeof(*suspended));
6716 suspended->issued = 0;
6717 suspended->revoked = 0;
6718 suspended->suspender = strdup(str);
6719 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6720 suspended->expires = str ? atoi(str) : 0;
6721 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6722 suspended->reason = strdup(str ? str : "No reason");
6723 suspended->previous = NULL;
6724 cData->suspended = suspended;
6725 suspended->cData = cData;
6729 cData->flags &= ~CHANNEL_SUSPENDED;
6730 suspended = NULL; /* to squelch a warning */
6733 if(IsSuspended(cData)) {
6734 if(suspended->expires > now)
6735 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6736 else if(suspended->expires)
6737 cData->flags &= ~CHANNEL_SUSPENDED;
6740 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6741 struct mod_chanmode change;
6742 mod_chanmode_init(&change);
6744 change.args[0].mode = MODE_CHANOP;
6745 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6746 mod_chanmode_announce(chanserv, cNode, &change);
6749 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6750 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6751 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6752 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6753 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6754 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6755 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6756 cData->max = str ? atoi(str) : 0;
6757 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6758 cData->greeting = str ? strdup(str) : NULL;
6759 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6760 cData->user_greeting = str ? strdup(str) : NULL;
6761 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6762 cData->topic_mask = str ? strdup(str) : NULL;
6763 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6764 cData->topic = str ? strdup(str) : NULL;
6766 if(!IsSuspended(cData)
6767 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6768 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6769 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6770 cData->modes = *modes;
6771 if(chanserv_conf.use_registered_mode)
6772 cData->modes.modes_set |= MODE_REGISTERED;
6773 if(cData->modes.argc > 1)
6774 cData->modes.argc = 1;
6775 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6776 mod_chanmode_free(modes);
6779 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6780 for(it = dict_first(obj); it; it = iter_next(it))
6781 user_read_helper(iter_key(it), iter_data(it), cData);
6783 if(!cData->users && !IsProtected(cData))
6785 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6786 unregister_channel(cData, "has empty user list.");
6790 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6791 for(it = dict_first(obj); it; it = iter_next(it))
6792 ban_read_helper(iter_key(it), iter_data(it), cData);
6794 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6795 for(it = dict_first(obj); it; it = iter_next(it))
6797 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6798 struct record_data *rd = iter_data(it);
6799 const char *note, *setter;
6801 if(rd->type != RECDB_OBJECT)
6803 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6807 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6809 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6811 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6815 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6816 if(!setter) setter = "<unknown>";
6817 chanserv_add_channel_note(cData, ntype, setter, note);
6825 chanserv_dnr_read(const char *key, struct record_data *hir)
6827 const char *setter, *reason, *str;
6828 struct do_not_register *dnr;
6830 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6833 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6836 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6839 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6842 dnr = chanserv_add_dnr(key, setter, reason);
6845 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6847 dnr->set = atoi(str);
6853 chanserv_saxdb_read(struct dict *database)
6855 struct dict *section;
6858 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6859 for(it = dict_first(section); it; it = iter_next(it))
6860 chanserv_note_type_read(iter_key(it), iter_data(it));
6862 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6863 for(it = dict_first(section); it; it = iter_next(it))
6864 chanserv_channel_read(iter_key(it), iter_data(it));
6866 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6867 for(it = dict_first(section); it; it = iter_next(it))
6868 chanserv_dnr_read(iter_key(it), iter_data(it));
6874 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6876 int high_present = 0;
6877 saxdb_start_record(ctx, KEY_USERS, 1);
6878 for(; uData; uData = uData->next)
6880 if((uData->access >= UL_PRESENT) && uData->present)
6882 saxdb_start_record(ctx, uData->handle->handle, 0);
6883 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6884 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6886 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6888 saxdb_write_string(ctx, KEY_INFO, uData->info);
6889 saxdb_end_record(ctx);
6891 saxdb_end_record(ctx);
6892 return high_present;
6896 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6900 saxdb_start_record(ctx, KEY_BANS, 1);
6901 for(; bData; bData = bData->next)
6903 saxdb_start_record(ctx, bData->mask, 0);
6904 saxdb_write_int(ctx, KEY_SET, bData->set);
6905 if(bData->triggered)
6906 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6908 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6910 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6912 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6913 saxdb_end_record(ctx);
6915 saxdb_end_record(ctx);
6919 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6921 saxdb_start_record(ctx, name, 0);
6922 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6923 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6925 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6927 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6929 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6931 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6932 saxdb_end_record(ctx);
6936 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6940 enum levelOption lvlOpt;
6941 enum charOption chOpt;
6943 saxdb_start_record(ctx, channel->channel->name, 1);
6945 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6946 saxdb_write_int(ctx, KEY_MAX, channel->max);
6948 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6949 if(channel->registrar)
6950 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6951 if(channel->greeting)
6952 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6953 if(channel->user_greeting)
6954 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6955 if(channel->topic_mask)
6956 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6957 if(channel->suspended)
6958 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6960 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6961 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6962 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6963 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6964 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6966 buf[0] = channel->chOpts[chOpt];
6968 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6970 saxdb_end_record(ctx);
6972 if(channel->modes.modes_set || channel->modes.modes_clear)
6974 mod_chanmode_format(&channel->modes, buf);
6975 saxdb_write_string(ctx, KEY_MODES, buf);
6978 high_present = chanserv_write_users(ctx, channel->users);
6979 chanserv_write_bans(ctx, channel->bans);
6981 if(dict_size(channel->notes))
6985 saxdb_start_record(ctx, KEY_NOTES, 1);
6986 for(it = dict_first(channel->notes); it; it = iter_next(it))
6988 struct note *note = iter_data(it);
6989 saxdb_start_record(ctx, iter_key(it), 0);
6990 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
6991 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
6992 saxdb_end_record(ctx);
6994 saxdb_end_record(ctx);
6997 if(channel->ownerTransfer)
6998 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
6999 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7000 saxdb_end_record(ctx);
7004 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7008 saxdb_start_record(ctx, ntype->name, 0);
7009 switch(ntype->set_access_type)
7011 case NOTE_SET_CHANNEL_ACCESS:
7012 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7014 case NOTE_SET_CHANNEL_SETTER:
7015 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7017 case NOTE_SET_PRIVILEGED: default:
7018 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7021 switch(ntype->visible_type)
7023 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7024 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7025 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7027 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7028 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7029 saxdb_end_record(ctx);
7033 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7035 struct do_not_register *dnr;
7038 for(it = dict_first(dnrs); it; it = iter_next(it))
7040 dnr = iter_data(it);
7041 saxdb_start_record(ctx, dnr->chan_name, 0);
7043 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7044 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7045 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7046 saxdb_end_record(ctx);
7051 chanserv_saxdb_write(struct saxdb_context *ctx)
7054 struct chanData *channel;
7057 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7058 for(it = dict_first(note_types); it; it = iter_next(it))
7059 chanserv_write_note_type(ctx, iter_data(it));
7060 saxdb_end_record(ctx);
7063 saxdb_start_record(ctx, KEY_DNR, 1);
7064 write_dnrs_helper(ctx, handle_dnrs);
7065 write_dnrs_helper(ctx, plain_dnrs);
7066 write_dnrs_helper(ctx, mask_dnrs);
7067 saxdb_end_record(ctx);
7070 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7071 for(channel = channelList; channel; channel = channel->next)
7072 chanserv_write_channel(ctx, channel);
7073 saxdb_end_record(ctx);
7079 chanserv_db_cleanup(void) {
7081 unreg_part_func(handle_part);
7083 unregister_channel(channelList, "terminating.");
7084 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7085 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7086 free(chanserv_conf.support_channels.list);
7087 dict_delete(handle_dnrs);
7088 dict_delete(plain_dnrs);
7089 dict_delete(mask_dnrs);
7090 dict_delete(note_types);
7091 free_string_list(chanserv_conf.eightball);
7092 free_string_list(chanserv_conf.old_ban_names);
7093 free_string_list(chanserv_conf.set_shows);
7094 free(set_shows_list.list);
7095 free(uset_shows_list.list);
7098 struct userData *helper = helperList;
7099 helperList = helperList->next;
7104 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7105 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7106 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7109 init_chanserv(const char *nick)
7111 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7112 conf_register_reload(chanserv_conf_read);
7114 reg_server_link_func(handle_server_link);
7116 reg_new_channel_func(handle_new_channel);
7117 reg_join_func(handle_join);
7118 reg_part_func(handle_part);
7119 reg_kick_func(handle_kick);
7120 reg_topic_func(handle_topic);
7121 reg_mode_change_func(handle_mode);
7122 reg_nick_change_func(handle_nick_change);
7124 reg_auth_func(handle_auth);
7125 reg_handle_rename_func(handle_rename);
7126 reg_unreg_func(handle_unreg);
7128 handle_dnrs = dict_new();
7129 dict_set_free_data(handle_dnrs, free);
7130 plain_dnrs = dict_new();
7131 dict_set_free_data(plain_dnrs, free);
7132 mask_dnrs = dict_new();
7133 dict_set_free_data(mask_dnrs, free);
7135 reg_svccmd_unbind_func(handle_svccmd_unbind);
7136 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7137 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7138 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7139 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7140 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7141 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7142 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7143 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7144 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7146 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7147 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7149 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7150 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7151 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7152 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7153 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7155 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7156 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7157 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7158 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7159 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7161 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7162 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7163 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7164 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7166 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7167 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7168 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7169 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7170 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7171 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7172 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7173 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7175 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7176 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7177 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7178 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7179 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7180 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7181 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7182 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7183 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7184 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7185 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7186 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7187 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7188 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7190 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7191 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7192 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7193 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7194 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7196 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7197 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7199 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7200 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7201 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7202 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7203 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7204 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7205 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7206 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7207 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7208 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7209 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7211 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7212 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7214 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7215 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7216 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7217 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7219 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7220 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7221 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7222 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7223 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7225 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7226 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7227 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7228 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7229 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7230 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7232 /* Channel options */
7233 DEFINE_CHANNEL_OPTION(defaulttopic);
7234 DEFINE_CHANNEL_OPTION(topicmask);
7235 DEFINE_CHANNEL_OPTION(greeting);
7236 DEFINE_CHANNEL_OPTION(usergreeting);
7237 DEFINE_CHANNEL_OPTION(modes);
7238 DEFINE_CHANNEL_OPTION(enfops);
7239 DEFINE_CHANNEL_OPTION(giveops);
7240 DEFINE_CHANNEL_OPTION(protect);
7241 DEFINE_CHANNEL_OPTION(enfmodes);
7242 DEFINE_CHANNEL_OPTION(enftopic);
7243 DEFINE_CHANNEL_OPTION(pubcmd);
7244 DEFINE_CHANNEL_OPTION(givevoice);
7245 DEFINE_CHANNEL_OPTION(userinfo);
7246 DEFINE_CHANNEL_OPTION(dynlimit);
7247 DEFINE_CHANNEL_OPTION(topicsnarf);
7248 DEFINE_CHANNEL_OPTION(nodelete);
7249 DEFINE_CHANNEL_OPTION(toys);
7250 DEFINE_CHANNEL_OPTION(setters);
7251 DEFINE_CHANNEL_OPTION(topicrefresh);
7252 DEFINE_CHANNEL_OPTION(ctcpusers);
7253 DEFINE_CHANNEL_OPTION(ctcpreaction);
7254 DEFINE_CHANNEL_OPTION(inviteme);
7256 DEFINE_CHANNEL_OPTION(offchannel);
7257 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7259 /* Alias set topic to set defaulttopic for compatibility. */
7260 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7263 DEFINE_USER_OPTION(noautoop);
7264 DEFINE_USER_OPTION(autoinvite);
7265 DEFINE_USER_OPTION(info);
7267 /* Alias uset autovoice to uset autoop. */
7268 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7270 note_types = dict_new();
7271 dict_set_free_data(note_types, chanserv_deref_note_type);
7274 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7275 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7276 service_register(chanserv)->trigger = '!';
7277 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7279 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7281 if(chanserv_conf.channel_expire_frequency)
7282 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7284 if(chanserv_conf.refresh_period)
7286 time_t next_refresh;
7287 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7288 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7291 reg_exit_func(chanserv_db_cleanup);
7292 message_register_table(msgtab);