1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2006 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
103 #define KEY_OWNER_TRANSFER "owner_transfer"
106 #define KEY_LEVEL "level"
107 #define KEY_INFO "info"
108 #define KEY_SEEN "seen"
111 #define KEY_OWNER "owner"
112 #define KEY_REASON "reason"
113 #define KEY_SET "set"
114 #define KEY_DURATION "duration"
115 #define KEY_EXPIRES "expires"
116 #define KEY_TRIGGERED "triggered"
118 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL)
119 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
121 /* Administrative messages */
122 static const struct message_entry msgtab[] = {
123 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
125 /* Channel registration */
126 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
127 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
128 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
129 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
130 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
131 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
133 /* Do-not-register channels */
134 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
135 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
136 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
137 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
138 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
139 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
140 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
141 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
142 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
143 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
144 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
145 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
146 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
147 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
149 /* Channel unregistration */
150 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
151 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
152 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
153 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
156 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
157 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
159 /* Channel merging */
160 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
161 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
162 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
163 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
164 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
166 /* Handle unregistration */
167 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
170 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
171 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
172 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
173 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
174 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
175 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
176 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
177 { "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." },
178 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
179 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
180 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
181 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
182 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
184 /* Removing yourself from a channel. */
185 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
186 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
187 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
189 /* User management */
190 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
191 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
192 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
193 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
194 { "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." },
195 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
196 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
197 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
199 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
200 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
201 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
202 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
203 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
204 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
205 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
208 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
209 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
210 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
211 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
212 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
213 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
214 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
215 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
216 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
217 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
218 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
219 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
220 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
221 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
222 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
223 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
225 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
227 /* Channel management */
228 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
229 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
230 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
232 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
233 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
234 { "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" },
235 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
236 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
237 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
238 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
240 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
241 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
242 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
243 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
244 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
245 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
246 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
247 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
248 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
249 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
250 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
251 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
252 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
253 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
254 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
255 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
256 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
257 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
258 { "CSMSG_SET_MODES", "$bModes $b %s" },
259 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
260 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
261 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
262 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
263 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
264 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
265 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
266 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
267 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
268 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
269 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
270 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
271 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
272 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
273 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
274 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
275 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
276 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
277 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
278 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
279 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
280 { "CSMSG_USET_INFO", "$bInfo $b %s" },
282 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
283 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
284 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
285 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
286 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
287 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
288 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
289 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
290 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
291 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
292 { "CSMSG_PROTECT_NONE", "No users will be protected." },
293 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
294 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
295 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
296 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
297 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
298 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
299 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
300 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
301 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
302 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
303 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
306 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
307 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
308 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
309 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
310 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
311 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
312 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
313 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
315 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
316 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
317 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
319 /* Channel userlist */
320 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
321 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
322 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
323 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
325 /* Channel note list */
326 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
327 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
328 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
329 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
330 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
331 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
332 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
333 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
334 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
335 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
336 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
337 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
338 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
339 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
340 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
342 /* Channel [un]suspension */
343 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
344 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
345 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
346 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
347 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
348 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
349 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
351 /* Access information */
352 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
353 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
354 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
355 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
356 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
357 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
358 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
359 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
360 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
361 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
362 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
363 { "CSMSG_UC_H_TITLE", "network helper" },
364 { "CSMSG_LC_H_TITLE", "support helper" },
365 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
367 /* Seen information */
368 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
369 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
370 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
371 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
373 /* Names information */
374 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
375 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
377 /* Channel information */
378 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
379 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
380 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
381 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
382 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
383 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
384 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
385 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
386 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
387 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
388 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
389 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
390 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
391 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
397 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
398 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
400 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
401 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
402 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
403 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
404 { "CSMSG_PEEK_OPS", "$bOps:$b" },
405 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
407 /* Network information */
408 { "CSMSG_NETWORK_INFO", "Network Information:" },
409 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
410 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
411 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
412 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
413 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
414 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
415 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
416 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
419 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
420 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
421 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
423 /* Channel searches */
424 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
425 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
426 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
427 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
429 /* Channel configuration */
430 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
431 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
432 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
433 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
436 { "CSMSG_USER_OPTIONS", "User Options:" },
437 { "CSMSG_USER_PROTECTED", "That user is protected." },
440 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
441 { "CSMSG_PING_RESPONSE", "Pong!" },
442 { "CSMSG_WUT_RESPONSE", "wut" },
443 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
444 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
445 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
446 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
447 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
448 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
449 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
452 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
456 /* eject_user and unban_user flags */
457 #define ACTION_KICK 0x0001
458 #define ACTION_BAN 0x0002
459 #define ACTION_ADD_BAN 0x0004
460 #define ACTION_ADD_TIMED_BAN 0x0008
461 #define ACTION_UNBAN 0x0010
462 #define ACTION_DEL_BAN 0x0020
464 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
465 #define MODELEN 40 + KEYLEN
469 #define CSFUNC_ARGS user, channel, argc, argv, cmd
471 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
472 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
473 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
474 reply("MSG_MISSING_PARAMS", argv[0]); \
478 DECLARE_LIST(dnrList, struct do_not_register *);
479 DEFINE_LIST(dnrList, struct do_not_register *);
481 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
483 struct userNode *chanserv;
486 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
487 static struct log_type *CS_LOG;
491 struct channelList support_channels;
492 struct mod_chanmode default_modes;
494 unsigned long db_backup_frequency;
495 unsigned long channel_expire_frequency;
496 unsigned long dnr_expire_frequency;
499 unsigned int adjust_delay;
500 long channel_expire_delay;
501 unsigned int nodelete_level;
503 unsigned int adjust_threshold;
504 int join_flood_threshold;
506 unsigned int greeting_length;
507 unsigned int refresh_period;
508 unsigned int giveownership_period;
510 unsigned int max_owned;
511 unsigned int max_chan_users;
512 unsigned int max_chan_bans;
513 unsigned int max_userinfo_length;
515 struct string_list *set_shows;
516 struct string_list *eightball;
517 struct string_list *old_ban_names;
519 const char *ctcp_short_ban_duration;
520 const char *ctcp_long_ban_duration;
522 const char *irc_operator_epithet;
523 const char *network_helper_epithet;
524 const char *support_helper_epithet;
529 struct userNode *user;
530 struct userNode *bot;
531 struct chanNode *channel;
533 unsigned short lowest;
534 unsigned short highest;
535 struct userData **users;
536 struct helpfile_table table;
539 enum note_access_type
541 NOTE_SET_CHANNEL_ACCESS,
542 NOTE_SET_CHANNEL_SETTER,
546 enum note_visible_type
549 NOTE_VIS_CHANNEL_USERS,
555 enum note_access_type set_access_type;
557 unsigned int min_opserv;
558 unsigned short min_ulevel;
560 enum note_visible_type visible_type;
561 unsigned int max_length;
568 struct note_type *type;
569 char setter[NICKSERV_HANDLE_LEN+1];
573 static unsigned int registered_channels;
574 static unsigned int banCount;
576 static const struct {
579 unsigned short level;
582 { "peon", "Peon", UL_PEON, '+' },
583 { "op", "Op", UL_OP, '@' },
584 { "master", "Master", UL_MASTER, '%' },
585 { "coowner", "Coowner", UL_COOWNER, '*' },
586 { "owner", "Owner", UL_OWNER, '!' },
587 { "helper", "BUG:", UL_HELPER, 'X' }
590 static const struct {
593 unsigned short default_value;
594 unsigned int old_idx;
595 unsigned int old_flag;
596 unsigned short flag_value;
598 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
599 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
600 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
601 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
602 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
603 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
604 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
605 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
606 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
607 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
608 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
611 struct charOptionValues {
614 } protectValues[] = {
615 { 'a', "CSMSG_PROTECT_ALL" },
616 { 'e', "CSMSG_PROTECT_EQUAL" },
617 { 'l', "CSMSG_PROTECT_LOWER" },
618 { 'n', "CSMSG_PROTECT_NONE" }
620 { 'd', "CSMSG_TOYS_DISABLED" },
621 { 'n', "CSMSG_TOYS_PRIVATE" },
622 { 'p', "CSMSG_TOYS_PUBLIC" }
623 }, topicRefreshValues[] = {
624 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
625 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
626 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
627 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
628 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
629 }, ctcpReactionValues[] = {
630 { 'k', "CSMSG_CTCPREACTION_KICK" },
631 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
632 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
633 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
636 static const struct {
640 unsigned int old_idx;
642 struct charOptionValues *values;
644 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
645 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
646 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
647 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
650 struct userData *helperList;
651 struct chanData *channelList;
652 static struct module *chanserv_module;
653 static unsigned int userCount;
655 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
656 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
657 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
660 user_level_from_name(const char *name, unsigned short clamp_level)
662 unsigned int level = 0, ii;
664 level = strtoul(name, NULL, 10);
665 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
666 if(!irccasecmp(name, accessLevels[ii].name))
667 level = accessLevels[ii].level;
668 if(level > clamp_level)
674 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
677 *minl = strtoul(arg, &sep, 10);
685 *maxl = strtoul(sep+1, &sep, 10);
693 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
695 struct userData *uData, **head;
697 if(!channel || !handle)
700 if(override && HANDLE_FLAGGED(handle, HELPING)
701 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
703 for(uData = helperList;
704 uData && uData->handle != handle;
705 uData = uData->next);
709 uData = calloc(1, sizeof(struct userData));
710 uData->handle = handle;
712 uData->access = UL_HELPER;
718 uData->next = helperList;
720 helperList->prev = uData;
728 for(uData = channel->users; uData; uData = uData->next)
729 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
732 head = &(channel->users);
735 if(uData && (uData != *head))
737 /* Shuffle the user to the head of whatever list he was in. */
739 uData->next->prev = uData->prev;
741 uData->prev->next = uData->next;
747 (**head).prev = uData;
754 /* Returns non-zero if user has at least the minimum access.
755 * exempt_owner is set when handling !set, so the owner can set things
758 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
760 struct userData *uData;
761 struct chanData *cData = channel->channel_info;
762 unsigned short minimum = cData->lvlOpts[opt];
765 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
768 if(minimum <= uData->access)
770 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
775 /* Scan for other users authenticated to the same handle
776 still in the channel. If so, keep them listed as present.
778 user is optional, if not null, it skips checking that userNode
779 (for the handle_part function) */
781 scan_user_presence(struct userData *uData, struct userNode *user)
785 if(IsSuspended(uData->channel)
786 || IsUserSuspended(uData)
787 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
799 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
801 unsigned int eflags, argc;
803 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
805 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
806 if(!channel->channel_info
807 || IsSuspended(channel->channel_info)
809 || !ircncasecmp(text, "ACTION ", 7))
811 /* Figure out the minimum level needed to CTCP the channel */
812 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
814 /* We need to enforce against them; do so. */
816 argv[0] = (char*)text;
817 argv[1] = user->nick;
819 if(GetUserMode(channel, user))
820 eflags |= ACTION_KICK;
821 switch(channel->channel_info->chOpts[chCTCPReaction]) {
822 default: case 'k': /* just do the kick */ break;
824 eflags |= ACTION_BAN;
827 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
828 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
831 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
832 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
835 argv[argc++] = bad_ctcp_reason;
836 eject_user(chanserv, channel, argc, argv, NULL, eflags);
840 chanserv_create_note_type(const char *name)
842 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
843 strcpy(ntype->name, name);
845 dict_insert(note_types, ntype->name, ntype);
850 chanserv_deref_note_type(void *data)
852 struct note_type *ntype = data;
854 if(--ntype->refs > 0)
860 chanserv_flush_note_type(struct note_type *ntype)
862 struct chanData *cData;
863 for(cData = channelList; cData; cData = cData->next)
864 dict_remove(cData->notes, ntype->name);
868 chanserv_truncate_notes(struct note_type *ntype)
870 struct chanData *cData;
872 unsigned int size = sizeof(*note) + ntype->max_length;
874 for(cData = channelList; cData; cData = cData->next) {
875 note = dict_find(cData->notes, ntype->name, NULL);
878 if(strlen(note->note) <= ntype->max_length)
880 dict_remove2(cData->notes, ntype->name, 1);
881 note = realloc(note, size);
882 note->note[ntype->max_length] = 0;
883 dict_insert(cData->notes, ntype->name, note);
887 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
890 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
893 unsigned int len = strlen(text);
895 if(len > type->max_length) len = type->max_length;
896 note = calloc(1, sizeof(*note) + len);
898 strncpy(note->setter, setter, sizeof(note->setter)-1);
899 memcpy(note->note, text, len);
901 dict_insert(channel->notes, type->name, note);
907 chanserv_free_note(void *data)
909 struct note *note = data;
911 chanserv_deref_note_type(note->type);
912 assert(note->type->refs > 0); /* must use delnote to remove the type */
916 static MODCMD_FUNC(cmd_createnote) {
917 struct note_type *ntype;
918 unsigned int arg = 1, existed = 0, max_length;
920 if((ntype = dict_find(note_types, argv[1], NULL)))
923 ntype = chanserv_create_note_type(argv[arg]);
924 if(!irccasecmp(argv[++arg], "privileged"))
927 ntype->set_access_type = NOTE_SET_PRIVILEGED;
928 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
930 else if(!irccasecmp(argv[arg], "channel"))
932 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
935 reply("CSMSG_INVALID_ACCESS", argv[arg]);
938 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
939 ntype->set_access.min_ulevel = ulvl;
941 else if(!irccasecmp(argv[arg], "setter"))
943 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
947 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
951 if(!irccasecmp(argv[++arg], "privileged"))
952 ntype->visible_type = NOTE_VIS_PRIVILEGED;
953 else if(!irccasecmp(argv[arg], "channel_users"))
954 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
955 else if(!irccasecmp(argv[arg], "all"))
956 ntype->visible_type = NOTE_VIS_ALL;
958 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
962 if((arg+1) >= argc) {
963 reply("MSG_MISSING_PARAMS", argv[0]);
966 max_length = strtoul(argv[++arg], NULL, 0);
967 if(max_length < 20 || max_length > 450)
969 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
972 if(existed && (max_length < ntype->max_length))
974 ntype->max_length = max_length;
975 chanserv_truncate_notes(ntype);
977 ntype->max_length = max_length;
980 reply("CSMSG_NOTE_MODIFIED", ntype->name);
982 reply("CSMSG_NOTE_CREATED", ntype->name);
987 dict_remove(note_types, ntype->name);
991 static MODCMD_FUNC(cmd_removenote) {
992 struct note_type *ntype;
995 ntype = dict_find(note_types, argv[1], NULL);
996 force = (argc > 2) && !irccasecmp(argv[2], "force");
999 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1006 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1009 chanserv_flush_note_type(ntype);
1011 dict_remove(note_types, argv[1]);
1012 reply("CSMSG_NOTE_DELETED", argv[1]);
1017 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1021 if(orig->modes_set & change->modes_clear)
1023 if(orig->modes_clear & change->modes_set)
1025 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1026 && strcmp(orig->new_key, change->new_key))
1028 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1029 && (orig->new_limit != change->new_limit))
1034 static char max_length_text[MAXLEN+1][16];
1036 static struct helpfile_expansion
1037 chanserv_expand_variable(const char *variable)
1039 struct helpfile_expansion exp;
1041 if(!irccasecmp(variable, "notes"))
1044 exp.type = HF_TABLE;
1045 exp.value.table.length = 1;
1046 exp.value.table.width = 3;
1047 exp.value.table.flags = 0;
1048 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1049 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1050 exp.value.table.contents[0][0] = "Note Type";
1051 exp.value.table.contents[0][1] = "Visibility";
1052 exp.value.table.contents[0][2] = "Max Length";
1053 for(it=dict_first(note_types); it; it=iter_next(it))
1055 struct note_type *ntype = iter_data(it);
1058 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1059 row = exp.value.table.length++;
1060 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1061 exp.value.table.contents[row][0] = ntype->name;
1062 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1063 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1065 if(!max_length_text[ntype->max_length][0])
1066 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1067 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1072 exp.type = HF_STRING;
1073 exp.value.str = NULL;
1077 static struct chanData*
1078 register_channel(struct chanNode *cNode, char *registrar)
1080 struct chanData *channel;
1081 enum levelOption lvlOpt;
1082 enum charOption chOpt;
1084 channel = calloc(1, sizeof(struct chanData));
1086 channel->notes = dict_new();
1087 dict_set_free_data(channel->notes, chanserv_free_note);
1089 channel->registrar = strdup(registrar);
1090 channel->registered = now;
1091 channel->visited = now;
1092 channel->limitAdjusted = now;
1093 channel->ownerTransfer = now;
1094 channel->flags = CHANNEL_DEFAULT_FLAGS;
1095 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1096 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1097 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1098 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1100 channel->prev = NULL;
1101 channel->next = channelList;
1104 channelList->prev = channel;
1105 channelList = channel;
1106 registered_channels++;
1108 channel->channel = cNode;
1110 cNode->channel_info = channel;
1115 static struct userData*
1116 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1118 struct userData *ud;
1120 if(access > UL_OWNER)
1123 ud = calloc(1, sizeof(*ud));
1124 ud->channel = channel;
1125 ud->handle = handle;
1127 ud->access = access;
1128 ud->info = info ? strdup(info) : NULL;
1131 ud->next = channel->users;
1133 channel->users->prev = ud;
1134 channel->users = ud;
1136 channel->userCount++;
1140 ud->u_next = ud->handle->channels;
1142 ud->u_next->u_prev = ud;
1143 ud->handle->channels = ud;
1148 static void unregister_channel(struct chanData *channel, const char *reason);
1151 del_channel_user(struct userData *user, int do_gc)
1153 struct chanData *channel = user->channel;
1155 channel->userCount--;
1159 user->prev->next = user->next;
1161 channel->users = user->next;
1163 user->next->prev = user->prev;
1166 user->u_prev->u_next = user->u_next;
1168 user->handle->channels = user->u_next;
1170 user->u_next->u_prev = user->u_prev;
1174 if(do_gc && !channel->users && !IsProtected(channel))
1175 unregister_channel(channel, "lost all users.");
1178 static void expire_ban(void *data);
1180 static struct banData*
1181 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1184 unsigned int ii, l1, l2;
1189 bd = malloc(sizeof(struct banData));
1191 bd->channel = channel;
1193 bd->triggered = triggered;
1194 bd->expires = expires;
1196 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1198 extern const char *hidden_host_suffix;
1199 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1203 l2 = strlen(old_name);
1206 if(irccasecmp(mask + l1 - l2, old_name))
1208 new_mask = alloca(MAXLEN);
1209 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1212 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1214 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1215 bd->reason = strdup(reason);
1218 timeq_add(expires, expire_ban, bd);
1221 bd->next = channel->bans;
1223 channel->bans->prev = bd;
1225 channel->banCount++;
1232 del_channel_ban(struct banData *ban)
1234 ban->channel->banCount--;
1238 ban->prev->next = ban->next;
1240 ban->channel->bans = ban->next;
1243 ban->next->prev = ban->prev;
1246 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1255 expire_ban(void *data)
1257 struct banData *bd = data;
1258 if(!IsSuspended(bd->channel))
1260 struct banList bans;
1261 struct mod_chanmode change;
1263 bans = bd->channel->channel->banlist;
1264 mod_chanmode_init(&change);
1265 for(ii=0; ii<bans.used; ii++)
1267 if(!strcmp(bans.list[ii]->ban, bd->mask))
1270 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1271 change.args[0].u.hostmask = bd->mask;
1272 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1278 del_channel_ban(bd);
1281 static void chanserv_expire_suspension(void *data);
1284 unregister_channel(struct chanData *channel, const char *reason)
1286 struct mod_chanmode change;
1287 char msgbuf[MAXLEN];
1289 /* After channel unregistration, the following must be cleaned
1291 - Channel information.
1294 - Channel suspension data.
1295 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1301 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1305 mod_chanmode_init(&change);
1306 change.modes_clear |= MODE_REGISTERED;
1307 mod_chanmode_announce(chanserv, channel->channel, &change);
1310 while(channel->users)
1311 del_channel_user(channel->users, 0);
1313 while(channel->bans)
1314 del_channel_ban(channel->bans);
1316 free(channel->topic);
1317 free(channel->registrar);
1318 free(channel->greeting);
1319 free(channel->user_greeting);
1320 free(channel->topic_mask);
1323 channel->prev->next = channel->next;
1325 channelList = channel->next;
1328 channel->next->prev = channel->prev;
1330 if(channel->suspended)
1332 struct chanNode *cNode = channel->channel;
1333 struct suspended *suspended, *next_suspended;
1335 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1337 next_suspended = suspended->previous;
1338 free(suspended->suspender);
1339 free(suspended->reason);
1340 if(suspended->expires)
1341 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1346 cNode->channel_info = NULL;
1348 channel->channel->channel_info = NULL;
1350 dict_delete(channel->notes);
1351 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1352 if(!IsSuspended(channel))
1353 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1354 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1355 UnlockChannel(channel->channel);
1357 registered_channels--;
1361 expire_channels(UNUSED_ARG(void *data))
1363 struct chanData *channel, *next;
1364 struct userData *user;
1365 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1367 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1368 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1370 for(channel = channelList; channel; channel = next)
1372 next = channel->next;
1374 /* See if the channel can be expired. */
1375 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1376 || IsProtected(channel))
1379 /* Make sure there are no high-ranking users still in the channel. */
1380 for(user=channel->users; user; user=user->next)
1381 if(user->present && (user->access >= UL_PRESENT))
1386 /* Unregister the channel */
1387 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1388 unregister_channel(channel, "registration expired.");
1391 if(chanserv_conf.channel_expire_frequency)
1392 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1396 expire_dnrs(UNUSED_ARG(void *data))
1399 struct do_not_register *dnr;
1401 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1403 dnr = iter_data(it);
1404 if(!dnr->expires || dnr->expires > now)
1406 dict_remove(handle_dnrs, dnr->chan_name + 1);
1408 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1410 dnr = iter_data(it);
1411 if(!dnr->expires || dnr->expires > now)
1413 dict_remove(plain_dnrs, dnr->chan_name);
1415 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1417 dnr = iter_data(it);
1418 if(!dnr->expires || dnr->expires > now)
1420 dict_remove(mask_dnrs, dnr->chan_name);
1423 if(chanserv_conf.dnr_expire_frequency)
1424 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1428 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1430 char protect = channel->chOpts[chProtect];
1431 struct userData *cs_victim, *cs_aggressor;
1433 /* Don't protect if no one is to be protected, someone is attacking
1434 himself, or if the aggressor is an IRC Operator. */
1435 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1438 /* Don't protect if the victim isn't authenticated (because they
1439 can't be a channel user), unless we are to protect non-users
1441 cs_victim = GetChannelAccess(channel, victim->handle_info);
1442 if(protect != 'a' && !cs_victim)
1445 /* Protect if the aggressor isn't a user because at this point,
1446 the aggressor can only be less than or equal to the victim. */
1447 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1451 /* If the aggressor was a user, then the victim can't be helped. */
1458 if(cs_victim->access > cs_aggressor->access)
1463 if(cs_victim->access >= cs_aggressor->access)
1472 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1474 struct chanData *cData = channel->channel_info;
1475 struct userData *cs_victim;
1477 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1478 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1479 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1481 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1489 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1491 if(IsService(victim))
1493 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1497 if(protect_user(victim, user, channel->channel_info))
1499 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1506 static struct do_not_register *
1507 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1509 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1510 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1511 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1512 strcpy(dnr->reason, reason);
1514 dnr->expires = expires;
1515 if(dnr->chan_name[0] == '*')
1516 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1517 else if(strpbrk(dnr->chan_name, "*?"))
1518 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1520 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1524 static struct dnrList
1525 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1527 struct dnrList list;
1528 dict_iterator_t it, next;
1529 struct do_not_register *dnr;
1531 dnrList_init(&list);
1533 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1535 if(dnr->expires && dnr->expires <= now)
1536 dict_remove(handle_dnrs, handle);
1537 else if(list.used < max)
1538 dnrList_append(&list, dnr);
1541 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1543 if(dnr->expires && dnr->expires <= now)
1544 dict_remove(plain_dnrs, chan_name);
1545 else if(list.used < max)
1546 dnrList_append(&list, dnr);
1551 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1553 next = iter_next(it);
1554 if(!match_ircglob(chan_name, iter_key(it)))
1556 dnr = iter_data(it);
1557 if(dnr->expires && dnr->expires <= now)
1558 dict_remove(mask_dnrs, iter_key(it));
1560 dnrList_append(&list, dnr);
1567 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1569 struct userNode *user;
1570 char buf1[INTERVALLEN];
1571 char buf2[INTERVALLEN];
1575 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
1578 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
1579 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1583 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1586 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1591 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1593 struct dnrList list;
1596 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1597 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1598 dnr_print_func(list.list[ii], user);
1600 reply("CSMSG_MORE_DNRS", list.used - ii);
1605 struct do_not_register *
1606 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1608 struct dnrList list;
1609 struct do_not_register *dnr;
1611 list = chanserv_find_dnrs(chan_name, handle->handle, 1);
1612 dnr = list.used ? list.list[0] : NULL;
1617 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1619 struct do_not_register *dnr;
1620 dict_iterator_t it, next;
1621 unsigned int matches = 0;
1623 for(it = dict_first(dict); it; it = next)
1625 dnr = iter_data(it);
1626 next = iter_next(it);
1627 if(dnr->expires && dnr->expires <= now)
1629 dict_remove(dict, iter_key(it));
1632 dnr_print_func(dnr, user);
1639 static CHANSERV_FUNC(cmd_noregister)
1642 time_t expiry, duration;
1643 unsigned int matches;
1647 reply("CSMSG_DNR_SEARCH_RESULTS");
1648 matches = send_dnrs(user, handle_dnrs);
1649 matches += send_dnrs(user, plain_dnrs);
1650 matches += send_dnrs(user, mask_dnrs);
1652 reply("MSG_MATCH_COUNT", matches);
1654 reply("MSG_NO_MATCHES");
1660 if(!IsChannelName(target) && (*target != '*'))
1662 reply("CSMSG_NOT_DNR", target);
1670 reply("MSG_INVALID_DURATION", argv[2]);
1674 if(!strcmp(argv[2], "0"))
1676 else if((duration = ParseInterval(argv[2])))
1677 expiry = now + duration;
1680 reply("MSG_INVALID_DURATION", argv[2]);
1684 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1685 if((*target == '*') && !get_handle_info(target + 1))
1687 reply("MSG_HANDLE_UNKNOWN", target + 1);
1690 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1691 reply("CSMSG_NOREGISTER_CHANNEL", target);
1695 reply("CSMSG_DNR_SEARCH_RESULTS");
1697 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1699 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1701 reply("MSG_NO_MATCHES");
1705 static CHANSERV_FUNC(cmd_allowregister)
1707 const char *chan_name = argv[1];
1709 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1710 || dict_remove(plain_dnrs, chan_name)
1711 || dict_remove(mask_dnrs, chan_name))
1713 reply("CSMSG_DNR_REMOVED", chan_name);
1716 reply("CSMSG_NO_SUCH_DNR", chan_name);
1721 struct userNode *source;
1725 time_t min_set, max_set;
1726 time_t min_expires, max_expires;
1731 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1733 return !((dnr->set < search->min_set)
1734 || (dnr->set > search->max_set)
1735 || (dnr->expires && ((dnr->expires < search->min_expires)
1736 || (dnr->expires > search->max_expires)))
1737 || (search->chan_mask
1738 && !match_ircglob(search->chan_mask, dnr->chan_name))
1739 || (search->setter_mask
1740 && !match_ircglob(search->setter_mask, dnr->setter))
1741 || (search->reason_mask
1742 && !match_ircglob(search->reason_mask, dnr->reason)));
1745 static struct dnr_search *
1746 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1748 struct dnr_search *discrim;
1751 discrim = calloc(1, sizeof(*discrim));
1752 discrim->source = user;
1753 discrim->chan_mask = NULL;
1754 discrim->setter_mask = NULL;
1755 discrim->reason_mask = NULL;
1756 discrim->max_set = INT_MAX;
1757 discrim->max_expires = INT_MAX;
1758 discrim->limit = 50;
1760 for(ii=0; ii<argc; ++ii)
1764 reply("MSG_MISSING_PARAMS", argv[ii]);
1767 else if(0 == irccasecmp(argv[ii], "channel"))
1769 discrim->chan_mask = argv[++ii];
1771 else if(0 == irccasecmp(argv[ii], "setter"))
1773 discrim->setter_mask = argv[++ii];
1775 else if(0 == irccasecmp(argv[ii], "reason"))
1777 discrim->reason_mask = argv[++ii];
1779 else if(0 == irccasecmp(argv[ii], "limit"))
1781 discrim->limit = strtoul(argv[++ii], NULL, 0);
1783 else if(0 == irccasecmp(argv[ii], "set"))
1785 const char *cmp = argv[++ii];
1788 discrim->min_set = now - ParseInterval(cmp + 2);
1790 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1791 } else if(cmp[0] == '=') {
1792 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1793 } else if(cmp[0] == '>') {
1795 discrim->max_set = now - ParseInterval(cmp + 2);
1797 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1799 discrim->max_set = now - (ParseInterval(cmp) - 1);
1802 else if(0 == irccasecmp(argv[ii], "expires"))
1804 const char *cmp = argv[++ii];
1807 discrim->max_expires = now + ParseInterval(cmp + 2);
1809 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1810 } else if(cmp[0] == '=') {
1811 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1812 } else if(cmp[0] == '>') {
1814 discrim->min_expires = now + ParseInterval(cmp + 2);
1816 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1818 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1823 reply("MSG_INVALID_CRITERIA", argv[ii]);
1834 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1837 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1839 struct do_not_register *dnr;
1840 dict_iterator_t next;
1845 /* Initialize local variables. */
1848 if(discrim->chan_mask)
1850 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1851 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1855 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1857 /* Check against account-based DNRs. */
1858 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1859 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1862 else if(target_fixed)
1864 /* Check against channel-based DNRs. */
1865 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1866 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1871 /* Exhaustively search account DNRs. */
1872 for(it = dict_first(handle_dnrs); it; it = next)
1874 next = iter_next(it);
1875 dnr = iter_data(it);
1876 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1880 /* Do the same for channel DNRs. */
1881 for(it = dict_first(plain_dnrs); it; it = next)
1883 next = iter_next(it);
1884 dnr = iter_data(it);
1885 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1889 /* Do the same for wildcarded channel DNRs. */
1890 for(it = dict_first(mask_dnrs); it; it = next)
1892 next = iter_next(it);
1893 dnr = iter_data(it);
1894 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1902 dnr_remove_func(struct do_not_register *match, void *extra)
1904 struct userNode *user;
1907 chan_name = alloca(strlen(match->chan_name) + 1);
1908 strcpy(chan_name, match->chan_name);
1910 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1911 || dict_remove(plain_dnrs, chan_name)
1912 || dict_remove(mask_dnrs, chan_name))
1914 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1919 static MODCMD_FUNC(cmd_dnrsearch)
1921 struct dnr_search *discrim;
1922 dnr_search_func action;
1923 struct svccmd *subcmd;
1924 unsigned int matches;
1927 sprintf(buf, "dnrsearch %s", argv[1]);
1928 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1931 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1934 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1936 if(!irccasecmp(argv[1], "print"))
1937 action = dnr_print_func;
1938 else if(!irccasecmp(argv[1], "remove"))
1939 action = dnr_remove_func;
1942 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1946 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1950 if(action == dnr_print_func)
1951 reply("CSMSG_DNR_SEARCH_RESULTS");
1952 matches = dnr_search(discrim, action, user);
1954 reply("MSG_MATCH_COUNT", matches);
1956 reply("MSG_NO_MATCHES");
1962 chanserv_get_owned_count(struct handle_info *hi)
1964 struct userData *cList;
1967 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1968 if(cList->access == UL_OWNER)
1973 static CHANSERV_FUNC(cmd_register)
1975 struct handle_info *handle;
1976 struct chanData *cData;
1977 struct modeNode *mn;
1978 char reason[MAXLEN];
1980 unsigned int new_channel, force=0;
1981 struct do_not_register *dnr;
1985 if(channel->channel_info)
1987 reply("CSMSG_ALREADY_REGGED", channel->name);
1991 if(channel->bad_channel)
1993 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
1998 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2000 reply("CSMSG_MUST_BE_OPPED", channel->name);
2005 chan_name = channel->name;
2009 if((argc < 2) || !IsChannelName(argv[1]))
2011 reply("MSG_NOT_CHANNEL_NAME");
2015 if(opserv_bad_channel(argv[1]))
2017 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2022 chan_name = argv[1];
2025 if(argc >= (new_channel+2))
2027 if(!IsHelping(user))
2029 reply("CSMSG_PROXY_FORBIDDEN");
2033 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2035 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2036 dnr = chanserv_is_dnr(chan_name, handle);
2040 handle = user->handle_info;
2041 dnr = chanserv_is_dnr(chan_name, handle);
2045 if(!IsHelping(user))
2046 reply("CSMSG_DNR_CHANNEL", chan_name);
2048 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2052 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2054 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2059 channel = AddChannel(argv[1], now, NULL, NULL);
2061 cData = register_channel(channel, user->handle_info->handle);
2062 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2063 cData->modes = chanserv_conf.default_modes;
2065 cData->modes.modes_set |= MODE_REGISTERED;
2066 if (IsOffChannel(cData))
2068 mod_chanmode_announce(chanserv, channel, &cData->modes);
2072 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2073 change->args[change->argc].mode = MODE_CHANOP;
2074 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2076 mod_chanmode_announce(chanserv, channel, change);
2077 mod_chanmode_free(change);
2080 /* Initialize the channel's max user record. */
2081 cData->max = channel->members.used;
2083 if(handle != user->handle_info)
2084 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2086 reply("CSMSG_REG_SUCCESS", channel->name);
2088 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2089 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2094 make_confirmation_string(struct userData *uData)
2096 static char strbuf[16];
2101 for(src = uData->handle->handle; *src; )
2102 accum = accum * 31 + toupper(*src++);
2104 for(src = uData->channel->channel->name; *src; )
2105 accum = accum * 31 + toupper(*src++);
2106 sprintf(strbuf, "%08x", accum);
2110 static CHANSERV_FUNC(cmd_unregister)
2113 char reason[MAXLEN];
2114 struct chanData *cData;
2115 struct userData *uData;
2117 cData = channel->channel_info;
2120 reply("CSMSG_NOT_REGISTERED", channel->name);
2124 uData = GetChannelUser(cData, user->handle_info);
2125 if(!uData || (uData->access < UL_OWNER))
2127 reply("CSMSG_NO_ACCESS");
2131 if(IsProtected(cData))
2133 reply("CSMSG_UNREG_NODELETE", channel->name);
2137 if(!IsHelping(user))
2139 const char *confirm_string;
2140 if(IsSuspended(cData))
2142 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2145 confirm_string = make_confirmation_string(uData);
2146 if((argc < 2) || strcmp(argv[1], confirm_string))
2148 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2153 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2154 name = strdup(channel->name);
2155 unregister_channel(cData, reason);
2156 reply("CSMSG_UNREG_SUCCESS", name);
2161 static CHANSERV_FUNC(cmd_move)
2163 struct mod_chanmode change;
2164 struct chanNode *target;
2165 struct modeNode *mn;
2166 struct userData *uData;
2167 char reason[MAXLEN];
2168 struct do_not_register *dnr;
2172 if(IsProtected(channel->channel_info))
2174 reply("CSMSG_MOVE_NODELETE", channel->name);
2178 if(!IsChannelName(argv[1]))
2180 reply("MSG_NOT_CHANNEL_NAME");
2184 if(opserv_bad_channel(argv[1]))
2186 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2190 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2192 for(uData = channel->channel_info->users; uData; uData = uData->next)
2194 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2196 if(!IsHelping(user))
2197 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2199 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2205 mod_chanmode_init(&change);
2206 if(!(target = GetChannel(argv[1])))
2208 target = AddChannel(argv[1], now, NULL, NULL);
2209 if(!IsSuspended(channel->channel_info))
2210 AddChannelUser(chanserv, target);
2212 else if(target->channel_info)
2214 reply("CSMSG_ALREADY_REGGED", target->name);
2217 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2218 && !IsHelping(user))
2220 reply("CSMSG_MUST_BE_OPPED", target->name);
2223 else if(!IsSuspended(channel->channel_info))
2226 change.args[0].mode = MODE_CHANOP;
2227 change.args[0].u.member = AddChannelUser(chanserv, target);
2228 mod_chanmode_announce(chanserv, target, &change);
2233 /* Clear MODE_REGISTERED from old channel, add it to new. */
2235 change.modes_clear = MODE_REGISTERED;
2236 mod_chanmode_announce(chanserv, channel, &change);
2237 change.modes_clear = 0;
2238 change.modes_set = MODE_REGISTERED;
2239 mod_chanmode_announce(chanserv, target, &change);
2242 /* Move the channel_info to the target channel; it
2243 shouldn't be necessary to clear timeq callbacks
2244 for the old channel. */
2245 target->channel_info = channel->channel_info;
2246 target->channel_info->channel = target;
2247 channel->channel_info = NULL;
2249 reply("CSMSG_MOVE_SUCCESS", target->name);
2251 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2252 if(!IsSuspended(target->channel_info))
2254 char reason2[MAXLEN];
2255 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2256 DelChannelUser(chanserv, channel, reason2, 0);
2258 UnlockChannel(channel);
2259 LockChannel(target);
2260 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2265 merge_users(struct chanData *source, struct chanData *target)
2267 struct userData *suData, *tuData, *next;
2273 /* Insert the source's users into the scratch area. */
2274 for(suData = source->users; suData; suData = suData->next)
2275 dict_insert(merge, suData->handle->handle, suData);
2277 /* Iterate through the target's users, looking for
2278 users common to both channels. The lower access is
2279 removed from either the scratch area or target user
2281 for(tuData = target->users; tuData; tuData = next)
2283 struct userData *choice;
2285 next = tuData->next;
2287 /* If a source user exists with the same handle as a target
2288 channel's user, resolve the conflict by removing one. */
2289 suData = dict_find(merge, tuData->handle->handle, NULL);
2293 /* Pick the data we want to keep. */
2294 /* If the access is the same, use the later seen time. */
2295 if(suData->access == tuData->access)
2296 choice = (suData->seen > tuData->seen) ? suData : tuData;
2297 else /* Otherwise, keep the higher access level. */
2298 choice = (suData->access > tuData->access) ? suData : tuData;
2300 /* Remove the user that wasn't picked. */
2301 if(choice == tuData)
2303 dict_remove(merge, suData->handle->handle);
2304 del_channel_user(suData, 0);
2307 del_channel_user(tuData, 0);
2310 /* Move the remaining users to the target channel. */
2311 for(it = dict_first(merge); it; it = iter_next(it))
2313 suData = iter_data(it);
2315 /* Insert the user into the target channel's linked list. */
2316 suData->prev = NULL;
2317 suData->next = target->users;
2318 suData->channel = target;
2321 target->users->prev = suData;
2322 target->users = suData;
2324 /* Update the user counts for the target channel; the
2325 source counts are left alone. */
2326 target->userCount++;
2329 /* Possible to assert (source->users == NULL) here. */
2330 source->users = NULL;
2335 merge_bans(struct chanData *source, struct chanData *target)
2337 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2339 /* Hold on to the original head of the target ban list
2340 to avoid comparing source bans with source bans. */
2341 tFront = target->bans;
2343 /* Perform a totally expensive O(n*m) merge, ick. */
2344 for(sbData = source->bans; sbData; sbData = sNext)
2346 /* Flag to track whether the ban's been moved
2347 to the destination yet. */
2350 /* Possible to assert (sbData->prev == NULL) here. */
2351 sNext = sbData->next;
2353 for(tbData = tFront; tbData; tbData = tNext)
2355 tNext = tbData->next;
2357 /* Perform two comparisons between each source
2358 and target ban, conflicts are resolved by
2359 keeping the broader ban and copying the later
2360 expiration and triggered time. */
2361 if(match_ircglobs(tbData->mask, sbData->mask))
2363 /* There is a broader ban in the target channel that
2364 overrides one in the source channel; remove the
2365 source ban and break. */
2366 if(sbData->expires > tbData->expires)
2367 tbData->expires = sbData->expires;
2368 if(sbData->triggered > tbData->triggered)
2369 tbData->triggered = sbData->triggered;
2370 del_channel_ban(sbData);
2373 else if(match_ircglobs(sbData->mask, tbData->mask))
2375 /* There is a broader ban in the source channel that
2376 overrides one in the target channel; remove the
2377 target ban, fall through and move the source over. */
2378 if(tbData->expires > sbData->expires)
2379 sbData->expires = tbData->expires;
2380 if(tbData->triggered > sbData->triggered)
2381 sbData->triggered = tbData->triggered;
2382 if(tbData == tFront)
2384 del_channel_ban(tbData);
2387 /* Source bans can override multiple target bans, so
2388 we allow a source to run through this loop multiple
2389 times, but we can only move it once. */
2394 /* Remove the source ban from the source ban list. */
2396 sbData->next->prev = sbData->prev;
2398 /* Modify the source ban's associated channel. */
2399 sbData->channel = target;
2401 /* Insert the ban into the target channel's linked list. */
2402 sbData->prev = NULL;
2403 sbData->next = target->bans;
2406 target->bans->prev = sbData;
2407 target->bans = sbData;
2409 /* Update the user counts for the target channel. */
2414 /* Possible to assert (source->bans == NULL) here. */
2415 source->bans = NULL;
2419 merge_data(struct chanData *source, struct chanData *target)
2421 /* Use more recent visited and owner-transfer time; use older
2422 * registered time. Bitwise or may_opchan. Use higher max.
2423 * Do not touch last_refresh, ban count or user counts.
2425 if(source->visited > target->visited)
2426 target->visited = source->visited;
2427 if(source->registered < target->registered)
2428 target->registered = source->registered;
2429 if(source->ownerTransfer > target->ownerTransfer)
2430 target->ownerTransfer = source->ownerTransfer;
2431 if(source->may_opchan)
2432 target->may_opchan = 1;
2433 if(source->max > target->max)
2434 target->max = source->max;
2438 merge_channel(struct chanData *source, struct chanData *target)
2440 merge_users(source, target);
2441 merge_bans(source, target);
2442 merge_data(source, target);
2445 static CHANSERV_FUNC(cmd_merge)
2447 struct userData *target_user;
2448 struct chanNode *target;
2449 char reason[MAXLEN];
2453 /* Make sure the target channel exists and is registered to the user
2454 performing the command. */
2455 if(!(target = GetChannel(argv[1])))
2457 reply("MSG_INVALID_CHANNEL");
2461 if(!target->channel_info)
2463 reply("CSMSG_NOT_REGISTERED", target->name);
2467 if(IsProtected(channel->channel_info))
2469 reply("CSMSG_MERGE_NODELETE");
2473 if(IsSuspended(target->channel_info))
2475 reply("CSMSG_MERGE_SUSPENDED");
2479 if(channel == target)
2481 reply("CSMSG_MERGE_SELF");
2485 target_user = GetChannelUser(target->channel_info, user->handle_info);
2486 if(!target_user || (target_user->access < UL_OWNER))
2488 reply("CSMSG_MERGE_NOT_OWNER");
2492 /* Merge the channel structures and associated data. */
2493 merge_channel(channel->channel_info, target->channel_info);
2494 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2495 unregister_channel(channel->channel_info, reason);
2496 reply("CSMSG_MERGE_SUCCESS", target->name);
2500 static CHANSERV_FUNC(cmd_opchan)
2502 struct mod_chanmode change;
2503 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2505 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2508 channel->channel_info->may_opchan = 0;
2509 mod_chanmode_init(&change);
2511 change.args[0].mode = MODE_CHANOP;
2512 change.args[0].u.member = GetUserMode(channel, chanserv);
2513 mod_chanmode_announce(chanserv, channel, &change);
2514 reply("CSMSG_OPCHAN_DONE", channel->name);
2518 static CHANSERV_FUNC(cmd_adduser)
2520 struct userData *actee;
2521 struct userData *actor, *real_actor;
2522 struct handle_info *handle;
2523 unsigned short access, override = 0;
2527 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2529 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2533 access = user_level_from_name(argv[2], UL_OWNER);
2536 reply("CSMSG_INVALID_ACCESS", argv[2]);
2540 actor = GetChannelUser(channel->channel_info, user->handle_info);
2541 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2543 if(actor->access <= access)
2545 reply("CSMSG_NO_BUMP_ACCESS");
2549 /* Trying to add someone with equal/more access? */
2550 if (!real_actor || real_actor->access <= access)
2551 override = CMD_LOG_OVERRIDE;
2553 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2556 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2558 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2562 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2563 scan_user_presence(actee, NULL);
2564 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2565 return 1 | override;
2568 static CHANSERV_FUNC(cmd_clvl)
2570 struct handle_info *handle;
2571 struct userData *victim;
2572 struct userData *actor, *real_actor;
2573 unsigned short new_access, override = 0;
2574 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2578 actor = GetChannelUser(channel->channel_info, user->handle_info);
2579 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2581 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2584 if(handle == user->handle_info && !privileged)
2586 reply("CSMSG_NO_SELF_CLVL");
2590 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2592 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2596 if(actor->access <= victim->access && !privileged)
2598 reply("MSG_USER_OUTRANKED", handle->handle);
2602 new_access = user_level_from_name(argv[2], UL_OWNER);
2606 reply("CSMSG_INVALID_ACCESS", argv[2]);
2610 if(new_access >= actor->access && !privileged)
2612 reply("CSMSG_NO_BUMP_ACCESS");
2616 /* Trying to clvl a equal/higher user? */
2617 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2618 override = CMD_LOG_OVERRIDE;
2619 /* Trying to clvl someone to equal/higher access? */
2620 if(!real_actor || new_access >= real_actor->access)
2621 override = CMD_LOG_OVERRIDE;
2622 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2623 * If they lower their own access it's not a big problem.
2626 victim->access = new_access;
2627 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2628 return 1 | override;
2631 static CHANSERV_FUNC(cmd_deluser)
2633 struct handle_info *handle;
2634 struct userData *victim;
2635 struct userData *actor, *real_actor;
2636 unsigned short access, override = 0;
2641 actor = GetChannelUser(channel->channel_info, user->handle_info);
2642 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2644 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2647 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2649 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2655 access = user_level_from_name(argv[1], UL_OWNER);
2658 reply("CSMSG_INVALID_ACCESS", argv[1]);
2661 if(access != victim->access)
2663 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2669 access = victim->access;
2672 if((actor->access <= victim->access) && !IsHelping(user))
2674 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2678 /* If people delete themselves it is an override, but they
2679 * could've used deleteme so we don't log it as an override
2681 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2682 override = CMD_LOG_OVERRIDE;
2684 chan_name = strdup(channel->name);
2685 del_channel_user(victim, 1);
2686 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2688 return 1 | override;
2692 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2694 struct userData *actor, *real_actor, *uData, *next;
2695 unsigned int override = 0;
2697 actor = GetChannelUser(channel->channel_info, user->handle_info);
2698 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2700 if(min_access > max_access)
2702 reply("CSMSG_BAD_RANGE", min_access, max_access);
2706 if((actor->access <= max_access) && !IsHelping(user))
2708 reply("CSMSG_NO_ACCESS");
2712 if(!real_actor || real_actor->access <= max_access)
2713 override = CMD_LOG_OVERRIDE;
2715 for(uData = channel->channel_info->users; uData; uData = next)
2719 if((uData->access >= min_access)
2720 && (uData->access <= max_access)
2721 && match_ircglob(uData->handle->handle, mask))
2722 del_channel_user(uData, 1);
2725 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2726 return 1 | override;
2729 static CHANSERV_FUNC(cmd_mdelowner)
2731 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2734 static CHANSERV_FUNC(cmd_mdelcoowner)
2736 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2739 static CHANSERV_FUNC(cmd_mdelmaster)
2741 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2744 static CHANSERV_FUNC(cmd_mdelop)
2746 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2749 static CHANSERV_FUNC(cmd_mdelpeon)
2751 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2755 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2757 struct banData *bData, *next;
2758 char interval[INTERVALLEN];
2763 limit = now - duration;
2764 for(bData = channel->channel_info->bans; bData; bData = next)
2768 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2771 del_channel_ban(bData);
2775 intervalString(interval, duration, user->handle_info);
2776 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2781 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2783 struct userData *actor, *uData, *next;
2784 char interval[INTERVALLEN];
2788 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2789 if(min_access > max_access)
2791 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2795 if(!actor || actor->access <= max_access)
2797 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2802 limit = now - duration;
2803 for(uData = channel->channel_info->users; uData; uData = next)
2807 if((uData->seen > limit)
2809 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2812 if(((uData->access >= min_access) && (uData->access <= max_access))
2813 || (!max_access && (uData->access < actor->access)))
2815 del_channel_user(uData, 1);
2823 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2825 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2829 static CHANSERV_FUNC(cmd_trim)
2831 unsigned long duration;
2832 unsigned short min_level, max_level;
2837 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2838 duration = ParseInterval(argv[2]);
2841 reply("CSMSG_CANNOT_TRIM");
2845 if(!irccasecmp(argv[1], "bans"))
2847 cmd_trim_bans(user, channel, duration);
2850 else if(!irccasecmp(argv[1], "users"))
2852 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2855 else if(parse_level_range(&min_level, &max_level, argv[1]))
2857 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2860 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2862 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2867 reply("CSMSG_INVALID_TRIM", argv[1]);
2872 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2873 to the user. cmd_all takes advantage of this. */
2874 static CHANSERV_FUNC(cmd_up)
2876 struct mod_chanmode change;
2877 struct userData *uData;
2880 mod_chanmode_init(&change);
2882 change.args[0].u.member = GetUserMode(channel, user);
2883 if(!change.args[0].u.member)
2886 reply("MSG_CHANNEL_ABSENT", channel->name);
2890 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2894 reply("CSMSG_GODMODE_UP", argv[0]);
2897 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2899 change.args[0].mode = MODE_CHANOP;
2900 errmsg = "CSMSG_ALREADY_OPPED";
2902 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2904 change.args[0].mode = MODE_VOICE;
2905 errmsg = "CSMSG_ALREADY_VOICED";
2910 reply("CSMSG_NO_ACCESS");
2913 change.args[0].mode &= ~change.args[0].u.member->modes;
2914 if(!change.args[0].mode)
2917 reply(errmsg, channel->name);
2920 modcmd_chanmode_announce(&change);
2924 static CHANSERV_FUNC(cmd_down)
2926 struct mod_chanmode change;
2928 mod_chanmode_init(&change);
2930 change.args[0].u.member = GetUserMode(channel, user);
2931 if(!change.args[0].u.member)
2934 reply("MSG_CHANNEL_ABSENT", channel->name);
2938 if(!change.args[0].u.member->modes)
2941 reply("CSMSG_ALREADY_DOWN", channel->name);
2945 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2946 modcmd_chanmode_announce(&change);
2950 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)
2952 struct userData *cList;
2954 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2956 if(IsSuspended(cList->channel)
2957 || IsUserSuspended(cList)
2958 || !GetUserMode(cList->channel->channel, user))
2961 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2967 static CHANSERV_FUNC(cmd_upall)
2969 return cmd_all(CSFUNC_ARGS, cmd_up);
2972 static CHANSERV_FUNC(cmd_downall)
2974 return cmd_all(CSFUNC_ARGS, cmd_down);
2977 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2978 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2981 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)
2983 unsigned int ii, valid;
2984 struct userNode *victim;
2985 struct mod_chanmode *change;
2987 change = mod_chanmode_alloc(argc - 1);
2989 for(ii=valid=0; ++ii < argc; )
2991 if(!(victim = GetUserH(argv[ii])))
2993 change->args[valid].mode = mode;
2994 change->args[valid].u.member = GetUserMode(channel, victim);
2995 if(!change->args[valid].u.member)
2997 if(validate && !validate(user, channel, victim))
3002 change->argc = valid;
3003 if(valid < (argc-1))
3004 reply("CSMSG_PROCESS_FAILED");
3007 modcmd_chanmode_announce(change);
3008 reply(action, channel->name);
3010 mod_chanmode_free(change);
3014 static CHANSERV_FUNC(cmd_op)
3016 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3019 static CHANSERV_FUNC(cmd_deop)
3021 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3024 static CHANSERV_FUNC(cmd_voice)
3026 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3029 static CHANSERV_FUNC(cmd_devoice)
3031 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3035 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3041 for(ii=0; ii<channel->members.used; ii++)
3043 struct modeNode *mn = channel->members.list[ii];
3045 if(IsService(mn->user))
3048 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3051 if(protect_user(mn->user, user, channel->channel_info))
3055 victims[(*victimCount)++] = mn;
3061 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3063 struct userNode *victim;
3064 struct modeNode **victims;
3065 unsigned int offset, n, victimCount, duration = 0;
3066 char *reason = "Bye.", *ban, *name;
3067 char interval[INTERVALLEN];
3069 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3070 REQUIRE_PARAMS(offset);
3073 reason = unsplit_string(argv + offset, argc - offset, NULL);
3074 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3076 /* Truncate the reason to a length of TOPICLEN, as
3077 the ircd does; however, leave room for an ellipsis
3078 and the kicker's nick. */
3079 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3083 if((victim = GetUserH(argv[1])))
3085 victims = alloca(sizeof(victims[0]));
3086 victims[0] = GetUserMode(channel, victim);
3087 /* XXX: The comparison with ACTION_KICK is just because all
3088 * other actions can work on users outside the channel, and we
3089 * want to allow those (e.g. unbans) in that case. If we add
3090 * some other ejection action for in-channel users, change
3092 victimCount = victims[0] ? 1 : 0;
3094 if(IsService(victim))
3096 reply("MSG_SERVICE_IMMUNE", victim->nick);
3100 if((action == ACTION_KICK) && !victimCount)
3102 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3106 if(protect_user(victim, user, channel->channel_info))
3108 reply("CSMSG_USER_PROTECTED", victim->nick);
3112 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3113 name = victim->nick;
3117 if(!is_ircmask(argv[1]))
3119 reply("MSG_NICK_UNKNOWN", argv[1]);
3123 victims = alloca(sizeof(victims[0]) * channel->members.used);
3125 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3127 reply("CSMSG_MASK_PROTECTED", argv[1]);
3131 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3133 reply("CSMSG_LAME_MASK", argv[1]);
3137 if((action == ACTION_KICK) && (victimCount == 0))
3139 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3143 name = ban = strdup(argv[1]);
3146 /* Truncate the ban in place if necessary; we must ensure
3147 that 'ban' is a valid ban mask before sanitizing it. */
3148 sanitize_ircmask(ban);
3150 if(action & ACTION_ADD_BAN)
3152 struct banData *bData, *next;
3154 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3156 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3161 if(action & ACTION_ADD_TIMED_BAN)
3163 duration = ParseInterval(argv[2]);
3167 reply("CSMSG_DURATION_TOO_LOW");
3171 else if(duration > (86400 * 365 * 2))
3173 reply("CSMSG_DURATION_TOO_HIGH");
3179 for(bData = channel->channel_info->bans; bData; bData = next)
3181 if(match_ircglobs(bData->mask, ban))
3183 int exact = !irccasecmp(bData->mask, ban);
3185 /* The ban is redundant; there is already a ban
3186 with the same effect in place. */
3190 free(bData->reason);
3191 bData->reason = strdup(reason);
3192 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3194 reply("CSMSG_REASON_CHANGE", ban);
3198 if(exact && bData->expires)
3202 /* If the ban matches an existing one exactly,
3203 extend the expiration time if the provided
3204 duration is longer. */
3205 if(duration && ((time_t)(now + duration) > bData->expires))
3207 bData->expires = now + duration;
3218 /* Delete the expiration timeq entry and
3219 requeue if necessary. */
3220 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3223 timeq_add(bData->expires, expire_ban, bData);
3227 /* automated kickban */
3230 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3232 reply("CSMSG_BAN_ADDED", name, channel->name);
3238 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3245 if(match_ircglobs(ban, bData->mask))
3247 /* The ban we are adding makes previously existing
3248 bans redundant; silently remove them. */
3249 del_channel_ban(bData);
3253 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);
3255 name = ban = strdup(bData->mask);
3259 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3261 extern const char *hidden_host_suffix;
3262 const char *old_name = chanserv_conf.old_ban_names->list[n];
3264 unsigned int l1, l2;
3267 l2 = strlen(old_name);
3270 if(irccasecmp(ban + l1 - l2, old_name))
3272 new_mask = malloc(MAXLEN);
3273 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3275 name = ban = new_mask;
3280 if(action & ACTION_BAN)
3282 unsigned int exists;
3283 struct mod_chanmode *change;
3285 if(channel->banlist.used >= MAXBANS)
3288 reply("CSMSG_BANLIST_FULL", channel->name);
3293 exists = ChannelBanExists(channel, ban);
3294 change = mod_chanmode_alloc(victimCount + 1);
3295 for(n = 0; n < victimCount; ++n)
3297 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3298 change->args[n].u.member = victims[n];
3302 change->args[n].mode = MODE_BAN;
3303 change->args[n++].u.hostmask = ban;
3307 modcmd_chanmode_announce(change);
3309 mod_chanmode_announce(chanserv, channel, change);
3310 mod_chanmode_free(change);
3312 if(exists && (action == ACTION_BAN))
3315 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3321 if(action & ACTION_KICK)
3323 char kick_reason[MAXLEN];
3324 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3326 for(n = 0; n < victimCount; n++)
3327 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3332 /* No response, since it was automated. */
3334 else if(action & ACTION_ADD_BAN)
3337 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3339 reply("CSMSG_BAN_ADDED", name, channel->name);
3341 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3342 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3343 else if(action & ACTION_BAN)
3344 reply("CSMSG_BAN_DONE", name, channel->name);
3345 else if(action & ACTION_KICK && victimCount)
3346 reply("CSMSG_KICK_DONE", name, channel->name);
3352 static CHANSERV_FUNC(cmd_kickban)
3354 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3357 static CHANSERV_FUNC(cmd_kick)
3359 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3362 static CHANSERV_FUNC(cmd_ban)
3364 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3367 static CHANSERV_FUNC(cmd_addban)
3369 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3372 static CHANSERV_FUNC(cmd_addtimedban)
3374 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3377 static struct mod_chanmode *
3378 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3380 struct mod_chanmode *change;
3381 unsigned char *match;
3382 unsigned int ii, count;
3384 match = alloca(bans->used);
3387 for(ii = count = 0; ii < bans->used; ++ii)
3389 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3390 MATCH_USENICK | MATCH_VISIBLE);
3397 for(ii = count = 0; ii < bans->used; ++ii)
3399 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3406 change = mod_chanmode_alloc(count);
3407 for(ii = count = 0; ii < bans->used; ++ii)
3411 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3412 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3414 assert(count == change->argc);
3419 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3421 struct userNode *actee;
3427 /* may want to allow a comma delimited list of users... */
3428 if(!(actee = GetUserH(argv[1])))
3430 if(!is_ircmask(argv[1]))
3432 reply("MSG_NICK_UNKNOWN", argv[1]);
3436 mask = strdup(argv[1]);
3439 /* We don't sanitize the mask here because ircu
3441 if(action & ACTION_UNBAN)
3443 struct mod_chanmode *change;
3444 change = find_matching_bans(&channel->banlist, actee, mask);
3449 modcmd_chanmode_announce(change);
3450 for(ii = 0; ii < change->argc; ++ii)
3451 free((char*)change->args[ii].u.hostmask);
3452 mod_chanmode_free(change);
3457 if(action & ACTION_DEL_BAN)
3459 struct banData *ban, *next;
3461 ban = channel->channel_info->bans;
3465 for( ; ban && !user_matches_glob(actee, ban->mask,
3466 MATCH_USENICK | MATCH_VISIBLE);
3469 for( ; ban && !match_ircglobs(mask, ban->mask);
3474 del_channel_ban(ban);
3481 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3483 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3489 static CHANSERV_FUNC(cmd_unban)
3491 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3494 static CHANSERV_FUNC(cmd_delban)
3496 /* it doesn't necessarily have to remove the channel ban - may want
3497 to make that an option. */
3498 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3501 static CHANSERV_FUNC(cmd_unbanme)
3503 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3504 long flags = ACTION_UNBAN;
3506 /* remove permanent bans if the user has the proper access. */
3507 if(uData->access >= UL_MASTER)
3508 flags |= ACTION_DEL_BAN;
3510 argv[1] = user->nick;
3511 return unban_user(user, channel, 2, argv, cmd, flags);
3514 static CHANSERV_FUNC(cmd_unbanall)
3516 struct mod_chanmode *change;
3519 if(!channel->banlist.used)
3521 reply("CSMSG_NO_BANS", channel->name);
3525 change = mod_chanmode_alloc(channel->banlist.used);
3526 for(ii=0; ii<channel->banlist.used; ii++)
3528 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3529 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3531 modcmd_chanmode_announce(change);
3532 for(ii = 0; ii < change->argc; ++ii)
3533 free((char*)change->args[ii].u.hostmask);
3534 mod_chanmode_free(change);
3535 reply("CSMSG_BANS_REMOVED", channel->name);
3539 static CHANSERV_FUNC(cmd_open)
3541 struct mod_chanmode *change;
3544 change = find_matching_bans(&channel->banlist, user, NULL);
3546 change = mod_chanmode_alloc(0);
3547 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3548 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3549 && channel->channel_info->modes.modes_set)
3550 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3551 modcmd_chanmode_announce(change);
3552 reply("CSMSG_CHANNEL_OPENED", channel->name);
3553 for(ii = 0; ii < change->argc; ++ii)
3554 free((char*)change->args[ii].u.hostmask);
3555 mod_chanmode_free(change);
3559 static CHANSERV_FUNC(cmd_myaccess)
3561 static struct string_buffer sbuf;
3562 struct handle_info *target_handle;
3563 struct userData *uData;
3566 target_handle = user->handle_info;
3567 else if(!IsHelping(user))
3569 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3572 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3575 if(!target_handle->channels)
3577 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3581 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3582 for(uData = target_handle->channels; uData; uData = uData->u_next)
3584 struct chanData *cData = uData->channel;
3586 if(uData->access > UL_OWNER)
3588 if(IsProtected(cData)
3589 && (target_handle != user->handle_info)
3590 && !GetTrueChannelAccess(cData, user->handle_info))
3593 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3594 if(uData->flags != USER_AUTO_OP)
3595 string_buffer_append(&sbuf, ',');
3596 if(IsUserSuspended(uData))
3597 string_buffer_append(&sbuf, 's');
3598 if(IsUserAutoOp(uData))
3600 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3601 string_buffer_append(&sbuf, 'o');
3602 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3603 string_buffer_append(&sbuf, 'v');
3605 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3606 string_buffer_append(&sbuf, 'i');
3608 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3610 string_buffer_append_string(&sbuf, ")]");
3611 string_buffer_append(&sbuf, '\0');
3612 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3618 static CHANSERV_FUNC(cmd_access)
3620 struct userNode *target;
3621 struct handle_info *target_handle;
3622 struct userData *uData;
3624 char prefix[MAXLEN];
3629 target_handle = target->handle_info;
3631 else if((target = GetUserH(argv[1])))
3633 target_handle = target->handle_info;
3635 else if(argv[1][0] == '*')
3637 if(!(target_handle = get_handle_info(argv[1]+1)))
3639 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3645 reply("MSG_NICK_UNKNOWN", argv[1]);
3649 assert(target || target_handle);
3651 if(target == chanserv)
3653 reply("CSMSG_IS_CHANSERV");
3661 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3666 reply("MSG_USER_AUTHENTICATE", target->nick);
3669 reply("MSG_AUTHENTICATE");
3675 const char *epithet = NULL, *type = NULL;
3678 epithet = chanserv_conf.irc_operator_epithet;
3679 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3681 else if(IsNetworkHelper(target))
3683 epithet = chanserv_conf.network_helper_epithet;
3684 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3686 else if(IsSupportHelper(target))
3688 epithet = chanserv_conf.support_helper_epithet;
3689 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3693 if(target_handle->epithet)
3694 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3696 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3698 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3702 sprintf(prefix, "%s", target_handle->handle);
3705 if(!channel->channel_info)
3707 reply("CSMSG_NOT_REGISTERED", channel->name);
3711 helping = HANDLE_FLAGGED(target_handle, HELPING)
3712 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3713 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3715 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3716 /* To prevent possible information leaks, only show infolines
3717 * if the requestor is in the channel or it's their own
3719 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3721 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3723 /* Likewise, only say it's suspended if the user has active
3724 * access in that channel or it's their own entry. */
3725 if(IsUserSuspended(uData)
3726 && (GetChannelUser(channel->channel_info, user->handle_info)
3727 || (user->handle_info == uData->handle)))
3729 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3734 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3741 zoot_list(struct listData *list)
3743 struct userData *uData;
3744 unsigned int start, curr, highest, lowest;
3745 struct helpfile_table tmp_table;
3746 const char **temp, *msg;
3748 if(list->table.length == 1)
3751 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3753 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3754 msg = user_find_message(list->user, "MSG_NONE");
3755 send_message_type(4, list->user, list->bot, " %s", msg);
3757 tmp_table.width = list->table.width;
3758 tmp_table.flags = list->table.flags;
3759 list->table.contents[0][0] = " ";
3760 highest = list->highest;
3761 if(list->lowest != 0)
3762 lowest = list->lowest;
3763 else if(highest < 100)
3766 lowest = highest - 100;
3767 for(start = curr = 1; curr < list->table.length; )
3769 uData = list->users[curr-1];
3770 list->table.contents[curr++][0] = " ";
3771 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3774 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3776 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3777 temp = list->table.contents[--start];
3778 list->table.contents[start] = list->table.contents[0];
3779 tmp_table.contents = list->table.contents + start;
3780 tmp_table.length = curr - start;
3781 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3782 list->table.contents[start] = temp;
3784 highest = lowest - 1;
3785 lowest = (highest < 100) ? 0 : (highest - 99);
3791 def_list(struct listData *list)
3795 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3797 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3798 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3799 if(list->table.length == 1)
3801 msg = user_find_message(list->user, "MSG_NONE");
3802 send_message_type(4, list->user, list->bot, " %s", msg);
3807 userData_access_comp(const void *arg_a, const void *arg_b)
3809 const struct userData *a = *(struct userData**)arg_a;
3810 const struct userData *b = *(struct userData**)arg_b;
3812 if(a->access != b->access)
3813 res = b->access - a->access;
3815 res = irccasecmp(a->handle->handle, b->handle->handle);
3820 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3822 void (*send_list)(struct listData *);
3823 struct userData *uData;
3824 struct listData lData;
3825 unsigned int matches;
3829 lData.bot = cmd->parent->bot;
3830 lData.channel = channel;
3831 lData.lowest = lowest;
3832 lData.highest = highest;
3833 lData.search = (argc > 1) ? argv[1] : NULL;
3834 send_list = def_list;
3835 (void)zoot_list; /* since it doesn't show user levels */
3837 if(user->handle_info)
3839 switch(user->handle_info->userlist_style)
3841 case HI_STYLE_DEF: send_list = def_list; break;
3842 case HI_STYLE_ZOOT: send_list = def_list; break;
3846 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3848 for(uData = channel->channel_info->users; uData; uData = uData->next)
3850 if((uData->access < lowest)
3851 || (uData->access > highest)
3852 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3854 lData.users[matches++] = uData;
3856 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3858 lData.table.length = matches+1;
3859 lData.table.width = 4;
3860 lData.table.flags = TABLE_NO_FREE;
3861 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3862 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3863 lData.table.contents[0] = ary;
3866 ary[2] = "Last Seen";
3868 for(matches = 1; matches < lData.table.length; ++matches)
3870 struct userData *uData = lData.users[matches-1];
3871 char seen[INTERVALLEN];
3873 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3874 lData.table.contents[matches] = ary;
3875 ary[0] = strtab(uData->access);
3876 ary[1] = uData->handle->handle;
3879 else if(!uData->seen)
3882 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3883 ary[2] = strdup(ary[2]);
3884 if(IsUserSuspended(uData))
3885 ary[3] = "Suspended";
3886 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3887 ary[3] = "Vacation";
3892 for(matches = 1; matches < lData.table.length; ++matches)
3894 free((char*)lData.table.contents[matches][2]);
3895 free(lData.table.contents[matches]);
3897 free(lData.table.contents[0]);
3898 free(lData.table.contents);
3902 static CHANSERV_FUNC(cmd_users)
3904 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3907 static CHANSERV_FUNC(cmd_wlist)
3909 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3912 static CHANSERV_FUNC(cmd_clist)
3914 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3917 static CHANSERV_FUNC(cmd_mlist)
3919 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3922 static CHANSERV_FUNC(cmd_olist)
3924 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3927 static CHANSERV_FUNC(cmd_plist)
3929 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3932 static CHANSERV_FUNC(cmd_bans)
3934 struct userNode *search_u = NULL;
3935 struct helpfile_table tbl;
3936 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3937 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3938 const char *msg_never, *triggered, *expires;
3939 struct banData *ban, **bans;
3943 else if(strchr(search = argv[1], '!'))
3946 search_wilds = search[strcspn(search, "?*")];
3948 else if(!(search_u = GetUserH(search)))
3949 reply("MSG_NICK_UNKNOWN", search);
3951 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3953 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3957 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3962 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3965 bans[matches++] = ban;
3970 tbl.length = matches + 1;
3971 tbl.width = 4 + timed;
3973 tbl.flags = TABLE_NO_FREE;
3974 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3975 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3976 tbl.contents[0][0] = "Mask";
3977 tbl.contents[0][1] = "Set By";
3978 tbl.contents[0][2] = "Triggered";
3981 tbl.contents[0][3] = "Expires";
3982 tbl.contents[0][4] = "Reason";
3985 tbl.contents[0][3] = "Reason";
3988 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3990 free(tbl.contents[0]);
3995 msg_never = user_find_message(user, "MSG_NEVER");
3996 for(ii = 0; ii < matches; )
4002 else if(ban->expires)
4003 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4005 expires = msg_never;
4008 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4010 triggered = msg_never;
4012 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4013 tbl.contents[ii][0] = ban->mask;
4014 tbl.contents[ii][1] = ban->owner;
4015 tbl.contents[ii][2] = strdup(triggered);
4018 tbl.contents[ii][3] = strdup(expires);
4019 tbl.contents[ii][4] = ban->reason;
4022 tbl.contents[ii][3] = ban->reason;
4024 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4025 reply("MSG_MATCH_COUNT", matches);
4026 for(ii = 1; ii < tbl.length; ++ii)
4028 free((char*)tbl.contents[ii][2]);
4030 free((char*)tbl.contents[ii][3]);
4031 free(tbl.contents[ii]);
4033 free(tbl.contents[0]);
4039 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4041 struct chanData *cData = channel->channel_info;
4042 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4044 if(cData->topic_mask)
4045 return !match_ircglob(new_topic, cData->topic_mask);
4046 else if(cData->topic)
4047 return irccasecmp(new_topic, cData->topic);
4052 static CHANSERV_FUNC(cmd_topic)
4054 struct chanData *cData;
4057 cData = channel->channel_info;
4062 SetChannelTopic(channel, chanserv, cData->topic, 1);
4063 reply("CSMSG_TOPIC_SET", cData->topic);
4067 reply("CSMSG_NO_TOPIC", channel->name);
4071 topic = unsplit_string(argv + 1, argc - 1, NULL);
4072 /* If they say "!topic *", use an empty topic. */
4073 if((topic[0] == '*') && (topic[1] == 0))
4075 if(bad_topic(channel, user, topic))
4077 char *topic_mask = cData->topic_mask;
4080 char new_topic[TOPICLEN+1], tchar;
4081 int pos=0, starpos=-1, dpos=0, len;
4083 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4090 len = strlen(topic);
4091 if((dpos + len) > TOPICLEN)
4092 len = TOPICLEN + 1 - dpos;
4093 memcpy(new_topic+dpos, topic, len);
4097 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4098 default: new_topic[dpos++] = tchar; break;
4101 if((dpos > TOPICLEN) || tchar)
4104 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4105 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4108 new_topic[dpos] = 0;
4109 SetChannelTopic(channel, chanserv, new_topic, 1);
4111 reply("CSMSG_TOPIC_LOCKED", channel->name);
4116 SetChannelTopic(channel, chanserv, topic, 1);
4118 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4120 /* Grab the topic and save it as the default topic. */
4122 cData->topic = strdup(channel->topic);
4128 static CHANSERV_FUNC(cmd_mode)
4130 struct userData *uData;
4131 struct mod_chanmode *change;
4136 change = &channel->channel_info->modes;
4137 if(change->modes_set || change->modes_clear) {
4138 modcmd_chanmode_announce(change);
4139 reply("CSMSG_DEFAULTED_MODES", channel->name);
4141 reply("CSMSG_NO_MODES", channel->name);
4145 uData = GetChannelUser(channel->channel_info, user->handle_info);
4147 base_oplevel = MAXOPLEVEL;
4148 else if (uData->access >= UL_OWNER)
4151 base_oplevel = 1 + UL_OWNER - uData->access;
4152 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4155 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4159 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4160 && mode_lock_violated(&channel->channel_info->modes, change))
4163 mod_chanmode_format(&channel->channel_info->modes, modes);
4164 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4168 modcmd_chanmode_announce(change);
4169 mod_chanmode_free(change);
4170 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4174 static CHANSERV_FUNC(cmd_invite)
4176 struct userData *uData;
4177 struct userNode *invite;
4179 uData = GetChannelUser(channel->channel_info, user->handle_info);
4183 if(!(invite = GetUserH(argv[1])))
4185 reply("MSG_NICK_UNKNOWN", argv[1]);
4192 if(GetUserMode(channel, invite))
4194 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4202 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4203 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4206 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4208 irc_invite(chanserv, invite, channel);
4210 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4215 static CHANSERV_FUNC(cmd_inviteme)
4217 if(GetUserMode(channel, user))
4219 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4222 if(channel->channel_info
4223 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4225 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4228 irc_invite(cmd->parent->bot, user, channel);
4233 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4236 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4238 /* We display things based on two dimensions:
4239 * - Issue time: present or absent
4240 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4241 * (in order of precedence, so something both expired and revoked
4242 * only counts as revoked)
4244 combo = (suspended->issued ? 4 : 0)
4245 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4247 case 0: /* no issue time, indefinite expiration */
4248 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4250 case 1: /* no issue time, expires in future */
4251 intervalString(buf1, suspended->expires-now, user->handle_info);
4252 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4254 case 2: /* no issue time, expired */
4255 intervalString(buf1, now-suspended->expires, user->handle_info);
4256 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4258 case 3: /* no issue time, revoked */
4259 intervalString(buf1, now-suspended->revoked, user->handle_info);
4260 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4262 case 4: /* issue time set, indefinite expiration */
4263 intervalString(buf1, now-suspended->issued, user->handle_info);
4264 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4266 case 5: /* issue time set, expires in future */
4267 intervalString(buf1, now-suspended->issued, user->handle_info);
4268 intervalString(buf2, suspended->expires-now, user->handle_info);
4269 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4271 case 6: /* issue time set, expired */
4272 intervalString(buf1, now-suspended->issued, user->handle_info);
4273 intervalString(buf2, now-suspended->expires, user->handle_info);
4274 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4276 case 7: /* issue time set, revoked */
4277 intervalString(buf1, now-suspended->issued, user->handle_info);
4278 intervalString(buf2, now-suspended->revoked, user->handle_info);
4279 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4282 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4287 static CHANSERV_FUNC(cmd_info)
4289 char modes[MAXLEN], buffer[INTERVALLEN];
4290 struct userData *uData, *owner;
4291 struct chanData *cData;
4292 struct do_not_register *dnr;
4297 cData = channel->channel_info;
4298 reply("CSMSG_CHANNEL_INFO", channel->name);
4300 uData = GetChannelUser(cData, user->handle_info);
4301 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4303 mod_chanmode_format(&cData->modes, modes);
4304 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4305 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4308 for(it = dict_first(cData->notes); it; it = iter_next(it))
4312 note = iter_data(it);
4313 if(!note_type_visible_to_user(cData, note->type, user))
4316 padding = PADLEN - 1 - strlen(iter_key(it));
4317 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4320 reply("CSMSG_CHANNEL_MAX", cData->max);
4321 for(owner = cData->users; owner; owner = owner->next)
4322 if(owner->access == UL_OWNER)
4323 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4324 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4325 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4326 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4328 privileged = IsStaff(user);
4330 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4331 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4332 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4334 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4335 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4337 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4339 struct suspended *suspended;
4340 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4341 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4342 show_suspension_info(cmd, user, suspended);
4344 else if(IsSuspended(cData))
4346 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4347 show_suspension_info(cmd, user, cData->suspended);
4352 static CHANSERV_FUNC(cmd_netinfo)
4354 extern time_t boot_time;
4355 extern unsigned long burst_length;
4356 char interval[INTERVALLEN];
4358 reply("CSMSG_NETWORK_INFO");
4359 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4360 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4361 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4362 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4363 reply("CSMSG_NETWORK_BANS", banCount);
4364 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4365 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4366 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4371 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4373 struct helpfile_table table;
4375 struct userNode *user;
4380 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4381 table.contents = alloca(list->used*sizeof(*table.contents));
4382 for(nn=0; nn<list->used; nn++)
4384 user = list->list[nn];
4385 if(user->modes & skip_flags)
4389 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4392 nick = alloca(strlen(user->nick)+3);
4393 sprintf(nick, "(%s)", user->nick);
4397 table.contents[table.length][0] = nick;
4400 table_send(chanserv, to->nick, 0, NULL, table);
4403 static CHANSERV_FUNC(cmd_ircops)
4405 reply("CSMSG_STAFF_OPERS");
4406 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4410 static CHANSERV_FUNC(cmd_helpers)
4412 reply("CSMSG_STAFF_HELPERS");
4413 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4417 static CHANSERV_FUNC(cmd_staff)
4419 reply("CSMSG_NETWORK_STAFF");
4420 cmd_ircops(CSFUNC_ARGS);
4421 cmd_helpers(CSFUNC_ARGS);
4425 static CHANSERV_FUNC(cmd_peek)
4427 struct modeNode *mn;
4428 char modes[MODELEN];
4430 struct helpfile_table table;
4432 irc_make_chanmode(channel, modes);
4434 reply("CSMSG_PEEK_INFO", channel->name);
4435 reply("CSMSG_PEEK_TOPIC", channel->topic);
4436 reply("CSMSG_PEEK_MODES", modes);
4437 reply("CSMSG_PEEK_USERS", channel->members.used);
4441 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4442 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4443 for(n = 0; n < channel->members.used; n++)
4445 mn = channel->members.list[n];
4446 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4448 table.contents[table.length] = alloca(sizeof(**table.contents));
4449 table.contents[table.length][0] = mn->user->nick;
4454 reply("CSMSG_PEEK_OPS");
4455 table_send(chanserv, user->nick, 0, NULL, table);
4458 reply("CSMSG_PEEK_NO_OPS");
4462 static MODCMD_FUNC(cmd_wipeinfo)
4464 struct handle_info *victim;
4465 struct userData *ud, *actor, *real_actor;
4466 unsigned int override = 0;
4469 actor = GetChannelUser(channel->channel_info, user->handle_info);
4470 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4471 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4473 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4475 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4478 if((ud->access >= actor->access) && (ud != actor))
4480 reply("MSG_USER_OUTRANKED", victim->handle);
4483 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4484 override = CMD_LOG_OVERRIDE;
4488 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4489 return 1 | override;
4492 static CHANSERV_FUNC(cmd_resync)
4494 struct mod_chanmode *changes;
4495 struct chanData *cData = channel->channel_info;
4496 unsigned int ii, used;
4498 changes = mod_chanmode_alloc(channel->members.used * 2);
4499 for(ii = used = 0; ii < channel->members.used; ++ii)
4501 struct modeNode *mn = channel->members.list[ii];
4502 struct userData *uData;
4504 if(IsService(mn->user))
4507 uData = GetChannelAccess(cData, mn->user->handle_info);
4508 if(!cData->lvlOpts[lvlGiveOps]
4509 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4511 if(!(mn->modes & MODE_CHANOP))
4513 changes->args[used].mode = MODE_CHANOP;
4514 changes->args[used++].u.member = mn;
4517 else if(!cData->lvlOpts[lvlGiveVoice]
4518 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4520 if(mn->modes & MODE_CHANOP)
4522 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4523 changes->args[used++].u.member = mn;
4525 if(!(mn->modes & MODE_VOICE))
4527 changes->args[used].mode = MODE_VOICE;
4528 changes->args[used++].u.member = mn;
4535 changes->args[used].mode = MODE_REMOVE | mn->modes;
4536 changes->args[used++].u.member = mn;
4540 changes->argc = used;
4541 modcmd_chanmode_announce(changes);
4542 mod_chanmode_free(changes);
4543 reply("CSMSG_RESYNCED_USERS", channel->name);
4547 static CHANSERV_FUNC(cmd_seen)
4549 struct userData *uData;
4550 struct handle_info *handle;
4551 char seen[INTERVALLEN];
4555 if(!irccasecmp(argv[1], chanserv->nick))
4557 reply("CSMSG_IS_CHANSERV");
4561 if(!(handle = get_handle_info(argv[1])))
4563 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4567 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4569 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4574 reply("CSMSG_USER_PRESENT", handle->handle);
4575 else if(uData->seen)
4576 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4578 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4580 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4581 reply("CSMSG_USER_VACATION", handle->handle);
4586 static MODCMD_FUNC(cmd_names)
4588 struct userNode *targ;
4589 struct userData *targData;
4590 unsigned int ii, pos;
4593 for(ii=pos=0; ii<channel->members.used; ++ii)
4595 targ = channel->members.list[ii]->user;
4596 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4599 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4602 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4606 if(IsUserSuspended(targData))
4608 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4611 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4612 reply("CSMSG_END_NAMES", channel->name);
4617 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4619 switch(ntype->visible_type)
4621 case NOTE_VIS_ALL: return 1;
4622 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4623 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4628 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4630 struct userData *uData;
4632 switch(ntype->set_access_type)
4634 case NOTE_SET_CHANNEL_ACCESS:
4635 if(!user->handle_info)
4637 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4639 return uData->access >= ntype->set_access.min_ulevel;
4640 case NOTE_SET_CHANNEL_SETTER:
4641 return check_user_level(channel, user, lvlSetters, 1, 0);
4642 case NOTE_SET_PRIVILEGED: default:
4643 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4647 static CHANSERV_FUNC(cmd_note)
4649 struct chanData *cData;
4651 struct note_type *ntype;
4653 cData = channel->channel_info;
4656 reply("CSMSG_NOT_REGISTERED", channel->name);
4660 /* If no arguments, show all visible notes for the channel. */
4666 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4668 note = iter_data(it);
4669 if(!note_type_visible_to_user(cData, note->type, user))
4672 reply("CSMSG_NOTELIST_HEADER", channel->name);
4673 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4676 reply("CSMSG_NOTELIST_END", channel->name);
4678 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4680 /* If one argument, show the named note. */
4683 if((note = dict_find(cData->notes, argv[1], NULL))
4684 && note_type_visible_to_user(cData, note->type, user))
4686 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4688 else if((ntype = dict_find(note_types, argv[1], NULL))
4689 && note_type_visible_to_user(NULL, ntype, user))
4691 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4696 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4700 /* Assume they're trying to set a note. */
4704 ntype = dict_find(note_types, argv[1], NULL);
4707 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4710 else if(note_type_settable_by_user(channel, ntype, user))
4712 note_text = unsplit_string(argv+2, argc-2, NULL);
4713 if((note = dict_find(cData->notes, argv[1], NULL)))
4714 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4715 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4716 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4718 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4720 /* The note is viewable to staff only, so return 0
4721 to keep the invocation from getting logged (or
4722 regular users can see it in !events). */
4728 reply("CSMSG_NO_ACCESS");
4735 static CHANSERV_FUNC(cmd_delnote)
4740 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4741 || !note_type_settable_by_user(channel, note->type, user))
4743 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4746 dict_remove(channel->channel_info->notes, note->type->name);
4747 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4751 static CHANSERV_FUNC(cmd_events)
4753 struct logSearch discrim;
4754 struct logReport report;
4755 unsigned int matches, limit;
4757 limit = (argc > 1) ? atoi(argv[1]) : 10;
4758 if(limit < 1 || limit > 200)
4761 memset(&discrim, 0, sizeof(discrim));
4762 discrim.masks.bot = chanserv;
4763 discrim.masks.channel_name = channel->name;
4765 discrim.masks.command = argv[2];
4766 discrim.limit = limit;
4767 discrim.max_time = INT_MAX;
4768 discrim.severities = 1 << LOG_COMMAND;
4769 report.reporter = chanserv;
4771 reply("CSMSG_EVENT_SEARCH_RESULTS");
4772 matches = log_entry_search(&discrim, log_report_entry, &report);
4774 reply("MSG_MATCH_COUNT", matches);
4776 reply("MSG_NO_MATCHES");
4780 static CHANSERV_FUNC(cmd_say)
4786 msg = unsplit_string(argv + 1, argc - 1, NULL);
4787 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4789 else if(GetUserH(argv[1]))
4792 msg = unsplit_string(argv + 2, argc - 2, NULL);
4793 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4797 reply("MSG_NOT_TARGET_NAME");
4803 static CHANSERV_FUNC(cmd_emote)
4809 /* CTCP is so annoying. */
4810 msg = unsplit_string(argv + 1, argc - 1, NULL);
4811 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4813 else if(GetUserH(argv[1]))
4815 msg = unsplit_string(argv + 2, argc - 2, NULL);
4816 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4820 reply("MSG_NOT_TARGET_NAME");
4826 struct channelList *
4827 chanserv_support_channels(void)
4829 return &chanserv_conf.support_channels;
4832 static CHANSERV_FUNC(cmd_expire)
4834 int channel_count = registered_channels;
4835 expire_channels(NULL);
4836 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4841 chanserv_expire_suspension(void *data)
4843 struct suspended *suspended = data;
4844 struct chanNode *channel;
4846 if(!suspended->expires || (now < suspended->expires))
4847 suspended->revoked = now;
4848 channel = suspended->cData->channel;
4849 suspended->cData->channel = channel;
4850 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4851 if(!IsOffChannel(suspended->cData))
4853 struct mod_chanmode change;
4854 mod_chanmode_init(&change);
4856 change.args[0].mode = MODE_CHANOP;
4857 change.args[0].u.member = AddChannelUser(chanserv, channel);
4858 mod_chanmode_announce(chanserv, channel, &change);
4862 static CHANSERV_FUNC(cmd_csuspend)
4864 struct suspended *suspended;
4865 char reason[MAXLEN];
4866 time_t expiry, duration;
4867 struct userData *uData;
4871 if(IsProtected(channel->channel_info))
4873 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4877 if(argv[1][0] == '!')
4879 else if(IsSuspended(channel->channel_info))
4881 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4882 show_suspension_info(cmd, user, channel->channel_info->suspended);
4886 if(!strcmp(argv[1], "0"))
4888 else if((duration = ParseInterval(argv[1])))
4889 expiry = now + duration;
4892 reply("MSG_INVALID_DURATION", argv[1]);
4896 unsplit_string(argv + 2, argc - 2, reason);
4898 suspended = calloc(1, sizeof(*suspended));
4899 suspended->revoked = 0;
4900 suspended->issued = now;
4901 suspended->suspender = strdup(user->handle_info->handle);
4902 suspended->expires = expiry;
4903 suspended->reason = strdup(reason);
4904 suspended->cData = channel->channel_info;
4905 suspended->previous = suspended->cData->suspended;
4906 suspended->cData->suspended = suspended;
4908 if(suspended->expires)
4909 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4911 if(IsSuspended(channel->channel_info))
4913 suspended->previous->revoked = now;
4914 if(suspended->previous->expires)
4915 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4916 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4917 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4921 /* Mark all users in channel as absent. */
4922 for(uData = channel->channel_info->users; uData; uData = uData->next)
4931 /* Mark the channel as suspended, then part. */
4932 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4933 DelChannelUser(chanserv, channel, suspended->reason, 0);
4934 reply("CSMSG_SUSPENDED", channel->name);
4935 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4936 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4941 static CHANSERV_FUNC(cmd_cunsuspend)
4943 struct suspended *suspended;
4944 char message[MAXLEN];
4946 if(!IsSuspended(channel->channel_info))
4948 reply("CSMSG_NOT_SUSPENDED", channel->name);
4952 suspended = channel->channel_info->suspended;
4954 /* Expire the suspension and join ChanServ to the channel. */
4955 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4956 chanserv_expire_suspension(suspended);
4957 reply("CSMSG_UNSUSPENDED", channel->name);
4958 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4959 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4963 typedef struct chanservSearch
4971 unsigned long flags;
4975 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4978 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4983 search = malloc(sizeof(struct chanservSearch));
4984 memset(search, 0, sizeof(*search));
4987 for(i = 0; i < argc; i++)
4989 /* Assume all criteria require arguments. */
4992 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4996 if(!irccasecmp(argv[i], "name"))
4997 search->name = argv[++i];
4998 else if(!irccasecmp(argv[i], "registrar"))
4999 search->registrar = argv[++i];
5000 else if(!irccasecmp(argv[i], "unvisited"))
5001 search->unvisited = ParseInterval(argv[++i]);
5002 else if(!irccasecmp(argv[i], "registered"))
5003 search->registered = ParseInterval(argv[++i]);
5004 else if(!irccasecmp(argv[i], "flags"))
5007 if(!irccasecmp(argv[i], "nodelete"))
5008 search->flags |= CHANNEL_NODELETE;
5009 else if(!irccasecmp(argv[i], "suspended"))
5010 search->flags |= CHANNEL_SUSPENDED;
5013 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5017 else if(!irccasecmp(argv[i], "limit"))
5018 search->limit = strtoul(argv[++i], NULL, 10);
5021 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5026 if(search->name && !strcmp(search->name, "*"))
5028 if(search->registrar && !strcmp(search->registrar, "*"))
5029 search->registrar = 0;
5038 chanserv_channel_match(struct chanData *channel, search_t search)
5040 const char *name = channel->channel->name;
5041 if((search->name && !match_ircglob(name, search->name)) ||
5042 (search->registrar && !channel->registrar) ||
5043 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5044 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5045 (search->registered && (now - channel->registered) > search->registered) ||
5046 (search->flags && ((search->flags & channel->flags) != search->flags)))
5053 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5055 struct chanData *channel;
5056 unsigned int matches = 0;
5058 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5060 if(!chanserv_channel_match(channel, search))
5070 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5075 search_print(struct chanData *channel, void *data)
5077 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5080 static CHANSERV_FUNC(cmd_search)
5083 unsigned int matches;
5084 channel_search_func action;
5088 if(!irccasecmp(argv[1], "count"))
5089 action = search_count;
5090 else if(!irccasecmp(argv[1], "print"))
5091 action = search_print;
5094 reply("CSMSG_ACTION_INVALID", argv[1]);
5098 search = chanserv_search_create(user, argc - 2, argv + 2);
5102 if(action == search_count)
5103 search->limit = INT_MAX;
5105 if(action == search_print)
5106 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5108 matches = chanserv_channel_search(search, action, user);
5111 reply("MSG_MATCH_COUNT", matches);
5113 reply("MSG_NO_MATCHES");
5119 static CHANSERV_FUNC(cmd_unvisited)
5121 struct chanData *cData;
5122 time_t interval = chanserv_conf.channel_expire_delay;
5123 char buffer[INTERVALLEN];
5124 unsigned int limit = 25, matches = 0;
5128 interval = ParseInterval(argv[1]);
5130 limit = atoi(argv[2]);
5133 intervalString(buffer, interval, user->handle_info);
5134 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5136 for(cData = channelList; cData && matches < limit; cData = cData->next)
5138 if((now - cData->visited) < interval)
5141 intervalString(buffer, now - cData->visited, user->handle_info);
5142 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5149 static MODCMD_FUNC(chan_opt_defaulttopic)
5155 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5157 reply("CSMSG_TOPIC_LOCKED", channel->name);
5161 topic = unsplit_string(argv+1, argc-1, NULL);
5163 free(channel->channel_info->topic);
5164 if(topic[0] == '*' && topic[1] == 0)
5166 topic = channel->channel_info->topic = NULL;
5170 topic = channel->channel_info->topic = strdup(topic);
5171 if(channel->channel_info->topic_mask
5172 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5173 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5175 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5178 if(channel->channel_info->topic)
5179 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5181 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5185 static MODCMD_FUNC(chan_opt_topicmask)
5189 struct chanData *cData = channel->channel_info;
5192 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5194 reply("CSMSG_TOPIC_LOCKED", channel->name);
5198 mask = unsplit_string(argv+1, argc-1, NULL);
5200 if(cData->topic_mask)
5201 free(cData->topic_mask);
5202 if(mask[0] == '*' && mask[1] == 0)
5204 cData->topic_mask = 0;
5208 cData->topic_mask = strdup(mask);
5210 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5211 else if(!match_ircglob(cData->topic, cData->topic_mask))
5212 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5216 if(channel->channel_info->topic_mask)
5217 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5219 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5223 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5227 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5231 if(greeting[0] == '*' && greeting[1] == 0)
5235 unsigned int length = strlen(greeting);
5236 if(length > chanserv_conf.greeting_length)
5238 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5241 *data = strdup(greeting);
5250 reply(name, user_find_message(user, "MSG_NONE"));
5254 static MODCMD_FUNC(chan_opt_greeting)
5256 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5259 static MODCMD_FUNC(chan_opt_usergreeting)
5261 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5264 static MODCMD_FUNC(chan_opt_modes)
5266 struct mod_chanmode *new_modes;
5267 char modes[MODELEN];
5271 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5273 reply("CSMSG_NO_ACCESS");
5276 if(argv[1][0] == '*' && argv[1][1] == 0)
5278 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5280 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5282 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5285 else if(new_modes->argc > 1)
5287 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5288 mod_chanmode_free(new_modes);
5293 channel->channel_info->modes = *new_modes;
5294 modcmd_chanmode_announce(new_modes);
5295 mod_chanmode_free(new_modes);
5299 mod_chanmode_format(&channel->channel_info->modes, modes);
5301 reply("CSMSG_SET_MODES", modes);
5303 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5307 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5309 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5311 struct chanData *cData = channel->channel_info;
5316 /* Set flag according to value. */
5317 if(enabled_string(argv[1]))
5319 cData->flags |= mask;
5322 else if(disabled_string(argv[1]))
5324 cData->flags &= ~mask;
5329 reply("MSG_INVALID_BINARY", argv[1]);
5335 /* Find current option value. */
5336 value = (cData->flags & mask) ? 1 : 0;
5340 reply(name, user_find_message(user, "MSG_ON"));
5342 reply(name, user_find_message(user, "MSG_OFF"));
5346 static MODCMD_FUNC(chan_opt_nodelete)
5348 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5350 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5354 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5357 static MODCMD_FUNC(chan_opt_dynlimit)
5359 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5362 static MODCMD_FUNC(chan_opt_offchannel)
5364 struct chanData *cData = channel->channel_info;
5369 /* Set flag according to value. */
5370 if(enabled_string(argv[1]))
5372 if(!IsOffChannel(cData))
5373 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5374 cData->flags |= CHANNEL_OFFCHANNEL;
5377 else if(disabled_string(argv[1]))
5379 if(IsOffChannel(cData))
5381 struct mod_chanmode change;
5382 mod_chanmode_init(&change);
5384 change.args[0].mode = MODE_CHANOP;
5385 change.args[0].u.member = AddChannelUser(chanserv, channel);
5386 mod_chanmode_announce(chanserv, channel, &change);
5388 cData->flags &= ~CHANNEL_OFFCHANNEL;
5393 reply("MSG_INVALID_BINARY", argv[1]);
5399 /* Find current option value. */
5400 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5404 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5406 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5410 static MODCMD_FUNC(chan_opt_defaults)
5412 struct userData *uData;
5413 struct chanData *cData;
5414 const char *confirm;
5415 enum levelOption lvlOpt;
5416 enum charOption chOpt;
5418 cData = channel->channel_info;
5419 uData = GetChannelUser(cData, user->handle_info);
5420 if(!uData || (uData->access < UL_OWNER))
5422 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5425 confirm = make_confirmation_string(uData);
5426 if((argc < 2) || strcmp(argv[1], confirm))
5428 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5431 cData->flags = CHANNEL_DEFAULT_FLAGS;
5432 cData->modes = chanserv_conf.default_modes;
5433 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5434 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5435 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5436 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5437 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5442 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5444 struct chanData *cData = channel->channel_info;
5445 struct userData *uData;
5446 unsigned short value;
5450 if(!check_user_level(channel, user, option, 1, 1))
5452 reply("CSMSG_CANNOT_SET");
5455 value = user_level_from_name(argv[1], UL_OWNER+1);
5456 if(!value && strcmp(argv[1], "0"))
5458 reply("CSMSG_INVALID_ACCESS", argv[1]);
5461 uData = GetChannelUser(cData, user->handle_info);
5462 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5464 reply("CSMSG_BAD_SETLEVEL");
5470 if(value > cData->lvlOpts[lvlGiveOps])
5472 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5477 if(value < cData->lvlOpts[lvlGiveVoice])
5479 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5484 /* This test only applies to owners, since non-owners
5485 * trying to set an option to above their level get caught
5486 * by the CSMSG_BAD_SETLEVEL test above.
5488 if(value > uData->access)
5490 reply("CSMSG_BAD_SETTERS");
5497 cData->lvlOpts[option] = value;
5499 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5503 static MODCMD_FUNC(chan_opt_enfops)
5505 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5508 static MODCMD_FUNC(chan_opt_giveops)
5510 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5513 static MODCMD_FUNC(chan_opt_enfmodes)
5515 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5518 static MODCMD_FUNC(chan_opt_enftopic)
5520 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5523 static MODCMD_FUNC(chan_opt_pubcmd)
5525 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5528 static MODCMD_FUNC(chan_opt_setters)
5530 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5533 static MODCMD_FUNC(chan_opt_ctcpusers)
5535 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5538 static MODCMD_FUNC(chan_opt_userinfo)
5540 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5543 static MODCMD_FUNC(chan_opt_givevoice)
5545 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5548 static MODCMD_FUNC(chan_opt_topicsnarf)
5550 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5553 static MODCMD_FUNC(chan_opt_inviteme)
5555 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5559 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5561 struct chanData *cData = channel->channel_info;
5562 int count = charOptions[option].count, index;
5566 index = atoi(argv[1]);
5568 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5570 reply("CSMSG_INVALID_NUMERIC", index);
5571 /* Show possible values. */
5572 for(index = 0; index < count; index++)
5573 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5577 cData->chOpts[option] = charOptions[option].values[index].value;
5581 /* Find current option value. */
5584 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5588 /* Somehow, the option value is corrupt; reset it to the default. */
5589 cData->chOpts[option] = charOptions[option].default_value;
5594 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5598 static MODCMD_FUNC(chan_opt_protect)
5600 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5603 static MODCMD_FUNC(chan_opt_toys)
5605 return channel_multiple_option(chToys, CSFUNC_ARGS);
5608 static MODCMD_FUNC(chan_opt_ctcpreaction)
5610 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5613 static MODCMD_FUNC(chan_opt_topicrefresh)
5615 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5618 static struct svccmd_list set_shows_list;
5621 handle_svccmd_unbind(struct svccmd *target) {
5623 for(ii=0; ii<set_shows_list.used; ++ii)
5624 if(target == set_shows_list.list[ii])
5625 set_shows_list.used = 0;
5628 static CHANSERV_FUNC(cmd_set)
5630 struct svccmd *subcmd;
5634 /* Check if we need to (re-)initialize set_shows_list. */
5635 if(!set_shows_list.used)
5637 if(!set_shows_list.size)
5639 set_shows_list.size = chanserv_conf.set_shows->used;
5640 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5642 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5644 const char *name = chanserv_conf.set_shows->list[ii];
5645 sprintf(buf, "%s %s", argv[0], name);
5646 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5649 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5652 svccmd_list_append(&set_shows_list, subcmd);
5658 reply("CSMSG_CHANNEL_OPTIONS");
5659 for(ii = 0; ii < set_shows_list.used; ii++)
5661 subcmd = set_shows_list.list[ii];
5662 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5667 sprintf(buf, "%s %s", argv[0], argv[1]);
5668 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5671 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5674 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5676 reply("CSMSG_NO_ACCESS");
5680 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5684 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5686 struct userData *uData;
5688 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5691 reply("CSMSG_NOT_USER", channel->name);
5697 /* Just show current option value. */
5699 else if(enabled_string(argv[1]))
5701 uData->flags |= mask;
5703 else if(disabled_string(argv[1]))
5705 uData->flags &= ~mask;
5709 reply("MSG_INVALID_BINARY", argv[1]);
5713 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5717 static MODCMD_FUNC(user_opt_noautoop)
5719 struct userData *uData;
5721 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5724 reply("CSMSG_NOT_USER", channel->name);
5727 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5728 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5730 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5733 static MODCMD_FUNC(user_opt_autoinvite)
5735 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5738 static MODCMD_FUNC(user_opt_info)
5740 struct userData *uData;
5743 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5747 /* If they got past the command restrictions (which require access)
5748 * but fail this test, we have some fool with security override on.
5750 reply("CSMSG_NOT_USER", channel->name);
5757 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5758 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5760 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5763 bp = strcspn(infoline, "\001");
5766 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5771 if(infoline[0] == '*' && infoline[1] == 0)
5774 uData->info = strdup(infoline);
5777 reply("CSMSG_USET_INFO", uData->info);
5779 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5783 struct svccmd_list uset_shows_list;
5785 static CHANSERV_FUNC(cmd_uset)
5787 struct svccmd *subcmd;
5791 /* Check if we need to (re-)initialize uset_shows_list. */
5792 if(!uset_shows_list.used)
5796 "NoAutoOp", "AutoInvite", "Info"
5799 if(!uset_shows_list.size)
5801 uset_shows_list.size = ArrayLength(options);
5802 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5804 for(ii = 0; ii < ArrayLength(options); ii++)
5806 const char *name = options[ii];
5807 sprintf(buf, "%s %s", argv[0], name);
5808 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5811 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5814 svccmd_list_append(&uset_shows_list, subcmd);
5820 /* Do this so options are presented in a consistent order. */
5821 reply("CSMSG_USER_OPTIONS");
5822 for(ii = 0; ii < uset_shows_list.used; ii++)
5823 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5827 sprintf(buf, "%s %s", argv[0], argv[1]);
5828 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5831 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5835 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5838 static CHANSERV_FUNC(cmd_giveownership)
5840 struct handle_info *new_owner_hi;
5841 struct userData *new_owner;
5842 struct userData *curr_user;
5843 struct userData *invoker;
5844 struct chanData *cData = channel->channel_info;
5845 struct do_not_register *dnr;
5846 const char *confirm;
5848 unsigned short co_access;
5849 char reason[MAXLEN];
5852 curr_user = GetChannelAccess(cData, user->handle_info);
5853 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5854 if(!curr_user || (curr_user->access != UL_OWNER))
5856 struct userData *owner = NULL;
5857 for(curr_user = channel->channel_info->users;
5859 curr_user = curr_user->next)
5861 if(curr_user->access != UL_OWNER)
5865 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5872 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5874 char delay[INTERVALLEN];
5875 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5876 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5879 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5881 if(new_owner_hi == user->handle_info)
5883 reply("CSMSG_NO_TRANSFER_SELF");
5886 new_owner = GetChannelAccess(cData, new_owner_hi);
5891 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5895 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5899 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5901 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5904 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5905 if(!IsHelping(user))
5906 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5908 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5911 invoker = GetChannelUser(cData, user->handle_info);
5912 if(invoker->access <= UL_OWNER)
5914 confirm = make_confirmation_string(curr_user);
5915 if((argc < 3) || strcmp(argv[2], confirm))
5917 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5921 if(new_owner->access >= UL_COOWNER)
5922 co_access = new_owner->access;
5924 co_access = UL_COOWNER;
5925 new_owner->access = UL_OWNER;
5927 curr_user->access = co_access;
5928 cData->ownerTransfer = now;
5929 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5930 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5931 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5935 static CHANSERV_FUNC(cmd_suspend)
5937 struct handle_info *hi;
5938 struct userData *self, *real_self, *target;
5939 unsigned int override = 0;
5942 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5943 self = GetChannelUser(channel->channel_info, user->handle_info);
5944 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5945 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5947 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5950 if(target->access >= self->access)
5952 reply("MSG_USER_OUTRANKED", hi->handle);
5955 if(target->flags & USER_SUSPENDED)
5957 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
5962 target->present = 0;
5965 if(!real_self || target->access >= real_self->access)
5966 override = CMD_LOG_OVERRIDE;
5967 target->flags |= USER_SUSPENDED;
5968 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
5969 return 1 | override;
5972 static CHANSERV_FUNC(cmd_unsuspend)
5974 struct handle_info *hi;
5975 struct userData *self, *real_self, *target;
5976 unsigned int override = 0;
5979 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
5980 self = GetChannelUser(channel->channel_info, user->handle_info);
5981 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
5982 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
5984 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
5987 if(target->access >= self->access)
5989 reply("MSG_USER_OUTRANKED", hi->handle);
5992 if(!(target->flags & USER_SUSPENDED))
5994 reply("CSMSG_NOT_SUSPENDED", hi->handle);
5997 if(!real_self || target->access >= real_self->access)
5998 override = CMD_LOG_OVERRIDE;
5999 target->flags &= ~USER_SUSPENDED;
6000 scan_user_presence(target, NULL);
6001 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6002 return 1 | override;
6005 static MODCMD_FUNC(cmd_deleteme)
6007 struct handle_info *hi;
6008 struct userData *target;
6009 const char *confirm_string;
6010 unsigned short access;
6013 hi = user->handle_info;
6014 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6016 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6019 if(target->access == UL_OWNER)
6021 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6024 confirm_string = make_confirmation_string(target);
6025 if((argc < 2) || strcmp(argv[1], confirm_string))
6027 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6030 access = target->access;
6031 channel_name = strdup(channel->name);
6032 del_channel_user(target, 1);
6033 reply("CSMSG_DELETED_YOU", access, channel_name);
6039 chanserv_refresh_topics(UNUSED_ARG(void *data))
6041 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6042 struct chanData *cData;
6045 for(cData = channelList; cData; cData = cData->next)
6047 if(IsSuspended(cData))
6049 opt = cData->chOpts[chTopicRefresh];
6052 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6055 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6056 cData->last_refresh = refresh_num;
6058 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6061 static CHANSERV_FUNC(cmd_unf)
6065 char response[MAXLEN];
6066 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6067 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6068 irc_privmsg(cmd->parent->bot, channel->name, response);
6071 reply("CSMSG_UNF_RESPONSE");
6075 static CHANSERV_FUNC(cmd_ping)
6079 char response[MAXLEN];
6080 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6081 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6082 irc_privmsg(cmd->parent->bot, channel->name, response);
6085 reply("CSMSG_PING_RESPONSE");
6089 static CHANSERV_FUNC(cmd_wut)
6093 char response[MAXLEN];
6094 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6095 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6096 irc_privmsg(cmd->parent->bot, channel->name, response);
6099 reply("CSMSG_WUT_RESPONSE");
6103 static CHANSERV_FUNC(cmd_8ball)
6105 unsigned int i, j, accum;
6110 for(i=1; i<argc; i++)
6111 for(j=0; argv[i][j]; j++)
6112 accum = (accum << 5) - accum + toupper(argv[i][j]);
6113 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6116 char response[MAXLEN];
6117 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6118 irc_privmsg(cmd->parent->bot, channel->name, response);
6121 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6125 static CHANSERV_FUNC(cmd_d)
6127 unsigned long sides, count, modifier, ii, total;
6128 char response[MAXLEN], *sep;
6132 if((count = strtoul(argv[1], &sep, 10)) < 1)
6142 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6143 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6147 else if((sep[0] == '-') && isdigit(sep[1]))
6148 modifier = strtoul(sep, NULL, 10);
6149 else if((sep[0] == '+') && isdigit(sep[1]))
6150 modifier = strtoul(sep+1, NULL, 10);
6157 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6162 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6165 for(total = ii = 0; ii < count; ++ii)
6166 total += (rand() % sides) + 1;
6169 if((count > 1) || modifier)
6171 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6172 sprintf(response, fmt, total, count, sides, modifier);
6176 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6177 sprintf(response, fmt, total, sides);
6180 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6182 send_message_type(4, user, cmd->parent->bot, "%s", response);
6186 static CHANSERV_FUNC(cmd_huggle)
6188 /* CTCP must be via PRIVMSG, never notice */
6190 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6192 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6197 chanserv_adjust_limit(void *data)
6199 struct mod_chanmode change;
6200 struct chanData *cData = data;
6201 struct chanNode *channel = cData->channel;
6204 if(IsSuspended(cData))
6207 cData->limitAdjusted = now;
6208 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6209 if(cData->modes.modes_set & MODE_LIMIT)
6211 if(limit > cData->modes.new_limit)
6212 limit = cData->modes.new_limit;
6213 else if(limit == cData->modes.new_limit)
6217 mod_chanmode_init(&change);
6218 change.modes_set = MODE_LIMIT;
6219 change.new_limit = limit;
6220 mod_chanmode_announce(chanserv, channel, &change);
6224 handle_new_channel(struct chanNode *channel)
6226 struct chanData *cData;
6228 if(!(cData = channel->channel_info))
6231 if(cData->modes.modes_set || cData->modes.modes_clear)
6232 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6234 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6235 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6238 /* Welcome to my worst nightmare. Warning: Read (or modify)
6239 the code below at your own risk. */
6241 handle_join(struct modeNode *mNode)
6243 struct mod_chanmode change;
6244 struct userNode *user = mNode->user;
6245 struct chanNode *channel = mNode->channel;
6246 struct chanData *cData;
6247 struct userData *uData = NULL;
6248 struct banData *bData;
6249 struct handle_info *handle;
6250 unsigned int modes = 0, info = 0;
6253 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6256 cData = channel->channel_info;
6257 if(channel->members.used > cData->max)
6258 cData->max = channel->members.used;
6260 /* Check for bans. If they're joining through a ban, one of two
6262 * 1: Join during a netburst, by riding the break. Kick them
6263 * unless they have ops or voice in the channel.
6264 * 2: They're allowed to join through the ban (an invite in
6265 * ircu2.10, or a +e on Hybrid, or something).
6266 * If they're not joining through a ban, and the banlist is not
6267 * full, see if they're on the banlist for the channel. If so,
6270 if(user->uplink->burst && !mNode->modes)
6273 for(ii = 0; ii < channel->banlist.used; ii++)
6275 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6277 /* Riding a netburst. Naughty. */
6278 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6284 mod_chanmode_init(&change);
6286 if(channel->banlist.used < MAXBANS)
6288 /* Not joining through a ban. */
6289 for(bData = cData->bans;
6290 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6291 bData = bData->next);
6295 char kick_reason[MAXLEN];
6296 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6298 bData->triggered = now;
6299 if(bData != cData->bans)
6301 /* Shuffle the ban to the head of the list. */
6303 bData->next->prev = bData->prev;
6305 bData->prev->next = bData->next;
6308 bData->next = cData->bans;
6311 cData->bans->prev = bData;
6312 cData->bans = bData;
6315 change.args[0].mode = MODE_BAN;
6316 change.args[0].u.hostmask = bData->mask;
6317 mod_chanmode_announce(chanserv, channel, &change);
6318 KickChannelUser(user, channel, chanserv, kick_reason);
6323 /* ChanServ will not modify the limits in join-flooded channels.
6324 It will also skip DynLimit processing when the user (or srvx)
6325 is bursting in, because there are likely more incoming. */
6326 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6327 && !user->uplink->burst
6328 && !channel->join_flooded
6329 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6331 /* The user count has begun "bumping" into the channel limit,
6332 so set a timer to raise the limit a bit. Any previous
6333 timers are removed so three incoming users within the delay
6334 results in one limit change, not three. */
6336 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6337 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6340 if(channel->join_flooded)
6342 /* don't automatically give ops or voice during a join flood */
6344 else if(cData->lvlOpts[lvlGiveOps] == 0)
6345 modes |= MODE_CHANOP;
6346 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6347 modes |= MODE_VOICE;
6349 greeting = cData->greeting;
6350 if(user->handle_info)
6352 handle = user->handle_info;
6354 if(IsHelper(user) && !IsHelping(user))
6357 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6359 if(channel == chanserv_conf.support_channels.list[ii])
6361 HANDLE_SET_FLAG(user->handle_info, HELPING);
6367 uData = GetTrueChannelAccess(cData, handle);
6368 if(uData && !IsUserSuspended(uData))
6370 /* Ops and above were handled by the above case. */
6371 if(IsUserAutoOp(uData))
6373 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6374 modes |= MODE_CHANOP;
6375 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6376 modes |= MODE_VOICE;
6378 if(uData->access >= UL_PRESENT)
6379 cData->visited = now;
6380 if(cData->user_greeting)
6381 greeting = cData->user_greeting;
6383 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6384 && ((now - uData->seen) >= chanserv_conf.info_delay)
6392 /* If user joining normally (not during burst), apply op or voice,
6393 * and send greeting/userinfo as appropriate.
6395 if(!user->uplink->burst)
6399 if(modes & MODE_CHANOP)
6400 modes &= ~MODE_VOICE;
6401 change.args[0].mode = modes;
6402 change.args[0].u.member = mNode;
6403 mod_chanmode_announce(chanserv, channel, &change);
6406 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6408 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6414 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6416 struct mod_chanmode change;
6417 struct userData *channel;
6418 unsigned int ii, jj;
6420 if(!user->handle_info)
6423 mod_chanmode_init(&change);
6425 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6427 struct chanNode *cn;
6428 struct modeNode *mn;
6429 if(IsUserSuspended(channel)
6430 || IsSuspended(channel->channel)
6431 || !(cn = channel->channel->channel))
6434 mn = GetUserMode(cn, user);
6437 if(!IsUserSuspended(channel)
6438 && IsUserAutoInvite(channel)
6439 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6441 && !user->uplink->burst)
6442 irc_invite(chanserv, user, cn);
6446 if(channel->access >= UL_PRESENT)
6447 channel->channel->visited = now;
6449 if(IsUserAutoOp(channel))
6451 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6452 change.args[0].mode = MODE_CHANOP;
6453 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6454 change.args[0].mode = MODE_VOICE;
6456 change.args[0].mode = 0;
6457 change.args[0].u.member = mn;
6458 if(change.args[0].mode)
6459 mod_chanmode_announce(chanserv, cn, &change);
6462 channel->seen = now;
6463 channel->present = 1;
6466 for(ii = 0; ii < user->channels.used; ++ii)
6468 struct chanNode *channel = user->channels.list[ii]->channel;
6469 struct banData *ban;
6471 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6472 || !channel->channel_info
6473 || IsSuspended(channel->channel_info))
6475 for(jj = 0; jj < channel->banlist.used; ++jj)
6476 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6478 if(jj < channel->banlist.used)
6480 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6482 char kick_reason[MAXLEN];
6483 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6485 change.args[0].mode = MODE_BAN;
6486 change.args[0].u.hostmask = ban->mask;
6487 mod_chanmode_announce(chanserv, channel, &change);
6488 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6489 KickChannelUser(user, channel, chanserv, kick_reason);
6490 ban->triggered = now;
6495 if(IsSupportHelper(user))
6497 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6499 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6501 HANDLE_SET_FLAG(user->handle_info, HELPING);
6509 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6511 struct chanData *cData;
6512 struct userData *uData;
6514 cData = mn->channel->channel_info;
6515 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6518 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6520 /* Allow for a bit of padding so that the limit doesn't
6521 track the user count exactly, which could get annoying. */
6522 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6524 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6525 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6529 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6531 scan_user_presence(uData, mn->user);
6533 if (uData->access >= UL_PRESENT)
6534 cData->visited = now;
6537 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6539 unsigned int ii, jj;
6540 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6542 for(jj = 0; jj < mn->user->channels.used; ++jj)
6543 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6545 if(jj < mn->user->channels.used)
6548 if(ii == chanserv_conf.support_channels.used)
6549 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6554 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6556 struct userData *uData;
6558 if(!channel->channel_info || !kicker || IsService(kicker)
6559 || (kicker == victim) || IsSuspended(channel->channel_info)
6560 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6563 if(protect_user(victim, kicker, channel->channel_info))
6565 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6566 KickChannelUser(kicker, channel, chanserv, reason);
6569 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6574 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6576 struct chanData *cData;
6578 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6581 cData = channel->channel_info;
6582 if(bad_topic(channel, user, channel->topic))
6584 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6585 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6586 SetChannelTopic(channel, chanserv, old_topic, 1);
6587 else if(cData->topic)
6588 SetChannelTopic(channel, chanserv, cData->topic, 1);
6591 /* With topicsnarf, grab the topic and save it as the default topic. */
6592 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6595 cData->topic = strdup(channel->topic);
6601 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6603 struct mod_chanmode *bounce = NULL;
6604 unsigned int bnc, ii;
6607 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6610 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6611 && mode_lock_violated(&channel->channel_info->modes, change))
6613 char correct[MAXLEN];
6614 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6615 mod_chanmode_format(&channel->channel_info->modes, correct);
6616 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6618 for(ii = bnc = 0; ii < change->argc; ++ii)
6620 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6622 const struct userNode *victim = change->args[ii].u.member->user;
6623 if(!protect_user(victim, user, channel->channel_info))
6626 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6629 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6630 bounce->args[bnc].u.member = GetUserMode(channel, user);
6631 if(bounce->args[bnc].u.member)
6635 bounce->args[bnc].mode = MODE_CHANOP;
6636 bounce->args[bnc].u.member = change->args[ii].u.member;
6638 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6640 else if(change->args[ii].mode & MODE_CHANOP)
6642 const struct userNode *victim = change->args[ii].u.member->user;
6643 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6646 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6647 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6648 bounce->args[bnc].u.member = change->args[ii].u.member;
6651 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6653 const char *ban = change->args[ii].u.hostmask;
6654 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6657 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6658 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6659 bounce->args[bnc].u.hostmask = strdup(ban);
6661 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6666 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6667 mod_chanmode_announce(chanserv, channel, bounce);
6668 for(ii = 0; ii < change->argc; ++ii)
6669 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6670 free((char*)bounce->args[ii].u.hostmask);
6671 mod_chanmode_free(bounce);
6676 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6678 struct chanNode *channel;
6679 struct banData *bData;
6680 struct mod_chanmode change;
6681 unsigned int ii, jj;
6682 char kick_reason[MAXLEN];
6684 mod_chanmode_init(&change);
6686 change.args[0].mode = MODE_BAN;
6687 for(ii = 0; ii < user->channels.used; ++ii)
6689 channel = user->channels.list[ii]->channel;
6690 /* Need not check for bans if they're opped or voiced. */
6691 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6693 /* Need not check for bans unless channel registration is active. */
6694 if(!channel->channel_info || IsSuspended(channel->channel_info))
6696 /* Look for a matching ban already on the channel. */
6697 for(jj = 0; jj < channel->banlist.used; ++jj)
6698 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6700 /* Need not act if we found one. */
6701 if(jj < channel->banlist.used)
6703 /* Look for a matching ban in this channel. */
6704 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6706 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6708 change.args[0].u.hostmask = bData->mask;
6709 mod_chanmode_announce(chanserv, channel, &change);
6710 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6711 KickChannelUser(user, channel, chanserv, kick_reason);
6712 bData->triggered = now;
6713 break; /* we don't need to check any more bans in the channel */
6718 static void handle_rename(struct handle_info *handle, const char *old_handle)
6720 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6724 dict_remove2(handle_dnrs, old_handle, 1);
6725 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6726 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6731 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6733 struct userNode *h_user;
6735 if(handle->channels)
6737 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6738 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6740 while(handle->channels)
6741 del_channel_user(handle->channels, 1);
6746 handle_server_link(UNUSED_ARG(struct server *server))
6748 struct chanData *cData;
6750 for(cData = channelList; cData; cData = cData->next)
6752 if(!IsSuspended(cData))
6753 cData->may_opchan = 1;
6754 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6755 && !cData->channel->join_flooded
6756 && ((cData->channel->limit - cData->channel->members.used)
6757 < chanserv_conf.adjust_threshold))
6759 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6760 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6766 chanserv_conf_read(void)
6770 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6771 struct mod_chanmode *change;
6772 struct string_list *strlist;
6773 struct chanNode *chan;
6776 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6778 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6781 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6782 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6783 chanserv_conf.support_channels.used = 0;
6784 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6786 for(ii = 0; ii < strlist->used; ++ii)
6788 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6791 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6793 channelList_append(&chanserv_conf.support_channels, chan);
6796 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6799 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6802 chan = AddChannel(str, now, str2, NULL);
6804 channelList_append(&chanserv_conf.support_channels, chan);
6806 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6807 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6808 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6809 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6810 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6811 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6812 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6813 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6814 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6815 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6816 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6817 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6818 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6819 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6820 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6821 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6822 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6823 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6824 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6825 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6826 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6827 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6828 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6829 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6830 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6832 NickChange(chanserv, str, 0);
6833 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6834 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6835 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6836 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6837 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6838 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6839 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6840 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6841 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6842 chanserv_conf.max_owned = str ? atoi(str) : 5;
6843 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6844 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6845 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6846 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6847 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6848 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6849 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6852 safestrncpy(mode_line, str, sizeof(mode_line));
6853 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6854 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6855 && (change->argc < 2))
6857 chanserv_conf.default_modes = *change;
6858 mod_chanmode_free(change);
6860 free_string_list(chanserv_conf.set_shows);
6861 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6863 strlist = string_list_copy(strlist);
6866 static const char *list[] = {
6867 /* free form text */
6868 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6869 /* options based on user level */
6870 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6871 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6872 /* multiple choice options */
6873 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6874 /* binary options */
6875 "DynLimit", "NoDelete",
6880 strlist = alloc_string_list(ArrayLength(list)-1);
6881 for(ii=0; list[ii]; ii++)
6882 string_list_append(strlist, strdup(list[ii]));
6884 chanserv_conf.set_shows = strlist;
6885 /* We don't look things up now, in case the list refers to options
6886 * defined by modules initialized after this point. Just mark the
6887 * function list as invalid, so it will be initialized.
6889 set_shows_list.used = 0;
6890 free_string_list(chanserv_conf.eightball);
6891 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6894 strlist = string_list_copy(strlist);
6898 strlist = alloc_string_list(4);
6899 string_list_append(strlist, strdup("Yes."));
6900 string_list_append(strlist, strdup("No."));
6901 string_list_append(strlist, strdup("Maybe so."));
6903 chanserv_conf.eightball = strlist;
6904 free_string_list(chanserv_conf.old_ban_names);
6905 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6907 strlist = string_list_copy(strlist);
6909 strlist = alloc_string_list(2);
6910 chanserv_conf.old_ban_names = strlist;
6911 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6912 off_channel = str ? atoi(str) : 0;
6916 chanserv_note_type_read(const char *key, struct record_data *rd)
6919 struct note_type *ntype;
6922 if(!(obj = GET_RECORD_OBJECT(rd)))
6924 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6927 if(!(ntype = chanserv_create_note_type(key)))
6929 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6933 /* Figure out set access */
6934 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6936 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6937 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
6939 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
6941 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
6942 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
6944 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
6946 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
6950 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
6951 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6952 ntype->set_access.min_opserv = 0;
6955 /* Figure out visibility */
6956 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
6957 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6958 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
6959 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6960 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
6961 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
6962 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
6963 ntype->visible_type = NOTE_VIS_ALL;
6965 ntype->visible_type = NOTE_VIS_PRIVILEGED;
6967 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
6968 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
6972 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
6974 struct handle_info *handle;
6975 struct userData *uData;
6976 char *seen, *inf, *flags;
6978 unsigned short access;
6980 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
6982 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
6986 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
6987 if(access > UL_OWNER)
6989 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
6993 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
6994 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
6995 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
6996 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
6997 handle = get_handle_info(key);
7000 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7004 uData = add_channel_user(chan, handle, access, last_seen, inf);
7005 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7009 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7011 struct banData *bData;
7012 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7013 time_t set_time, triggered_time, expires_time;
7015 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7017 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7021 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7022 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7023 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7024 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7025 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7026 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7027 if (!reason || !owner)
7030 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7031 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7033 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7035 expires_time = set_time + atoi(s_duration);
7039 if(!reason || (expires_time && (expires_time < now)))
7042 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7045 static struct suspended *
7046 chanserv_read_suspended(dict_t obj)
7048 struct suspended *suspended = calloc(1, sizeof(*suspended));
7052 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7053 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7054 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7055 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7056 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7057 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7058 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7059 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7060 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7061 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7066 chanserv_channel_read(const char *key, struct record_data *hir)
7068 struct suspended *suspended;
7069 struct mod_chanmode *modes;
7070 struct chanNode *cNode;
7071 struct chanData *cData;
7072 struct dict *channel, *obj;
7073 char *str, *argv[10];
7077 channel = hir->d.object;
7079 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7082 cNode = AddChannel(key, now, NULL, NULL);
7085 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7088 cData = register_channel(cNode, str);
7091 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7095 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7097 enum levelOption lvlOpt;
7098 enum charOption chOpt;
7100 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7101 cData->flags = atoi(str);
7103 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7105 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7107 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7108 else if(levelOptions[lvlOpt].old_flag)
7110 if(cData->flags & levelOptions[lvlOpt].old_flag)
7111 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7113 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7117 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7119 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7121 cData->chOpts[chOpt] = str[0];
7124 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7126 enum levelOption lvlOpt;
7127 enum charOption chOpt;
7130 cData->flags = base64toint(str, 5);
7131 count = strlen(str += 5);
7132 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7135 if(levelOptions[lvlOpt].old_flag)
7137 if(cData->flags & levelOptions[lvlOpt].old_flag)
7138 lvl = levelOptions[lvlOpt].flag_value;
7140 lvl = levelOptions[lvlOpt].default_value;
7142 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7144 case 'c': lvl = UL_COOWNER; break;
7145 case 'm': lvl = UL_MASTER; break;
7146 case 'n': lvl = UL_OWNER+1; break;
7147 case 'o': lvl = UL_OP; break;
7148 case 'p': lvl = UL_PEON; break;
7149 case 'w': lvl = UL_OWNER; break;
7150 default: lvl = 0; break;
7152 cData->lvlOpts[lvlOpt] = lvl;
7154 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7155 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7158 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7160 suspended = chanserv_read_suspended(obj);
7161 cData->suspended = suspended;
7162 suspended->cData = cData;
7163 /* We could use suspended->expires and suspended->revoked to
7164 * set the CHANNEL_SUSPENDED flag, but we don't. */
7166 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7168 suspended = calloc(1, sizeof(*suspended));
7169 suspended->issued = 0;
7170 suspended->revoked = 0;
7171 suspended->suspender = strdup(str);
7172 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7173 suspended->expires = str ? atoi(str) : 0;
7174 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7175 suspended->reason = strdup(str ? str : "No reason");
7176 suspended->previous = NULL;
7177 cData->suspended = suspended;
7178 suspended->cData = cData;
7182 cData->flags &= ~CHANNEL_SUSPENDED;
7183 suspended = NULL; /* to squelch a warning */
7186 if(IsSuspended(cData)) {
7187 if(suspended->expires > now)
7188 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7189 else if(suspended->expires)
7190 cData->flags &= ~CHANNEL_SUSPENDED;
7193 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7194 struct mod_chanmode change;
7195 mod_chanmode_init(&change);
7197 change.args[0].mode = MODE_CHANOP;
7198 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7199 mod_chanmode_announce(chanserv, cNode, &change);
7202 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7203 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7204 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7205 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7206 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7207 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7208 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7209 cData->max = str ? atoi(str) : 0;
7210 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7211 cData->greeting = str ? strdup(str) : NULL;
7212 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7213 cData->user_greeting = str ? strdup(str) : NULL;
7214 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7215 cData->topic_mask = str ? strdup(str) : NULL;
7216 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7217 cData->topic = str ? strdup(str) : NULL;
7219 if(!IsSuspended(cData)
7220 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7221 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7222 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7223 cData->modes = *modes;
7225 cData->modes.modes_set |= MODE_REGISTERED;
7226 if(cData->modes.argc > 1)
7227 cData->modes.argc = 1;
7228 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7229 mod_chanmode_free(modes);
7232 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7233 for(it = dict_first(obj); it; it = iter_next(it))
7234 user_read_helper(iter_key(it), iter_data(it), cData);
7236 if(!cData->users && !IsProtected(cData))
7238 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7239 unregister_channel(cData, "has empty user list.");
7243 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7244 for(it = dict_first(obj); it; it = iter_next(it))
7245 ban_read_helper(iter_key(it), iter_data(it), cData);
7247 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7248 for(it = dict_first(obj); it; it = iter_next(it))
7250 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7251 struct record_data *rd = iter_data(it);
7252 const char *note, *setter;
7254 if(rd->type != RECDB_OBJECT)
7256 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7260 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7262 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7264 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7268 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7269 if(!setter) setter = "<unknown>";
7270 chanserv_add_channel_note(cData, ntype, setter, note);
7278 chanserv_dnr_read(const char *key, struct record_data *hir)
7280 const char *setter, *reason, *str;
7281 struct do_not_register *dnr;
7284 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7287 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7290 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7293 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7296 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7297 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7298 if(expiry && expiry <= now)
7300 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7303 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7305 dnr->set = atoi(str);
7311 chanserv_saxdb_read(struct dict *database)
7313 struct dict *section;
7316 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7317 for(it = dict_first(section); it; it = iter_next(it))
7318 chanserv_note_type_read(iter_key(it), iter_data(it));
7320 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7321 for(it = dict_first(section); it; it = iter_next(it))
7322 chanserv_channel_read(iter_key(it), iter_data(it));
7324 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7325 for(it = dict_first(section); it; it = iter_next(it))
7326 chanserv_dnr_read(iter_key(it), iter_data(it));
7332 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7334 int high_present = 0;
7335 saxdb_start_record(ctx, KEY_USERS, 1);
7336 for(; uData; uData = uData->next)
7338 if((uData->access >= UL_PRESENT) && uData->present)
7340 saxdb_start_record(ctx, uData->handle->handle, 0);
7341 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7342 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7344 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7346 saxdb_write_string(ctx, KEY_INFO, uData->info);
7347 saxdb_end_record(ctx);
7349 saxdb_end_record(ctx);
7350 return high_present;
7354 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7358 saxdb_start_record(ctx, KEY_BANS, 1);
7359 for(; bData; bData = bData->next)
7361 saxdb_start_record(ctx, bData->mask, 0);
7362 saxdb_write_int(ctx, KEY_SET, bData->set);
7363 if(bData->triggered)
7364 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7366 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7368 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7370 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7371 saxdb_end_record(ctx);
7373 saxdb_end_record(ctx);
7377 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7379 saxdb_start_record(ctx, name, 0);
7380 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7381 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7383 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7385 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7387 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7389 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7390 saxdb_end_record(ctx);
7394 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7398 enum levelOption lvlOpt;
7399 enum charOption chOpt;
7401 saxdb_start_record(ctx, channel->channel->name, 1);
7403 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7404 saxdb_write_int(ctx, KEY_MAX, channel->max);
7406 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7407 if(channel->registrar)
7408 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7409 if(channel->greeting)
7410 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7411 if(channel->user_greeting)
7412 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7413 if(channel->topic_mask)
7414 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7415 if(channel->suspended)
7416 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7418 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7419 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7420 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7421 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7422 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7424 buf[0] = channel->chOpts[chOpt];
7426 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7428 saxdb_end_record(ctx);
7430 if(channel->modes.modes_set || channel->modes.modes_clear)
7432 mod_chanmode_format(&channel->modes, buf);
7433 saxdb_write_string(ctx, KEY_MODES, buf);
7436 high_present = chanserv_write_users(ctx, channel->users);
7437 chanserv_write_bans(ctx, channel->bans);
7439 if(dict_size(channel->notes))
7443 saxdb_start_record(ctx, KEY_NOTES, 1);
7444 for(it = dict_first(channel->notes); it; it = iter_next(it))
7446 struct note *note = iter_data(it);
7447 saxdb_start_record(ctx, iter_key(it), 0);
7448 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7449 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7450 saxdb_end_record(ctx);
7452 saxdb_end_record(ctx);
7455 if(channel->ownerTransfer)
7456 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7457 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7458 saxdb_end_record(ctx);
7462 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7466 saxdb_start_record(ctx, ntype->name, 0);
7467 switch(ntype->set_access_type)
7469 case NOTE_SET_CHANNEL_ACCESS:
7470 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7472 case NOTE_SET_CHANNEL_SETTER:
7473 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7475 case NOTE_SET_PRIVILEGED: default:
7476 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7479 switch(ntype->visible_type)
7481 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7482 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7483 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7485 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7486 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7487 saxdb_end_record(ctx);
7491 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7493 struct do_not_register *dnr;
7494 dict_iterator_t it, next;
7496 for(it = dict_first(dnrs); it; it = next)
7498 next = iter_next(it);
7499 dnr = iter_data(it);
7500 if(dnr->expires && dnr->expires <= now)
7502 dict_remove(dnrs, iter_key(it));
7505 saxdb_start_record(ctx, dnr->chan_name, 0);
7507 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7509 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7510 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7511 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7512 saxdb_end_record(ctx);
7517 chanserv_saxdb_write(struct saxdb_context *ctx)
7520 struct chanData *channel;
7523 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7524 for(it = dict_first(note_types); it; it = iter_next(it))
7525 chanserv_write_note_type(ctx, iter_data(it));
7526 saxdb_end_record(ctx);
7529 saxdb_start_record(ctx, KEY_DNR, 1);
7530 write_dnrs_helper(ctx, handle_dnrs);
7531 write_dnrs_helper(ctx, plain_dnrs);
7532 write_dnrs_helper(ctx, mask_dnrs);
7533 saxdb_end_record(ctx);
7536 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7537 for(channel = channelList; channel; channel = channel->next)
7538 chanserv_write_channel(ctx, channel);
7539 saxdb_end_record(ctx);
7545 chanserv_db_cleanup(void) {
7547 unreg_part_func(handle_part);
7549 unregister_channel(channelList, "terminating.");
7550 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7551 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7552 free(chanserv_conf.support_channels.list);
7553 dict_delete(handle_dnrs);
7554 dict_delete(plain_dnrs);
7555 dict_delete(mask_dnrs);
7556 dict_delete(note_types);
7557 free_string_list(chanserv_conf.eightball);
7558 free_string_list(chanserv_conf.old_ban_names);
7559 free_string_list(chanserv_conf.set_shows);
7560 free(set_shows_list.list);
7561 free(uset_shows_list.list);
7564 struct userData *helper = helperList;
7565 helperList = helperList->next;
7570 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7571 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7572 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7575 init_chanserv(const char *nick)
7577 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7578 conf_register_reload(chanserv_conf_read);
7582 reg_server_link_func(handle_server_link);
7583 reg_new_channel_func(handle_new_channel);
7584 reg_join_func(handle_join);
7585 reg_part_func(handle_part);
7586 reg_kick_func(handle_kick);
7587 reg_topic_func(handle_topic);
7588 reg_mode_change_func(handle_mode);
7589 reg_nick_change_func(handle_nick_change);
7590 reg_auth_func(handle_auth);
7593 reg_handle_rename_func(handle_rename);
7594 reg_unreg_func(handle_unreg);
7596 handle_dnrs = dict_new();
7597 dict_set_free_data(handle_dnrs, free);
7598 plain_dnrs = dict_new();
7599 dict_set_free_data(plain_dnrs, free);
7600 mask_dnrs = dict_new();
7601 dict_set_free_data(mask_dnrs, free);
7603 reg_svccmd_unbind_func(handle_svccmd_unbind);
7604 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7605 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7606 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7607 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7608 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7609 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7610 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7611 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7612 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7613 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7614 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7615 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7617 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7618 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7620 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7621 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7622 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7623 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7624 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7626 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7627 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7628 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7629 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7630 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7632 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7633 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7634 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7635 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7637 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7638 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7639 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7640 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7641 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7642 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7643 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7644 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7646 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7647 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7648 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7649 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7650 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7651 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7652 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7653 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7654 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7655 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7656 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7657 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7658 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7659 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7661 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7662 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7663 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7664 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7665 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7667 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7668 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7670 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7671 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7672 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7673 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7674 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7675 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7676 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7677 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7678 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7679 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7680 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7682 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7683 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7685 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7686 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7687 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7688 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7690 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7691 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7692 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7693 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7694 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7696 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7697 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7698 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7699 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7700 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7701 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7703 /* Channel options */
7704 DEFINE_CHANNEL_OPTION(defaulttopic);
7705 DEFINE_CHANNEL_OPTION(topicmask);
7706 DEFINE_CHANNEL_OPTION(greeting);
7707 DEFINE_CHANNEL_OPTION(usergreeting);
7708 DEFINE_CHANNEL_OPTION(modes);
7709 DEFINE_CHANNEL_OPTION(enfops);
7710 DEFINE_CHANNEL_OPTION(giveops);
7711 DEFINE_CHANNEL_OPTION(protect);
7712 DEFINE_CHANNEL_OPTION(enfmodes);
7713 DEFINE_CHANNEL_OPTION(enftopic);
7714 DEFINE_CHANNEL_OPTION(pubcmd);
7715 DEFINE_CHANNEL_OPTION(givevoice);
7716 DEFINE_CHANNEL_OPTION(userinfo);
7717 DEFINE_CHANNEL_OPTION(dynlimit);
7718 DEFINE_CHANNEL_OPTION(topicsnarf);
7719 DEFINE_CHANNEL_OPTION(nodelete);
7720 DEFINE_CHANNEL_OPTION(toys);
7721 DEFINE_CHANNEL_OPTION(setters);
7722 DEFINE_CHANNEL_OPTION(topicrefresh);
7723 DEFINE_CHANNEL_OPTION(ctcpusers);
7724 DEFINE_CHANNEL_OPTION(ctcpreaction);
7725 DEFINE_CHANNEL_OPTION(inviteme);
7727 DEFINE_CHANNEL_OPTION(offchannel);
7728 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7730 /* Alias set topic to set defaulttopic for compatibility. */
7731 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7734 DEFINE_USER_OPTION(noautoop);
7735 DEFINE_USER_OPTION(autoinvite);
7736 DEFINE_USER_OPTION(info);
7738 /* Alias uset autovoice to uset autoop. */
7739 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7741 note_types = dict_new();
7742 dict_set_free_data(note_types, chanserv_deref_note_type);
7745 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7746 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7747 service_register(chanserv)->trigger = '!';
7748 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7750 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7752 if(chanserv_conf.channel_expire_frequency)
7753 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7755 if(chanserv_conf.dnr_expire_frequency)
7756 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7758 if(chanserv_conf.refresh_period)
7760 time_t next_refresh;
7761 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7762 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7765 reg_exit_func(chanserv_db_cleanup);
7766 message_register_table(msgtab);