1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_MAX_CHAN_USERS "max_chan_users"
42 #define KEY_MAX_CHAN_BANS "max_chan_bans"
43 #define KEY_NICK "nick"
44 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
45 #define KEY_8BALL_RESPONSES "8ball"
46 #define KEY_OLD_BAN_NAMES "old_ban_names"
47 #define KEY_REFRESH_PERIOD "refresh_period"
48 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
49 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
50 #define KEY_MAX_OWNED "max_owned"
51 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
52 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
53 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
54 #define KEY_NODELETE_LEVEL "nodelete_level"
55 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
56 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
58 /* ChanServ database */
59 #define KEY_CHANNELS "channels"
60 #define KEY_NOTE_TYPES "note_types"
62 /* Note type parameters */
63 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
64 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
65 #define KEY_NOTE_SETTER_ACCESS "setter_access"
66 #define KEY_NOTE_VISIBILITY "visibility"
67 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
68 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
69 #define KEY_NOTE_VIS_ALL "all"
70 #define KEY_NOTE_MAX_LENGTH "max_length"
71 #define KEY_NOTE_SETTER "setter"
72 #define KEY_NOTE_NOTE "note"
74 /* Do-not-register channels */
76 #define KEY_DNR_SET "set"
77 #define KEY_DNR_SETTER "setter"
78 #define KEY_DNR_REASON "reason"
81 #define KEY_REGISTERED "registered"
82 #define KEY_REGISTRAR "registrar"
83 #define KEY_SUSPENDED "suspended"
84 #define KEY_PREVIOUS "previous"
85 #define KEY_SUSPENDER "suspender"
86 #define KEY_ISSUED "issued"
87 #define KEY_REVOKED "revoked"
88 #define KEY_SUSPEND_EXPIRES "suspend_expires"
89 #define KEY_SUSPEND_REASON "suspend_reason"
90 #define KEY_VISITED "visited"
91 #define KEY_TOPIC "topic"
92 #define KEY_GREETING "greeting"
93 #define KEY_USER_GREETING "user_greeting"
94 #define KEY_MODES "modes"
95 #define KEY_FLAGS "flags"
96 #define KEY_OPTIONS "options"
97 #define KEY_USERS "users"
98 #define KEY_BANS "bans"
100 #define KEY_NOTES "notes"
101 #define KEY_TOPIC_MASK "topic_mask"
102 #define KEY_OWNER_TRANSFER "owner_transfer"
105 #define KEY_LEVEL "level"
106 #define KEY_INFO "info"
107 #define KEY_SEEN "seen"
110 #define KEY_OWNER "owner"
111 #define KEY_REASON "reason"
112 #define KEY_SET "set"
113 #define KEY_DURATION "duration"
114 #define KEY_EXPIRES "expires"
115 #define KEY_TRIGGERED "triggered"
117 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
118 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
120 /* Administrative messages */
121 static const struct message_entry msgtab[] = {
122 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
124 /* Channel registration */
125 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
126 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
127 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
128 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
129 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
130 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
132 /* Do-not-register channels */
133 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
134 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
135 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
136 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
137 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
138 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
139 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
140 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
141 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
142 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
143 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
145 /* Channel unregistration */
146 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
147 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
148 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
149 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
152 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
153 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
155 /* Channel merging */
156 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
157 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
158 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
159 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
160 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
162 /* Handle unregistration */
163 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
166 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
167 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
168 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
169 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
170 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
171 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
172 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
173 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
174 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
175 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
176 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
177 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
178 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
180 /* Removing yourself from a channel. */
181 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
182 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
183 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
185 /* User management */
186 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
187 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
188 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
189 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
190 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
191 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
192 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
193 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
195 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
196 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
197 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
198 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
199 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
200 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
203 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
204 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
205 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
206 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
207 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
208 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
209 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
210 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
211 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
212 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
213 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
214 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
215 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
216 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
217 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
218 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
220 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
222 /* Channel management */
223 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
224 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
225 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
227 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
228 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
229 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
230 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic at most %d characters and matches the topic mask pattern." },
231 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
232 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
233 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
235 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
236 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
237 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
238 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
239 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
240 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
241 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
242 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
243 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
244 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
245 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
246 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
247 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
248 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
249 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
250 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
251 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
252 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
253 { "CSMSG_SET_MODES", "$bModes $b %s" },
254 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
255 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
256 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
257 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
258 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
259 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
260 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
261 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
262 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
263 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
264 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
265 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
266 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
267 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
268 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
269 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
270 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
271 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
272 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
273 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
274 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
275 { "CSMSG_USET_INFO", "$bInfo $b %s" },
277 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
278 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
279 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
280 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
281 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
282 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
283 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
284 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
285 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
286 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
287 { "CSMSG_PROTECT_NONE", "No users will be protected." },
288 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
289 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
290 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
291 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
292 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
293 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
294 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
295 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
296 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
297 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
298 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
299 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
301 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
302 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
303 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
304 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
305 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
306 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
307 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
308 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
310 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
311 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
312 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
314 /* Channel userlist */
315 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
316 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
317 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
318 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
320 /* Channel note list */
321 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
322 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
323 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
324 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
325 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
326 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
327 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
328 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
329 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
330 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
331 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
332 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
333 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
334 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
335 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
337 /* Channel [un]suspension */
338 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
339 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
340 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
341 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
342 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
343 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
344 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
346 /* Access information */
347 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
348 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
349 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
350 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
351 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
352 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
353 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
354 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
355 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
356 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
357 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
359 /* Seen information */
360 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
361 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
362 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
363 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
365 /* Names information */
366 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
367 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
369 /* Channel information */
370 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
371 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
372 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
373 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
374 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
375 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
376 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
377 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
378 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
379 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
380 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
381 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
382 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
383 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
384 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
385 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
386 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
387 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
388 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
389 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
390 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
392 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
393 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
394 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
395 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
396 { "CSMSG_PEEK_OPS", "$bOps:$b" },
397 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
399 /* Network information */
400 { "CSMSG_NETWORK_INFO", "Network Information:" },
401 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
402 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
403 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
404 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
405 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
406 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
407 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
408 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
411 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
412 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
413 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
415 /* Channel searches */
416 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
417 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
418 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
419 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
421 /* Channel configuration */
422 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
423 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
424 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
427 { "CSMSG_USER_OPTIONS", "User Options:" },
428 { "CSMSG_USER_PROTECTED", "That user is protected." },
431 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
432 { "CSMSG_PING_RESPONSE", "Pong!" },
433 { "CSMSG_WUT_RESPONSE", "wut" },
434 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
435 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
436 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
437 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
438 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
439 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
440 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
443 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
447 /* eject_user and unban_user flags */
448 #define ACTION_KICK 0x0001
449 #define ACTION_BAN 0x0002
450 #define ACTION_ADD_BAN 0x0004
451 #define ACTION_ADD_TIMED_BAN 0x0008
452 #define ACTION_UNBAN 0x0010
453 #define ACTION_DEL_BAN 0x0020
455 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
456 #define MODELEN 40 + KEYLEN
460 #define CSFUNC_ARGS user, channel, argc, argv, cmd
462 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
463 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
464 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
465 reply("MSG_MISSING_PARAMS", argv[0]); \
469 DECLARE_LIST(dnrList, struct do_not_register *);
470 DEFINE_LIST(dnrList, struct do_not_register *);
472 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
474 struct userNode *chanserv;
477 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
478 static struct log_type *CS_LOG;
482 struct channelList support_channels;
483 struct mod_chanmode default_modes;
485 unsigned long db_backup_frequency;
486 unsigned long channel_expire_frequency;
489 unsigned int adjust_delay;
490 long channel_expire_delay;
491 unsigned int nodelete_level;
493 unsigned int adjust_threshold;
494 int join_flood_threshold;
496 unsigned int greeting_length;
497 unsigned int refresh_period;
498 unsigned int giveownership_period;
500 unsigned int max_owned;
501 unsigned int max_chan_users;
502 unsigned int max_chan_bans;
503 unsigned int max_userinfo_length;
505 struct string_list *set_shows;
506 struct string_list *eightball;
507 struct string_list *old_ban_names;
509 const char *ctcp_short_ban_duration;
510 const char *ctcp_long_ban_duration;
512 const char *irc_operator_epithet;
513 const char *network_helper_epithet;
514 const char *support_helper_epithet;
519 struct userNode *user;
520 struct userNode *bot;
521 struct chanNode *channel;
523 unsigned short lowest;
524 unsigned short highest;
525 struct userData **users;
526 struct helpfile_table table;
529 enum note_access_type
531 NOTE_SET_CHANNEL_ACCESS,
532 NOTE_SET_CHANNEL_SETTER,
536 enum note_visible_type
539 NOTE_VIS_CHANNEL_USERS,
545 enum note_access_type set_access_type;
547 unsigned int min_opserv;
548 unsigned short min_ulevel;
550 enum note_visible_type visible_type;
551 unsigned int max_length;
558 struct note_type *type;
559 char setter[NICKSERV_HANDLE_LEN+1];
563 static unsigned int registered_channels;
564 static unsigned int banCount;
566 static const struct {
569 unsigned short level;
572 { "peon", "Peon", UL_PEON, '+' },
573 { "op", "Op", UL_OP, '@' },
574 { "master", "Master", UL_MASTER, '%' },
575 { "coowner", "Coowner", UL_COOWNER, '*' },
576 { "owner", "Owner", UL_OWNER, '!' },
577 { "helper", "BUG:", UL_HELPER, 'X' }
580 static const struct {
583 unsigned short default_value;
584 unsigned int old_idx;
585 unsigned int old_flag;
586 unsigned short flag_value;
588 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
589 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
590 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
591 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
592 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
593 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
594 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
595 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
596 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
597 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
598 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
601 struct charOptionValues {
604 } protectValues[] = {
605 { 'a', "CSMSG_PROTECT_ALL" },
606 { 'e', "CSMSG_PROTECT_EQUAL" },
607 { 'l', "CSMSG_PROTECT_LOWER" },
608 { 'n', "CSMSG_PROTECT_NONE" }
610 { 'd', "CSMSG_TOYS_DISABLED" },
611 { 'n', "CSMSG_TOYS_PRIVATE" },
612 { 'p', "CSMSG_TOYS_PUBLIC" }
613 }, topicRefreshValues[] = {
614 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
615 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
616 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
617 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
618 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
619 }, ctcpReactionValues[] = {
620 { 'k', "CSMSG_CTCPREACTION_KICK" },
621 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
622 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
623 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
626 static const struct {
630 unsigned int old_idx;
632 struct charOptionValues *values;
634 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
635 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
636 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
637 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
640 struct userData *helperList;
641 struct chanData *channelList;
642 static struct module *chanserv_module;
643 static unsigned int userCount;
645 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
646 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
647 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
650 user_level_from_name(const char *name, unsigned short clamp_level)
652 unsigned int level = 0, ii;
654 level = strtoul(name, NULL, 10);
655 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
656 if(!irccasecmp(name, accessLevels[ii].name))
657 level = accessLevels[ii].level;
658 if(level > clamp_level)
664 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
667 *minl = strtoul(arg, &sep, 10);
675 *maxl = strtoul(sep+1, &sep, 10);
683 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
685 struct userData *uData, **head;
687 if(!channel || !handle)
690 if(override && HANDLE_FLAGGED(handle, HELPING)
691 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
693 for(uData = helperList;
694 uData && uData->handle != handle;
695 uData = uData->next);
699 uData = calloc(1, sizeof(struct userData));
700 uData->handle = handle;
702 uData->access = UL_HELPER;
708 uData->next = helperList;
710 helperList->prev = uData;
718 for(uData = channel->users; uData; uData = uData->next)
719 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
722 head = &(channel->users);
725 if(uData && (uData != *head))
727 /* Shuffle the user to the head of whatever list he was in. */
729 uData->next->prev = uData->prev;
731 uData->prev->next = uData->next;
737 (**head).prev = uData;
744 /* Returns non-zero if user has at least the minimum access.
745 * exempt_owner is set when handling !set, so the owner can set things
748 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
750 struct userData *uData;
751 struct chanData *cData = channel->channel_info;
752 unsigned short minimum = cData->lvlOpts[opt];
755 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
758 if(minimum <= uData->access)
760 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
765 /* Scan for other users authenticated to the same handle
766 still in the channel. If so, keep them listed as present.
768 user is optional, if not null, it skips checking that userNode
769 (for the handle_part function) */
771 scan_user_presence(struct userData *uData, struct userNode *user)
775 if(IsSuspended(uData->channel)
776 || IsUserSuspended(uData)
777 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
789 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, char *text, UNUSED_ARG(struct userNode *bot))
791 unsigned int eflags, argc;
793 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
795 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
796 if(!channel->channel_info
797 || IsSuspended(channel->channel_info)
799 || !ircncasecmp(text, "ACTION ", 7))
801 /* Figure out the minimum level needed to CTCP the channel */
802 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
804 /* We need to enforce against them; do so. */
807 argv[1] = user->nick;
809 if(GetUserMode(channel, user))
810 eflags |= ACTION_KICK;
811 switch(channel->channel_info->chOpts[chCTCPReaction]) {
812 default: case 'k': /* just do the kick */ break;
814 eflags |= ACTION_BAN;
817 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
818 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
821 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
822 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
825 argv[argc++] = bad_ctcp_reason;
826 eject_user(chanserv, channel, argc, argv, NULL, eflags);
830 chanserv_create_note_type(const char *name)
832 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
833 strcpy(ntype->name, name);
835 dict_insert(note_types, ntype->name, ntype);
840 chanserv_deref_note_type(void *data)
842 struct note_type *ntype = data;
844 if(--ntype->refs > 0)
850 chanserv_flush_note_type(struct note_type *ntype)
852 struct chanData *cData;
853 for(cData = channelList; cData; cData = cData->next)
854 dict_remove(cData->notes, ntype->name);
858 chanserv_truncate_notes(struct note_type *ntype)
860 struct chanData *cData;
862 unsigned int size = sizeof(*note) + ntype->max_length;
864 for(cData = channelList; cData; cData = cData->next) {
865 note = dict_find(cData->notes, ntype->name, NULL);
868 if(strlen(note->note) <= ntype->max_length)
870 dict_remove2(cData->notes, ntype->name, 1);
871 note = realloc(note, size);
872 note->note[ntype->max_length] = 0;
873 dict_insert(cData->notes, ntype->name, note);
877 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
880 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
883 unsigned int len = strlen(text);
885 if(len > type->max_length) len = type->max_length;
886 note = calloc(1, sizeof(*note) + len);
888 strncpy(note->setter, setter, sizeof(note->setter)-1);
889 memcpy(note->note, text, len);
891 dict_insert(channel->notes, type->name, note);
897 chanserv_free_note(void *data)
899 struct note *note = data;
901 chanserv_deref_note_type(note->type);
902 assert(note->type->refs > 0); /* must use delnote to remove the type */
906 static MODCMD_FUNC(cmd_createnote) {
907 struct note_type *ntype;
908 unsigned int arg = 1, existed = 0, max_length;
910 if((ntype = dict_find(note_types, argv[1], NULL)))
913 ntype = chanserv_create_note_type(argv[arg]);
914 if(!irccasecmp(argv[++arg], "privileged"))
917 ntype->set_access_type = NOTE_SET_PRIVILEGED;
918 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
920 else if(!irccasecmp(argv[arg], "channel"))
922 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
925 reply("CSMSG_INVALID_ACCESS", argv[arg]);
928 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
929 ntype->set_access.min_ulevel = ulvl;
931 else if(!irccasecmp(argv[arg], "setter"))
933 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
937 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
941 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->visible_type = NOTE_VIS_PRIVILEGED;
943 else if(!irccasecmp(argv[arg], "channel_users"))
944 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
945 else if(!irccasecmp(argv[arg], "all"))
946 ntype->visible_type = NOTE_VIS_ALL;
948 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
952 if((arg+1) >= argc) {
953 reply("MSG_MISSING_PARAMS", argv[0]);
956 max_length = strtoul(argv[++arg], NULL, 0);
957 if(max_length < 20 || max_length > 450)
959 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
962 if(existed && (max_length < ntype->max_length))
964 ntype->max_length = max_length;
965 chanserv_truncate_notes(ntype);
967 ntype->max_length = max_length;
970 reply("CSMSG_NOTE_MODIFIED", ntype->name);
972 reply("CSMSG_NOTE_CREATED", ntype->name);
977 dict_remove(note_types, ntype->name);
981 static MODCMD_FUNC(cmd_removenote) {
982 struct note_type *ntype;
985 ntype = dict_find(note_types, argv[1], NULL);
986 force = (argc > 2) && !irccasecmp(argv[2], "force");
989 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
996 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
999 chanserv_flush_note_type(ntype);
1001 dict_remove(note_types, argv[1]);
1002 reply("CSMSG_NOTE_DELETED", argv[1]);
1007 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1011 if(orig->modes_set & change->modes_clear)
1013 if(orig->modes_clear & change->modes_set)
1015 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1016 && strcmp(orig->new_key, change->new_key))
1018 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1019 && (orig->new_limit != change->new_limit))
1024 static char max_length_text[MAXLEN+1][16];
1026 static struct helpfile_expansion
1027 chanserv_expand_variable(const char *variable)
1029 struct helpfile_expansion exp;
1031 if(!irccasecmp(variable, "notes"))
1034 exp.type = HF_TABLE;
1035 exp.value.table.length = 1;
1036 exp.value.table.width = 3;
1037 exp.value.table.flags = 0;
1038 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1039 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1040 exp.value.table.contents[0][0] = "Note Type";
1041 exp.value.table.contents[0][1] = "Visibility";
1042 exp.value.table.contents[0][2] = "Max Length";
1043 for(it=dict_first(note_types); it; it=iter_next(it))
1045 struct note_type *ntype = iter_data(it);
1048 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1049 row = exp.value.table.length++;
1050 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1051 exp.value.table.contents[row][0] = ntype->name;
1052 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1053 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1055 if(!max_length_text[ntype->max_length][0])
1056 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1057 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1062 exp.type = HF_STRING;
1063 exp.value.str = NULL;
1067 static struct chanData*
1068 register_channel(struct chanNode *cNode, char *registrar)
1070 struct chanData *channel;
1071 enum levelOption lvlOpt;
1072 enum charOption chOpt;
1074 channel = calloc(1, sizeof(struct chanData));
1076 channel->notes = dict_new();
1077 dict_set_free_data(channel->notes, chanserv_free_note);
1079 channel->registrar = strdup(registrar);
1080 channel->registered = now;
1081 channel->visited = now;
1082 channel->limitAdjusted = now;
1083 channel->ownerTransfer = now;
1084 channel->flags = CHANNEL_DEFAULT_FLAGS;
1085 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1086 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1087 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1088 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1090 channel->prev = NULL;
1091 channel->next = channelList;
1094 channelList->prev = channel;
1095 channelList = channel;
1096 registered_channels++;
1098 channel->channel = cNode;
1100 cNode->channel_info = channel;
1105 static struct userData*
1106 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1108 struct userData *ud;
1110 if(access > UL_OWNER)
1113 ud = calloc(1, sizeof(*ud));
1114 ud->channel = channel;
1115 ud->handle = handle;
1117 ud->access = access;
1118 ud->info = info ? strdup(info) : NULL;
1121 ud->next = channel->users;
1123 channel->users->prev = ud;
1124 channel->users = ud;
1126 channel->userCount++;
1130 ud->u_next = ud->handle->channels;
1132 ud->u_next->u_prev = ud;
1133 ud->handle->channels = ud;
1138 static void unregister_channel(struct chanData *channel, const char *reason);
1141 del_channel_user(struct userData *user, int do_gc)
1143 struct chanData *channel = user->channel;
1145 channel->userCount--;
1149 user->prev->next = user->next;
1151 channel->users = user->next;
1153 user->next->prev = user->prev;
1156 user->u_prev->u_next = user->u_next;
1158 user->handle->channels = user->u_next;
1160 user->u_next->u_prev = user->u_prev;
1164 if(do_gc && !channel->users && !IsProtected(channel))
1165 unregister_channel(channel, "lost all users.");
1168 static void expire_ban(void *data);
1170 static struct banData*
1171 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1174 unsigned int ii, l1, l2;
1179 bd = malloc(sizeof(struct banData));
1181 bd->channel = channel;
1183 bd->triggered = triggered;
1184 bd->expires = expires;
1186 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1188 extern const char *hidden_host_suffix;
1189 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1193 l2 = strlen(old_name);
1196 if(irccasecmp(mask + l1 - l2, old_name))
1198 new_mask = alloca(MAXLEN);
1199 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1202 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1204 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1205 bd->reason = strdup(reason);
1208 timeq_add(expires, expire_ban, bd);
1211 bd->next = channel->bans;
1213 channel->bans->prev = bd;
1215 channel->banCount++;
1222 del_channel_ban(struct banData *ban)
1224 ban->channel->banCount--;
1228 ban->prev->next = ban->next;
1230 ban->channel->bans = ban->next;
1233 ban->next->prev = ban->prev;
1236 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1245 expire_ban(void *data)
1247 struct banData *bd = data;
1248 if(!IsSuspended(bd->channel))
1250 struct banList bans;
1251 struct mod_chanmode change;
1253 bans = bd->channel->channel->banlist;
1254 mod_chanmode_init(&change);
1255 for(ii=0; ii<bans.used; ii++)
1257 if(!strcmp(bans.list[ii]->ban, bd->mask))
1260 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1261 change.args[0].u.hostmask = bd->mask;
1262 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1268 del_channel_ban(bd);
1271 static void chanserv_expire_suspension(void *data);
1274 unregister_channel(struct chanData *channel, const char *reason)
1276 struct mod_chanmode change;
1277 char msgbuf[MAXLEN];
1279 /* After channel unregistration, the following must be cleaned
1281 - Channel information.
1284 - Channel suspension data.
1285 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1291 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1295 mod_chanmode_init(&change);
1296 change.modes_clear |= MODE_REGISTERED;
1297 mod_chanmode_announce(chanserv, channel->channel, &change);
1300 while(channel->users)
1301 del_channel_user(channel->users, 0);
1303 while(channel->bans)
1304 del_channel_ban(channel->bans);
1306 free(channel->topic);
1307 free(channel->registrar);
1308 free(channel->greeting);
1309 free(channel->user_greeting);
1310 free(channel->topic_mask);
1313 channel->prev->next = channel->next;
1315 channelList = channel->next;
1318 channel->next->prev = channel->prev;
1320 if(channel->suspended)
1322 struct chanNode *cNode = channel->channel;
1323 struct suspended *suspended, *next_suspended;
1325 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1327 next_suspended = suspended->previous;
1328 free(suspended->suspender);
1329 free(suspended->reason);
1330 if(suspended->expires)
1331 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1336 cNode->channel_info = NULL;
1338 channel->channel->channel_info = NULL;
1340 dict_delete(channel->notes);
1341 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1342 if(!IsSuspended(channel))
1343 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1344 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1345 UnlockChannel(channel->channel);
1347 registered_channels--;
1351 expire_channels(UNUSED_ARG(void *data))
1353 struct chanData *channel, *next;
1354 struct userData *user;
1355 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1357 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1358 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1360 for(channel = channelList; channel; channel = next)
1362 next = channel->next;
1364 /* See if the channel can be expired. */
1365 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1366 || IsProtected(channel))
1369 /* Make sure there are no high-ranking users still in the channel. */
1370 for(user=channel->users; user; user=user->next)
1371 if(user->present && (user->access >= UL_PRESENT))
1376 /* Unregister the channel */
1377 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1378 unregister_channel(channel, "registration expired.");
1381 if(chanserv_conf.channel_expire_frequency)
1382 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1386 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1388 char protect = channel->chOpts[chProtect];
1389 struct userData *cs_victim, *cs_aggressor;
1391 /* Don't protect if no one is to be protected, someone is attacking
1392 himself, or if the aggressor is an IRC Operator. */
1393 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1396 /* Don't protect if the victim isn't authenticated (because they
1397 can't be a channel user), unless we are to protect non-users
1399 cs_victim = GetChannelAccess(channel, victim->handle_info);
1400 if(protect != 'a' && !cs_victim)
1403 /* Protect if the aggressor isn't a user because at this point,
1404 the aggressor can only be less than or equal to the victim. */
1405 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1409 /* If the aggressor was a user, then the victim can't be helped. */
1416 if(cs_victim->access > cs_aggressor->access)
1421 if(cs_victim->access >= cs_aggressor->access)
1430 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1432 struct chanData *cData = channel->channel_info;
1433 struct userData *cs_victim;
1435 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1436 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1437 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1439 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1447 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1449 if(IsService(victim))
1451 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1455 if(protect_user(victim, user, channel->channel_info))
1457 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1464 static struct do_not_register *
1465 chanserv_add_dnr(const char *chan_name, const char *setter, const char *reason)
1467 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1468 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1469 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1470 strcpy(dnr->reason, reason);
1472 if(dnr->chan_name[0] == '*')
1473 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1474 else if(strpbrk(dnr->chan_name, "*?"))
1475 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1477 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1481 static struct dnrList
1482 chanserv_find_dnrs(const char *chan_name, const char *handle)
1484 struct dnrList list;
1486 struct do_not_register *dnr;
1488 dnrList_init(&list);
1489 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1490 dnrList_append(&list, dnr);
1491 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1492 dnrList_append(&list, dnr);
1494 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1495 if(match_ircglob(chan_name, iter_key(it)))
1496 dnrList_append(&list, iter_data(it));
1501 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1503 struct dnrList list;
1504 struct do_not_register *dnr;
1506 char buf[INTERVALLEN];
1508 list = chanserv_find_dnrs(chan_name, handle);
1509 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1511 dnr = list.list[ii];
1514 strftime(buf, sizeof(buf), "%Y %b %d", localtime(&dnr->set));
1515 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, buf, dnr->setter, dnr->reason);
1518 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1521 reply("CSMSG_MORE_DNRS", list.used - ii);
1526 struct do_not_register *
1527 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1529 struct do_not_register *dnr;
1532 if(handle && (dnr = dict_find(handle_dnrs, handle->handle, NULL)))
1536 if((dnr = dict_find(plain_dnrs, chan_name, NULL)))
1538 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1539 if(match_ircglob(chan_name, iter_key(it)))
1540 return iter_data(it);
1545 static CHANSERV_FUNC(cmd_noregister)
1548 struct do_not_register *dnr;
1549 char buf[INTERVALLEN];
1550 unsigned int matches;
1556 reply("CSMSG_DNR_SEARCH_RESULTS");
1558 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1560 dnr = iter_data(it);
1562 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1564 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1567 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1569 dnr = iter_data(it);
1571 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1573 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1576 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1578 dnr = iter_data(it);
1580 reply("CSMSG_DNR_INFO_SET", dnr->chan_name, intervalString(buf, now - dnr->set, user->handle_info), dnr->setter, dnr->reason);
1582 reply("CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1587 reply("MSG_MATCH_COUNT", matches);
1589 reply("MSG_NO_MATCHES");
1595 if(!IsChannelName(target) && (*target != '*'))
1597 reply("CSMSG_NOT_DNR", target);
1603 const char *reason = unsplit_string(argv + 2, argc - 2, NULL);
1604 if((*target == '*') && !get_handle_info(target + 1))
1606 reply("MSG_HANDLE_UNKNOWN", target + 1);
1609 chanserv_add_dnr(target, user->handle_info->handle, reason);
1610 reply("CSMSG_NOREGISTER_CHANNEL", target);
1614 reply("CSMSG_DNR_SEARCH_RESULTS");
1616 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1618 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1620 reply("MSG_NO_MATCHES");
1624 static CHANSERV_FUNC(cmd_allowregister)
1626 const char *chan_name = argv[1];
1628 if((chan_name[0] == '*') && dict_find(handle_dnrs, chan_name+1, NULL))
1630 dict_remove(handle_dnrs, chan_name+1);
1631 reply("CSMSG_DNR_REMOVED", chan_name);
1633 else if(dict_find(plain_dnrs, chan_name, NULL))
1635 dict_remove(plain_dnrs, chan_name);
1636 reply("CSMSG_DNR_REMOVED", chan_name);
1638 else if(dict_find(mask_dnrs, chan_name, NULL))
1640 dict_remove(mask_dnrs, chan_name);
1641 reply("CSMSG_DNR_REMOVED", chan_name);
1645 reply("CSMSG_NO_SUCH_DNR", chan_name);
1652 chanserv_get_owned_count(struct handle_info *hi)
1654 struct userData *cList;
1657 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1658 if(cList->access == UL_OWNER)
1663 static CHANSERV_FUNC(cmd_register)
1665 struct mod_chanmode *change;
1666 struct handle_info *handle;
1667 struct chanData *cData;
1668 struct modeNode *mn;
1669 char reason[MAXLEN];
1671 unsigned int new_channel, force=0;
1672 struct do_not_register *dnr;
1676 if(channel->channel_info)
1678 reply("CSMSG_ALREADY_REGGED", channel->name);
1682 if(channel->bad_channel)
1684 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1688 if(!IsHelping(user) && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
1690 reply("CSMSG_MUST_BE_OPPED", channel->name);
1695 chan_name = channel->name;
1699 if((argc < 2) || !IsChannelName(argv[1]))
1701 reply("MSG_NOT_CHANNEL_NAME");
1705 if(opserv_bad_channel(argv[1]))
1707 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1712 chan_name = argv[1];
1715 if(argc >= (new_channel+2))
1717 if(!IsHelping(user))
1719 reply("CSMSG_PROXY_FORBIDDEN");
1723 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
1725 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
1726 dnr = chanserv_is_dnr(chan_name, handle);
1730 handle = user->handle_info;
1731 dnr = chanserv_is_dnr(chan_name, handle);
1735 if(!IsHelping(user))
1736 reply("CSMSG_DNR_CHANNEL", chan_name);
1738 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
1742 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
1744 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
1749 channel = AddChannel(argv[1], now, NULL, NULL);
1751 cData = register_channel(channel, user->handle_info->handle);
1752 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
1753 cData->modes = chanserv_conf.default_modes;
1755 cData->modes.modes_set |= MODE_REGISTERED;
1756 change = mod_chanmode_dup(&cData->modes, 1);
1757 change->args[change->argc].mode = MODE_CHANOP;
1758 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
1760 mod_chanmode_announce(chanserv, channel, change);
1761 mod_chanmode_free(change);
1763 /* Initialize the channel's max user record. */
1764 cData->max = channel->members.used;
1766 if(handle != user->handle_info)
1767 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
1769 reply("CSMSG_REG_SUCCESS", channel->name);
1771 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
1772 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1777 make_confirmation_string(struct userData *uData)
1779 static char strbuf[16];
1784 for(src = uData->handle->handle; *src; )
1785 accum = accum * 31 + toupper(*src++);
1787 for(src = uData->channel->channel->name; *src; )
1788 accum = accum * 31 + toupper(*src++);
1789 sprintf(strbuf, "%08x", accum);
1793 static CHANSERV_FUNC(cmd_unregister)
1796 char reason[MAXLEN];
1797 struct chanData *cData;
1798 struct userData *uData;
1800 cData = channel->channel_info;
1803 reply("CSMSG_NOT_REGISTERED", channel->name);
1807 uData = GetChannelUser(cData, user->handle_info);
1808 if(!uData || (uData->access < UL_OWNER))
1810 reply("CSMSG_NO_ACCESS");
1814 if(IsProtected(cData))
1816 reply("CSMSG_UNREG_NODELETE", channel->name);
1820 if(!IsHelping(user))
1822 const char *confirm_string;
1823 if(IsSuspended(cData))
1825 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
1828 confirm_string = make_confirmation_string(uData);
1829 if((argc < 2) || strcmp(argv[1], confirm_string))
1831 reply("CSMSG_CONFIRM_UNREG", confirm_string);
1836 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
1837 name = strdup(channel->name);
1838 unregister_channel(cData, reason);
1839 reply("CSMSG_UNREG_SUCCESS", name);
1844 static CHANSERV_FUNC(cmd_move)
1846 struct mod_chanmode change;
1847 struct chanNode *target;
1848 struct modeNode *mn;
1849 struct userData *uData;
1850 char reason[MAXLEN];
1851 struct do_not_register *dnr;
1855 if(IsProtected(channel->channel_info))
1857 reply("CSMSG_MOVE_NODELETE", channel->name);
1861 if(!IsChannelName(argv[1]))
1863 reply("MSG_NOT_CHANNEL_NAME");
1867 if(opserv_bad_channel(argv[1]))
1869 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
1873 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
1875 for(uData = channel->channel_info->users; uData; uData = uData->next)
1877 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
1879 if(!IsHelping(user))
1880 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
1882 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
1888 mod_chanmode_init(&change);
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))
1909 change.args[0].mode = MODE_CHANOP;
1910 change.args[0].u.member = AddChannelUser(chanserv, target);
1911 mod_chanmode_announce(chanserv, target, &change);
1916 /* Clear MODE_REGISTERED from old channel, add it to new. */
1918 change.modes_clear = MODE_REGISTERED;
1919 mod_chanmode_announce(chanserv, channel, &change);
1920 change.modes_clear = 0;
1921 change.modes_set = MODE_REGISTERED;
1922 mod_chanmode_announce(chanserv, target, &change);
1925 /* Move the channel_info to the target channel; it
1926 shouldn't be necessary to clear timeq callbacks
1927 for the old channel. */
1928 target->channel_info = channel->channel_info;
1929 target->channel_info->channel = target;
1930 channel->channel_info = NULL;
1932 reply("CSMSG_MOVE_SUCCESS", target->name);
1934 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
1935 if(!IsSuspended(target->channel_info))
1937 char reason2[MAXLEN];
1938 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
1939 DelChannelUser(chanserv, channel, reason2, 0);
1941 UnlockChannel(channel);
1942 LockChannel(target);
1943 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
1948 merge_users(struct chanData *source, struct chanData *target)
1950 struct userData *suData, *tuData, *next;
1956 /* Insert the source's users into the scratch area. */
1957 for(suData = source->users; suData; suData = suData->next)
1958 dict_insert(merge, suData->handle->handle, suData);
1960 /* Iterate through the target's users, looking for
1961 users common to both channels. The lower access is
1962 removed from either the scratch area or target user
1964 for(tuData = target->users; tuData; tuData = next)
1966 struct userData *choice;
1968 next = tuData->next;
1970 /* If a source user exists with the same handle as a target
1971 channel's user, resolve the conflict by removing one. */
1972 suData = dict_find(merge, tuData->handle->handle, NULL);
1976 /* Pick the data we want to keep. */
1977 /* If the access is the same, use the later seen time. */
1978 if(suData->access == tuData->access)
1979 choice = (suData->seen > tuData->seen) ? suData : tuData;
1980 else /* Otherwise, keep the higher access level. */
1981 choice = (suData->access > tuData->access) ? suData : tuData;
1983 /* Remove the user that wasn't picked. */
1984 if(choice == tuData)
1986 dict_remove(merge, suData->handle->handle);
1987 del_channel_user(suData, 0);
1990 del_channel_user(tuData, 0);
1993 /* Move the remaining users to the target channel. */
1994 for(it = dict_first(merge); it; it = iter_next(it))
1996 suData = iter_data(it);
1998 /* Insert the user into the target channel's linked list. */
1999 suData->prev = NULL;
2000 suData->next = target->users;
2001 suData->channel = target;
2004 target->users->prev = suData;
2005 target->users = suData;
2007 /* Update the user counts for the target channel; the
2008 source counts are left alone. */
2009 target->userCount++;
2012 /* Possible to assert (source->users == NULL) here. */
2013 source->users = NULL;
2018 merge_bans(struct chanData *source, struct chanData *target)
2020 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2022 /* Hold on to the original head of the target ban list
2023 to avoid comparing source bans with source bans. */
2024 tFront = target->bans;
2026 /* Perform a totally expensive O(n*m) merge, ick. */
2027 for(sbData = source->bans; sbData; sbData = sNext)
2029 /* Flag to track whether the ban's been moved
2030 to the destination yet. */
2033 /* Possible to assert (sbData->prev == NULL) here. */
2034 sNext = sbData->next;
2036 for(tbData = tFront; tbData; tbData = tNext)
2038 tNext = tbData->next;
2040 /* Perform two comparisons between each source
2041 and target ban, conflicts are resolved by
2042 keeping the broader ban and copying the later
2043 expiration and triggered time. */
2044 if(match_ircglobs(tbData->mask, sbData->mask))
2046 /* There is a broader ban in the target channel that
2047 overrides one in the source channel; remove the
2048 source ban and break. */
2049 if(sbData->expires > tbData->expires)
2050 tbData->expires = sbData->expires;
2051 if(sbData->triggered > tbData->triggered)
2052 tbData->triggered = sbData->triggered;
2053 del_channel_ban(sbData);
2056 else if(match_ircglobs(sbData->mask, tbData->mask))
2058 /* There is a broader ban in the source channel that
2059 overrides one in the target channel; remove the
2060 target ban, fall through and move the source over. */
2061 if(tbData->expires > sbData->expires)
2062 sbData->expires = tbData->expires;
2063 if(tbData->triggered > sbData->triggered)
2064 sbData->triggered = tbData->triggered;
2065 if(tbData == tFront)
2067 del_channel_ban(tbData);
2070 /* Source bans can override multiple target bans, so
2071 we allow a source to run through this loop multiple
2072 times, but we can only move it once. */
2077 /* Remove the source ban from the source ban list. */
2079 sbData->next->prev = sbData->prev;
2081 /* Modify the source ban's associated channel. */
2082 sbData->channel = target;
2084 /* Insert the ban into the target channel's linked list. */
2085 sbData->prev = NULL;
2086 sbData->next = target->bans;
2089 target->bans->prev = sbData;
2090 target->bans = sbData;
2092 /* Update the user counts for the target channel. */
2097 /* Possible to assert (source->bans == NULL) here. */
2098 source->bans = NULL;
2102 merge_data(struct chanData *source, struct chanData *target)
2104 if(source->visited > target->visited)
2105 target->visited = source->visited;
2109 merge_channel(struct chanData *source, struct chanData *target)
2111 merge_users(source, target);
2112 merge_bans(source, target);
2113 merge_data(source, target);
2116 static CHANSERV_FUNC(cmd_merge)
2118 struct userData *target_user;
2119 struct chanNode *target;
2120 char reason[MAXLEN];
2124 /* Make sure the target channel exists and is registered to the user
2125 performing the command. */
2126 if(!(target = GetChannel(argv[1])))
2128 reply("MSG_INVALID_CHANNEL");
2132 if(!target->channel_info)
2134 reply("CSMSG_NOT_REGISTERED", target->name);
2138 if(IsProtected(channel->channel_info))
2140 reply("CSMSG_MERGE_NODELETE");
2144 if(IsSuspended(target->channel_info))
2146 reply("CSMSG_MERGE_SUSPENDED");
2150 if(channel == target)
2152 reply("CSMSG_MERGE_SELF");
2156 target_user = GetChannelUser(target->channel_info, user->handle_info);
2157 if(!target_user || (target_user->access < UL_OWNER))
2159 reply("CSMSG_MERGE_NOT_OWNER");
2163 /* Merge the channel structures and associated data. */
2164 merge_channel(channel->channel_info, target->channel_info);
2165 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2166 unregister_channel(channel->channel_info, reason);
2167 reply("CSMSG_MERGE_SUCCESS", target->name);
2171 static CHANSERV_FUNC(cmd_opchan)
2173 struct mod_chanmode change;
2174 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2176 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2179 channel->channel_info->may_opchan = 0;
2180 mod_chanmode_init(&change);
2182 change.args[0].mode = MODE_CHANOP;
2183 change.args[0].u.member = GetUserMode(channel, chanserv);
2184 mod_chanmode_announce(chanserv, channel, &change);
2185 reply("CSMSG_OPCHAN_DONE", channel->name);
2189 static CHANSERV_FUNC(cmd_adduser)
2191 struct userData *actee;
2192 struct userData *actor;
2193 struct handle_info *handle;
2194 unsigned short access;
2198 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2200 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2204 access = user_level_from_name(argv[2], UL_OWNER);
2207 reply("CSMSG_INVALID_ACCESS", argv[2]);
2211 actor = GetChannelUser(channel->channel_info, user->handle_info);
2212 if(actor->access <= access)
2214 reply("CSMSG_NO_BUMP_ACCESS");
2218 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2221 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2223 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2227 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2228 scan_user_presence(actee, NULL);
2229 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2233 static CHANSERV_FUNC(cmd_clvl)
2235 struct handle_info *handle;
2236 struct userData *victim;
2237 struct userData *actor;
2238 unsigned short new_access;
2239 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2243 actor = GetChannelUser(channel->channel_info, user->handle_info);
2245 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2248 if(handle == user->handle_info && !privileged)
2250 reply("CSMSG_NO_SELF_CLVL");
2254 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2256 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2260 if(actor->access <= victim->access && !privileged)
2262 reply("MSG_USER_OUTRANKED", handle->handle);
2266 new_access = user_level_from_name(argv[2], UL_OWNER);
2270 reply("CSMSG_INVALID_ACCESS", argv[2]);
2274 if(new_access >= actor->access && !privileged)
2276 reply("CSMSG_NO_BUMP_ACCESS");
2280 victim->access = new_access;
2281 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2285 static CHANSERV_FUNC(cmd_deluser)
2287 struct handle_info *handle;
2288 struct userData *victim;
2289 struct userData *actor;
2290 unsigned short access;
2295 actor = GetChannelUser(channel->channel_info, user->handle_info);
2297 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2300 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2302 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2308 access = user_level_from_name(argv[1], UL_OWNER);
2311 reply("CSMSG_INVALID_ACCESS", argv[1]);
2314 if(access != victim->access)
2316 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2322 access = victim->access;
2325 if((actor->access <= victim->access) && !IsHelping(user))
2327 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2331 chan_name = strdup(channel->name);
2332 del_channel_user(victim, 1);
2333 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2339 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2341 struct userData *actor, *uData, *next;
2343 actor = GetChannelUser(channel->channel_info, user->handle_info);
2345 if(min_access > max_access)
2347 reply("CSMSG_BAD_RANGE", min_access, max_access);
2351 if((actor->access <= max_access) && !IsHelping(user))
2353 reply("CSMSG_NO_ACCESS");
2357 for(uData = channel->channel_info->users; uData; uData = next)
2361 if((uData->access >= min_access)
2362 && (uData->access <= max_access)
2363 && match_ircglob(uData->handle->handle, mask))
2364 del_channel_user(uData, 1);
2367 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2371 static CHANSERV_FUNC(cmd_mdelowner)
2373 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2376 static CHANSERV_FUNC(cmd_mdelcoowner)
2378 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2381 static CHANSERV_FUNC(cmd_mdelmaster)
2383 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2386 static CHANSERV_FUNC(cmd_mdelop)
2388 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2391 static CHANSERV_FUNC(cmd_mdelpeon)
2393 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2397 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2399 struct banData *bData, *next;
2400 char interval[INTERVALLEN];
2405 limit = now - duration;
2406 for(bData = channel->channel_info->bans; bData; bData = next)
2410 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2413 del_channel_ban(bData);
2417 intervalString(interval, duration, user->handle_info);
2418 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2423 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration)
2425 struct userData *actor, *uData, *next;
2426 char interval[INTERVALLEN];
2430 actor = GetChannelUser(channel->channel_info, user->handle_info);
2431 if(min_access > max_access)
2433 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2437 if((actor->access <= max_access) && !IsHelping(user))
2439 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2444 limit = now - duration;
2445 for(uData = channel->channel_info->users; uData; uData = next)
2449 if((uData->seen > limit) || uData->present)
2452 if(((uData->access >= min_access) && (uData->access <= max_access))
2453 || (!max_access && (uData->access < actor->access)))
2455 del_channel_user(uData, 1);
2463 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2465 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2469 static CHANSERV_FUNC(cmd_trim)
2471 unsigned long duration;
2472 unsigned short min_level, max_level;
2476 duration = ParseInterval(argv[2]);
2479 reply("CSMSG_CANNOT_TRIM");
2483 if(!irccasecmp(argv[1], "bans"))
2485 cmd_trim_bans(user, channel, duration);
2488 else if(!irccasecmp(argv[1], "users"))
2490 cmd_trim_users(user, channel, 0, 0, duration);
2493 else if(parse_level_range(&min_level, &max_level, argv[1]))
2495 cmd_trim_users(user, channel, min_level, max_level, duration);
2498 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2500 cmd_trim_users(user, channel, min_level, min_level, duration);
2505 reply("CSMSG_INVALID_TRIM", argv[1]);
2510 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2511 to the user. cmd_all takes advantage of this. */
2512 static CHANSERV_FUNC(cmd_up)
2514 struct mod_chanmode change;
2515 struct userData *uData;
2518 mod_chanmode_init(&change);
2520 change.args[0].u.member = GetUserMode(channel, user);
2521 if(!change.args[0].u.member)
2524 reply("MSG_CHANNEL_ABSENT", channel->name);
2528 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2532 reply("CSMSG_GODMODE_UP", argv[0]);
2535 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2537 change.args[0].mode = MODE_CHANOP;
2538 errmsg = "CSMSG_ALREADY_OPPED";
2540 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2542 change.args[0].mode = MODE_VOICE;
2543 errmsg = "CSMSG_ALREADY_VOICED";
2548 reply("CSMSG_NO_ACCESS");
2551 change.args[0].mode &= ~change.args[0].u.member->modes;
2552 if(!change.args[0].mode)
2555 reply(errmsg, channel->name);
2558 modcmd_chanmode_announce(&change);
2562 static CHANSERV_FUNC(cmd_down)
2564 struct mod_chanmode change;
2566 mod_chanmode_init(&change);
2568 change.args[0].u.member = GetUserMode(channel, user);
2569 if(!change.args[0].u.member)
2572 reply("MSG_CHANNEL_ABSENT", channel->name);
2576 if(!change.args[0].u.member->modes)
2579 reply("CSMSG_ALREADY_DOWN", channel->name);
2583 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2584 modcmd_chanmode_announce(&change);
2588 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)
2590 struct userData *cList;
2592 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2594 if(IsSuspended(cList->channel)
2595 || IsUserSuspended(cList)
2596 || !GetUserMode(cList->channel->channel, user))
2599 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2605 static CHANSERV_FUNC(cmd_upall)
2607 return cmd_all(CSFUNC_ARGS, cmd_up);
2610 static CHANSERV_FUNC(cmd_downall)
2612 return cmd_all(CSFUNC_ARGS, cmd_down);
2615 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2616 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2619 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)
2621 unsigned int ii, valid;
2622 struct userNode *victim;
2623 struct mod_chanmode *change;
2625 change = mod_chanmode_alloc(argc - 1);
2627 for(ii=valid=0; ++ii < argc; )
2629 if(!(victim = GetUserH(argv[ii])))
2631 change->args[valid].mode = mode;
2632 change->args[valid].u.member = GetUserMode(channel, victim);
2633 if(!change->args[valid].u.member)
2635 if(validate && !validate(user, channel, victim))
2640 change->argc = valid;
2641 if(valid < (argc-1))
2642 reply("CSMSG_PROCESS_FAILED");
2645 modcmd_chanmode_announce(change);
2646 reply(action, channel->name);
2648 mod_chanmode_free(change);
2652 static CHANSERV_FUNC(cmd_op)
2654 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
2657 static CHANSERV_FUNC(cmd_deop)
2659 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
2662 static CHANSERV_FUNC(cmd_voice)
2664 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
2667 static CHANSERV_FUNC(cmd_devoice)
2669 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
2673 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, int *victimCount, struct modeNode **victims)
2679 for(ii=0; ii<channel->members.used; ii++)
2681 struct modeNode *mn = channel->members.list[ii];
2683 if(IsService(mn->user))
2686 if(!user_matches_glob(mn->user, ban, 1))
2689 if(protect_user(mn->user, user, channel->channel_info))
2693 victims[(*victimCount)++] = mn;
2699 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
2701 struct userNode *victim;
2702 struct modeNode **victims;
2703 unsigned int offset, n, victimCount, duration = 0;
2704 char *reason = "Bye.", *ban, *name;
2705 char interval[INTERVALLEN];
2707 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
2708 REQUIRE_PARAMS(offset);
2711 reason = unsplit_string(argv + offset, argc - offset, NULL);
2712 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
2714 /* Truncate the reason to a length of TOPICLEN, as
2715 the ircd does; however, leave room for an ellipsis
2716 and the kicker's nick. */
2717 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
2721 if((victim = GetUserH(argv[1])))
2723 victims = alloca(sizeof(victims[0]));
2724 victims[0] = GetUserMode(channel, victim);
2725 /* XXX: The comparison with ACTION_KICK is just because all
2726 * other actions can work on users outside the channel, and we
2727 * want to allow those (e.g. unbans) in that case. If we add
2728 * some other ejection action for in-channel users, change
2730 victimCount = victims[0] ? 1 : 0;
2732 if(IsService(victim))
2734 reply("MSG_SERVICE_IMMUNE", victim->nick);
2738 if((action == ACTION_KICK) && !victimCount)
2740 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
2744 if(protect_user(victim, user, channel->channel_info))
2746 reply("CSMSG_USER_PROTECTED", victim->nick);
2750 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
2751 name = victim->nick;
2755 if(!is_ircmask(argv[1]))
2757 reply("MSG_NICK_UNKNOWN", argv[1]);
2761 victims = alloca(sizeof(victims[0]) * channel->members.used);
2763 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
2765 reply("CSMSG_MASK_PROTECTED", argv[1]);
2769 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
2771 reply("CSMSG_LAME_MASK", argv[1]);
2775 if((action == ACTION_KICK) && (victimCount == 0))
2777 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
2781 name = ban = strdup(argv[1]);
2784 /* Truncate the ban in place if necessary; we must ensure
2785 that 'ban' is a valid ban mask before sanitizing it. */
2786 sanitize_ircmask(ban);
2788 if(action & ACTION_ADD_BAN)
2790 struct banData *bData, *next;
2792 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
2794 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
2799 if(action & ACTION_ADD_TIMED_BAN)
2801 duration = ParseInterval(argv[2]);
2805 reply("CSMSG_DURATION_TOO_LOW");
2809 else if(duration > (86400 * 365 * 2))
2811 reply("CSMSG_DURATION_TOO_HIGH");
2817 for(bData = channel->channel_info->bans; bData; bData = next)
2819 if(match_ircglobs(bData->mask, ban))
2821 int exact = !irccasecmp(bData->mask, ban);
2823 /* The ban is redundant; there is already a ban
2824 with the same effect in place. */
2828 free(bData->reason);
2829 bData->reason = strdup(reason);
2830 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
2832 reply("CSMSG_REASON_CHANGE", ban);
2836 if(exact && bData->expires)
2840 /* If the ban matches an existing one exactly,
2841 extend the expiration time if the provided
2842 duration is longer. */
2843 if(duration && ((time_t)(now + duration) > bData->expires))
2845 bData->expires = now + duration;
2856 /* Delete the expiration timeq entry and
2857 requeue if necessary. */
2858 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
2861 timeq_add(bData->expires, expire_ban, bData);
2865 /* automated kickban */
2868 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
2870 reply("CSMSG_BAN_ADDED", name, channel->name);
2876 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2883 if(match_ircglobs(ban, bData->mask))
2885 /* The ban we are adding makes previously existing
2886 bans redundant; silently remove them. */
2887 del_channel_ban(bData);
2891 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);
2893 name = ban = strdup(bData->mask);
2897 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
2899 extern const char *hidden_host_suffix;
2900 const char *old_name = chanserv_conf.old_ban_names->list[n];
2902 unsigned int l1, l2;
2905 l2 = strlen(old_name);
2908 if(irccasecmp(ban + l1 - l2, old_name))
2910 new_mask = malloc(MAXLEN);
2911 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
2913 name = ban = new_mask;
2918 if(action & ACTION_BAN)
2920 unsigned int exists;
2921 struct mod_chanmode *change;
2923 if(channel->banlist.used >= MAXBANS)
2926 reply("CSMSG_BANLIST_FULL", channel->name);
2931 exists = ChannelBanExists(channel, ban);
2932 change = mod_chanmode_alloc(victimCount + 1);
2933 for(n = 0; n < victimCount; ++n)
2935 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
2936 change->args[n].u.member = victims[n];
2940 change->args[n].mode = MODE_BAN;
2941 change->args[n++].u.hostmask = ban;
2945 modcmd_chanmode_announce(change);
2947 mod_chanmode_announce(chanserv, channel, change);
2948 mod_chanmode_free(change);
2950 if(exists && (action == ACTION_BAN))
2953 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
2959 if(action & ACTION_KICK)
2961 char kick_reason[MAXLEN];
2962 sprintf(kick_reason, "(%s) %s", user->nick, reason);
2964 for(n = 0; n < victimCount; n++)
2965 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
2970 /* No response, since it was automated. */
2972 else if(action & ACTION_ADD_BAN)
2975 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
2977 reply("CSMSG_BAN_ADDED", name, channel->name);
2979 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
2980 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
2981 else if(action & ACTION_BAN)
2982 reply("CSMSG_BAN_DONE", name, channel->name);
2983 else if(action & ACTION_KICK && victimCount)
2984 reply("CSMSG_KICK_DONE", name, channel->name);
2990 static CHANSERV_FUNC(cmd_kickban)
2992 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
2995 static CHANSERV_FUNC(cmd_kick)
2997 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3000 static CHANSERV_FUNC(cmd_ban)
3002 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3005 static CHANSERV_FUNC(cmd_addban)
3007 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3010 static CHANSERV_FUNC(cmd_addtimedban)
3012 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3015 static struct mod_chanmode *
3016 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3018 struct mod_chanmode *change;
3019 unsigned char *match;
3020 unsigned int ii, count;
3022 match = alloca(bans->used);
3025 for(ii = count = 0; ii < bans->used; ++ii)
3027 match[ii] = user_matches_glob(actee, bans->list[ii]->ban, 1);
3034 for(ii = count = 0; ii < bans->used; ++ii)
3036 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3043 change = mod_chanmode_alloc(count);
3044 for(ii = count = 0; ii < bans->used; ++ii)
3048 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3049 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3051 assert(count == change->argc);
3056 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3058 struct userNode *actee;
3064 /* may want to allow a comma delimited list of users... */
3065 if(!(actee = GetUserH(argv[1])))
3067 if(!is_ircmask(argv[1]))
3069 reply("MSG_NICK_UNKNOWN", argv[1]);
3073 mask = strdup(argv[1]);
3076 /* We don't sanitize the mask here because ircu
3078 if(action & ACTION_UNBAN)
3080 struct mod_chanmode *change;
3081 change = find_matching_bans(&channel->banlist, actee, mask);
3086 modcmd_chanmode_announce(change);
3087 for(ii = 0; ii < change->argc; ++ii)
3088 free((char*)change->args[ii].u.hostmask);
3089 mod_chanmode_free(change);
3094 if(action & ACTION_DEL_BAN)
3096 struct banData *ban, *next;
3098 ban = channel->channel_info->bans;
3102 for( ; ban && !user_matches_glob(actee, ban->mask, 1);
3105 for( ; ban && !match_ircglobs(mask, ban->mask);
3110 del_channel_ban(ban);
3117 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3119 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3125 static CHANSERV_FUNC(cmd_unban)
3127 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3130 static CHANSERV_FUNC(cmd_delban)
3132 /* it doesn't necessarily have to remove the channel ban - may want
3133 to make that an option. */
3134 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3137 static CHANSERV_FUNC(cmd_unbanme)
3139 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3140 long flags = ACTION_UNBAN;
3142 /* remove permanent bans if the user has the proper access. */
3143 if(uData->access >= UL_MASTER)
3144 flags |= ACTION_DEL_BAN;
3146 argv[1] = user->nick;
3147 return unban_user(user, channel, 2, argv, cmd, flags);
3150 static CHANSERV_FUNC(cmd_unbanall)
3152 struct mod_chanmode *change;
3155 if(!channel->banlist.used)
3157 reply("CSMSG_NO_BANS", channel->name);
3161 change = mod_chanmode_alloc(channel->banlist.used);
3162 for(ii=0; ii<channel->banlist.used; ii++)
3164 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3165 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3167 modcmd_chanmode_announce(change);
3168 for(ii = 0; ii < change->argc; ++ii)
3169 free((char*)change->args[ii].u.hostmask);
3170 mod_chanmode_free(change);
3171 reply("CSMSG_BANS_REMOVED", channel->name);
3175 static CHANSERV_FUNC(cmd_open)
3177 struct mod_chanmode *change;
3180 change = find_matching_bans(&channel->banlist, user, NULL);
3182 change = mod_chanmode_alloc(0);
3183 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3184 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3185 && channel->channel_info->modes.modes_set)
3186 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3187 modcmd_chanmode_announce(change);
3188 reply("CSMSG_CHANNEL_OPENED", channel->name);
3189 for(ii = 0; ii < change->argc; ++ii)
3190 free((char*)change->args[ii].u.hostmask);
3191 mod_chanmode_free(change);
3195 static CHANSERV_FUNC(cmd_myaccess)
3197 static struct string_buffer sbuf;
3198 struct handle_info *target_handle;
3199 struct userData *uData;
3202 target_handle = user->handle_info;
3203 else if(!IsHelping(user))
3205 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3208 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3211 if(!target_handle->channels)
3213 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3217 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3218 for(uData = target_handle->channels; uData; uData = uData->u_next)
3220 struct chanData *cData = uData->channel;
3222 if(uData->access > UL_OWNER)
3224 if(IsProtected(cData)
3225 && (target_handle != user->handle_info)
3226 && !GetTrueChannelAccess(cData, user->handle_info))
3229 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3230 if(uData->flags != USER_AUTO_OP)
3231 string_buffer_append(&sbuf, ',');
3232 if(IsUserSuspended(uData))
3233 string_buffer_append(&sbuf, 's');
3234 if(IsUserAutoOp(uData))
3236 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3237 string_buffer_append(&sbuf, 'o');
3238 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3239 string_buffer_append(&sbuf, 'v');
3241 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3242 string_buffer_append(&sbuf, 'i');
3244 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3246 string_buffer_append_string(&sbuf, ")]");
3247 string_buffer_append(&sbuf, '\0');
3248 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3254 static CHANSERV_FUNC(cmd_access)
3256 struct userNode *target;
3257 struct handle_info *target_handle;
3258 struct userData *uData;
3260 char prefix[MAXLEN];
3265 target_handle = target->handle_info;
3267 else if((target = GetUserH(argv[1])))
3269 target_handle = target->handle_info;
3271 else if(argv[1][0] == '*')
3273 if(!(target_handle = get_handle_info(argv[1]+1)))
3275 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3281 reply("MSG_NICK_UNKNOWN", argv[1]);
3285 assert(target || target_handle);
3287 if(target == chanserv)
3289 reply("CSMSG_IS_CHANSERV");
3297 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3302 reply("MSG_USER_AUTHENTICATE", target->nick);
3305 reply("MSG_AUTHENTICATE");
3311 const char *epithet = NULL, *type = NULL;
3314 epithet = chanserv_conf.irc_operator_epithet;
3317 else if(IsNetworkHelper(target))
3319 epithet = chanserv_conf.network_helper_epithet;
3320 type = "network helper";
3322 else if(IsSupportHelper(target))
3324 epithet = chanserv_conf.support_helper_epithet;
3325 type = "support helper";
3329 if(target_handle->epithet)
3330 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3332 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3334 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3338 sprintf(prefix, "%s", target_handle->handle);
3341 if(!channel->channel_info)
3343 reply("CSMSG_NOT_REGISTERED", channel->name);
3347 helping = HANDLE_FLAGGED(target_handle, HELPING)
3348 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3349 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3351 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3352 /* To prevent possible information leaks, only show infolines
3353 * if the requestor is in the channel or it's their own
3355 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3357 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3359 /* Likewise, only say it's suspended if the user has active
3360 * access in that channel or it's their own entry. */
3361 if(IsUserSuspended(uData)
3362 && (GetChannelUser(channel->channel_info, user->handle_info)
3363 || (user->handle_info == uData->handle)))
3365 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3370 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3377 zoot_list(struct listData *list)
3379 struct userData *uData;
3380 unsigned int start, curr, highest, lowest;
3381 struct helpfile_table tmp_table;
3382 const char **temp, *msg;
3384 if(list->table.length == 1)
3387 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3389 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3390 msg = user_find_message(list->user, "MSG_NONE");
3391 send_message_type(4, list->user, list->bot, " %s", msg);
3393 tmp_table.width = list->table.width;
3394 tmp_table.flags = list->table.flags;
3395 list->table.contents[0][0] = " ";
3396 highest = list->highest;
3397 if(list->lowest != 0)
3398 lowest = list->lowest;
3399 else if(highest < 100)
3402 lowest = highest - 100;
3403 for(start = curr = 1; curr < list->table.length; )
3405 uData = list->users[curr-1];
3406 list->table.contents[curr++][0] = " ";
3407 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3410 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3412 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3413 temp = list->table.contents[--start];
3414 list->table.contents[start] = list->table.contents[0];
3415 tmp_table.contents = list->table.contents + start;
3416 tmp_table.length = curr - start;
3417 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3418 list->table.contents[start] = temp;
3420 highest = lowest - 1;
3421 lowest = (highest < 100) ? 0 : (highest - 99);
3427 def_list(struct listData *list)
3431 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3433 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3434 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3435 if(list->table.length == 1)
3437 msg = user_find_message(list->user, "MSG_NONE");
3438 send_message_type(4, list->user, list->bot, " %s", msg);
3443 userData_access_comp(const void *arg_a, const void *arg_b)
3445 const struct userData *a = *(struct userData**)arg_a;
3446 const struct userData *b = *(struct userData**)arg_b;
3448 if(a->access != b->access)
3449 res = b->access - a->access;
3451 res = irccasecmp(a->handle->handle, b->handle->handle);
3456 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3458 void (*send_list)(struct listData *);
3459 struct userData *uData;
3460 struct listData lData;
3461 unsigned int matches;
3465 lData.bot = cmd->parent->bot;
3466 lData.channel = channel;
3467 lData.lowest = lowest;
3468 lData.highest = highest;
3469 lData.search = (argc > 1) ? argv[1] : NULL;
3470 send_list = def_list;
3471 (void)zoot_list; /* since it doesn't show user levels */
3473 if(user->handle_info)
3475 switch(user->handle_info->userlist_style)
3477 case HI_STYLE_DEF: send_list = def_list; break;
3478 case HI_STYLE_ZOOT: send_list = def_list; break;
3482 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3484 for(uData = channel->channel_info->users; uData; uData = uData->next)
3486 if((uData->access < lowest)
3487 || (uData->access > highest)
3488 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3490 lData.users[matches++] = uData;
3492 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3494 lData.table.length = matches+1;
3495 lData.table.width = 4;
3496 lData.table.flags = TABLE_NO_FREE;
3497 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3498 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3499 lData.table.contents[0] = ary;
3502 ary[2] = "Last Seen";
3504 for(matches = 1; matches < lData.table.length; ++matches)
3506 struct userData *uData = lData.users[matches-1];
3507 char seen[INTERVALLEN];
3509 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3510 lData.table.contents[matches] = ary;
3511 ary[0] = strtab(uData->access);
3512 ary[1] = uData->handle->handle;
3515 else if(!uData->seen)
3518 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3519 ary[2] = strdup(ary[2]);
3520 if(IsUserSuspended(uData))
3521 ary[3] = "Suspended";
3522 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3523 ary[3] = "Vacation";
3528 for(matches = 1; matches < lData.table.length; ++matches)
3530 free((char*)lData.table.contents[matches][2]);
3531 free(lData.table.contents[matches]);
3533 free(lData.table.contents[0]);
3534 free(lData.table.contents);
3538 static CHANSERV_FUNC(cmd_users)
3540 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3543 static CHANSERV_FUNC(cmd_wlist)
3545 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3548 static CHANSERV_FUNC(cmd_clist)
3550 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3553 static CHANSERV_FUNC(cmd_mlist)
3555 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3558 static CHANSERV_FUNC(cmd_olist)
3560 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3563 static CHANSERV_FUNC(cmd_plist)
3565 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3568 static CHANSERV_FUNC(cmd_bans)
3570 struct helpfile_table tbl;
3571 unsigned int matches = 0, timed = 0, ii;
3572 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3573 const char *msg_never, *triggered, *expires;
3574 struct banData *ban, **bans;
3581 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3583 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3585 if(search && !match_ircglobs(search, ban->mask))
3587 bans[matches++] = ban;
3592 tbl.length = matches + 1;
3593 tbl.width = 4 + timed;
3595 tbl.flags = TABLE_NO_FREE;
3596 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3597 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3598 tbl.contents[0][0] = "Mask";
3599 tbl.contents[0][1] = "Set By";
3600 tbl.contents[0][2] = "Triggered";
3603 tbl.contents[0][3] = "Expires";
3604 tbl.contents[0][4] = "Reason";
3607 tbl.contents[0][3] = "Reason";
3610 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3612 free(tbl.contents[0]);
3617 msg_never = user_find_message(user, "MSG_NEVER");
3618 for(ii = 0; ii < matches; )
3624 else if(ban->expires)
3625 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
3627 expires = msg_never;
3630 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
3632 triggered = msg_never;
3634 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3635 tbl.contents[ii][0] = ban->mask;
3636 tbl.contents[ii][1] = ban->owner;
3637 tbl.contents[ii][2] = strdup(triggered);
3640 tbl.contents[ii][3] = strdup(expires);
3641 tbl.contents[ii][4] = ban->reason;
3644 tbl.contents[ii][3] = ban->reason;
3646 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3647 reply("MSG_MATCH_COUNT", matches);
3648 for(ii = 1; ii < tbl.length; ++ii)
3650 free((char*)tbl.contents[ii][2]);
3652 free((char*)tbl.contents[ii][3]);
3653 free(tbl.contents[ii]);
3655 free(tbl.contents[0]);
3661 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
3663 struct chanData *cData = channel->channel_info;
3664 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
3666 if(cData->topic_mask)
3667 return !match_ircglob(new_topic, cData->topic_mask);
3668 else if(cData->topic)
3669 return irccasecmp(new_topic, cData->topic);
3674 static CHANSERV_FUNC(cmd_topic)
3676 struct chanData *cData;
3679 cData = channel->channel_info;
3684 SetChannelTopic(channel, chanserv, cData->topic, 1);
3685 reply("CSMSG_TOPIC_SET", cData->topic);
3689 reply("CSMSG_NO_TOPIC", channel->name);
3693 topic = unsplit_string(argv + 1, argc - 1, NULL);
3694 /* If they say "!topic *", use an empty topic. */
3695 if((topic[0] == '*') && (topic[1] == 0))
3697 if(bad_topic(channel, user, topic))
3699 char *topic_mask = cData->topic_mask;
3702 char new_topic[TOPICLEN+1], tchar;
3703 int pos=0, starpos=-1, dpos=0, len;
3705 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
3712 len = strlen(topic);
3713 if((dpos + len) > TOPICLEN)
3714 len = TOPICLEN + 1 - dpos;
3715 memcpy(new_topic+dpos, topic, len);
3719 case '\\': tchar = topic_mask[pos++]; /* and fall through */
3720 default: new_topic[dpos++] = tchar; break;
3723 if((dpos > TOPICLEN) || tchar)
3726 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
3727 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
3730 new_topic[dpos] = 0;
3731 SetChannelTopic(channel, chanserv, new_topic, 1);
3733 reply("CSMSG_TOPIC_LOCKED", channel->name);
3738 SetChannelTopic(channel, chanserv, topic, 1);
3740 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
3742 /* Grab the topic and save it as the default topic. */
3744 cData->topic = strdup(channel->topic);
3750 static CHANSERV_FUNC(cmd_mode)
3752 struct mod_chanmode *change;
3756 change = &channel->channel_info->modes;
3757 if(change->modes_set || change->modes_clear) {
3758 modcmd_chanmode_announce(change);
3759 reply("CSMSG_DEFAULTED_MODES", channel->name);
3761 reply("CSMSG_NO_MODES", channel->name);
3765 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED);
3768 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
3772 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3773 && mode_lock_violated(&channel->channel_info->modes, change))
3776 mod_chanmode_format(&channel->channel_info->modes, modes);
3777 reply("CSMSG_MODE_LOCKED", modes, channel->name);
3781 modcmd_chanmode_announce(change);
3782 mod_chanmode_free(change);
3783 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
3787 static CHANSERV_FUNC(cmd_invite)
3789 struct userData *uData;
3790 struct userNode *invite;
3792 uData = GetChannelUser(channel->channel_info, user->handle_info);
3796 if(!(invite = GetUserH(argv[1])))
3798 reply("MSG_NICK_UNKNOWN", argv[1]);
3805 if(GetUserMode(channel, invite))
3807 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
3815 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
3816 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
3819 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
3821 irc_invite(chanserv, invite, channel);
3823 reply("CSMSG_INVITED_USER", argv[1], channel->name);
3828 static CHANSERV_FUNC(cmd_inviteme)
3830 if(GetUserMode(channel, user))
3832 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
3835 if(channel->channel_info
3836 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
3838 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
3841 irc_invite(cmd->parent->bot, user, channel);
3846 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
3849 char buf1[INTERVALLEN], buf2[INTERVALLEN];
3851 /* We display things based on two dimensions:
3852 * - Issue time: present or absent
3853 * - Expiration: revoked, expired, expires in future, or indefinite expiration
3854 * (in order of precedence, so something both expired and revoked
3855 * only counts as revoked)
3857 combo = (suspended->issued ? 4 : 0)
3858 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
3860 case 0: /* no issue time, indefinite expiration */
3861 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
3863 case 1: /* no issue time, expires in future */
3864 intervalString(buf1, suspended->expires-now, user->handle_info);
3865 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
3867 case 2: /* no issue time, expired */
3868 intervalString(buf1, now-suspended->expires, user->handle_info);
3869 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
3871 case 3: /* no issue time, revoked */
3872 intervalString(buf1, now-suspended->revoked, user->handle_info);
3873 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
3875 case 4: /* issue time set, indefinite expiration */
3876 intervalString(buf1, now-suspended->issued, user->handle_info);
3877 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
3879 case 5: /* issue time set, expires in future */
3880 intervalString(buf1, now-suspended->issued, user->handle_info);
3881 intervalString(buf2, suspended->expires-now, user->handle_info);
3882 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
3884 case 6: /* issue time set, expired */
3885 intervalString(buf1, now-suspended->issued, user->handle_info);
3886 intervalString(buf2, now-suspended->expires, user->handle_info);
3887 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
3889 case 7: /* issue time set, revoked */
3890 intervalString(buf1, now-suspended->issued, user->handle_info);
3891 intervalString(buf2, now-suspended->revoked, user->handle_info);
3892 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
3895 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
3900 static CHANSERV_FUNC(cmd_info)
3902 char modes[MAXLEN], buffer[INTERVALLEN];
3903 struct userData *uData, *owner;
3904 struct chanData *cData;
3905 struct do_not_register *dnr;
3910 cData = channel->channel_info;
3911 reply("CSMSG_CHANNEL_INFO", channel->name);
3913 uData = GetChannelUser(cData, user->handle_info);
3914 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
3916 mod_chanmode_format(&cData->modes, modes);
3917 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
3918 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
3921 for(it = dict_first(cData->notes); it; it = iter_next(it))
3925 note = iter_data(it);
3926 if(!note_type_visible_to_user(cData, note->type, user))
3929 padding = PADLEN - 1 - strlen(iter_key(it));
3930 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
3933 reply("CSMSG_CHANNEL_MAX", cData->max);
3934 for(owner = cData->users; owner; owner = owner->next)
3935 if(owner->access == UL_OWNER)
3936 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
3937 reply("CSMSG_CHANNEL_USERS", cData->userCount);
3938 reply("CSMSG_CHANNEL_BANS", cData->banCount);
3939 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
3940 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
3942 privileged = IsStaff(user);
3943 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
3944 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
3946 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
3947 chanserv_show_dnrs(user, cmd, channel->name, NULL);
3949 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
3951 struct suspended *suspended;
3952 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
3953 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
3954 show_suspension_info(cmd, user, suspended);
3956 else if(IsSuspended(cData))
3958 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
3959 show_suspension_info(cmd, user, cData->suspended);
3964 static CHANSERV_FUNC(cmd_netinfo)
3966 extern time_t boot_time;
3967 extern unsigned long burst_length;
3968 char interval[INTERVALLEN];
3970 reply("CSMSG_NETWORK_INFO");
3971 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
3972 reply("CSMSG_NETWORK_USERS", dict_size(clients));
3973 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
3974 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
3975 reply("CSMSG_NETWORK_BANS", banCount);
3976 reply("CSMSG_NETWORK_CHANUSERS", userCount);
3977 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
3978 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
3983 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
3985 struct helpfile_table table;
3987 struct userNode *user;
3992 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
3993 table.contents = alloca(list->used*sizeof(*table.contents));
3994 for(nn=0; nn<list->used; nn++)
3996 user = list->list[nn];
3997 if(user->modes & skip_flags)
4001 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4004 nick = alloca(strlen(user->nick)+3);
4005 sprintf(nick, "(%s)", user->nick);
4009 table.contents[table.length][0] = nick;
4012 table_send(chanserv, to->nick, 0, NULL, table);
4015 static CHANSERV_FUNC(cmd_ircops)
4017 reply("CSMSG_STAFF_OPERS");
4018 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4022 static CHANSERV_FUNC(cmd_helpers)
4024 reply("CSMSG_STAFF_HELPERS");
4025 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4029 static CHANSERV_FUNC(cmd_staff)
4031 reply("CSMSG_NETWORK_STAFF");
4032 cmd_ircops(CSFUNC_ARGS);
4033 cmd_helpers(CSFUNC_ARGS);
4037 static CHANSERV_FUNC(cmd_peek)
4039 struct modeNode *mn;
4040 char modes[MODELEN];
4042 struct helpfile_table table;
4044 irc_make_chanmode(channel, modes);
4046 reply("CSMSG_PEEK_INFO", channel->name);
4047 reply("CSMSG_PEEK_TOPIC", channel->topic);
4048 reply("CSMSG_PEEK_MODES", modes);
4049 reply("CSMSG_PEEK_USERS", channel->members.used);
4053 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4054 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4055 for(n = 0; n < channel->members.used; n++)
4057 mn = channel->members.list[n];
4058 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4060 table.contents[table.length] = alloca(sizeof(**table.contents));
4061 table.contents[table.length][0] = mn->user->nick;
4066 reply("CSMSG_PEEK_OPS");
4067 table_send(chanserv, user->nick, 0, NULL, table);
4070 reply("CSMSG_PEEK_NO_OPS");
4074 static MODCMD_FUNC(cmd_wipeinfo)
4076 struct handle_info *victim;
4077 struct userData *ud, *actor;
4080 actor = GetChannelUser(channel->channel_info, user->handle_info);
4081 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4083 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4085 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4088 if((ud->access >= actor->access) && (ud != actor))
4090 reply("MSG_USER_OUTRANKED", victim->handle);
4096 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4100 static CHANSERV_FUNC(cmd_resync)
4102 struct mod_chanmode *changes;
4103 struct chanData *cData = channel->channel_info;
4104 unsigned int ii, used;
4106 changes = mod_chanmode_alloc(channel->members.used * 2);
4107 for(ii = used = 0; ii < channel->members.used; ++ii)
4109 struct modeNode *mn = channel->members.list[ii];
4110 struct userData *uData;
4112 if(IsService(mn->user))
4115 uData = GetChannelAccess(cData, mn->user->handle_info);
4116 if(!cData->lvlOpts[lvlGiveOps]
4117 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4119 if(!(mn->modes & MODE_CHANOP))
4121 changes->args[used].mode = MODE_CHANOP;
4122 changes->args[used++].u.member = mn;
4125 else if(!cData->lvlOpts[lvlGiveVoice]
4126 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4128 if(mn->modes & MODE_CHANOP)
4130 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4131 changes->args[used++].u.member = mn;
4133 if(!(mn->modes & MODE_VOICE))
4135 changes->args[used].mode = MODE_VOICE;
4136 changes->args[used++].u.member = mn;
4143 changes->args[used].mode = MODE_REMOVE | mn->modes;
4144 changes->args[used++].u.member = mn;
4148 changes->argc = used;
4149 modcmd_chanmode_announce(changes);
4150 mod_chanmode_free(changes);
4151 reply("CSMSG_RESYNCED_USERS", channel->name);
4155 static CHANSERV_FUNC(cmd_seen)
4157 struct userData *uData;
4158 struct handle_info *handle;
4159 char seen[INTERVALLEN];
4163 if(!irccasecmp(argv[1], chanserv->nick))
4165 reply("CSMSG_IS_CHANSERV");
4169 if(!(handle = get_handle_info(argv[1])))
4171 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4175 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4177 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4182 reply("CSMSG_USER_PRESENT", handle->handle);
4183 else if(uData->seen)
4184 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4186 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4188 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4189 reply("CSMSG_USER_VACATION", handle->handle);
4194 static MODCMD_FUNC(cmd_names)
4196 struct userNode *targ;
4197 struct userData *targData;
4198 unsigned int ii, pos;
4201 for(ii=pos=0; ii<channel->members.used; ++ii)
4203 targ = channel->members.list[ii]->user;
4204 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4207 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4210 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4214 if(IsUserSuspended(targData))
4216 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4219 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4220 reply("CSMSG_END_NAMES", channel->name);
4225 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4227 switch(ntype->visible_type)
4229 case NOTE_VIS_ALL: return 1;
4230 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4231 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4236 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4238 struct userData *uData;
4240 switch(ntype->set_access_type)
4242 case NOTE_SET_CHANNEL_ACCESS:
4243 if(!user->handle_info)
4245 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4247 return uData->access >= ntype->set_access.min_ulevel;
4248 case NOTE_SET_CHANNEL_SETTER:
4249 return check_user_level(channel, user, lvlSetters, 1, 0);
4250 case NOTE_SET_PRIVILEGED: default:
4251 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4255 static CHANSERV_FUNC(cmd_note)
4257 struct chanData *cData;
4259 struct note_type *ntype;
4261 cData = channel->channel_info;
4264 reply("CSMSG_NOT_REGISTERED", channel->name);
4268 /* If no arguments, show all visible notes for the channel. */
4274 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4276 note = iter_data(it);
4277 if(!note_type_visible_to_user(cData, note->type, user))
4280 reply("CSMSG_NOTELIST_HEADER", channel->name);
4281 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4284 reply("CSMSG_NOTELIST_END", channel->name);
4286 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4288 /* If one argument, show the named note. */
4291 if((note = dict_find(cData->notes, argv[1], NULL))
4292 && note_type_visible_to_user(cData, note->type, user))
4294 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4296 else if((ntype = dict_find(note_types, argv[1], NULL))
4297 && note_type_visible_to_user(NULL, ntype, user))
4299 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4304 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4308 /* Assume they're trying to set a note. */
4312 ntype = dict_find(note_types, argv[1], NULL);
4315 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4318 else if(note_type_settable_by_user(channel, ntype, user))
4320 note_text = unsplit_string(argv+2, argc-2, NULL);
4321 if((note = dict_find(cData->notes, argv[1], NULL)))
4322 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4323 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4324 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4326 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4328 /* The note is viewable to staff only, so return 0
4329 to keep the invocation from getting logged (or
4330 regular users can see it in !events). */
4336 reply("CSMSG_NO_ACCESS");
4343 static CHANSERV_FUNC(cmd_delnote)
4348 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4349 || !note_type_settable_by_user(channel, note->type, user))
4351 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4354 dict_remove(channel->channel_info->notes, note->type->name);
4355 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4359 static CHANSERV_FUNC(cmd_events)
4361 struct logSearch discrim;
4362 struct logReport report;
4363 unsigned int matches, limit;
4365 limit = (argc > 1) ? atoi(argv[1]) : 10;
4366 if(limit < 1 || limit > 200)
4369 memset(&discrim, 0, sizeof(discrim));
4370 discrim.masks.bot = chanserv;
4371 discrim.masks.channel_name = channel->name;
4373 discrim.masks.command = argv[2];
4374 discrim.limit = limit;
4375 discrim.max_time = INT_MAX;
4376 discrim.severities = 1 << LOG_COMMAND;
4377 report.reporter = chanserv;
4379 reply("CSMSG_EVENT_SEARCH_RESULTS");
4380 matches = log_entry_search(&discrim, log_report_entry, &report);
4382 reply("MSG_MATCH_COUNT", matches);
4384 reply("MSG_NO_MATCHES");
4388 static CHANSERV_FUNC(cmd_say)
4394 msg = unsplit_string(argv + 1, argc - 1, NULL);
4395 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4397 else if(GetUserH(argv[1]))
4400 msg = unsplit_string(argv + 2, argc - 2, NULL);
4401 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4405 reply("MSG_NOT_TARGET_NAME");
4411 static CHANSERV_FUNC(cmd_emote)
4417 /* CTCP is so annoying. */
4418 msg = unsplit_string(argv + 1, argc - 1, NULL);
4419 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4421 else if(GetUserH(argv[1]))
4423 msg = unsplit_string(argv + 2, argc - 2, NULL);
4424 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4428 reply("MSG_NOT_TARGET_NAME");
4434 struct channelList *
4435 chanserv_support_channels(void)
4437 return &chanserv_conf.support_channels;
4440 static CHANSERV_FUNC(cmd_expire)
4442 int channel_count = registered_channels;
4443 expire_channels(NULL);
4444 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4449 chanserv_expire_suspension(void *data)
4451 struct suspended *suspended = data;
4452 struct chanNode *channel;
4453 struct mod_chanmode change;
4455 if(!suspended->expires || (now < suspended->expires))
4456 suspended->revoked = now;
4457 channel = suspended->cData->channel;
4458 suspended->cData->channel = channel;
4459 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4460 mod_chanmode_init(&change);
4462 change.args[0].mode = MODE_CHANOP;
4463 change.args[0].u.member = AddChannelUser(chanserv, channel);
4464 mod_chanmode_announce(chanserv, channel, &change);
4467 static CHANSERV_FUNC(cmd_csuspend)
4469 struct suspended *suspended;
4470 char reason[MAXLEN];
4471 time_t expiry, duration;
4472 struct userData *uData;
4476 if(IsProtected(channel->channel_info))
4478 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4482 if(argv[1][0] == '!')
4484 else if(IsSuspended(channel->channel_info))
4486 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4487 show_suspension_info(cmd, user, channel->channel_info->suspended);
4491 if(!strcmp(argv[1], "0"))
4493 else if((duration = ParseInterval(argv[1])))
4494 expiry = now + duration;
4497 reply("MSG_INVALID_DURATION", argv[1]);
4501 unsplit_string(argv + 2, argc - 2, reason);
4503 suspended = calloc(1, sizeof(*suspended));
4504 suspended->revoked = 0;
4505 suspended->issued = now;
4506 suspended->suspender = strdup(user->handle_info->handle);
4507 suspended->expires = expiry;
4508 suspended->reason = strdup(reason);
4509 suspended->cData = channel->channel_info;
4510 suspended->previous = suspended->cData->suspended;
4511 suspended->cData->suspended = suspended;
4513 if(suspended->expires)
4514 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4516 if(IsSuspended(channel->channel_info))
4518 suspended->previous->revoked = now;
4519 if(suspended->previous->expires)
4520 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4521 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4522 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4526 /* Mark all users in channel as absent. */
4527 for(uData = channel->channel_info->users; uData; uData = uData->next)
4536 /* Mark the channel as suspended, then part. */
4537 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4538 DelChannelUser(chanserv, channel, suspended->reason, 0);
4539 reply("CSMSG_SUSPENDED", channel->name);
4540 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4541 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4546 static CHANSERV_FUNC(cmd_cunsuspend)
4548 struct suspended *suspended;
4549 char message[MAXLEN];
4551 if(!IsSuspended(channel->channel_info))
4553 reply("CSMSG_NOT_SUSPENDED", channel->name);
4557 suspended = channel->channel_info->suspended;
4559 /* Expire the suspension and join ChanServ to the channel. */
4560 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4561 chanserv_expire_suspension(suspended);
4562 reply("CSMSG_UNSUSPENDED", channel->name);
4563 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4564 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4568 typedef struct chanservSearch
4576 unsigned long flags;
4580 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4583 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4588 search = malloc(sizeof(struct chanservSearch));
4589 memset(search, 0, sizeof(*search));
4592 for(i = 0; i < argc; i++)
4594 /* Assume all criteria require arguments. */
4597 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4601 if(!irccasecmp(argv[i], "name"))
4602 search->name = argv[++i];
4603 else if(!irccasecmp(argv[i], "registrar"))
4604 search->registrar = argv[++i];
4605 else if(!irccasecmp(argv[i], "unvisited"))
4606 search->unvisited = ParseInterval(argv[++i]);
4607 else if(!irccasecmp(argv[i], "registered"))
4608 search->registered = ParseInterval(argv[++i]);
4609 else if(!irccasecmp(argv[i], "flags"))
4612 if(!irccasecmp(argv[i], "nodelete"))
4613 search->flags |= CHANNEL_NODELETE;
4614 else if(!irccasecmp(argv[i], "suspended"))
4615 search->flags |= CHANNEL_SUSPENDED;
4618 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
4622 else if(!irccasecmp(argv[i], "limit"))
4623 search->limit = strtoul(argv[++i], NULL, 10);
4626 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
4631 if(search->name && !strcmp(search->name, "*"))
4633 if(search->registrar && !strcmp(search->registrar, "*"))
4634 search->registrar = 0;
4643 chanserv_channel_match(struct chanData *channel, search_t search)
4645 const char *name = channel->channel->name;
4646 if((search->name && !match_ircglob(name, search->name)) ||
4647 (search->registrar && !channel->registrar) ||
4648 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
4649 (search->unvisited && (now - channel->visited) < search->unvisited) ||
4650 (search->registered && (now - channel->registered) > search->registered) ||
4651 (search->flags && ((search->flags & channel->flags) != search->flags)))
4658 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
4660 struct chanData *channel;
4661 unsigned int matches = 0;
4663 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
4665 if(!chanserv_channel_match(channel, search))
4675 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
4680 search_print(struct chanData *channel, void *data)
4682 send_message_type(4, data, chanserv, "%s", channel->channel->name);
4685 static CHANSERV_FUNC(cmd_search)
4688 unsigned int matches;
4689 channel_search_func action;
4693 if(!irccasecmp(argv[1], "count"))
4694 action = search_count;
4695 else if(!irccasecmp(argv[1], "print"))
4696 action = search_print;
4699 reply("CSMSG_ACTION_INVALID", argv[1]);
4703 search = chanserv_search_create(user, argc - 2, argv + 2);
4707 if(action == search_count)
4708 search->limit = INT_MAX;
4710 if(action == search_print)
4711 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
4713 matches = chanserv_channel_search(search, action, user);
4716 reply("MSG_MATCH_COUNT", matches);
4718 reply("MSG_NO_MATCHES");
4724 static CHANSERV_FUNC(cmd_unvisited)
4726 struct chanData *cData;
4727 time_t interval = chanserv_conf.channel_expire_delay;
4728 char buffer[INTERVALLEN];
4729 unsigned int limit = 25, matches = 0;
4733 interval = ParseInterval(argv[1]);
4735 limit = atoi(argv[2]);
4738 intervalString(buffer, interval, user->handle_info);
4739 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
4741 for(cData = channelList; cData && matches < limit; cData = cData->next)
4743 if((now - cData->visited) < interval)
4746 intervalString(buffer, now - cData->visited, user->handle_info);
4747 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
4754 static MODCMD_FUNC(chan_opt_defaulttopic)
4760 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4762 reply("CSMSG_TOPIC_LOCKED", channel->name);
4766 topic = unsplit_string(argv+1, argc-1, NULL);
4768 free(channel->channel_info->topic);
4769 if(topic[0] == '*' && topic[1] == 0)
4771 topic = channel->channel_info->topic = NULL;
4775 topic = channel->channel_info->topic = strdup(topic);
4776 if(channel->channel_info->topic_mask
4777 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
4778 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4780 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
4783 if(channel->channel_info->topic)
4784 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
4786 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
4790 static MODCMD_FUNC(chan_opt_topicmask)
4794 struct chanData *cData = channel->channel_info;
4797 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
4799 reply("CSMSG_TOPIC_LOCKED", channel->name);
4803 mask = unsplit_string(argv+1, argc-1, NULL);
4805 if(cData->topic_mask)
4806 free(cData->topic_mask);
4807 if(mask[0] == '*' && mask[1] == 0)
4809 cData->topic_mask = 0;
4813 cData->topic_mask = strdup(mask);
4815 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
4816 else if(!match_ircglob(cData->topic, cData->topic_mask))
4817 reply("CSMSG_TOPIC_MISMATCH", channel->name);
4821 if(channel->channel_info->topic_mask)
4822 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
4824 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
4828 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
4832 char *greeting = unsplit_string(argv+1, argc-1, NULL);
4836 if(greeting[0] == '*' && greeting[1] == 0)
4840 unsigned int length = strlen(greeting);
4841 if(length > chanserv_conf.greeting_length)
4843 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
4846 *data = strdup(greeting);
4855 reply(name, user_find_message(user, "MSG_NONE"));
4859 static MODCMD_FUNC(chan_opt_greeting)
4861 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
4864 static MODCMD_FUNC(chan_opt_usergreeting)
4866 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
4869 static MODCMD_FUNC(chan_opt_modes)
4871 struct mod_chanmode *new_modes;
4872 char modes[MODELEN];
4876 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
4878 reply("CSMSG_NO_ACCESS");
4881 if(argv[1][0] == '*' && argv[1][1] == 0)
4883 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
4885 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED)))
4887 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4890 else if(new_modes->argc > 1)
4892 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
4893 mod_chanmode_free(new_modes);
4898 channel->channel_info->modes = *new_modes;
4899 modcmd_chanmode_announce(new_modes);
4900 mod_chanmode_free(new_modes);
4904 mod_chanmode_format(&channel->channel_info->modes, modes);
4906 reply("CSMSG_SET_MODES", modes);
4908 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
4912 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
4914 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
4916 struct chanData *cData = channel->channel_info;
4921 /* Set flag according to value. */
4922 if(enabled_string(argv[1]))
4924 cData->flags |= mask;
4927 else if(disabled_string(argv[1]))
4929 cData->flags &= ~mask;
4934 reply("MSG_INVALID_BINARY", argv[1]);
4940 /* Find current option value. */
4941 value = (cData->flags & mask) ? 1 : 0;
4945 reply(name, user_find_message(user, "MSG_ON"));
4947 reply(name, user_find_message(user, "MSG_OFF"));
4951 static MODCMD_FUNC(chan_opt_nodelete)
4953 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
4955 reply("MSG_SETTING_PRIVILEGED", argv[0]);
4959 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
4962 static MODCMD_FUNC(chan_opt_dynlimit)
4964 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
4967 static MODCMD_FUNC(chan_opt_offchannel)
4969 struct chanData *cData = channel->channel_info;
4974 /* Set flag according to value. */
4975 if(enabled_string(argv[1]))
4977 if(!IsOffChannel(cData))
4978 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
4979 cData->flags |= CHANNEL_OFFCHANNEL;
4982 else if(disabled_string(argv[1]))
4984 if(IsOffChannel(cData))
4986 struct mod_chanmode change;
4987 mod_chanmode_init(&change);
4989 change.args[0].mode = MODE_CHANOP;
4990 change.args[0].u.member = AddChannelUser(chanserv, channel);
4991 mod_chanmode_announce(chanserv, channel, &change);
4993 cData->flags &= ~CHANNEL_OFFCHANNEL;
4998 reply("MSG_INVALID_BINARY", argv[1]);
5004 /* Find current option value. */
5005 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5009 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5011 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5015 static MODCMD_FUNC(chan_opt_defaults)
5017 struct userData *uData;
5018 struct chanData *cData;
5019 const char *confirm;
5020 enum levelOption lvlOpt;
5021 enum charOption chOpt;
5023 cData = channel->channel_info;
5024 uData = GetChannelUser(cData, user->handle_info);
5025 if(!uData || (uData->access < UL_OWNER))
5027 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5030 confirm = make_confirmation_string(uData);
5031 if((argc < 2) || strcmp(argv[1], confirm))
5033 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5036 cData->flags = CHANNEL_DEFAULT_FLAGS;
5037 cData->modes = chanserv_conf.default_modes;
5038 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5039 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5040 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5041 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5042 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5047 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5049 struct chanData *cData = channel->channel_info;
5050 struct userData *uData;
5051 unsigned short value;
5055 if(!check_user_level(channel, user, option, 1, 1))
5057 reply("CSMSG_CANNOT_SET");
5060 value = user_level_from_name(argv[1], UL_OWNER+1);
5061 if(!value && strcmp(argv[1], "0"))
5063 reply("CSMSG_INVALID_ACCESS", argv[1]);
5066 uData = GetChannelUser(cData, user->handle_info);
5067 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5069 reply("CSMSG_BAD_SETLEVEL");
5075 if(value > cData->lvlOpts[lvlGiveOps])
5077 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5082 if(value < cData->lvlOpts[lvlGiveVoice])
5084 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5089 /* This test only applies to owners, since non-owners
5090 * trying to set an option to above their level get caught
5091 * by the CSMSG_BAD_SETLEVEL test above.
5093 if(value > uData->access)
5095 reply("CSMSG_BAD_SETTERS");
5102 cData->lvlOpts[option] = value;
5104 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5108 static MODCMD_FUNC(chan_opt_enfops)
5110 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5113 static MODCMD_FUNC(chan_opt_giveops)
5115 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5118 static MODCMD_FUNC(chan_opt_enfmodes)
5120 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5123 static MODCMD_FUNC(chan_opt_enftopic)
5125 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5128 static MODCMD_FUNC(chan_opt_pubcmd)
5130 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5133 static MODCMD_FUNC(chan_opt_setters)
5135 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5138 static MODCMD_FUNC(chan_opt_ctcpusers)
5140 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5143 static MODCMD_FUNC(chan_opt_userinfo)
5145 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5148 static MODCMD_FUNC(chan_opt_givevoice)
5150 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5153 static MODCMD_FUNC(chan_opt_topicsnarf)
5155 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5158 static MODCMD_FUNC(chan_opt_inviteme)
5160 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5164 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5166 struct chanData *cData = channel->channel_info;
5167 int count = charOptions[option].count, index;
5171 index = atoi(argv[1]);
5173 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5175 reply("CSMSG_INVALID_NUMERIC", index);
5176 /* Show possible values. */
5177 for(index = 0; index < count; index++)
5178 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5182 cData->chOpts[option] = charOptions[option].values[index].value;
5186 /* Find current option value. */
5189 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5193 /* Somehow, the option value is corrupt; reset it to the default. */
5194 cData->chOpts[option] = charOptions[option].default_value;
5199 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5203 static MODCMD_FUNC(chan_opt_protect)
5205 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5208 static MODCMD_FUNC(chan_opt_toys)
5210 return channel_multiple_option(chToys, CSFUNC_ARGS);
5213 static MODCMD_FUNC(chan_opt_ctcpreaction)
5215 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5218 static MODCMD_FUNC(chan_opt_topicrefresh)
5220 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5223 static struct svccmd_list set_shows_list;
5226 handle_svccmd_unbind(struct svccmd *target) {
5228 for(ii=0; ii<set_shows_list.used; ++ii)
5229 if(target == set_shows_list.list[ii])
5230 set_shows_list.used = 0;
5233 static CHANSERV_FUNC(cmd_set)
5235 struct svccmd *subcmd;
5239 /* Check if we need to (re-)initialize set_shows_list. */
5240 if(!set_shows_list.used)
5242 if(!set_shows_list.size)
5244 set_shows_list.size = chanserv_conf.set_shows->used;
5245 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5247 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5249 const char *name = chanserv_conf.set_shows->list[ii];
5250 sprintf(buf, "%s %s", argv[0], name);
5251 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5254 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5257 svccmd_list_append(&set_shows_list, subcmd);
5263 reply("CSMSG_CHANNEL_OPTIONS");
5264 for(ii = 0; ii < set_shows_list.used; ii++)
5266 subcmd = set_shows_list.list[ii];
5267 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5272 sprintf(buf, "%s %s", argv[0], argv[1]);
5273 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5276 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5279 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5281 reply("CSMSG_NO_ACCESS");
5285 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5289 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5291 struct userData *uData;
5293 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5296 reply("CSMSG_NOT_USER", channel->name);
5302 /* Just show current option value. */
5304 else if(enabled_string(argv[1]))
5306 uData->flags |= mask;
5308 else if(disabled_string(argv[1]))
5310 uData->flags &= ~mask;
5314 reply("MSG_INVALID_BINARY", argv[1]);
5318 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5322 static MODCMD_FUNC(user_opt_noautoop)
5324 struct userData *uData;
5326 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5329 reply("CSMSG_NOT_USER", channel->name);
5332 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5333 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5335 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5338 static MODCMD_FUNC(user_opt_autoinvite)
5340 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5343 static MODCMD_FUNC(user_opt_info)
5345 struct userData *uData;
5348 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5352 /* If they got past the command restrictions (which require access)
5353 * but fail this test, we have some fool with security override on.
5355 reply("CSMSG_NOT_USER", channel->name);
5362 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5363 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5365 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5368 bp = strcspn(infoline, "\001");
5371 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5376 if(infoline[0] == '*' && infoline[1] == 0)
5379 uData->info = strdup(infoline);
5382 reply("CSMSG_USET_INFO", uData->info);
5384 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5388 struct svccmd_list uset_shows_list;
5390 static CHANSERV_FUNC(cmd_uset)
5392 struct svccmd *subcmd;
5396 /* Check if we need to (re-)initialize uset_shows_list. */
5397 if(!uset_shows_list.used)
5401 "NoAutoOp", "AutoInvite", "Info"
5404 if(!uset_shows_list.size)
5406 uset_shows_list.size = ArrayLength(options);
5407 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5409 for(ii = 0; ii < ArrayLength(options); ii++)
5411 const char *name = options[ii];
5412 sprintf(buf, "%s %s", argv[0], name);
5413 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5416 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5419 svccmd_list_append(&uset_shows_list, subcmd);
5425 /* Do this so options are presented in a consistent order. */
5426 reply("CSMSG_USER_OPTIONS");
5427 for(ii = 0; ii < uset_shows_list.used; ii++)
5428 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5432 sprintf(buf, "%s %s", argv[0], argv[1]);
5433 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5436 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5440 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5443 static CHANSERV_FUNC(cmd_giveownership)
5445 struct handle_info *new_owner_hi;
5446 struct userData *new_owner, *curr_user;
5447 struct chanData *cData = channel->channel_info;
5448 struct do_not_register *dnr;
5450 unsigned short co_access;
5451 char reason[MAXLEN];
5454 curr_user = GetChannelAccess(cData, user->handle_info);
5455 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5456 if(!curr_user || (curr_user->access != UL_OWNER))
5458 struct userData *owner = NULL;
5459 for(curr_user = channel->channel_info->users;
5461 curr_user = curr_user->next)
5463 if(curr_user->access != UL_OWNER)
5467 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5474 else if (!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5476 char delay[INTERVALLEN];
5477 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5478 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5481 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5483 if(new_owner_hi == user->handle_info)
5485 reply("CSMSG_NO_TRANSFER_SELF");
5488 new_owner = GetChannelAccess(cData, new_owner_hi);
5491 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5494 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5496 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5499 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5500 if(!IsHelping(user))
5501 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5503 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5506 if(new_owner->access >= UL_COOWNER)
5507 co_access = new_owner->access;
5509 co_access = UL_COOWNER;
5510 new_owner->access = UL_OWNER;
5512 curr_user->access = co_access;
5513 cData->ownerTransfer = now;
5514 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5515 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5516 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5520 static CHANSERV_FUNC(cmd_suspend)
5522 struct handle_info *hi;
5523 struct userData *self, *target;
5526 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5527 self = GetChannelUser(channel->channel_info, user->handle_info);
5528 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5530 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5533 if(target->access >= self->access)
5535 reply("MSG_USER_OUTRANKED", hi->handle);
5538 if(target->flags & USER_SUSPENDED)
5540 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5545 target->present = 0;
5548 target->flags |= USER_SUSPENDED;
5549 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5553 static CHANSERV_FUNC(cmd_unsuspend)
5555 struct handle_info *hi;
5556 struct userData *self, *target;
5559 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5560 self = GetChannelUser(channel->channel_info, user->handle_info);
5561 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5563 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5566 if(target->access >= self->access)
5568 reply("MSG_USER_OUTRANKED", hi->handle);
5571 if(!(target->flags & USER_SUSPENDED))
5573 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5576 target->flags &= ~USER_SUSPENDED;
5577 scan_user_presence(target, NULL);
5578 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
5582 static MODCMD_FUNC(cmd_deleteme)
5584 struct handle_info *hi;
5585 struct userData *target;
5586 const char *confirm_string;
5587 unsigned short access;
5590 hi = user->handle_info;
5591 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5593 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5596 if(target->access == UL_OWNER)
5598 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
5601 confirm_string = make_confirmation_string(target);
5602 if((argc < 2) || strcmp(argv[1], confirm_string))
5604 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
5607 access = target->access;
5608 channel_name = strdup(channel->name);
5609 del_channel_user(target, 1);
5610 reply("CSMSG_DELETED_YOU", access, channel_name);
5616 chanserv_refresh_topics(UNUSED_ARG(void *data))
5618 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
5619 struct chanData *cData;
5622 for(cData = channelList; cData; cData = cData->next)
5624 if(IsSuspended(cData))
5626 opt = cData->chOpts[chTopicRefresh];
5629 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
5632 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
5633 cData->last_refresh = refresh_num;
5635 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
5638 static CHANSERV_FUNC(cmd_unf)
5642 char response[MAXLEN];
5643 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
5644 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5645 irc_privmsg(cmd->parent->bot, channel->name, response);
5648 reply("CSMSG_UNF_RESPONSE");
5652 static CHANSERV_FUNC(cmd_ping)
5656 char response[MAXLEN];
5657 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
5658 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5659 irc_privmsg(cmd->parent->bot, channel->name, response);
5662 reply("CSMSG_PING_RESPONSE");
5666 static CHANSERV_FUNC(cmd_wut)
5670 char response[MAXLEN];
5671 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
5672 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
5673 irc_privmsg(cmd->parent->bot, channel->name, response);
5676 reply("CSMSG_WUT_RESPONSE");
5680 static CHANSERV_FUNC(cmd_8ball)
5682 unsigned int i, j, accum;
5687 for(i=1; i<argc; i++)
5688 for(j=0; argv[i][j]; j++)
5689 accum = (accum << 5) - accum + toupper(argv[i][j]);
5690 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
5693 char response[MAXLEN];
5694 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
5695 irc_privmsg(cmd->parent->bot, channel->name, response);
5698 send_message_type(4, user, cmd->parent->bot, "%s", resp);
5702 static CHANSERV_FUNC(cmd_d)
5704 unsigned long sides, count, modifier, ii, total;
5705 char response[MAXLEN], *sep;
5709 if((count = strtoul(argv[1], &sep, 10)) < 1)
5719 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
5720 && (sides = strtoul(sep+1, &sep, 10)) > 1)
5724 else if((sep[0] == '-') && isdigit(sep[1]))
5725 modifier = strtoul(sep, NULL, 10);
5726 else if((sep[0] == '+') && isdigit(sep[1]))
5727 modifier = strtoul(sep+1, NULL, 10);
5734 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
5739 reply("CSMSG_BAD_DICE_COUNT", count, 10);
5742 for(total = ii = 0; ii < count; ++ii)
5743 total += (rand() % sides) + 1;
5746 if((count > 1) || modifier)
5748 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
5749 sprintf(response, fmt, total, count, sides, modifier);
5753 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
5754 sprintf(response, fmt, total, sides);
5757 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
5759 send_message_type(4, user, cmd->parent->bot, "%s", response);
5763 static CHANSERV_FUNC(cmd_huggle)
5765 /* CTCP must be via PRIVMSG, never notice */
5767 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
5769 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
5774 chanserv_adjust_limit(void *data)
5776 struct mod_chanmode change;
5777 struct chanData *cData = data;
5778 struct chanNode *channel = cData->channel;
5781 if(IsSuspended(cData))
5784 cData->limitAdjusted = now;
5785 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
5786 if(cData->modes.modes_set & MODE_LIMIT)
5788 if(limit > cData->modes.new_limit)
5789 limit = cData->modes.new_limit;
5790 else if(limit == cData->modes.new_limit)
5794 mod_chanmode_init(&change);
5795 change.modes_set = MODE_LIMIT;
5796 change.new_limit = limit;
5797 mod_chanmode_announce(chanserv, channel, &change);
5801 handle_new_channel(struct chanNode *channel)
5803 struct chanData *cData;
5805 if(!(cData = channel->channel_info))
5808 if(cData->modes.modes_set || cData->modes.modes_clear)
5809 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
5811 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
5812 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
5815 /* Welcome to my worst nightmare. Warning: Read (or modify)
5816 the code below at your own risk. */
5818 handle_join(struct modeNode *mNode)
5820 struct mod_chanmode change;
5821 struct userNode *user = mNode->user;
5822 struct chanNode *channel = mNode->channel;
5823 struct chanData *cData;
5824 struct userData *uData = NULL;
5825 struct banData *bData;
5826 struct handle_info *handle;
5827 unsigned int modes = 0, info = 0;
5830 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
5833 cData = channel->channel_info;
5834 if(channel->members.used > cData->max)
5835 cData->max = channel->members.used;
5837 /* Check for bans. If they're joining through a ban, one of two
5839 * 1: Join during a netburst, by riding the break. Kick them
5840 * unless they have ops or voice in the channel.
5841 * 2: They're allowed to join through the ban (an invite in
5842 * ircu2.10, or a +e on Hybrid, or something).
5843 * If they're not joining through a ban, and the banlist is not
5844 * full, see if they're on the banlist for the channel. If so,
5847 if(user->uplink->burst && !mNode->modes)
5850 for(ii = 0; ii < channel->banlist.used; ii++)
5852 if(user_matches_glob(user, channel->banlist.list[ii]->ban, 1))
5854 /* Riding a netburst. Naughty. */
5855 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
5861 mod_chanmode_init(&change);
5863 if(channel->banlist.used < MAXBANS)
5865 /* Not joining through a ban. */
5866 for(bData = cData->bans;
5867 bData && !user_matches_glob(user, bData->mask, 1);
5868 bData = bData->next);
5872 char kick_reason[MAXLEN];
5873 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
5875 bData->triggered = now;
5876 if(bData != cData->bans)
5878 /* Shuffle the ban to the head of the list. */
5880 bData->next->prev = bData->prev;
5882 bData->prev->next = bData->next;
5885 bData->next = cData->bans;
5888 cData->bans->prev = bData;
5889 cData->bans = bData;
5892 change.args[0].mode = MODE_BAN;
5893 change.args[0].u.hostmask = bData->mask;
5894 mod_chanmode_announce(chanserv, channel, &change);
5895 KickChannelUser(user, channel, chanserv, kick_reason);
5900 /* ChanServ will not modify the limits in join-flooded channels.
5901 It will also skip DynLimit processing when the user (or srvx)
5902 is bursting in, because there are likely more incoming. */
5903 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
5904 && !user->uplink->burst
5905 && !channel->join_flooded
5906 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
5908 /* The user count has begun "bumping" into the channel limit,
5909 so set a timer to raise the limit a bit. Any previous
5910 timers are removed so three incoming users within the delay
5911 results in one limit change, not three. */
5913 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
5914 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
5917 if(channel->join_flooded)
5919 /* don't automatically give ops or voice during a join flood */
5921 else if(cData->lvlOpts[lvlGiveOps] == 0)
5922 modes |= MODE_CHANOP;
5923 else if(cData->lvlOpts[lvlGiveVoice] == 0)
5924 modes |= MODE_VOICE;
5926 greeting = cData->greeting;
5927 if(user->handle_info)
5929 handle = user->handle_info;
5931 if(IsHelper(user) && !IsHelping(user))
5934 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
5936 if(channel == chanserv_conf.support_channels.list[ii])
5938 HANDLE_SET_FLAG(user->handle_info, HELPING);
5944 uData = GetTrueChannelAccess(cData, handle);
5945 if(uData && !IsUserSuspended(uData))
5947 /* Ops and above were handled by the above case. */
5948 if(IsUserAutoOp(uData))
5950 if(uData->access >= cData->lvlOpts[lvlGiveOps])
5951 modes |= MODE_CHANOP;
5952 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
5953 modes |= MODE_VOICE;
5955 if(uData->access >= UL_PRESENT)
5956 cData->visited = now;
5957 if(cData->user_greeting)
5958 greeting = cData->user_greeting;
5960 && (uData->access >= cData->lvlOpts[lvlUserInfo])
5961 && ((now - uData->seen) >= chanserv_conf.info_delay)
5968 if(!user->uplink->burst)
5972 if(modes & MODE_CHANOP)
5973 modes &= ~MODE_VOICE;
5974 change.args[0].mode = modes;
5975 change.args[0].u.member = mNode;
5976 mod_chanmode_announce(chanserv, channel, &change);
5978 if(greeting && !user->uplink->burst)
5979 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
5981 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
5987 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
5989 struct mod_chanmode change;
5990 struct userData *channel;
5991 unsigned int ii, jj;
5993 if(!user->handle_info)
5996 mod_chanmode_init(&change);
5998 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6000 struct chanNode *cn;
6001 struct modeNode *mn;
6002 if(IsUserSuspended(channel)
6003 || IsSuspended(channel->channel)
6004 || !(cn = channel->channel->channel))
6007 mn = GetUserMode(cn, user);
6010 if(!IsUserSuspended(channel)
6011 && IsUserAutoInvite(channel)
6012 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6014 && !user->uplink->burst)
6015 irc_invite(chanserv, user, cn);
6019 if(channel->access >= UL_PRESENT)
6020 channel->channel->visited = now;
6022 if(IsUserAutoOp(channel))
6024 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6025 change.args[0].mode = MODE_CHANOP;
6026 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6027 change.args[0].mode = MODE_VOICE;
6029 change.args[0].mode = 0;
6030 change.args[0].u.member = mn;
6031 if(change.args[0].mode)
6032 mod_chanmode_announce(chanserv, cn, &change);
6035 channel->seen = now;
6036 channel->present = 1;
6039 for(ii = 0; ii < user->channels.used; ++ii)
6041 struct chanNode *channel = user->channels.list[ii]->channel;
6042 struct banData *ban;
6044 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6045 || !channel->channel_info
6046 || IsSuspended(channel->channel_info))
6048 for(jj = 0; jj < channel->banlist.used; ++jj)
6049 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6051 if(jj < channel->banlist.used)
6053 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6055 char kick_reason[MAXLEN];
6056 if(!user_matches_glob(user, ban->mask, 1))
6058 change.args[0].mode = MODE_BAN;
6059 change.args[0].u.hostmask = ban->mask;
6060 mod_chanmode_announce(chanserv, channel, &change);
6061 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6062 KickChannelUser(user, channel, chanserv, kick_reason);
6063 ban->triggered = now;
6068 if(IsSupportHelper(user))
6070 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6072 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6074 HANDLE_SET_FLAG(user->handle_info, HELPING);
6082 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6084 struct chanData *cData;
6085 struct userData *uData;
6087 cData = mn->channel->channel_info;
6088 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6091 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6093 /* Allow for a bit of padding so that the limit doesn't
6094 track the user count exactly, which could get annoying. */
6095 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6097 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6098 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6102 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6104 scan_user_presence(uData, mn->user);
6108 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6110 unsigned int ii, jj;
6111 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6113 for(jj = 0; jj < mn->user->channels.used; ++jj)
6114 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6116 if(jj < mn->user->channels.used)
6119 if(ii == chanserv_conf.support_channels.used)
6120 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6125 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6127 struct userData *uData;
6129 if(!channel->channel_info || !kicker || IsService(kicker)
6130 || (kicker == victim) || IsSuspended(channel->channel_info)
6131 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6134 if(protect_user(victim, kicker, channel->channel_info))
6136 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6137 KickChannelUser(kicker, channel, chanserv, reason);
6140 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6145 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6147 struct chanData *cData;
6149 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6152 cData = channel->channel_info;
6153 if(bad_topic(channel, user, channel->topic))
6155 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6156 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6157 SetChannelTopic(channel, chanserv, old_topic, 1);
6158 else if(cData->topic)
6159 SetChannelTopic(channel, chanserv, cData->topic, 1);
6162 /* With topicsnarf, grab the topic and save it as the default topic. */
6163 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6166 cData->topic = strdup(channel->topic);
6172 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6174 struct mod_chanmode *bounce = NULL;
6175 unsigned int bnc, ii;
6178 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6181 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6182 && mode_lock_violated(&channel->channel_info->modes, change))
6184 char correct[MAXLEN];
6185 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6186 mod_chanmode_format(&channel->channel_info->modes, correct);
6187 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6189 for(ii = bnc = 0; ii < change->argc; ++ii)
6191 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6193 const struct userNode *victim = change->args[ii].u.member->user;
6194 if(!protect_user(victim, user, channel->channel_info))
6197 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6200 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6201 bounce->args[bnc].u.member = GetUserMode(channel, user);
6202 if(bounce->args[bnc].u.member)
6206 bounce->args[bnc].mode = MODE_CHANOP;
6207 bounce->args[bnc].u.member = change->args[ii].u.member;
6209 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6211 else if(change->args[ii].mode & MODE_CHANOP)
6213 const struct userNode *victim = change->args[ii].u.member->user;
6214 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6217 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6218 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6219 bounce->args[bnc].u.member = change->args[ii].u.member;
6222 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6224 const char *ban = change->args[ii].u.hostmask;
6225 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6228 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6229 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6230 bounce->args[bnc].u.hostmask = strdup(ban);
6232 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6237 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6238 mod_chanmode_announce(chanserv, channel, bounce);
6239 for(ii = 0; ii < change->argc; ++ii)
6240 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6241 free((char*)bounce->args[ii].u.hostmask);
6242 mod_chanmode_free(bounce);
6247 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6249 struct chanNode *channel;
6250 struct banData *bData;
6251 struct mod_chanmode change;
6252 unsigned int ii, jj;
6253 char kick_reason[MAXLEN];
6255 mod_chanmode_init(&change);
6257 change.args[0].mode = MODE_BAN;
6258 for(ii = 0; ii < user->channels.used; ++ii)
6260 channel = user->channels.list[ii]->channel;
6261 /* Need not check for bans if they're opped or voiced. */
6262 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6264 /* Need not check for bans unless channel registration is active. */
6265 if(!channel->channel_info || IsSuspended(channel->channel_info))
6267 /* Look for a matching ban already on the channel. */
6268 for(jj = 0; jj < channel->banlist.used; ++jj)
6269 if(user_matches_glob(user, channel->banlist.list[jj]->ban, 1))
6271 /* Need not act if we found one. */
6272 if(jj < channel->banlist.used)
6274 /* Look for a matching ban in this channel. */
6275 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6277 if(!user_matches_glob(user, bData->mask, 1))
6279 change.args[0].u.hostmask = bData->mask;
6280 mod_chanmode_announce(chanserv, channel, &change);
6281 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6282 KickChannelUser(user, channel, chanserv, kick_reason);
6283 bData->triggered = now;
6284 break; /* we don't need to check any more bans in the channel */
6289 static void handle_rename(struct handle_info *handle, const char *old_handle)
6291 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6295 dict_remove2(handle_dnrs, old_handle, 1);
6296 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6297 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6302 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6304 struct userNode *h_user;
6306 if(handle->channels)
6308 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6309 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6311 while(handle->channels)
6312 del_channel_user(handle->channels, 1);
6317 handle_server_link(UNUSED_ARG(struct server *server))
6319 struct chanData *cData;
6321 for(cData = channelList; cData; cData = cData->next)
6323 if(!IsSuspended(cData))
6324 cData->may_opchan = 1;
6325 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6326 && !cData->channel->join_flooded
6327 && ((cData->channel->limit - cData->channel->members.used)
6328 < chanserv_conf.adjust_threshold))
6330 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6331 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6337 chanserv_conf_read(void)
6341 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6342 struct mod_chanmode *change;
6343 struct string_list *strlist;
6344 struct chanNode *chan;
6347 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6349 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6352 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6353 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6354 chanserv_conf.support_channels.used = 0;
6355 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6357 for(ii = 0; ii < strlist->used; ++ii)
6359 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6362 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6364 channelList_append(&chanserv_conf.support_channels, chan);
6367 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6370 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6373 chan = AddChannel(str, now, str2, NULL);
6375 channelList_append(&chanserv_conf.support_channels, chan);
6377 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6378 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6379 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6380 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6381 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6382 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6383 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6384 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6385 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6386 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6387 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6388 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6389 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6390 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6391 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6392 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6393 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6394 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6395 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6396 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6397 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6398 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6399 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6401 NickChange(chanserv, str, 0);
6402 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6403 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6404 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6405 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6406 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6407 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6408 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6409 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6410 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6411 chanserv_conf.max_owned = str ? atoi(str) : 5;
6412 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6413 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6414 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6415 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6416 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6417 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6418 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6421 safestrncpy(mode_line, str, sizeof(mode_line));
6422 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6423 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE)) && (change->argc < 2))
6425 chanserv_conf.default_modes = *change;
6426 mod_chanmode_free(change);
6428 free_string_list(chanserv_conf.set_shows);
6429 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6431 strlist = string_list_copy(strlist);
6434 static const char *list[] = {
6435 /* free form text */
6436 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6437 /* options based on user level */
6438 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6439 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6440 /* multiple choice options */
6441 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6442 /* binary options */
6443 "DynLimit", "NoDelete",
6448 strlist = alloc_string_list(ArrayLength(list)-1);
6449 for(ii=0; list[ii]; ii++)
6450 string_list_append(strlist, strdup(list[ii]));
6452 chanserv_conf.set_shows = strlist;
6453 /* We don't look things up now, in case the list refers to options
6454 * defined by modules initialized after this point. Just mark the
6455 * function list as invalid, so it will be initialized.
6457 set_shows_list.used = 0;
6458 free_string_list(chanserv_conf.eightball);
6459 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6462 strlist = string_list_copy(strlist);
6466 strlist = alloc_string_list(4);
6467 string_list_append(strlist, strdup("Yes."));
6468 string_list_append(strlist, strdup("No."));
6469 string_list_append(strlist, strdup("Maybe so."));
6471 chanserv_conf.eightball = strlist;
6472 free_string_list(chanserv_conf.old_ban_names);
6473 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6475 strlist = string_list_copy(strlist);
6477 strlist = alloc_string_list(2);
6478 chanserv_conf.old_ban_names = strlist;
6479 /* the variable itself is actually declared in proto-common.c; this is equally
6481 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6482 off_channel = str ? atoi(str) : 0;
6486 chanserv_note_type_read(const char *key, struct record_data *rd)
6489 struct note_type *ntype;
6492 if(!(obj = GET_RECORD_OBJECT(rd)))
6494 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6497 if(!(ntype = chanserv_create_note_type(key)))
6499 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6503 /* Figure out set access */
6504 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6506 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6507 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6509 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6511 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6512 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6514 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6516 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6520 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6521 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6522 ntype->set_access.min_opserv = 0;
6525 /* Figure out visibility */
6526 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6527 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6528 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6529 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6530 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6531 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6532 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6533 ntype->visible_type = NOTE_VIS_ALL;
6535 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6537 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6538 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6542 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6544 struct handle_info *handle;
6545 struct userData *uData;
6546 char *seen, *inf, *flags;
6548 unsigned short access;
6550 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6552 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6556 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6557 if(access > UL_OWNER)
6559 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6563 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6564 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6565 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6566 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6567 handle = get_handle_info(key);
6570 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
6574 uData = add_channel_user(chan, handle, access, last_seen, inf);
6575 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
6579 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6581 struct banData *bData;
6582 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
6583 time_t set_time, triggered_time, expires_time;
6585 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6587 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
6591 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
6592 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
6593 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
6594 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
6595 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
6596 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
6598 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
6599 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
6601 expires_time = (time_t)strtoul(s_expires, NULL, 0);
6603 expires_time = set_time + atoi(s_duration);
6607 if(!reason || (expires_time && (expires_time < now)))
6610 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
6613 static struct suspended *
6614 chanserv_read_suspended(dict_t obj)
6616 struct suspended *suspended = calloc(1, sizeof(*suspended));
6620 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
6621 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
6622 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
6623 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
6624 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
6625 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
6626 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
6627 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
6628 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
6629 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
6634 chanserv_channel_read(const char *key, struct record_data *hir)
6636 struct suspended *suspended;
6637 struct mod_chanmode *modes;
6638 struct chanNode *cNode;
6639 struct chanData *cData;
6640 struct dict *channel, *obj;
6641 char *str, *argv[10];
6645 channel = hir->d.object;
6647 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
6650 cNode = AddChannel(key, now, NULL, NULL);
6653 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
6656 cData = register_channel(cNode, str);
6659 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
6663 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
6665 enum levelOption lvlOpt;
6666 enum charOption chOpt;
6668 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
6669 cData->flags = atoi(str);
6671 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6673 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
6675 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
6676 else if(levelOptions[lvlOpt].old_flag)
6678 if(cData->flags & levelOptions[lvlOpt].old_flag)
6679 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
6681 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
6685 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6687 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
6689 cData->chOpts[chOpt] = str[0];
6692 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
6694 enum levelOption lvlOpt;
6695 enum charOption chOpt;
6698 cData->flags = base64toint(str, 5);
6699 count = strlen(str += 5);
6700 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6703 if(levelOptions[lvlOpt].old_flag)
6705 if(cData->flags & levelOptions[lvlOpt].old_flag)
6706 lvl = levelOptions[lvlOpt].flag_value;
6708 lvl = levelOptions[lvlOpt].default_value;
6710 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
6712 case 'c': lvl = UL_COOWNER; break;
6713 case 'm': lvl = UL_MASTER; break;
6714 case 'n': lvl = UL_OWNER+1; break;
6715 case 'o': lvl = UL_OP; break;
6716 case 'p': lvl = UL_PEON; break;
6717 case 'w': lvl = UL_OWNER; break;
6718 default: lvl = 0; break;
6720 cData->lvlOpts[lvlOpt] = lvl;
6722 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6723 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
6726 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
6728 suspended = chanserv_read_suspended(obj);
6729 cData->suspended = suspended;
6730 suspended->cData = cData;
6731 /* We could use suspended->expires and suspended->revoked to
6732 * set the CHANNEL_SUSPENDED flag, but we don't. */
6734 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
6736 suspended = calloc(1, sizeof(*suspended));
6737 suspended->issued = 0;
6738 suspended->revoked = 0;
6739 suspended->suspender = strdup(str);
6740 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
6741 suspended->expires = str ? atoi(str) : 0;
6742 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
6743 suspended->reason = strdup(str ? str : "No reason");
6744 suspended->previous = NULL;
6745 cData->suspended = suspended;
6746 suspended->cData = cData;
6750 cData->flags &= ~CHANNEL_SUSPENDED;
6751 suspended = NULL; /* to squelch a warning */
6754 if(IsSuspended(cData)) {
6755 if(suspended->expires > now)
6756 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
6757 else if(suspended->expires)
6758 cData->flags &= ~CHANNEL_SUSPENDED;
6761 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
6762 struct mod_chanmode change;
6763 mod_chanmode_init(&change);
6765 change.args[0].mode = MODE_CHANOP;
6766 change.args[0].u.member = AddChannelUser(chanserv, cNode);
6767 mod_chanmode_announce(chanserv, cNode, &change);
6770 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
6771 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
6772 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
6773 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
6774 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
6775 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
6776 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
6777 cData->max = str ? atoi(str) : 0;
6778 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
6779 cData->greeting = str ? strdup(str) : NULL;
6780 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
6781 cData->user_greeting = str ? strdup(str) : NULL;
6782 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
6783 cData->topic_mask = str ? strdup(str) : NULL;
6784 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
6785 cData->topic = str ? strdup(str) : NULL;
6787 if(!IsSuspended(cData)
6788 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
6789 && (argc = split_line(str, 0, ArrayLength(argv), argv))
6790 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE))) {
6791 cData->modes = *modes;
6793 cData->modes.modes_set |= MODE_REGISTERED;
6794 if(cData->modes.argc > 1)
6795 cData->modes.argc = 1;
6796 mod_chanmode_announce(chanserv, cNode, &cData->modes);
6797 mod_chanmode_free(modes);
6800 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
6801 for(it = dict_first(obj); it; it = iter_next(it))
6802 user_read_helper(iter_key(it), iter_data(it), cData);
6804 if(!cData->users && !IsProtected(cData))
6806 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
6807 unregister_channel(cData, "has empty user list.");
6811 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
6812 for(it = dict_first(obj); it; it = iter_next(it))
6813 ban_read_helper(iter_key(it), iter_data(it), cData);
6815 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
6816 for(it = dict_first(obj); it; it = iter_next(it))
6818 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
6819 struct record_data *rd = iter_data(it);
6820 const char *note, *setter;
6822 if(rd->type != RECDB_OBJECT)
6824 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
6828 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
6830 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
6832 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
6836 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
6837 if(!setter) setter = "<unknown>";
6838 chanserv_add_channel_note(cData, ntype, setter, note);
6846 chanserv_dnr_read(const char *key, struct record_data *hir)
6848 const char *setter, *reason, *str;
6849 struct do_not_register *dnr;
6851 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
6854 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
6857 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
6860 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
6863 dnr = chanserv_add_dnr(key, setter, reason);
6866 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
6868 dnr->set = atoi(str);
6874 chanserv_saxdb_read(struct dict *database)
6876 struct dict *section;
6879 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
6880 for(it = dict_first(section); it; it = iter_next(it))
6881 chanserv_note_type_read(iter_key(it), iter_data(it));
6883 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
6884 for(it = dict_first(section); it; it = iter_next(it))
6885 chanserv_channel_read(iter_key(it), iter_data(it));
6887 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
6888 for(it = dict_first(section); it; it = iter_next(it))
6889 chanserv_dnr_read(iter_key(it), iter_data(it));
6895 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
6897 int high_present = 0;
6898 saxdb_start_record(ctx, KEY_USERS, 1);
6899 for(; uData; uData = uData->next)
6901 if((uData->access >= UL_PRESENT) && uData->present)
6903 saxdb_start_record(ctx, uData->handle->handle, 0);
6904 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
6905 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
6907 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
6909 saxdb_write_string(ctx, KEY_INFO, uData->info);
6910 saxdb_end_record(ctx);
6912 saxdb_end_record(ctx);
6913 return high_present;
6917 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
6921 saxdb_start_record(ctx, KEY_BANS, 1);
6922 for(; bData; bData = bData->next)
6924 saxdb_start_record(ctx, bData->mask, 0);
6925 saxdb_write_int(ctx, KEY_SET, bData->set);
6926 if(bData->triggered)
6927 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
6929 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
6931 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
6933 saxdb_write_string(ctx, KEY_REASON, bData->reason);
6934 saxdb_end_record(ctx);
6936 saxdb_end_record(ctx);
6940 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
6942 saxdb_start_record(ctx, name, 0);
6943 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
6944 saxdb_write_string(ctx, KEY_REASON, susp->reason);
6946 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
6948 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
6950 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
6952 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
6953 saxdb_end_record(ctx);
6957 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
6961 enum levelOption lvlOpt;
6962 enum charOption chOpt;
6964 saxdb_start_record(ctx, channel->channel->name, 1);
6966 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
6967 saxdb_write_int(ctx, KEY_MAX, channel->max);
6969 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
6970 if(channel->registrar)
6971 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
6972 if(channel->greeting)
6973 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
6974 if(channel->user_greeting)
6975 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
6976 if(channel->topic_mask)
6977 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
6978 if(channel->suspended)
6979 chanserv_write_suspended(ctx, "suspended", channel->suspended);
6981 saxdb_start_record(ctx, KEY_OPTIONS, 0);
6982 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
6983 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
6984 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
6985 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
6987 buf[0] = channel->chOpts[chOpt];
6989 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
6991 saxdb_end_record(ctx);
6993 if(channel->modes.modes_set || channel->modes.modes_clear)
6995 mod_chanmode_format(&channel->modes, buf);
6996 saxdb_write_string(ctx, KEY_MODES, buf);
6999 high_present = chanserv_write_users(ctx, channel->users);
7000 chanserv_write_bans(ctx, channel->bans);
7002 if(dict_size(channel->notes))
7006 saxdb_start_record(ctx, KEY_NOTES, 1);
7007 for(it = dict_first(channel->notes); it; it = iter_next(it))
7009 struct note *note = iter_data(it);
7010 saxdb_start_record(ctx, iter_key(it), 0);
7011 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7012 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7013 saxdb_end_record(ctx);
7015 saxdb_end_record(ctx);
7018 if(channel->ownerTransfer)
7019 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7020 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7021 saxdb_end_record(ctx);
7025 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7029 saxdb_start_record(ctx, ntype->name, 0);
7030 switch(ntype->set_access_type)
7032 case NOTE_SET_CHANNEL_ACCESS:
7033 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7035 case NOTE_SET_CHANNEL_SETTER:
7036 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7038 case NOTE_SET_PRIVILEGED: default:
7039 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7042 switch(ntype->visible_type)
7044 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7045 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7046 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7048 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7049 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7050 saxdb_end_record(ctx);
7054 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7056 struct do_not_register *dnr;
7059 for(it = dict_first(dnrs); it; it = iter_next(it))
7061 dnr = iter_data(it);
7062 saxdb_start_record(ctx, dnr->chan_name, 0);
7064 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7065 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7066 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7067 saxdb_end_record(ctx);
7072 chanserv_saxdb_write(struct saxdb_context *ctx)
7075 struct chanData *channel;
7078 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7079 for(it = dict_first(note_types); it; it = iter_next(it))
7080 chanserv_write_note_type(ctx, iter_data(it));
7081 saxdb_end_record(ctx);
7084 saxdb_start_record(ctx, KEY_DNR, 1);
7085 write_dnrs_helper(ctx, handle_dnrs);
7086 write_dnrs_helper(ctx, plain_dnrs);
7087 write_dnrs_helper(ctx, mask_dnrs);
7088 saxdb_end_record(ctx);
7091 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7092 for(channel = channelList; channel; channel = channel->next)
7093 chanserv_write_channel(ctx, channel);
7094 saxdb_end_record(ctx);
7100 chanserv_db_cleanup(void) {
7102 unreg_part_func(handle_part);
7104 unregister_channel(channelList, "terminating.");
7105 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7106 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7107 free(chanserv_conf.support_channels.list);
7108 dict_delete(handle_dnrs);
7109 dict_delete(plain_dnrs);
7110 dict_delete(mask_dnrs);
7111 dict_delete(note_types);
7112 free_string_list(chanserv_conf.eightball);
7113 free_string_list(chanserv_conf.old_ban_names);
7114 free_string_list(chanserv_conf.set_shows);
7115 free(set_shows_list.list);
7116 free(uset_shows_list.list);
7119 struct userData *helper = helperList;
7120 helperList = helperList->next;
7125 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7126 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7127 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7130 init_chanserv(const char *nick)
7132 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7133 conf_register_reload(chanserv_conf_read);
7135 reg_server_link_func(handle_server_link);
7137 reg_new_channel_func(handle_new_channel);
7138 reg_join_func(handle_join);
7139 reg_part_func(handle_part);
7140 reg_kick_func(handle_kick);
7141 reg_topic_func(handle_topic);
7142 reg_mode_change_func(handle_mode);
7143 reg_nick_change_func(handle_nick_change);
7145 reg_auth_func(handle_auth);
7146 reg_handle_rename_func(handle_rename);
7147 reg_unreg_func(handle_unreg);
7149 handle_dnrs = dict_new();
7150 dict_set_free_data(handle_dnrs, free);
7151 plain_dnrs = dict_new();
7152 dict_set_free_data(plain_dnrs, free);
7153 mask_dnrs = dict_new();
7154 dict_set_free_data(mask_dnrs, free);
7156 reg_svccmd_unbind_func(handle_svccmd_unbind);
7157 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7158 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7159 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7160 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7161 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7162 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7163 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7164 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7165 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7167 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7168 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7170 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7171 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7172 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7173 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7174 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7176 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7177 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7178 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7179 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7180 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7182 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7183 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7184 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7185 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7187 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7188 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7189 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7190 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7191 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7192 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7193 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7194 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7196 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7197 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7198 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7199 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7200 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7201 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7202 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7203 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7204 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7205 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7206 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7207 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7208 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7209 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7211 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7212 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7213 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7214 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7215 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7217 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7218 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7220 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7221 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7222 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7223 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7224 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7225 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7226 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7227 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7228 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7229 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7230 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7232 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7233 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7235 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7236 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7237 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7238 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7240 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7241 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7242 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7243 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7244 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7246 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7247 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7248 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7249 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7250 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7251 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7253 /* Channel options */
7254 DEFINE_CHANNEL_OPTION(defaulttopic);
7255 DEFINE_CHANNEL_OPTION(topicmask);
7256 DEFINE_CHANNEL_OPTION(greeting);
7257 DEFINE_CHANNEL_OPTION(usergreeting);
7258 DEFINE_CHANNEL_OPTION(modes);
7259 DEFINE_CHANNEL_OPTION(enfops);
7260 DEFINE_CHANNEL_OPTION(giveops);
7261 DEFINE_CHANNEL_OPTION(protect);
7262 DEFINE_CHANNEL_OPTION(enfmodes);
7263 DEFINE_CHANNEL_OPTION(enftopic);
7264 DEFINE_CHANNEL_OPTION(pubcmd);
7265 DEFINE_CHANNEL_OPTION(givevoice);
7266 DEFINE_CHANNEL_OPTION(userinfo);
7267 DEFINE_CHANNEL_OPTION(dynlimit);
7268 DEFINE_CHANNEL_OPTION(topicsnarf);
7269 DEFINE_CHANNEL_OPTION(nodelete);
7270 DEFINE_CHANNEL_OPTION(toys);
7271 DEFINE_CHANNEL_OPTION(setters);
7272 DEFINE_CHANNEL_OPTION(topicrefresh);
7273 DEFINE_CHANNEL_OPTION(ctcpusers);
7274 DEFINE_CHANNEL_OPTION(ctcpreaction);
7275 DEFINE_CHANNEL_OPTION(inviteme);
7277 DEFINE_CHANNEL_OPTION(offchannel);
7278 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7280 /* Alias set topic to set defaulttopic for compatibility. */
7281 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7284 DEFINE_USER_OPTION(noautoop);
7285 DEFINE_USER_OPTION(autoinvite);
7286 DEFINE_USER_OPTION(info);
7288 /* Alias uset autovoice to uset autoop. */
7289 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7291 note_types = dict_new();
7292 dict_set_free_data(note_types, chanserv_deref_note_type);
7295 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7296 chanserv = AddService(nick, modes ? modes : NULL, "Channel Services", NULL);
7297 service_register(chanserv)->trigger = '!';
7298 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7300 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7302 if(chanserv_conf.channel_expire_frequency)
7303 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7305 if(chanserv_conf.refresh_period)
7307 time_t next_refresh;
7308 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7309 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7312 reg_exit_func(chanserv_db_cleanup);
7313 message_register_table(msgtab);