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 | CHANNEL_UNREVIEWED)
119 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
120 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
122 /* Administrative messages */
123 static const struct message_entry msgtab[] = {
124 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
126 /* Channel registration */
127 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
128 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
129 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
130 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
131 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
132 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
134 /* Do-not-register channels */
135 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
136 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
137 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
138 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
140 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
141 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
142 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
143 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
144 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
145 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
146 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
147 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
148 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
150 /* Channel unregistration */
151 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
152 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
153 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
154 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
157 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
158 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
160 /* Channel merging */
161 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
162 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
163 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
164 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
165 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
167 /* Handle unregistration */
168 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
171 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
172 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
173 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
174 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
175 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
176 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
177 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
178 { "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." },
179 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
185 /* Removing yourself from a channel. */
186 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
187 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
188 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
190 /* User management */
191 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
192 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
193 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
194 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
195 { "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." },
196 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
197 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
198 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
200 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
201 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
202 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
203 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
204 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
205 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
206 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
209 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
210 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
211 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
212 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
213 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
214 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
215 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
216 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
217 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
218 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
219 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
220 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
221 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
222 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
223 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
224 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
226 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
228 /* Channel management */
229 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
230 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
231 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
233 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
234 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
235 { "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" },
236 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
237 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
238 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
239 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
241 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
242 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
243 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
244 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
245 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
246 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
247 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
248 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
249 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
250 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
251 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
252 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
253 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
254 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
255 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
256 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
257 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
258 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
259 { "CSMSG_SET_MODES", "$bModes $b %s" },
260 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
261 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
262 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
263 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
264 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
265 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
266 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
267 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
268 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
269 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
270 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
271 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
272 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
273 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
274 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
275 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
276 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
277 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
278 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
279 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
280 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
281 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
282 { "CSMSG_USET_INFO", "$bInfo $b %s" },
284 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
285 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
286 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
287 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
288 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
289 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
290 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
291 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
292 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
294 { "CSMSG_PROTECT_NONE", "No users will be protected." },
295 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
296 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
297 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
298 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
299 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
300 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
301 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
302 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
303 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
304 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
308 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
309 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
310 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
311 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
312 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
313 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s to use this command." },
314 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
315 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
317 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
318 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
319 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
321 /* Channel userlist */
322 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
323 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
324 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
325 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
327 /* Channel note list */
328 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
329 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
330 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
331 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
332 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
333 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
334 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
335 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
336 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
337 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
338 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
339 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
340 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
341 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
342 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
344 /* Channel [un]suspension */
345 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
346 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
347 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
348 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
349 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
350 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
351 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
353 /* Access information */
354 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
355 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
356 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
357 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
358 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
359 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
360 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
361 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
362 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
363 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
364 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
365 { "CSMSG_UC_H_TITLE", "network helper" },
366 { "CSMSG_LC_H_TITLE", "support helper" },
367 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
369 /* Seen information */
370 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
371 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
372 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
373 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
375 /* Names information */
376 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
377 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
379 /* Channel information */
380 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
381 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
382 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
383 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
384 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
385 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
386 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
387 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
388 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
389 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
390 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
391 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
392 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
399 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
400 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
402 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
403 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
404 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
405 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
406 { "CSMSG_PEEK_OPS", "$bOps:$b" },
407 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
409 /* Network information */
410 { "CSMSG_NETWORK_INFO", "Network Information:" },
411 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
412 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
413 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
414 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
416 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
417 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
418 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
421 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
422 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
423 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
425 /* Channel searches */
426 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
427 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
428 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
429 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
431 /* Channel configuration */
432 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
433 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
434 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
435 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
438 { "CSMSG_USER_OPTIONS", "User Options:" },
439 { "CSMSG_USER_PROTECTED", "That user is protected." },
442 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
443 { "CSMSG_PING_RESPONSE", "Pong!" },
444 { "CSMSG_WUT_RESPONSE", "wut" },
445 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
446 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
447 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
448 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
449 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
450 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
451 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
454 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
458 /* eject_user and unban_user flags */
459 #define ACTION_KICK 0x0001
460 #define ACTION_BAN 0x0002
461 #define ACTION_ADD_BAN 0x0004
462 #define ACTION_ADD_TIMED_BAN 0x0008
463 #define ACTION_UNBAN 0x0010
464 #define ACTION_DEL_BAN 0x0020
466 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
467 #define MODELEN 40 + KEYLEN
471 #define CSFUNC_ARGS user, channel, argc, argv, cmd
473 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
474 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
475 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
476 reply("MSG_MISSING_PARAMS", argv[0]); \
480 DECLARE_LIST(dnrList, struct do_not_register *);
481 DEFINE_LIST(dnrList, struct do_not_register *);
483 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
485 struct userNode *chanserv;
488 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
489 static struct log_type *CS_LOG;
493 struct channelList support_channels;
494 struct mod_chanmode default_modes;
496 unsigned long db_backup_frequency;
497 unsigned long channel_expire_frequency;
498 unsigned long dnr_expire_frequency;
501 unsigned int adjust_delay;
502 long channel_expire_delay;
503 unsigned int nodelete_level;
505 unsigned int adjust_threshold;
506 int join_flood_threshold;
508 unsigned int greeting_length;
509 unsigned int refresh_period;
510 unsigned int giveownership_period;
512 unsigned int max_owned;
513 unsigned int max_chan_users;
514 unsigned int max_chan_bans;
515 unsigned int max_userinfo_length;
517 struct string_list *set_shows;
518 struct string_list *eightball;
519 struct string_list *old_ban_names;
521 const char *ctcp_short_ban_duration;
522 const char *ctcp_long_ban_duration;
524 const char *irc_operator_epithet;
525 const char *network_helper_epithet;
526 const char *support_helper_epithet;
531 struct userNode *user;
532 struct userNode *bot;
533 struct chanNode *channel;
535 unsigned short lowest;
536 unsigned short highest;
537 struct userData **users;
538 struct helpfile_table table;
541 enum note_access_type
543 NOTE_SET_CHANNEL_ACCESS,
544 NOTE_SET_CHANNEL_SETTER,
548 enum note_visible_type
551 NOTE_VIS_CHANNEL_USERS,
557 enum note_access_type set_access_type;
559 unsigned int min_opserv;
560 unsigned short min_ulevel;
562 enum note_visible_type visible_type;
563 unsigned int max_length;
570 struct note_type *type;
571 char setter[NICKSERV_HANDLE_LEN+1];
575 static unsigned int registered_channels;
576 static unsigned int banCount;
578 static const struct {
581 unsigned short level;
584 { "peon", "Peon", UL_PEON, '+' },
585 { "op", "Op", UL_OP, '@' },
586 { "master", "Master", UL_MASTER, '%' },
587 { "coowner", "Coowner", UL_COOWNER, '*' },
588 { "owner", "Owner", UL_OWNER, '!' },
589 { "helper", "BUG:", UL_HELPER, 'X' }
592 static const struct {
595 unsigned short default_value;
596 unsigned int old_idx;
597 unsigned int old_flag;
598 unsigned short flag_value;
600 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
601 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
602 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
603 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
604 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
605 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
606 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
607 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
608 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
609 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
610 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
613 struct charOptionValues {
616 } protectValues[] = {
617 { 'a', "CSMSG_PROTECT_ALL" },
618 { 'e', "CSMSG_PROTECT_EQUAL" },
619 { 'l', "CSMSG_PROTECT_LOWER" },
620 { 'n', "CSMSG_PROTECT_NONE" }
622 { 'd', "CSMSG_TOYS_DISABLED" },
623 { 'n', "CSMSG_TOYS_PRIVATE" },
624 { 'p', "CSMSG_TOYS_PUBLIC" }
625 }, topicRefreshValues[] = {
626 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
627 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
628 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
629 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
630 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
631 }, ctcpReactionValues[] = {
632 { 'k', "CSMSG_CTCPREACTION_KICK" },
633 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
634 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
635 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
638 static const struct {
642 unsigned int old_idx;
644 struct charOptionValues *values;
646 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
647 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
648 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
649 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
652 struct userData *helperList;
653 struct chanData *channelList;
654 static struct module *chanserv_module;
655 static unsigned int userCount;
657 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
658 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
659 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
662 user_level_from_name(const char *name, unsigned short clamp_level)
664 unsigned int level = 0, ii;
666 level = strtoul(name, NULL, 10);
667 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
668 if(!irccasecmp(name, accessLevels[ii].name))
669 level = accessLevels[ii].level;
670 if(level > clamp_level)
676 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
679 *minl = strtoul(arg, &sep, 10);
687 *maxl = strtoul(sep+1, &sep, 10);
695 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
697 struct userData *uData, **head;
699 if(!channel || !handle)
702 if(override && HANDLE_FLAGGED(handle, HELPING)
703 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
705 for(uData = helperList;
706 uData && uData->handle != handle;
707 uData = uData->next);
711 uData = calloc(1, sizeof(struct userData));
712 uData->handle = handle;
714 uData->access = UL_HELPER;
720 uData->next = helperList;
722 helperList->prev = uData;
730 for(uData = channel->users; uData; uData = uData->next)
731 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
734 head = &(channel->users);
737 if(uData && (uData != *head))
739 /* Shuffle the user to the head of whatever list he was in. */
741 uData->next->prev = uData->prev;
743 uData->prev->next = uData->next;
749 (**head).prev = uData;
756 /* Returns non-zero if user has at least the minimum access.
757 * exempt_owner is set when handling !set, so the owner can set things
760 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
762 struct userData *uData;
763 struct chanData *cData = channel->channel_info;
764 unsigned short minimum = cData->lvlOpts[opt];
767 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
770 if(minimum <= uData->access)
772 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
777 /* Scan for other users authenticated to the same handle
778 still in the channel. If so, keep them listed as present.
780 user is optional, if not null, it skips checking that userNode
781 (for the handle_part function) */
783 scan_user_presence(struct userData *uData, struct userNode *user)
787 if(IsSuspended(uData->channel)
788 || IsUserSuspended(uData)
789 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
801 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot))
803 unsigned int eflags, argc;
805 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
807 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
808 if(!channel->channel_info
809 || IsSuspended(channel->channel_info)
811 || !ircncasecmp(text, "ACTION ", 7))
813 /* Figure out the minimum level needed to CTCP the channel */
814 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
816 /* We need to enforce against them; do so. */
818 argv[0] = (char*)text;
819 argv[1] = user->nick;
821 if(GetUserMode(channel, user))
822 eflags |= ACTION_KICK;
823 switch(channel->channel_info->chOpts[chCTCPReaction]) {
824 default: case 'k': /* just do the kick */ break;
826 eflags |= ACTION_BAN;
829 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
830 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
833 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
834 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
837 argv[argc++] = bad_ctcp_reason;
838 eject_user(chanserv, channel, argc, argv, NULL, eflags);
842 chanserv_create_note_type(const char *name)
844 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
845 strcpy(ntype->name, name);
847 dict_insert(note_types, ntype->name, ntype);
852 chanserv_deref_note_type(void *data)
854 struct note_type *ntype = data;
856 if(--ntype->refs > 0)
862 chanserv_flush_note_type(struct note_type *ntype)
864 struct chanData *cData;
865 for(cData = channelList; cData; cData = cData->next)
866 dict_remove(cData->notes, ntype->name);
870 chanserv_truncate_notes(struct note_type *ntype)
872 struct chanData *cData;
874 unsigned int size = sizeof(*note) + ntype->max_length;
876 for(cData = channelList; cData; cData = cData->next) {
877 note = dict_find(cData->notes, ntype->name, NULL);
880 if(strlen(note->note) <= ntype->max_length)
882 dict_remove2(cData->notes, ntype->name, 1);
883 note = realloc(note, size);
884 note->note[ntype->max_length] = 0;
885 dict_insert(cData->notes, ntype->name, note);
889 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
892 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
895 unsigned int len = strlen(text);
897 if(len > type->max_length) len = type->max_length;
898 note = calloc(1, sizeof(*note) + len);
900 strncpy(note->setter, setter, sizeof(note->setter)-1);
901 memcpy(note->note, text, len);
903 dict_insert(channel->notes, type->name, note);
909 chanserv_free_note(void *data)
911 struct note *note = data;
913 chanserv_deref_note_type(note->type);
914 assert(note->type->refs > 0); /* must use delnote to remove the type */
918 static MODCMD_FUNC(cmd_createnote) {
919 struct note_type *ntype;
920 unsigned int arg = 1, existed = 0, max_length;
922 if((ntype = dict_find(note_types, argv[1], NULL)))
925 ntype = chanserv_create_note_type(argv[arg]);
926 if(!irccasecmp(argv[++arg], "privileged"))
929 ntype->set_access_type = NOTE_SET_PRIVILEGED;
930 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
932 else if(!irccasecmp(argv[arg], "channel"))
934 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
937 reply("CSMSG_INVALID_ACCESS", argv[arg]);
940 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
941 ntype->set_access.min_ulevel = ulvl;
943 else if(!irccasecmp(argv[arg], "setter"))
945 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
949 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
953 if(!irccasecmp(argv[++arg], "privileged"))
954 ntype->visible_type = NOTE_VIS_PRIVILEGED;
955 else if(!irccasecmp(argv[arg], "channel_users"))
956 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
957 else if(!irccasecmp(argv[arg], "all"))
958 ntype->visible_type = NOTE_VIS_ALL;
960 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
964 if((arg+1) >= argc) {
965 reply("MSG_MISSING_PARAMS", argv[0]);
968 max_length = strtoul(argv[++arg], NULL, 0);
969 if(max_length < 20 || max_length > 450)
971 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
974 if(existed && (max_length < ntype->max_length))
976 ntype->max_length = max_length;
977 chanserv_truncate_notes(ntype);
979 ntype->max_length = max_length;
982 reply("CSMSG_NOTE_MODIFIED", ntype->name);
984 reply("CSMSG_NOTE_CREATED", ntype->name);
989 dict_remove(note_types, ntype->name);
993 static MODCMD_FUNC(cmd_removenote) {
994 struct note_type *ntype;
997 ntype = dict_find(note_types, argv[1], NULL);
998 force = (argc > 2) && !irccasecmp(argv[2], "force");
1001 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1008 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1011 chanserv_flush_note_type(ntype);
1013 dict_remove(note_types, argv[1]);
1014 reply("CSMSG_NOTE_DELETED", argv[1]);
1019 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1023 if(orig->modes_set & change->modes_clear)
1025 if(orig->modes_clear & change->modes_set)
1027 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1028 && strcmp(orig->new_key, change->new_key))
1030 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1031 && (orig->new_limit != change->new_limit))
1036 static char max_length_text[MAXLEN+1][16];
1038 static struct helpfile_expansion
1039 chanserv_expand_variable(const char *variable)
1041 struct helpfile_expansion exp;
1043 if(!irccasecmp(variable, "notes"))
1046 exp.type = HF_TABLE;
1047 exp.value.table.length = 1;
1048 exp.value.table.width = 3;
1049 exp.value.table.flags = 0;
1050 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1051 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1052 exp.value.table.contents[0][0] = "Note Type";
1053 exp.value.table.contents[0][1] = "Visibility";
1054 exp.value.table.contents[0][2] = "Max Length";
1055 for(it=dict_first(note_types); it; it=iter_next(it))
1057 struct note_type *ntype = iter_data(it);
1060 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1061 row = exp.value.table.length++;
1062 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1063 exp.value.table.contents[row][0] = ntype->name;
1064 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1065 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1067 if(!max_length_text[ntype->max_length][0])
1068 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1069 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1074 exp.type = HF_STRING;
1075 exp.value.str = NULL;
1079 static struct chanData*
1080 register_channel(struct chanNode *cNode, char *registrar)
1082 struct chanData *channel;
1083 enum levelOption lvlOpt;
1084 enum charOption chOpt;
1086 channel = calloc(1, sizeof(struct chanData));
1088 channel->notes = dict_new();
1089 dict_set_free_data(channel->notes, chanserv_free_note);
1091 channel->registrar = strdup(registrar);
1092 channel->registered = now;
1093 channel->visited = now;
1094 channel->limitAdjusted = now;
1095 channel->ownerTransfer = now;
1096 channel->flags = CHANNEL_DEFAULT_FLAGS;
1097 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1098 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1099 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1100 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1102 channel->prev = NULL;
1103 channel->next = channelList;
1106 channelList->prev = channel;
1107 channelList = channel;
1108 registered_channels++;
1110 channel->channel = cNode;
1112 cNode->channel_info = channel;
1117 static struct userData*
1118 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, time_t seen, const char *info)
1120 struct userData *ud;
1122 if(access > UL_OWNER)
1125 ud = calloc(1, sizeof(*ud));
1126 ud->channel = channel;
1127 ud->handle = handle;
1129 ud->access = access;
1130 ud->info = info ? strdup(info) : NULL;
1133 ud->next = channel->users;
1135 channel->users->prev = ud;
1136 channel->users = ud;
1138 channel->userCount++;
1142 ud->u_next = ud->handle->channels;
1144 ud->u_next->u_prev = ud;
1145 ud->handle->channels = ud;
1150 static void unregister_channel(struct chanData *channel, const char *reason);
1153 del_channel_user(struct userData *user, int do_gc)
1155 struct chanData *channel = user->channel;
1157 channel->userCount--;
1161 user->prev->next = user->next;
1163 channel->users = user->next;
1165 user->next->prev = user->prev;
1168 user->u_prev->u_next = user->u_next;
1170 user->handle->channels = user->u_next;
1172 user->u_next->u_prev = user->u_prev;
1176 if(do_gc && !channel->users && !IsProtected(channel))
1177 unregister_channel(channel, "lost all users.");
1180 static void expire_ban(void *data);
1182 static struct banData*
1183 add_channel_ban(struct chanData *channel, const char *mask, char *owner, time_t set, time_t triggered, time_t expires, char *reason)
1186 unsigned int ii, l1, l2;
1191 bd = malloc(sizeof(struct banData));
1193 bd->channel = channel;
1195 bd->triggered = triggered;
1196 bd->expires = expires;
1198 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1200 extern const char *hidden_host_suffix;
1201 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1205 l2 = strlen(old_name);
1208 if(irccasecmp(mask + l1 - l2, old_name))
1210 new_mask = alloca(MAXLEN);
1211 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1214 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1216 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1217 bd->reason = strdup(reason);
1220 timeq_add(expires, expire_ban, bd);
1223 bd->next = channel->bans;
1225 channel->bans->prev = bd;
1227 channel->banCount++;
1234 del_channel_ban(struct banData *ban)
1236 ban->channel->banCount--;
1240 ban->prev->next = ban->next;
1242 ban->channel->bans = ban->next;
1245 ban->next->prev = ban->prev;
1248 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1257 expire_ban(void *data)
1259 struct banData *bd = data;
1260 if(!IsSuspended(bd->channel))
1262 struct banList bans;
1263 struct mod_chanmode change;
1265 bans = bd->channel->channel->banlist;
1266 mod_chanmode_init(&change);
1267 for(ii=0; ii<bans.used; ii++)
1269 if(!strcmp(bans.list[ii]->ban, bd->mask))
1272 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1273 change.args[0].u.hostmask = bd->mask;
1274 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1280 del_channel_ban(bd);
1283 static void chanserv_expire_suspension(void *data);
1286 unregister_channel(struct chanData *channel, const char *reason)
1288 struct mod_chanmode change;
1289 char msgbuf[MAXLEN];
1291 /* After channel unregistration, the following must be cleaned
1293 - Channel information.
1296 - Channel suspension data.
1297 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1303 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1307 mod_chanmode_init(&change);
1308 change.modes_clear |= MODE_REGISTERED;
1309 mod_chanmode_announce(chanserv, channel->channel, &change);
1312 while(channel->users)
1313 del_channel_user(channel->users, 0);
1315 while(channel->bans)
1316 del_channel_ban(channel->bans);
1318 free(channel->topic);
1319 free(channel->registrar);
1320 free(channel->greeting);
1321 free(channel->user_greeting);
1322 free(channel->topic_mask);
1325 channel->prev->next = channel->next;
1327 channelList = channel->next;
1330 channel->next->prev = channel->prev;
1332 if(channel->suspended)
1334 struct chanNode *cNode = channel->channel;
1335 struct suspended *suspended, *next_suspended;
1337 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1339 next_suspended = suspended->previous;
1340 free(suspended->suspender);
1341 free(suspended->reason);
1342 if(suspended->expires)
1343 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1348 cNode->channel_info = NULL;
1350 channel->channel->channel_info = NULL;
1352 dict_delete(channel->notes);
1353 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1354 if(!IsSuspended(channel))
1355 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1356 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1357 UnlockChannel(channel->channel);
1359 registered_channels--;
1363 expire_channels(UNUSED_ARG(void *data))
1365 struct chanData *channel, *next;
1366 struct userData *user;
1367 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1369 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1370 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1372 for(channel = channelList; channel; channel = next)
1374 next = channel->next;
1376 /* See if the channel can be expired. */
1377 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1378 || IsProtected(channel))
1381 /* Make sure there are no high-ranking users still in the channel. */
1382 for(user=channel->users; user; user=user->next)
1383 if(user->present && (user->access >= UL_PRESENT))
1388 /* Unregister the channel */
1389 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1390 unregister_channel(channel, "registration expired.");
1393 if(chanserv_conf.channel_expire_frequency)
1394 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1398 expire_dnrs(UNUSED_ARG(void *data))
1401 struct do_not_register *dnr;
1403 for(it = dict_first(handle_dnrs); it; it = iter_next(it))
1405 dnr = iter_data(it);
1406 if(!dnr->expires || dnr->expires > now)
1408 dict_remove(handle_dnrs, dnr->chan_name + 1);
1410 for(it = dict_first(plain_dnrs); it; it = iter_next(it))
1412 dnr = iter_data(it);
1413 if(!dnr->expires || dnr->expires > now)
1415 dict_remove(plain_dnrs, dnr->chan_name);
1417 for(it = dict_first(mask_dnrs); it; it = iter_next(it))
1419 dnr = iter_data(it);
1420 if(!dnr->expires || dnr->expires > now)
1422 dict_remove(mask_dnrs, dnr->chan_name);
1425 if(chanserv_conf.dnr_expire_frequency)
1426 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1430 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1432 char protect = channel->chOpts[chProtect];
1433 struct userData *cs_victim, *cs_aggressor;
1435 /* Don't protect if no one is to be protected, someone is attacking
1436 himself, or if the aggressor is an IRC Operator. */
1437 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1440 /* Don't protect if the victim isn't authenticated (because they
1441 can't be a channel user), unless we are to protect non-users
1443 cs_victim = GetChannelAccess(channel, victim->handle_info);
1444 if(protect != 'a' && !cs_victim)
1447 /* Protect if the aggressor isn't a user because at this point,
1448 the aggressor can only be less than or equal to the victim. */
1449 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1453 /* If the aggressor was a user, then the victim can't be helped. */
1460 if(cs_victim->access > cs_aggressor->access)
1465 if(cs_victim->access >= cs_aggressor->access)
1474 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1476 struct chanData *cData = channel->channel_info;
1477 struct userData *cs_victim;
1479 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1480 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1481 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1483 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1491 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1493 if(IsService(victim))
1495 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1499 if(protect_user(victim, user, channel->channel_info))
1501 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1508 static struct do_not_register *
1509 chanserv_add_dnr(const char *chan_name, const char *setter, time_t expires, const char *reason)
1511 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1512 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1513 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1514 strcpy(dnr->reason, reason);
1516 dnr->expires = expires;
1517 if(dnr->chan_name[0] == '*')
1518 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1519 else if(strpbrk(dnr->chan_name, "*?"))
1520 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1522 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1526 static struct dnrList
1527 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1529 struct dnrList list;
1530 dict_iterator_t it, next;
1531 struct do_not_register *dnr;
1533 dnrList_init(&list);
1535 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1537 if(dnr->expires && dnr->expires <= now)
1538 dict_remove(handle_dnrs, handle);
1539 else if(list.used < max)
1540 dnrList_append(&list, dnr);
1543 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1545 if(dnr->expires && dnr->expires <= now)
1546 dict_remove(plain_dnrs, chan_name);
1547 else if(list.used < max)
1548 dnrList_append(&list, dnr);
1553 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1555 next = iter_next(it);
1556 if(!match_ircglob(chan_name, iter_key(it)))
1558 dnr = iter_data(it);
1559 if(dnr->expires && dnr->expires <= now)
1560 dict_remove(mask_dnrs, iter_key(it));
1562 dnrList_append(&list, dnr);
1569 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1571 struct userNode *user;
1572 char buf1[INTERVALLEN];
1573 char buf2[INTERVALLEN];
1577 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&dnr->set));
1580 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&dnr->expires));
1581 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1585 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1588 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1593 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1595 struct dnrList list;
1598 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1599 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1600 dnr_print_func(list.list[ii], user);
1602 reply("CSMSG_MORE_DNRS", list.used - ii);
1607 struct do_not_register *
1608 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1610 struct dnrList list;
1611 struct do_not_register *dnr;
1613 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1614 dnr = list.used ? list.list[0] : NULL;
1619 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1621 struct do_not_register *dnr;
1622 dict_iterator_t it, next;
1623 unsigned int matches = 0;
1625 for(it = dict_first(dict); it; it = next)
1627 dnr = iter_data(it);
1628 next = iter_next(it);
1629 if(dnr->expires && dnr->expires <= now)
1631 dict_remove(dict, iter_key(it));
1634 dnr_print_func(dnr, user);
1641 static CHANSERV_FUNC(cmd_noregister)
1644 time_t expiry, duration;
1645 unsigned int matches;
1649 reply("CSMSG_DNR_SEARCH_RESULTS");
1650 matches = send_dnrs(user, handle_dnrs);
1651 matches += send_dnrs(user, plain_dnrs);
1652 matches += send_dnrs(user, mask_dnrs);
1654 reply("MSG_MATCH_COUNT", matches);
1656 reply("MSG_NO_MATCHES");
1662 if(!IsChannelName(target) && (*target != '*'))
1664 reply("CSMSG_NOT_DNR", target);
1672 reply("MSG_INVALID_DURATION", argv[2]);
1676 if(!strcmp(argv[2], "0"))
1678 else if((duration = ParseInterval(argv[2])))
1679 expiry = now + duration;
1682 reply("MSG_INVALID_DURATION", argv[2]);
1686 const char *reason = unsplit_string(argv + 3, argc - 3, NULL);
1687 if((*target == '*') && !get_handle_info(target + 1))
1689 reply("MSG_HANDLE_UNKNOWN", target + 1);
1692 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1693 reply("CSMSG_NOREGISTER_CHANNEL", target);
1697 reply("CSMSG_DNR_SEARCH_RESULTS");
1699 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1701 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1703 reply("MSG_NO_MATCHES");
1707 static CHANSERV_FUNC(cmd_allowregister)
1709 const char *chan_name = argv[1];
1711 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1712 || dict_remove(plain_dnrs, chan_name)
1713 || dict_remove(mask_dnrs, chan_name))
1715 reply("CSMSG_DNR_REMOVED", chan_name);
1718 reply("CSMSG_NO_SUCH_DNR", chan_name);
1723 struct userNode *source;
1727 time_t min_set, max_set;
1728 time_t min_expires, max_expires;
1733 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1735 return !((dnr->set < search->min_set)
1736 || (dnr->set > search->max_set)
1737 || (dnr->expires && ((dnr->expires < search->min_expires)
1738 || (dnr->expires > search->max_expires)))
1739 || (search->chan_mask
1740 && !match_ircglob(dnr->chan_name, search->chan_mask))
1741 || (search->setter_mask
1742 && !match_ircglob(dnr->setter, search->setter_mask))
1743 || (search->reason_mask
1744 && !match_ircglob(dnr->reason, search->reason_mask)));
1747 static struct dnr_search *
1748 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1750 struct dnr_search *discrim;
1753 discrim = calloc(1, sizeof(*discrim));
1754 discrim->source = user;
1755 discrim->chan_mask = NULL;
1756 discrim->setter_mask = NULL;
1757 discrim->reason_mask = NULL;
1758 discrim->max_set = INT_MAX;
1759 discrim->max_expires = INT_MAX;
1760 discrim->limit = 50;
1762 for(ii=0; ii<argc; ++ii)
1766 reply("MSG_MISSING_PARAMS", argv[ii]);
1769 else if(0 == irccasecmp(argv[ii], "channel"))
1771 discrim->chan_mask = argv[++ii];
1773 else if(0 == irccasecmp(argv[ii], "setter"))
1775 discrim->setter_mask = argv[++ii];
1777 else if(0 == irccasecmp(argv[ii], "reason"))
1779 discrim->reason_mask = argv[++ii];
1781 else if(0 == irccasecmp(argv[ii], "limit"))
1783 discrim->limit = strtoul(argv[++ii], NULL, 0);
1785 else if(0 == irccasecmp(argv[ii], "set"))
1787 const char *cmp = argv[++ii];
1790 discrim->min_set = now - ParseInterval(cmp + 2);
1792 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1793 } else if(cmp[0] == '=') {
1794 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1795 } else if(cmp[0] == '>') {
1797 discrim->max_set = now - ParseInterval(cmp + 2);
1799 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1801 discrim->max_set = now - (ParseInterval(cmp) - 1);
1804 else if(0 == irccasecmp(argv[ii], "expires"))
1806 const char *cmp = argv[++ii];
1809 discrim->max_expires = now + ParseInterval(cmp + 2);
1811 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1812 } else if(cmp[0] == '=') {
1813 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1814 } else if(cmp[0] == '>') {
1816 discrim->min_expires = now + ParseInterval(cmp + 2);
1818 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1820 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1825 reply("MSG_INVALID_CRITERIA", argv[ii]);
1836 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1839 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1841 struct do_not_register *dnr;
1842 dict_iterator_t next;
1847 /* Initialize local variables. */
1850 if(discrim->chan_mask)
1852 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1853 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1857 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1859 /* Check against account-based DNRs. */
1860 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1861 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1864 else if(target_fixed)
1866 /* Check against channel-based DNRs. */
1867 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1868 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1873 /* Exhaustively search account DNRs. */
1874 for(it = dict_first(handle_dnrs); it; it = next)
1876 next = iter_next(it);
1877 dnr = iter_data(it);
1878 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1882 /* Do the same for channel DNRs. */
1883 for(it = dict_first(plain_dnrs); it; it = next)
1885 next = iter_next(it);
1886 dnr = iter_data(it);
1887 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1891 /* Do the same for wildcarded channel DNRs. */
1892 for(it = dict_first(mask_dnrs); it; it = next)
1894 next = iter_next(it);
1895 dnr = iter_data(it);
1896 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1904 dnr_remove_func(struct do_not_register *match, void *extra)
1906 struct userNode *user;
1909 chan_name = alloca(strlen(match->chan_name) + 1);
1910 strcpy(chan_name, match->chan_name);
1912 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1913 || dict_remove(plain_dnrs, chan_name)
1914 || dict_remove(mask_dnrs, chan_name))
1916 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1922 dnr_count_func(struct do_not_register *match, void *extra)
1924 return 0; (void)match; (void)extra;
1927 static MODCMD_FUNC(cmd_dnrsearch)
1929 struct dnr_search *discrim;
1930 dnr_search_func action;
1931 struct svccmd *subcmd;
1932 unsigned int matches;
1935 sprintf(buf, "dnrsearch %s", argv[1]);
1936 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1939 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1942 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1944 if(!irccasecmp(argv[1], "print"))
1945 action = dnr_print_func;
1946 else if(!irccasecmp(argv[1], "remove"))
1947 action = dnr_remove_func;
1948 else if(!irccasecmp(argv[1], "count"))
1949 action = dnr_count_func;
1952 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1956 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1960 if(action == dnr_print_func)
1961 reply("CSMSG_DNR_SEARCH_RESULTS");
1962 matches = dnr_search(discrim, action, user);
1964 reply("MSG_MATCH_COUNT", matches);
1966 reply("MSG_NO_MATCHES");
1972 chanserv_get_owned_count(struct handle_info *hi)
1974 struct userData *cList;
1977 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1978 if(cList->access == UL_OWNER)
1983 static CHANSERV_FUNC(cmd_register)
1985 struct handle_info *handle;
1986 struct chanData *cData;
1987 struct modeNode *mn;
1988 char reason[MAXLEN];
1990 unsigned int new_channel, force=0;
1991 struct do_not_register *dnr;
1995 if(channel->channel_info)
1997 reply("CSMSG_ALREADY_REGGED", channel->name);
2001 if(channel->bad_channel)
2003 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2008 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2010 reply("CSMSG_MUST_BE_OPPED", channel->name);
2015 chan_name = channel->name;
2019 if((argc < 2) || !IsChannelName(argv[1]))
2021 reply("MSG_NOT_CHANNEL_NAME");
2025 if(opserv_bad_channel(argv[1]))
2027 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2032 chan_name = argv[1];
2035 if(argc >= (new_channel+2))
2037 if(!IsHelping(user))
2039 reply("CSMSG_PROXY_FORBIDDEN");
2043 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2045 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2046 dnr = chanserv_is_dnr(chan_name, handle);
2050 handle = user->handle_info;
2051 dnr = chanserv_is_dnr(chan_name, handle);
2055 if(!IsHelping(user))
2056 reply("CSMSG_DNR_CHANNEL", chan_name);
2058 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2062 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2064 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2069 channel = AddChannel(argv[1], now, NULL, NULL);
2071 cData = register_channel(channel, user->handle_info->handle);
2072 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2073 cData->modes = chanserv_conf.default_modes;
2075 cData->modes.modes_set |= MODE_REGISTERED;
2076 if (IsOffChannel(cData))
2078 mod_chanmode_announce(chanserv, channel, &cData->modes);
2082 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2083 change->args[change->argc].mode = MODE_CHANOP;
2084 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2086 mod_chanmode_announce(chanserv, channel, change);
2087 mod_chanmode_free(change);
2090 /* Initialize the channel's max user record. */
2091 cData->max = channel->members.used;
2093 if(handle != user->handle_info)
2094 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2096 reply("CSMSG_REG_SUCCESS", channel->name);
2098 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2099 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2104 make_confirmation_string(struct userData *uData)
2106 static char strbuf[16];
2111 for(src = uData->handle->handle; *src; )
2112 accum = accum * 31 + toupper(*src++);
2114 for(src = uData->channel->channel->name; *src; )
2115 accum = accum * 31 + toupper(*src++);
2116 sprintf(strbuf, "%08x", accum);
2120 static CHANSERV_FUNC(cmd_unregister)
2123 char reason[MAXLEN];
2124 struct chanData *cData;
2125 struct userData *uData;
2127 cData = channel->channel_info;
2130 reply("CSMSG_NOT_REGISTERED", channel->name);
2134 uData = GetChannelUser(cData, user->handle_info);
2135 if(!uData || (uData->access < UL_OWNER))
2137 reply("CSMSG_NO_ACCESS");
2141 if(IsProtected(cData))
2143 reply("CSMSG_UNREG_NODELETE", channel->name);
2147 if(!IsHelping(user))
2149 const char *confirm_string;
2150 if(IsSuspended(cData))
2152 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2155 confirm_string = make_confirmation_string(uData);
2156 if((argc < 2) || strcmp(argv[1], confirm_string))
2158 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2163 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2164 name = strdup(channel->name);
2165 unregister_channel(cData, reason);
2166 reply("CSMSG_UNREG_SUCCESS", name);
2171 static CHANSERV_FUNC(cmd_move)
2173 struct mod_chanmode change;
2174 struct chanNode *target;
2175 struct modeNode *mn;
2176 struct userData *uData;
2177 char reason[MAXLEN];
2178 struct do_not_register *dnr;
2182 if(IsProtected(channel->channel_info))
2184 reply("CSMSG_MOVE_NODELETE", channel->name);
2188 if(!IsChannelName(argv[1]))
2190 reply("MSG_NOT_CHANNEL_NAME");
2194 if(opserv_bad_channel(argv[1]))
2196 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2200 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2202 for(uData = channel->channel_info->users; uData; uData = uData->next)
2204 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2206 if(!IsHelping(user))
2207 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2209 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2215 mod_chanmode_init(&change);
2216 if(!(target = GetChannel(argv[1])))
2218 target = AddChannel(argv[1], now, NULL, NULL);
2219 if(!IsSuspended(channel->channel_info))
2220 AddChannelUser(chanserv, target);
2222 else if(target->channel_info)
2224 reply("CSMSG_ALREADY_REGGED", target->name);
2227 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2228 && !IsHelping(user))
2230 reply("CSMSG_MUST_BE_OPPED", target->name);
2233 else if(!IsSuspended(channel->channel_info))
2236 change.args[0].mode = MODE_CHANOP;
2237 change.args[0].u.member = AddChannelUser(chanserv, target);
2238 mod_chanmode_announce(chanserv, target, &change);
2243 /* Clear MODE_REGISTERED from old channel, add it to new. */
2245 change.modes_clear = MODE_REGISTERED;
2246 mod_chanmode_announce(chanserv, channel, &change);
2247 change.modes_clear = 0;
2248 change.modes_set = MODE_REGISTERED;
2249 mod_chanmode_announce(chanserv, target, &change);
2252 /* Move the channel_info to the target channel; it
2253 shouldn't be necessary to clear timeq callbacks
2254 for the old channel. */
2255 target->channel_info = channel->channel_info;
2256 target->channel_info->channel = target;
2257 channel->channel_info = NULL;
2259 reply("CSMSG_MOVE_SUCCESS", target->name);
2261 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2262 if(!IsSuspended(target->channel_info))
2264 char reason2[MAXLEN];
2265 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2266 DelChannelUser(chanserv, channel, reason2, 0);
2268 UnlockChannel(channel);
2269 LockChannel(target);
2270 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2275 merge_users(struct chanData *source, struct chanData *target)
2277 struct userData *suData, *tuData, *next;
2283 /* Insert the source's users into the scratch area. */
2284 for(suData = source->users; suData; suData = suData->next)
2285 dict_insert(merge, suData->handle->handle, suData);
2287 /* Iterate through the target's users, looking for
2288 users common to both channels. The lower access is
2289 removed from either the scratch area or target user
2291 for(tuData = target->users; tuData; tuData = next)
2293 struct userData *choice;
2295 next = tuData->next;
2297 /* If a source user exists with the same handle as a target
2298 channel's user, resolve the conflict by removing one. */
2299 suData = dict_find(merge, tuData->handle->handle, NULL);
2303 /* Pick the data we want to keep. */
2304 /* If the access is the same, use the later seen time. */
2305 if(suData->access == tuData->access)
2306 choice = (suData->seen > tuData->seen) ? suData : tuData;
2307 else /* Otherwise, keep the higher access level. */
2308 choice = (suData->access > tuData->access) ? suData : tuData;
2310 /* Remove the user that wasn't picked. */
2311 if(choice == tuData)
2313 dict_remove(merge, suData->handle->handle);
2314 del_channel_user(suData, 0);
2317 del_channel_user(tuData, 0);
2320 /* Move the remaining users to the target channel. */
2321 for(it = dict_first(merge); it; it = iter_next(it))
2323 suData = iter_data(it);
2325 /* Insert the user into the target channel's linked list. */
2326 suData->prev = NULL;
2327 suData->next = target->users;
2328 suData->channel = target;
2331 target->users->prev = suData;
2332 target->users = suData;
2334 /* Update the user counts for the target channel; the
2335 source counts are left alone. */
2336 target->userCount++;
2339 /* Possible to assert (source->users == NULL) here. */
2340 source->users = NULL;
2345 merge_bans(struct chanData *source, struct chanData *target)
2347 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2349 /* Hold on to the original head of the target ban list
2350 to avoid comparing source bans with source bans. */
2351 tFront = target->bans;
2353 /* Perform a totally expensive O(n*m) merge, ick. */
2354 for(sbData = source->bans; sbData; sbData = sNext)
2356 /* Flag to track whether the ban's been moved
2357 to the destination yet. */
2360 /* Possible to assert (sbData->prev == NULL) here. */
2361 sNext = sbData->next;
2363 for(tbData = tFront; tbData; tbData = tNext)
2365 tNext = tbData->next;
2367 /* Perform two comparisons between each source
2368 and target ban, conflicts are resolved by
2369 keeping the broader ban and copying the later
2370 expiration and triggered time. */
2371 if(match_ircglobs(tbData->mask, sbData->mask))
2373 /* There is a broader ban in the target channel that
2374 overrides one in the source channel; remove the
2375 source ban and break. */
2376 if(sbData->expires > tbData->expires)
2377 tbData->expires = sbData->expires;
2378 if(sbData->triggered > tbData->triggered)
2379 tbData->triggered = sbData->triggered;
2380 del_channel_ban(sbData);
2383 else if(match_ircglobs(sbData->mask, tbData->mask))
2385 /* There is a broader ban in the source channel that
2386 overrides one in the target channel; remove the
2387 target ban, fall through and move the source over. */
2388 if(tbData->expires > sbData->expires)
2389 sbData->expires = tbData->expires;
2390 if(tbData->triggered > sbData->triggered)
2391 sbData->triggered = tbData->triggered;
2392 if(tbData == tFront)
2394 del_channel_ban(tbData);
2397 /* Source bans can override multiple target bans, so
2398 we allow a source to run through this loop multiple
2399 times, but we can only move it once. */
2404 /* Remove the source ban from the source ban list. */
2406 sbData->next->prev = sbData->prev;
2408 /* Modify the source ban's associated channel. */
2409 sbData->channel = target;
2411 /* Insert the ban into the target channel's linked list. */
2412 sbData->prev = NULL;
2413 sbData->next = target->bans;
2416 target->bans->prev = sbData;
2417 target->bans = sbData;
2419 /* Update the user counts for the target channel. */
2424 /* Possible to assert (source->bans == NULL) here. */
2425 source->bans = NULL;
2429 merge_data(struct chanData *source, struct chanData *target)
2431 /* Use more recent visited and owner-transfer time; use older
2432 * registered time. Bitwise or may_opchan. Use higher max.
2433 * Do not touch last_refresh, ban count or user counts.
2435 if(source->visited > target->visited)
2436 target->visited = source->visited;
2437 if(source->registered < target->registered)
2438 target->registered = source->registered;
2439 if(source->ownerTransfer > target->ownerTransfer)
2440 target->ownerTransfer = source->ownerTransfer;
2441 if(source->may_opchan)
2442 target->may_opchan = 1;
2443 if(source->max > target->max)
2444 target->max = source->max;
2448 merge_channel(struct chanData *source, struct chanData *target)
2450 merge_users(source, target);
2451 merge_bans(source, target);
2452 merge_data(source, target);
2455 static CHANSERV_FUNC(cmd_merge)
2457 struct userData *target_user;
2458 struct chanNode *target;
2459 char reason[MAXLEN];
2463 /* Make sure the target channel exists and is registered to the user
2464 performing the command. */
2465 if(!(target = GetChannel(argv[1])))
2467 reply("MSG_INVALID_CHANNEL");
2471 if(!target->channel_info)
2473 reply("CSMSG_NOT_REGISTERED", target->name);
2477 if(IsProtected(channel->channel_info))
2479 reply("CSMSG_MERGE_NODELETE");
2483 if(IsSuspended(target->channel_info))
2485 reply("CSMSG_MERGE_SUSPENDED");
2489 if(channel == target)
2491 reply("CSMSG_MERGE_SELF");
2495 target_user = GetChannelUser(target->channel_info, user->handle_info);
2496 if(!target_user || (target_user->access < UL_OWNER))
2498 reply("CSMSG_MERGE_NOT_OWNER");
2502 /* Merge the channel structures and associated data. */
2503 merge_channel(channel->channel_info, target->channel_info);
2504 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2505 unregister_channel(channel->channel_info, reason);
2506 reply("CSMSG_MERGE_SUCCESS", target->name);
2510 static CHANSERV_FUNC(cmd_opchan)
2512 struct mod_chanmode change;
2513 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2515 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2518 channel->channel_info->may_opchan = 0;
2519 mod_chanmode_init(&change);
2521 change.args[0].mode = MODE_CHANOP;
2522 change.args[0].u.member = GetUserMode(channel, chanserv);
2523 mod_chanmode_announce(chanserv, channel, &change);
2524 reply("CSMSG_OPCHAN_DONE", channel->name);
2528 static CHANSERV_FUNC(cmd_adduser)
2530 struct userData *actee;
2531 struct userData *actor, *real_actor;
2532 struct handle_info *handle;
2533 unsigned short access, override = 0;
2537 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2539 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2543 access = user_level_from_name(argv[2], UL_OWNER);
2546 reply("CSMSG_INVALID_ACCESS", argv[2]);
2550 actor = GetChannelUser(channel->channel_info, user->handle_info);
2551 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2553 if(actor->access <= access)
2555 reply("CSMSG_NO_BUMP_ACCESS");
2559 /* Trying to add someone with equal/more access? */
2560 if (!real_actor || real_actor->access <= access)
2561 override = CMD_LOG_OVERRIDE;
2563 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2566 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2568 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2572 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2573 scan_user_presence(actee, NULL);
2574 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2575 return 1 | override;
2578 static CHANSERV_FUNC(cmd_clvl)
2580 struct handle_info *handle;
2581 struct userData *victim;
2582 struct userData *actor, *real_actor;
2583 unsigned short new_access, override = 0;
2584 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2588 actor = GetChannelUser(channel->channel_info, user->handle_info);
2589 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2591 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2594 if(handle == user->handle_info && !privileged)
2596 reply("CSMSG_NO_SELF_CLVL");
2600 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2602 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2606 if(actor->access <= victim->access && !privileged)
2608 reply("MSG_USER_OUTRANKED", handle->handle);
2612 new_access = user_level_from_name(argv[2], UL_OWNER);
2616 reply("CSMSG_INVALID_ACCESS", argv[2]);
2620 if(new_access >= actor->access && !privileged)
2622 reply("CSMSG_NO_BUMP_ACCESS");
2626 /* Trying to clvl a equal/higher user? */
2627 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2628 override = CMD_LOG_OVERRIDE;
2629 /* Trying to clvl someone to equal/higher access? */
2630 if(!real_actor || new_access >= real_actor->access)
2631 override = CMD_LOG_OVERRIDE;
2632 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2633 * If they lower their own access it's not a big problem.
2636 victim->access = new_access;
2637 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2638 return 1 | override;
2641 static CHANSERV_FUNC(cmd_deluser)
2643 struct handle_info *handle;
2644 struct userData *victim;
2645 struct userData *actor, *real_actor;
2646 unsigned short access, override = 0;
2651 actor = GetChannelUser(channel->channel_info, user->handle_info);
2652 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2654 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2657 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2659 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2665 access = user_level_from_name(argv[1], UL_OWNER);
2668 reply("CSMSG_INVALID_ACCESS", argv[1]);
2671 if(access != victim->access)
2673 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2679 access = victim->access;
2682 if((actor->access <= victim->access) && !IsHelping(user))
2684 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2688 /* If people delete themselves it is an override, but they
2689 * could've used deleteme so we don't log it as an override
2691 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2692 override = CMD_LOG_OVERRIDE;
2694 chan_name = strdup(channel->name);
2695 del_channel_user(victim, 1);
2696 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2698 return 1 | override;
2702 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2704 struct userData *actor, *real_actor, *uData, *next;
2705 unsigned int override = 0;
2707 actor = GetChannelUser(channel->channel_info, user->handle_info);
2708 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2710 if(min_access > max_access)
2712 reply("CSMSG_BAD_RANGE", min_access, max_access);
2716 if((actor->access <= max_access) && !IsHelping(user))
2718 reply("CSMSG_NO_ACCESS");
2722 if(!real_actor || real_actor->access <= max_access)
2723 override = CMD_LOG_OVERRIDE;
2725 for(uData = channel->channel_info->users; uData; uData = next)
2729 if((uData->access >= min_access)
2730 && (uData->access <= max_access)
2731 && match_ircglob(uData->handle->handle, mask))
2732 del_channel_user(uData, 1);
2735 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2736 return 1 | override;
2739 static CHANSERV_FUNC(cmd_mdelowner)
2741 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2744 static CHANSERV_FUNC(cmd_mdelcoowner)
2746 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2749 static CHANSERV_FUNC(cmd_mdelmaster)
2751 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2754 static CHANSERV_FUNC(cmd_mdelop)
2756 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2759 static CHANSERV_FUNC(cmd_mdelpeon)
2761 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2765 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2767 struct banData *bData, *next;
2768 char interval[INTERVALLEN];
2773 limit = now - duration;
2774 for(bData = channel->channel_info->bans; bData; bData = next)
2778 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2781 del_channel_ban(bData);
2785 intervalString(interval, duration, user->handle_info);
2786 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2791 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2793 struct userData *actor, *uData, *next;
2794 char interval[INTERVALLEN];
2798 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2799 if(min_access > max_access)
2801 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2805 if(!actor || actor->access <= max_access)
2807 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2812 limit = now - duration;
2813 for(uData = channel->channel_info->users; uData; uData = next)
2817 if((uData->seen > limit)
2819 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2822 if(((uData->access >= min_access) && (uData->access <= max_access))
2823 || (!max_access && (uData->access < actor->access)))
2825 del_channel_user(uData, 1);
2833 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2835 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2839 static CHANSERV_FUNC(cmd_trim)
2841 unsigned long duration;
2842 unsigned short min_level, max_level;
2847 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2848 duration = ParseInterval(argv[2]);
2851 reply("CSMSG_CANNOT_TRIM");
2855 if(!irccasecmp(argv[1], "bans"))
2857 cmd_trim_bans(user, channel, duration);
2860 else if(!irccasecmp(argv[1], "users"))
2862 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2865 else if(parse_level_range(&min_level, &max_level, argv[1]))
2867 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2870 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2872 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2877 reply("CSMSG_INVALID_TRIM", argv[1]);
2882 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2883 to the user. cmd_all takes advantage of this. */
2884 static CHANSERV_FUNC(cmd_up)
2886 struct mod_chanmode change;
2887 struct userData *uData;
2890 mod_chanmode_init(&change);
2892 change.args[0].u.member = GetUserMode(channel, user);
2893 if(!change.args[0].u.member)
2896 reply("MSG_CHANNEL_ABSENT", channel->name);
2900 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2904 reply("CSMSG_GODMODE_UP", argv[0]);
2907 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2909 change.args[0].mode = MODE_CHANOP;
2910 errmsg = "CSMSG_ALREADY_OPPED";
2912 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2914 change.args[0].mode = MODE_VOICE;
2915 errmsg = "CSMSG_ALREADY_VOICED";
2920 reply("CSMSG_NO_ACCESS");
2923 change.args[0].mode &= ~change.args[0].u.member->modes;
2924 if(!change.args[0].mode)
2927 reply(errmsg, channel->name);
2930 modcmd_chanmode_announce(&change);
2934 static CHANSERV_FUNC(cmd_down)
2936 struct mod_chanmode change;
2938 mod_chanmode_init(&change);
2940 change.args[0].u.member = GetUserMode(channel, user);
2941 if(!change.args[0].u.member)
2944 reply("MSG_CHANNEL_ABSENT", channel->name);
2948 if(!change.args[0].u.member->modes)
2951 reply("CSMSG_ALREADY_DOWN", channel->name);
2955 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2956 modcmd_chanmode_announce(&change);
2960 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)
2962 struct userData *cList;
2964 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2966 if(IsSuspended(cList->channel)
2967 || IsUserSuspended(cList)
2968 || !GetUserMode(cList->channel->channel, user))
2971 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2977 static CHANSERV_FUNC(cmd_upall)
2979 return cmd_all(CSFUNC_ARGS, cmd_up);
2982 static CHANSERV_FUNC(cmd_downall)
2984 return cmd_all(CSFUNC_ARGS, cmd_down);
2987 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2988 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2991 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)
2993 unsigned int ii, valid;
2994 struct userNode *victim;
2995 struct mod_chanmode *change;
2997 change = mod_chanmode_alloc(argc - 1);
2999 for(ii=valid=0; ++ii < argc; )
3001 if(!(victim = GetUserH(argv[ii])))
3003 change->args[valid].mode = mode;
3004 change->args[valid].u.member = GetUserMode(channel, victim);
3005 if(!change->args[valid].u.member)
3007 if(validate && !validate(user, channel, victim))
3012 change->argc = valid;
3013 if(valid < (argc-1))
3014 reply("CSMSG_PROCESS_FAILED");
3017 modcmd_chanmode_announce(change);
3018 reply(action, channel->name);
3020 mod_chanmode_free(change);
3024 static CHANSERV_FUNC(cmd_op)
3026 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3029 static CHANSERV_FUNC(cmd_deop)
3031 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3034 static CHANSERV_FUNC(cmd_voice)
3036 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3039 static CHANSERV_FUNC(cmd_devoice)
3041 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3045 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3051 for(ii=0; ii<channel->members.used; ii++)
3053 struct modeNode *mn = channel->members.list[ii];
3055 if(IsService(mn->user))
3058 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3061 if(protect_user(mn->user, user, channel->channel_info))
3065 victims[(*victimCount)++] = mn;
3071 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3073 struct userNode *victim;
3074 struct modeNode **victims;
3075 unsigned int offset, n, victimCount, duration = 0;
3076 char *reason = "Bye.", *ban, *name;
3077 char interval[INTERVALLEN];
3079 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3080 REQUIRE_PARAMS(offset);
3083 reason = unsplit_string(argv + offset, argc - offset, NULL);
3084 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3086 /* Truncate the reason to a length of TOPICLEN, as
3087 the ircd does; however, leave room for an ellipsis
3088 and the kicker's nick. */
3089 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3093 if((victim = GetUserH(argv[1])))
3095 victims = alloca(sizeof(victims[0]));
3096 victims[0] = GetUserMode(channel, victim);
3097 /* XXX: The comparison with ACTION_KICK is just because all
3098 * other actions can work on users outside the channel, and we
3099 * want to allow those (e.g. unbans) in that case. If we add
3100 * some other ejection action for in-channel users, change
3102 victimCount = victims[0] ? 1 : 0;
3104 if(IsService(victim))
3106 reply("MSG_SERVICE_IMMUNE", victim->nick);
3110 if((action == ACTION_KICK) && !victimCount)
3112 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3116 if(protect_user(victim, user, channel->channel_info))
3118 reply("CSMSG_USER_PROTECTED", victim->nick);
3122 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3123 name = victim->nick;
3127 if(!is_ircmask(argv[1]))
3129 reply("MSG_NICK_UNKNOWN", argv[1]);
3133 victims = alloca(sizeof(victims[0]) * channel->members.used);
3135 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3137 reply("CSMSG_MASK_PROTECTED", argv[1]);
3141 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3143 reply("CSMSG_LAME_MASK", argv[1]);
3147 if((action == ACTION_KICK) && (victimCount == 0))
3149 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3153 name = ban = strdup(argv[1]);
3156 /* Truncate the ban in place if necessary; we must ensure
3157 that 'ban' is a valid ban mask before sanitizing it. */
3158 sanitize_ircmask(ban);
3160 if(action & ACTION_ADD_BAN)
3162 struct banData *bData, *next;
3164 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3166 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3171 if(action & ACTION_ADD_TIMED_BAN)
3173 duration = ParseInterval(argv[2]);
3177 reply("CSMSG_DURATION_TOO_LOW");
3181 else if(duration > (86400 * 365 * 2))
3183 reply("CSMSG_DURATION_TOO_HIGH");
3189 for(bData = channel->channel_info->bans; bData; bData = next)
3191 if(match_ircglobs(bData->mask, ban))
3193 int exact = !irccasecmp(bData->mask, ban);
3195 /* The ban is redundant; there is already a ban
3196 with the same effect in place. */
3200 free(bData->reason);
3201 bData->reason = strdup(reason);
3202 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3204 reply("CSMSG_REASON_CHANGE", ban);
3208 if(exact && bData->expires)
3212 /* If the ban matches an existing one exactly,
3213 extend the expiration time if the provided
3214 duration is longer. */
3215 if(duration && ((time_t)(now + duration) > bData->expires))
3217 bData->expires = now + duration;
3228 /* Delete the expiration timeq entry and
3229 requeue if necessary. */
3230 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3233 timeq_add(bData->expires, expire_ban, bData);
3237 /* automated kickban */
3240 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3242 reply("CSMSG_BAN_ADDED", name, channel->name);
3248 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3255 if(match_ircglobs(ban, bData->mask))
3257 /* The ban we are adding makes previously existing
3258 bans redundant; silently remove them. */
3259 del_channel_ban(bData);
3263 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);
3265 name = ban = strdup(bData->mask);
3269 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3271 extern const char *hidden_host_suffix;
3272 const char *old_name = chanserv_conf.old_ban_names->list[n];
3274 unsigned int l1, l2;
3277 l2 = strlen(old_name);
3280 if(irccasecmp(ban + l1 - l2, old_name))
3282 new_mask = malloc(MAXLEN);
3283 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3285 name = ban = new_mask;
3290 if(action & ACTION_BAN)
3292 unsigned int exists;
3293 struct mod_chanmode *change;
3295 if(channel->banlist.used >= MAXBANS)
3298 reply("CSMSG_BANLIST_FULL", channel->name);
3303 exists = ChannelBanExists(channel, ban);
3304 change = mod_chanmode_alloc(victimCount + 1);
3305 for(n = 0; n < victimCount; ++n)
3307 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3308 change->args[n].u.member = victims[n];
3312 change->args[n].mode = MODE_BAN;
3313 change->args[n++].u.hostmask = ban;
3317 modcmd_chanmode_announce(change);
3319 mod_chanmode_announce(chanserv, channel, change);
3320 mod_chanmode_free(change);
3322 if(exists && (action == ACTION_BAN))
3325 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3331 if(action & ACTION_KICK)
3333 char kick_reason[MAXLEN];
3334 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3336 for(n = 0; n < victimCount; n++)
3337 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3342 /* No response, since it was automated. */
3344 else if(action & ACTION_ADD_BAN)
3347 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3349 reply("CSMSG_BAN_ADDED", name, channel->name);
3351 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3352 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3353 else if(action & ACTION_BAN)
3354 reply("CSMSG_BAN_DONE", name, channel->name);
3355 else if(action & ACTION_KICK && victimCount)
3356 reply("CSMSG_KICK_DONE", name, channel->name);
3362 static CHANSERV_FUNC(cmd_kickban)
3364 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3367 static CHANSERV_FUNC(cmd_kick)
3369 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3372 static CHANSERV_FUNC(cmd_ban)
3374 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3377 static CHANSERV_FUNC(cmd_addban)
3379 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3382 static CHANSERV_FUNC(cmd_addtimedban)
3384 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3387 static struct mod_chanmode *
3388 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3390 struct mod_chanmode *change;
3391 unsigned char *match;
3392 unsigned int ii, count;
3394 match = alloca(bans->used);
3397 for(ii = count = 0; ii < bans->used; ++ii)
3399 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3400 MATCH_USENICK | MATCH_VISIBLE);
3407 for(ii = count = 0; ii < bans->used; ++ii)
3409 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3416 change = mod_chanmode_alloc(count);
3417 for(ii = count = 0; ii < bans->used; ++ii)
3421 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3422 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3424 assert(count == change->argc);
3429 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3431 struct userNode *actee;
3437 /* may want to allow a comma delimited list of users... */
3438 if(!(actee = GetUserH(argv[1])))
3440 if(!is_ircmask(argv[1]))
3442 reply("MSG_NICK_UNKNOWN", argv[1]);
3446 mask = strdup(argv[1]);
3449 /* We don't sanitize the mask here because ircu
3451 if(action & ACTION_UNBAN)
3453 struct mod_chanmode *change;
3454 change = find_matching_bans(&channel->banlist, actee, mask);
3459 modcmd_chanmode_announce(change);
3460 for(ii = 0; ii < change->argc; ++ii)
3461 free((char*)change->args[ii].u.hostmask);
3462 mod_chanmode_free(change);
3467 if(action & ACTION_DEL_BAN)
3469 struct banData *ban, *next;
3471 ban = channel->channel_info->bans;
3475 for( ; ban && !user_matches_glob(actee, ban->mask,
3476 MATCH_USENICK | MATCH_VISIBLE);
3479 for( ; ban && !match_ircglobs(mask, ban->mask);
3484 del_channel_ban(ban);
3491 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3493 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3499 static CHANSERV_FUNC(cmd_unban)
3501 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3504 static CHANSERV_FUNC(cmd_delban)
3506 /* it doesn't necessarily have to remove the channel ban - may want
3507 to make that an option. */
3508 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3511 static CHANSERV_FUNC(cmd_unbanme)
3513 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3514 long flags = ACTION_UNBAN;
3516 /* remove permanent bans if the user has the proper access. */
3517 if(uData->access >= UL_MASTER)
3518 flags |= ACTION_DEL_BAN;
3520 argv[1] = user->nick;
3521 return unban_user(user, channel, 2, argv, cmd, flags);
3524 static CHANSERV_FUNC(cmd_unbanall)
3526 struct mod_chanmode *change;
3529 if(!channel->banlist.used)
3531 reply("CSMSG_NO_BANS", channel->name);
3535 change = mod_chanmode_alloc(channel->banlist.used);
3536 for(ii=0; ii<channel->banlist.used; ii++)
3538 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3539 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3541 modcmd_chanmode_announce(change);
3542 for(ii = 0; ii < change->argc; ++ii)
3543 free((char*)change->args[ii].u.hostmask);
3544 mod_chanmode_free(change);
3545 reply("CSMSG_BANS_REMOVED", channel->name);
3549 static CHANSERV_FUNC(cmd_open)
3551 struct mod_chanmode *change;
3554 change = find_matching_bans(&channel->banlist, user, NULL);
3556 change = mod_chanmode_alloc(0);
3557 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3558 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3559 && channel->channel_info->modes.modes_set)
3560 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3561 modcmd_chanmode_announce(change);
3562 reply("CSMSG_CHANNEL_OPENED", channel->name);
3563 for(ii = 0; ii < change->argc; ++ii)
3564 free((char*)change->args[ii].u.hostmask);
3565 mod_chanmode_free(change);
3569 static CHANSERV_FUNC(cmd_myaccess)
3571 static struct string_buffer sbuf;
3572 struct handle_info *target_handle;
3573 struct userData *uData;
3576 target_handle = user->handle_info;
3577 else if(!IsHelping(user))
3579 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3582 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3585 if(!target_handle->channels)
3587 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3591 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3592 for(uData = target_handle->channels; uData; uData = uData->u_next)
3594 struct chanData *cData = uData->channel;
3596 if(uData->access > UL_OWNER)
3598 if(IsProtected(cData)
3599 && (target_handle != user->handle_info)
3600 && !GetTrueChannelAccess(cData, user->handle_info))
3603 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3604 if(uData->flags != USER_AUTO_OP)
3605 string_buffer_append(&sbuf, ',');
3606 if(IsUserSuspended(uData))
3607 string_buffer_append(&sbuf, 's');
3608 if(IsUserAutoOp(uData))
3610 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3611 string_buffer_append(&sbuf, 'o');
3612 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3613 string_buffer_append(&sbuf, 'v');
3615 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3616 string_buffer_append(&sbuf, 'i');
3618 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3620 string_buffer_append_string(&sbuf, ")]");
3621 string_buffer_append(&sbuf, '\0');
3622 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3628 static CHANSERV_FUNC(cmd_access)
3630 struct userNode *target;
3631 struct handle_info *target_handle;
3632 struct userData *uData;
3634 char prefix[MAXLEN];
3639 target_handle = target->handle_info;
3641 else if((target = GetUserH(argv[1])))
3643 target_handle = target->handle_info;
3645 else if(argv[1][0] == '*')
3647 if(!(target_handle = get_handle_info(argv[1]+1)))
3649 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3655 reply("MSG_NICK_UNKNOWN", argv[1]);
3659 assert(target || target_handle);
3661 if(target == chanserv)
3663 reply("CSMSG_IS_CHANSERV");
3671 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3676 reply("MSG_USER_AUTHENTICATE", target->nick);
3679 reply("MSG_AUTHENTICATE");
3685 const char *epithet = NULL, *type = NULL;
3688 epithet = chanserv_conf.irc_operator_epithet;
3689 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3691 else if(IsNetworkHelper(target))
3693 epithet = chanserv_conf.network_helper_epithet;
3694 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3696 else if(IsSupportHelper(target))
3698 epithet = chanserv_conf.support_helper_epithet;
3699 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3703 if(target_handle->epithet)
3704 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3706 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3708 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3712 sprintf(prefix, "%s", target_handle->handle);
3715 if(!channel->channel_info)
3717 reply("CSMSG_NOT_REGISTERED", channel->name);
3721 helping = HANDLE_FLAGGED(target_handle, HELPING)
3722 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3723 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3725 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3726 /* To prevent possible information leaks, only show infolines
3727 * if the requestor is in the channel or it's their own
3729 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3731 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3733 /* Likewise, only say it's suspended if the user has active
3734 * access in that channel or it's their own entry. */
3735 if(IsUserSuspended(uData)
3736 && (GetChannelUser(channel->channel_info, user->handle_info)
3737 || (user->handle_info == uData->handle)))
3739 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3744 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3751 zoot_list(struct listData *list)
3753 struct userData *uData;
3754 unsigned int start, curr, highest, lowest;
3755 struct helpfile_table tmp_table;
3756 const char **temp, *msg;
3758 if(list->table.length == 1)
3761 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3763 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3764 msg = user_find_message(list->user, "MSG_NONE");
3765 send_message_type(4, list->user, list->bot, " %s", msg);
3767 tmp_table.width = list->table.width;
3768 tmp_table.flags = list->table.flags;
3769 list->table.contents[0][0] = " ";
3770 highest = list->highest;
3771 if(list->lowest != 0)
3772 lowest = list->lowest;
3773 else if(highest < 100)
3776 lowest = highest - 100;
3777 for(start = curr = 1; curr < list->table.length; )
3779 uData = list->users[curr-1];
3780 list->table.contents[curr++][0] = " ";
3781 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3784 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3786 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3787 temp = list->table.contents[--start];
3788 list->table.contents[start] = list->table.contents[0];
3789 tmp_table.contents = list->table.contents + start;
3790 tmp_table.length = curr - start;
3791 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3792 list->table.contents[start] = temp;
3794 highest = lowest - 1;
3795 lowest = (highest < 100) ? 0 : (highest - 99);
3801 def_list(struct listData *list)
3805 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3807 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3808 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3809 if(list->table.length == 1)
3811 msg = user_find_message(list->user, "MSG_NONE");
3812 send_message_type(4, list->user, list->bot, " %s", msg);
3817 userData_access_comp(const void *arg_a, const void *arg_b)
3819 const struct userData *a = *(struct userData**)arg_a;
3820 const struct userData *b = *(struct userData**)arg_b;
3822 if(a->access != b->access)
3823 res = b->access - a->access;
3825 res = irccasecmp(a->handle->handle, b->handle->handle);
3830 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3832 void (*send_list)(struct listData *);
3833 struct userData *uData;
3834 struct listData lData;
3835 unsigned int matches;
3839 lData.bot = cmd->parent->bot;
3840 lData.channel = channel;
3841 lData.lowest = lowest;
3842 lData.highest = highest;
3843 lData.search = (argc > 1) ? argv[1] : NULL;
3844 send_list = def_list;
3845 (void)zoot_list; /* since it doesn't show user levels */
3847 if(user->handle_info)
3849 switch(user->handle_info->userlist_style)
3851 case HI_STYLE_DEF: send_list = def_list; break;
3852 case HI_STYLE_ZOOT: send_list = def_list; break;
3856 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3858 for(uData = channel->channel_info->users; uData; uData = uData->next)
3860 if((uData->access < lowest)
3861 || (uData->access > highest)
3862 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3864 lData.users[matches++] = uData;
3866 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3868 lData.table.length = matches+1;
3869 lData.table.width = 4;
3870 lData.table.flags = TABLE_NO_FREE;
3871 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3872 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3873 lData.table.contents[0] = ary;
3876 ary[2] = "Last Seen";
3878 for(matches = 1; matches < lData.table.length; ++matches)
3880 struct userData *uData = lData.users[matches-1];
3881 char seen[INTERVALLEN];
3883 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3884 lData.table.contents[matches] = ary;
3885 ary[0] = strtab(uData->access);
3886 ary[1] = uData->handle->handle;
3889 else if(!uData->seen)
3892 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3893 ary[2] = strdup(ary[2]);
3894 if(IsUserSuspended(uData))
3895 ary[3] = "Suspended";
3896 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3897 ary[3] = "Vacation";
3902 for(matches = 1; matches < lData.table.length; ++matches)
3904 free((char*)lData.table.contents[matches][2]);
3905 free(lData.table.contents[matches]);
3907 free(lData.table.contents[0]);
3908 free(lData.table.contents);
3912 static CHANSERV_FUNC(cmd_users)
3914 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3917 static CHANSERV_FUNC(cmd_wlist)
3919 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3922 static CHANSERV_FUNC(cmd_clist)
3924 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3927 static CHANSERV_FUNC(cmd_mlist)
3929 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3932 static CHANSERV_FUNC(cmd_olist)
3934 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3937 static CHANSERV_FUNC(cmd_plist)
3939 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3942 static CHANSERV_FUNC(cmd_bans)
3944 struct userNode *search_u = NULL;
3945 struct helpfile_table tbl;
3946 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3947 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3948 const char *msg_never, *triggered, *expires;
3949 struct banData *ban, **bans;
3953 else if(strchr(search = argv[1], '!'))
3956 search_wilds = search[strcspn(search, "?*")];
3958 else if(!(search_u = GetUserH(search)))
3959 reply("MSG_NICK_UNKNOWN", search);
3961 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3963 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3967 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3972 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3975 bans[matches++] = ban;
3980 tbl.length = matches + 1;
3981 tbl.width = 4 + timed;
3983 tbl.flags = TABLE_NO_FREE;
3984 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3985 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3986 tbl.contents[0][0] = "Mask";
3987 tbl.contents[0][1] = "Set By";
3988 tbl.contents[0][2] = "Triggered";
3991 tbl.contents[0][3] = "Expires";
3992 tbl.contents[0][4] = "Reason";
3995 tbl.contents[0][3] = "Reason";
3998 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4000 free(tbl.contents[0]);
4005 msg_never = user_find_message(user, "MSG_NEVER");
4006 for(ii = 0; ii < matches; )
4012 else if(ban->expires)
4013 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4015 expires = msg_never;
4018 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4020 triggered = msg_never;
4022 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4023 tbl.contents[ii][0] = ban->mask;
4024 tbl.contents[ii][1] = ban->owner;
4025 tbl.contents[ii][2] = strdup(triggered);
4028 tbl.contents[ii][3] = strdup(expires);
4029 tbl.contents[ii][4] = ban->reason;
4032 tbl.contents[ii][3] = ban->reason;
4034 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4035 reply("MSG_MATCH_COUNT", matches);
4036 for(ii = 1; ii < tbl.length; ++ii)
4038 free((char*)tbl.contents[ii][2]);
4040 free((char*)tbl.contents[ii][3]);
4041 free(tbl.contents[ii]);
4043 free(tbl.contents[0]);
4049 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4051 struct chanData *cData = channel->channel_info;
4052 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4054 if(cData->topic_mask)
4055 return !match_ircglob(new_topic, cData->topic_mask);
4056 else if(cData->topic)
4057 return irccasecmp(new_topic, cData->topic);
4062 static CHANSERV_FUNC(cmd_topic)
4064 struct chanData *cData;
4067 cData = channel->channel_info;
4072 SetChannelTopic(channel, chanserv, cData->topic, 1);
4073 reply("CSMSG_TOPIC_SET", cData->topic);
4077 reply("CSMSG_NO_TOPIC", channel->name);
4081 topic = unsplit_string(argv + 1, argc - 1, NULL);
4082 /* If they say "!topic *", use an empty topic. */
4083 if((topic[0] == '*') && (topic[1] == 0))
4085 if(bad_topic(channel, user, topic))
4087 char *topic_mask = cData->topic_mask;
4090 char new_topic[TOPICLEN+1], tchar;
4091 int pos=0, starpos=-1, dpos=0, len;
4093 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4100 len = strlen(topic);
4101 if((dpos + len) > TOPICLEN)
4102 len = TOPICLEN + 1 - dpos;
4103 memcpy(new_topic+dpos, topic, len);
4107 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4108 default: new_topic[dpos++] = tchar; break;
4111 if((dpos > TOPICLEN) || tchar)
4114 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4115 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4118 new_topic[dpos] = 0;
4119 SetChannelTopic(channel, chanserv, new_topic, 1);
4121 reply("CSMSG_TOPIC_LOCKED", channel->name);
4126 SetChannelTopic(channel, chanserv, topic, 1);
4128 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4130 /* Grab the topic and save it as the default topic. */
4132 cData->topic = strdup(channel->topic);
4138 static CHANSERV_FUNC(cmd_mode)
4140 struct userData *uData;
4141 struct mod_chanmode *change;
4146 change = &channel->channel_info->modes;
4147 if(change->modes_set || change->modes_clear) {
4148 modcmd_chanmode_announce(change);
4149 reply("CSMSG_DEFAULTED_MODES", channel->name);
4151 reply("CSMSG_NO_MODES", channel->name);
4155 uData = GetChannelUser(channel->channel_info, user->handle_info);
4157 base_oplevel = MAXOPLEVEL;
4158 else if (uData->access >= UL_OWNER)
4161 base_oplevel = 1 + UL_OWNER - uData->access;
4162 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4165 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4169 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4170 && mode_lock_violated(&channel->channel_info->modes, change))
4173 mod_chanmode_format(&channel->channel_info->modes, modes);
4174 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4178 modcmd_chanmode_announce(change);
4179 mod_chanmode_free(change);
4180 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4184 static CHANSERV_FUNC(cmd_invite)
4186 struct userData *uData;
4187 struct userNode *invite;
4189 uData = GetChannelUser(channel->channel_info, user->handle_info);
4193 if(!(invite = GetUserH(argv[1])))
4195 reply("MSG_NICK_UNKNOWN", argv[1]);
4202 if(GetUserMode(channel, invite))
4204 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4212 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4213 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4216 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4218 irc_invite(chanserv, invite, channel);
4220 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4225 static CHANSERV_FUNC(cmd_inviteme)
4227 if(GetUserMode(channel, user))
4229 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4232 if(channel->channel_info
4233 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4235 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4238 irc_invite(cmd->parent->bot, user, channel);
4243 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4246 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4248 /* We display things based on two dimensions:
4249 * - Issue time: present or absent
4250 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4251 * (in order of precedence, so something both expired and revoked
4252 * only counts as revoked)
4254 combo = (suspended->issued ? 4 : 0)
4255 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4257 case 0: /* no issue time, indefinite expiration */
4258 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4260 case 1: /* no issue time, expires in future */
4261 intervalString(buf1, suspended->expires-now, user->handle_info);
4262 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4264 case 2: /* no issue time, expired */
4265 intervalString(buf1, now-suspended->expires, user->handle_info);
4266 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4268 case 3: /* no issue time, revoked */
4269 intervalString(buf1, now-suspended->revoked, user->handle_info);
4270 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4272 case 4: /* issue time set, indefinite expiration */
4273 intervalString(buf1, now-suspended->issued, user->handle_info);
4274 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4276 case 5: /* issue time set, expires in future */
4277 intervalString(buf1, now-suspended->issued, user->handle_info);
4278 intervalString(buf2, suspended->expires-now, user->handle_info);
4279 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4281 case 6: /* issue time set, expired */
4282 intervalString(buf1, now-suspended->issued, user->handle_info);
4283 intervalString(buf2, now-suspended->expires, user->handle_info);
4284 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4286 case 7: /* issue time set, revoked */
4287 intervalString(buf1, now-suspended->issued, user->handle_info);
4288 intervalString(buf2, now-suspended->revoked, user->handle_info);
4289 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4292 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4297 static CHANSERV_FUNC(cmd_info)
4299 char modes[MAXLEN], buffer[INTERVALLEN];
4300 struct userData *uData, *owner;
4301 struct chanData *cData;
4302 struct do_not_register *dnr;
4307 cData = channel->channel_info;
4308 reply("CSMSG_CHANNEL_INFO", channel->name);
4310 uData = GetChannelUser(cData, user->handle_info);
4311 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4313 mod_chanmode_format(&cData->modes, modes);
4314 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4315 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4318 for(it = dict_first(cData->notes); it; it = iter_next(it))
4322 note = iter_data(it);
4323 if(!note_type_visible_to_user(cData, note->type, user))
4326 padding = PADLEN - 1 - strlen(iter_key(it));
4327 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4330 reply("CSMSG_CHANNEL_MAX", cData->max);
4331 for(owner = cData->users; owner; owner = owner->next)
4332 if(owner->access == UL_OWNER)
4333 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4334 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4335 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4336 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4338 privileged = IsStaff(user);
4340 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4341 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4342 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4344 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4345 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4347 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4349 struct suspended *suspended;
4350 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4351 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4352 show_suspension_info(cmd, user, suspended);
4354 else if(IsSuspended(cData))
4356 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4357 show_suspension_info(cmd, user, cData->suspended);
4362 static CHANSERV_FUNC(cmd_netinfo)
4364 extern time_t boot_time;
4365 extern unsigned long burst_length;
4366 char interval[INTERVALLEN];
4368 reply("CSMSG_NETWORK_INFO");
4369 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4370 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4371 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4372 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4373 reply("CSMSG_NETWORK_BANS", banCount);
4374 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4375 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4376 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4381 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4383 struct helpfile_table table;
4385 struct userNode *user;
4390 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4391 table.contents = alloca(list->used*sizeof(*table.contents));
4392 for(nn=0; nn<list->used; nn++)
4394 user = list->list[nn];
4395 if(user->modes & skip_flags)
4399 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4402 nick = alloca(strlen(user->nick)+3);
4403 sprintf(nick, "(%s)", user->nick);
4407 table.contents[table.length][0] = nick;
4410 table_send(chanserv, to->nick, 0, NULL, table);
4413 static CHANSERV_FUNC(cmd_ircops)
4415 reply("CSMSG_STAFF_OPERS");
4416 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4420 static CHANSERV_FUNC(cmd_helpers)
4422 reply("CSMSG_STAFF_HELPERS");
4423 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4427 static CHANSERV_FUNC(cmd_staff)
4429 reply("CSMSG_NETWORK_STAFF");
4430 cmd_ircops(CSFUNC_ARGS);
4431 cmd_helpers(CSFUNC_ARGS);
4435 static CHANSERV_FUNC(cmd_peek)
4437 struct modeNode *mn;
4438 char modes[MODELEN];
4440 struct helpfile_table table;
4442 irc_make_chanmode(channel, modes);
4444 reply("CSMSG_PEEK_INFO", channel->name);
4445 reply("CSMSG_PEEK_TOPIC", channel->topic);
4446 reply("CSMSG_PEEK_MODES", modes);
4447 reply("CSMSG_PEEK_USERS", channel->members.used);
4451 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4452 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4453 for(n = 0; n < channel->members.used; n++)
4455 mn = channel->members.list[n];
4456 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4458 table.contents[table.length] = alloca(sizeof(**table.contents));
4459 table.contents[table.length][0] = mn->user->nick;
4464 reply("CSMSG_PEEK_OPS");
4465 table_send(chanserv, user->nick, 0, NULL, table);
4468 reply("CSMSG_PEEK_NO_OPS");
4472 static MODCMD_FUNC(cmd_wipeinfo)
4474 struct handle_info *victim;
4475 struct userData *ud, *actor, *real_actor;
4476 unsigned int override = 0;
4479 actor = GetChannelUser(channel->channel_info, user->handle_info);
4480 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4481 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4483 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4485 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4488 if((ud->access >= actor->access) && (ud != actor))
4490 reply("MSG_USER_OUTRANKED", victim->handle);
4493 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4494 override = CMD_LOG_OVERRIDE;
4498 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4499 return 1 | override;
4502 static CHANSERV_FUNC(cmd_resync)
4504 struct mod_chanmode *changes;
4505 struct chanData *cData = channel->channel_info;
4506 unsigned int ii, used;
4508 changes = mod_chanmode_alloc(channel->members.used * 2);
4509 for(ii = used = 0; ii < channel->members.used; ++ii)
4511 struct modeNode *mn = channel->members.list[ii];
4512 struct userData *uData;
4514 if(IsService(mn->user))
4517 uData = GetChannelAccess(cData, mn->user->handle_info);
4518 if(!cData->lvlOpts[lvlGiveOps]
4519 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4521 if(!(mn->modes & MODE_CHANOP))
4523 changes->args[used].mode = MODE_CHANOP;
4524 changes->args[used++].u.member = mn;
4527 else if(!cData->lvlOpts[lvlGiveVoice]
4528 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4530 if(mn->modes & MODE_CHANOP)
4532 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4533 changes->args[used++].u.member = mn;
4535 if(!(mn->modes & MODE_VOICE))
4537 changes->args[used].mode = MODE_VOICE;
4538 changes->args[used++].u.member = mn;
4545 changes->args[used].mode = MODE_REMOVE | mn->modes;
4546 changes->args[used++].u.member = mn;
4550 changes->argc = used;
4551 modcmd_chanmode_announce(changes);
4552 mod_chanmode_free(changes);
4553 reply("CSMSG_RESYNCED_USERS", channel->name);
4557 static CHANSERV_FUNC(cmd_seen)
4559 struct userData *uData;
4560 struct handle_info *handle;
4561 char seen[INTERVALLEN];
4565 if(!irccasecmp(argv[1], chanserv->nick))
4567 reply("CSMSG_IS_CHANSERV");
4571 if(!(handle = get_handle_info(argv[1])))
4573 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4577 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4579 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4584 reply("CSMSG_USER_PRESENT", handle->handle);
4585 else if(uData->seen)
4586 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4588 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4590 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4591 reply("CSMSG_USER_VACATION", handle->handle);
4596 static MODCMD_FUNC(cmd_names)
4598 struct userNode *targ;
4599 struct userData *targData;
4600 unsigned int ii, pos;
4603 for(ii=pos=0; ii<channel->members.used; ++ii)
4605 targ = channel->members.list[ii]->user;
4606 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4609 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4612 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4616 if(IsUserSuspended(targData))
4618 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4621 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4622 reply("CSMSG_END_NAMES", channel->name);
4627 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4629 switch(ntype->visible_type)
4631 case NOTE_VIS_ALL: return 1;
4632 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4633 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4638 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4640 struct userData *uData;
4642 switch(ntype->set_access_type)
4644 case NOTE_SET_CHANNEL_ACCESS:
4645 if(!user->handle_info)
4647 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4649 return uData->access >= ntype->set_access.min_ulevel;
4650 case NOTE_SET_CHANNEL_SETTER:
4651 return check_user_level(channel, user, lvlSetters, 1, 0);
4652 case NOTE_SET_PRIVILEGED: default:
4653 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4657 static CHANSERV_FUNC(cmd_note)
4659 struct chanData *cData;
4661 struct note_type *ntype;
4663 cData = channel->channel_info;
4666 reply("CSMSG_NOT_REGISTERED", channel->name);
4670 /* If no arguments, show all visible notes for the channel. */
4676 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4678 note = iter_data(it);
4679 if(!note_type_visible_to_user(cData, note->type, user))
4682 reply("CSMSG_NOTELIST_HEADER", channel->name);
4683 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4686 reply("CSMSG_NOTELIST_END", channel->name);
4688 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4690 /* If one argument, show the named note. */
4693 if((note = dict_find(cData->notes, argv[1], NULL))
4694 && note_type_visible_to_user(cData, note->type, user))
4696 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4698 else if((ntype = dict_find(note_types, argv[1], NULL))
4699 && note_type_visible_to_user(NULL, ntype, user))
4701 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4706 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4710 /* Assume they're trying to set a note. */
4714 ntype = dict_find(note_types, argv[1], NULL);
4717 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4720 else if(note_type_settable_by_user(channel, ntype, user))
4722 note_text = unsplit_string(argv+2, argc-2, NULL);
4723 if((note = dict_find(cData->notes, argv[1], NULL)))
4724 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4725 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4726 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4728 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4730 /* The note is viewable to staff only, so return 0
4731 to keep the invocation from getting logged (or
4732 regular users can see it in !events). */
4738 reply("CSMSG_NO_ACCESS");
4745 static CHANSERV_FUNC(cmd_delnote)
4750 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4751 || !note_type_settable_by_user(channel, note->type, user))
4753 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4756 dict_remove(channel->channel_info->notes, note->type->name);
4757 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4761 static CHANSERV_FUNC(cmd_events)
4763 struct logSearch discrim;
4764 struct logReport report;
4765 unsigned int matches, limit;
4767 limit = (argc > 1) ? atoi(argv[1]) : 10;
4768 if(limit < 1 || limit > 200)
4771 memset(&discrim, 0, sizeof(discrim));
4772 discrim.masks.bot = chanserv;
4773 discrim.masks.channel_name = channel->name;
4775 discrim.masks.command = argv[2];
4776 discrim.limit = limit;
4777 discrim.max_time = INT_MAX;
4778 discrim.severities = 1 << LOG_COMMAND;
4779 report.reporter = chanserv;
4781 reply("CSMSG_EVENT_SEARCH_RESULTS");
4782 matches = log_entry_search(&discrim, log_report_entry, &report);
4784 reply("MSG_MATCH_COUNT", matches);
4786 reply("MSG_NO_MATCHES");
4790 static CHANSERV_FUNC(cmd_say)
4796 msg = unsplit_string(argv + 1, argc - 1, NULL);
4797 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4799 else if(GetUserH(argv[1]))
4802 msg = unsplit_string(argv + 2, argc - 2, NULL);
4803 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4807 reply("MSG_NOT_TARGET_NAME");
4813 static CHANSERV_FUNC(cmd_emote)
4819 /* CTCP is so annoying. */
4820 msg = unsplit_string(argv + 1, argc - 1, NULL);
4821 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4823 else if(GetUserH(argv[1]))
4825 msg = unsplit_string(argv + 2, argc - 2, NULL);
4826 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4830 reply("MSG_NOT_TARGET_NAME");
4836 struct channelList *
4837 chanserv_support_channels(void)
4839 return &chanserv_conf.support_channels;
4842 static CHANSERV_FUNC(cmd_expire)
4844 int channel_count = registered_channels;
4845 expire_channels(NULL);
4846 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4851 chanserv_expire_suspension(void *data)
4853 struct suspended *suspended = data;
4854 struct chanNode *channel;
4856 if(!suspended->expires || (now < suspended->expires))
4857 suspended->revoked = now;
4858 channel = suspended->cData->channel;
4859 suspended->cData->channel = channel;
4860 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4861 if(!IsOffChannel(suspended->cData))
4863 struct mod_chanmode change;
4864 mod_chanmode_init(&change);
4866 change.args[0].mode = MODE_CHANOP;
4867 change.args[0].u.member = AddChannelUser(chanserv, channel);
4868 mod_chanmode_announce(chanserv, channel, &change);
4872 static CHANSERV_FUNC(cmd_csuspend)
4874 struct suspended *suspended;
4875 char reason[MAXLEN];
4876 time_t expiry, duration;
4877 struct userData *uData;
4881 if(IsProtected(channel->channel_info))
4883 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4887 if(argv[1][0] == '!')
4889 else if(IsSuspended(channel->channel_info))
4891 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4892 show_suspension_info(cmd, user, channel->channel_info->suspended);
4896 if(!strcmp(argv[1], "0"))
4898 else if((duration = ParseInterval(argv[1])))
4899 expiry = now + duration;
4902 reply("MSG_INVALID_DURATION", argv[1]);
4906 unsplit_string(argv + 2, argc - 2, reason);
4908 suspended = calloc(1, sizeof(*suspended));
4909 suspended->revoked = 0;
4910 suspended->issued = now;
4911 suspended->suspender = strdup(user->handle_info->handle);
4912 suspended->expires = expiry;
4913 suspended->reason = strdup(reason);
4914 suspended->cData = channel->channel_info;
4915 suspended->previous = suspended->cData->suspended;
4916 suspended->cData->suspended = suspended;
4918 if(suspended->expires)
4919 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4921 if(IsSuspended(channel->channel_info))
4923 suspended->previous->revoked = now;
4924 if(suspended->previous->expires)
4925 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4926 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4927 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4931 /* Mark all users in channel as absent. */
4932 for(uData = channel->channel_info->users; uData; uData = uData->next)
4941 /* Mark the channel as suspended, then part. */
4942 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4943 DelChannelUser(chanserv, channel, suspended->reason, 0);
4944 reply("CSMSG_SUSPENDED", channel->name);
4945 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4946 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4951 static CHANSERV_FUNC(cmd_cunsuspend)
4953 struct suspended *suspended;
4954 char message[MAXLEN];
4956 if(!IsSuspended(channel->channel_info))
4958 reply("CSMSG_NOT_SUSPENDED", channel->name);
4962 suspended = channel->channel_info->suspended;
4964 /* Expire the suspension and join ChanServ to the channel. */
4965 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4966 chanserv_expire_suspension(suspended);
4967 reply("CSMSG_UNSUSPENDED", channel->name);
4968 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4969 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4973 typedef struct chanservSearch
4981 unsigned long flags;
4985 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4988 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4993 search = malloc(sizeof(struct chanservSearch));
4994 memset(search, 0, sizeof(*search));
4997 for(i = 0; i < argc; i++)
4999 /* Assume all criteria require arguments. */
5002 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5006 if(!irccasecmp(argv[i], "name"))
5007 search->name = argv[++i];
5008 else if(!irccasecmp(argv[i], "registrar"))
5009 search->registrar = argv[++i];
5010 else if(!irccasecmp(argv[i], "unvisited"))
5011 search->unvisited = ParseInterval(argv[++i]);
5012 else if(!irccasecmp(argv[i], "registered"))
5013 search->registered = ParseInterval(argv[++i]);
5014 else if(!irccasecmp(argv[i], "flags"))
5017 if(!irccasecmp(argv[i], "nodelete"))
5018 search->flags |= CHANNEL_NODELETE;
5019 else if(!irccasecmp(argv[i], "suspended"))
5020 search->flags |= CHANNEL_SUSPENDED;
5021 else if(!irccasecmp(argv[i], "unreviewed"))
5022 search->flags |= CHANNEL_UNREVIEWED;
5025 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5029 else if(!irccasecmp(argv[i], "limit"))
5030 search->limit = strtoul(argv[++i], NULL, 10);
5033 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5038 if(search->name && !strcmp(search->name, "*"))
5040 if(search->registrar && !strcmp(search->registrar, "*"))
5041 search->registrar = 0;
5050 chanserv_channel_match(struct chanData *channel, search_t search)
5052 const char *name = channel->channel->name;
5053 if((search->name && !match_ircglob(name, search->name)) ||
5054 (search->registrar && !channel->registrar) ||
5055 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5056 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5057 (search->registered && (now - channel->registered) > search->registered) ||
5058 (search->flags && ((search->flags & channel->flags) != search->flags)))
5065 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5067 struct chanData *channel;
5068 unsigned int matches = 0;
5070 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5072 if(!chanserv_channel_match(channel, search))
5082 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5087 search_print(struct chanData *channel, void *data)
5089 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5092 static CHANSERV_FUNC(cmd_search)
5095 unsigned int matches;
5096 channel_search_func action;
5100 if(!irccasecmp(argv[1], "count"))
5101 action = search_count;
5102 else if(!irccasecmp(argv[1], "print"))
5103 action = search_print;
5106 reply("CSMSG_ACTION_INVALID", argv[1]);
5110 search = chanserv_search_create(user, argc - 2, argv + 2);
5114 if(action == search_count)
5115 search->limit = INT_MAX;
5117 if(action == search_print)
5118 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5120 matches = chanserv_channel_search(search, action, user);
5123 reply("MSG_MATCH_COUNT", matches);
5125 reply("MSG_NO_MATCHES");
5131 static CHANSERV_FUNC(cmd_unvisited)
5133 struct chanData *cData;
5134 time_t interval = chanserv_conf.channel_expire_delay;
5135 char buffer[INTERVALLEN];
5136 unsigned int limit = 25, matches = 0;
5140 interval = ParseInterval(argv[1]);
5142 limit = atoi(argv[2]);
5145 intervalString(buffer, interval, user->handle_info);
5146 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5148 for(cData = channelList; cData && matches < limit; cData = cData->next)
5150 if((now - cData->visited) < interval)
5153 intervalString(buffer, now - cData->visited, user->handle_info);
5154 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5161 static MODCMD_FUNC(chan_opt_defaulttopic)
5167 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5169 reply("CSMSG_TOPIC_LOCKED", channel->name);
5173 topic = unsplit_string(argv+1, argc-1, NULL);
5175 free(channel->channel_info->topic);
5176 if(topic[0] == '*' && topic[1] == 0)
5178 topic = channel->channel_info->topic = NULL;
5182 topic = channel->channel_info->topic = strdup(topic);
5183 if(channel->channel_info->topic_mask
5184 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5185 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5187 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5190 if(channel->channel_info->topic)
5191 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5193 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5197 static MODCMD_FUNC(chan_opt_topicmask)
5201 struct chanData *cData = channel->channel_info;
5204 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5206 reply("CSMSG_TOPIC_LOCKED", channel->name);
5210 mask = unsplit_string(argv+1, argc-1, NULL);
5212 if(cData->topic_mask)
5213 free(cData->topic_mask);
5214 if(mask[0] == '*' && mask[1] == 0)
5216 cData->topic_mask = 0;
5220 cData->topic_mask = strdup(mask);
5222 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5223 else if(!match_ircglob(cData->topic, cData->topic_mask))
5224 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5228 if(channel->channel_info->topic_mask)
5229 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5231 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5235 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5239 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5243 if(greeting[0] == '*' && greeting[1] == 0)
5247 unsigned int length = strlen(greeting);
5248 if(length > chanserv_conf.greeting_length)
5250 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5253 *data = strdup(greeting);
5262 reply(name, user_find_message(user, "MSG_NONE"));
5266 static MODCMD_FUNC(chan_opt_greeting)
5268 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5271 static MODCMD_FUNC(chan_opt_usergreeting)
5273 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5276 static MODCMD_FUNC(chan_opt_modes)
5278 struct mod_chanmode *new_modes;
5279 char modes[MODELEN];
5283 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5285 reply("CSMSG_NO_ACCESS");
5288 if(argv[1][0] == '*' && argv[1][1] == 0)
5290 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5292 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5294 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5297 else if(new_modes->argc > 1)
5299 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5300 mod_chanmode_free(new_modes);
5305 channel->channel_info->modes = *new_modes;
5306 modcmd_chanmode_announce(new_modes);
5307 mod_chanmode_free(new_modes);
5311 mod_chanmode_format(&channel->channel_info->modes, modes);
5313 reply("CSMSG_SET_MODES", modes);
5315 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5319 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5321 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5323 struct chanData *cData = channel->channel_info;
5328 /* Set flag according to value. */
5329 if(enabled_string(argv[1]))
5331 cData->flags |= mask;
5334 else if(disabled_string(argv[1]))
5336 cData->flags &= ~mask;
5341 reply("MSG_INVALID_BINARY", argv[1]);
5347 /* Find current option value. */
5348 value = (cData->flags & mask) ? 1 : 0;
5352 reply(name, user_find_message(user, "MSG_ON"));
5354 reply(name, user_find_message(user, "MSG_OFF"));
5358 static MODCMD_FUNC(chan_opt_nodelete)
5360 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5362 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5366 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5369 static MODCMD_FUNC(chan_opt_dynlimit)
5371 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5374 static MODCMD_FUNC(chan_opt_offchannel)
5376 struct chanData *cData = channel->channel_info;
5381 /* Set flag according to value. */
5382 if(enabled_string(argv[1]))
5384 if(!IsOffChannel(cData))
5385 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5386 cData->flags |= CHANNEL_OFFCHANNEL;
5389 else if(disabled_string(argv[1]))
5391 if(IsOffChannel(cData))
5393 struct mod_chanmode change;
5394 mod_chanmode_init(&change);
5396 change.args[0].mode = MODE_CHANOP;
5397 change.args[0].u.member = AddChannelUser(chanserv, channel);
5398 mod_chanmode_announce(chanserv, channel, &change);
5400 cData->flags &= ~CHANNEL_OFFCHANNEL;
5405 reply("MSG_INVALID_BINARY", argv[1]);
5411 /* Find current option value. */
5412 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5416 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5418 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5422 static MODCMD_FUNC(chan_opt_unreviewed)
5424 struct chanData *cData = channel->channel_info;
5425 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5431 /* The two directions can have different ACLs. */
5432 if(enabled_string(argv[1]))
5434 else if(disabled_string(argv[1]))
5438 reply("MSG_INVALID_BINARY", argv[1]);
5442 if (new_value != value)
5444 struct svccmd *subcmd;
5445 char subcmd_name[32];
5447 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5448 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5451 reply("MSG_COMMAND_DISABLED", subcmd_name);
5454 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5458 cData->flags |= CHANNEL_UNREVIEWED;
5461 free(cData->registrar);
5462 cData->registrar = strdup(user->handle_info->handle);
5463 cData->flags &= ~CHANNEL_UNREVIEWED;
5470 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5472 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5476 static MODCMD_FUNC(chan_opt_defaults)
5478 struct userData *uData;
5479 struct chanData *cData;
5480 const char *confirm;
5481 enum levelOption lvlOpt;
5482 enum charOption chOpt;
5484 cData = channel->channel_info;
5485 uData = GetChannelUser(cData, user->handle_info);
5486 if(!uData || (uData->access < UL_OWNER))
5488 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5491 confirm = make_confirmation_string(uData);
5492 if((argc < 2) || strcmp(argv[1], confirm))
5494 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5497 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5498 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5499 cData->modes = chanserv_conf.default_modes;
5500 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5501 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5502 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5503 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5504 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5509 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5511 struct chanData *cData = channel->channel_info;
5512 struct userData *uData;
5513 unsigned short value;
5517 if(!check_user_level(channel, user, option, 1, 1))
5519 reply("CSMSG_CANNOT_SET");
5522 value = user_level_from_name(argv[1], UL_OWNER+1);
5523 if(!value && strcmp(argv[1], "0"))
5525 reply("CSMSG_INVALID_ACCESS", argv[1]);
5528 uData = GetChannelUser(cData, user->handle_info);
5529 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5531 reply("CSMSG_BAD_SETLEVEL");
5537 if(value > cData->lvlOpts[lvlGiveOps])
5539 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5544 if(value < cData->lvlOpts[lvlGiveVoice])
5546 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5551 /* This test only applies to owners, since non-owners
5552 * trying to set an option to above their level get caught
5553 * by the CSMSG_BAD_SETLEVEL test above.
5555 if(value > uData->access)
5557 reply("CSMSG_BAD_SETTERS");
5564 cData->lvlOpts[option] = value;
5566 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5570 static MODCMD_FUNC(chan_opt_enfops)
5572 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5575 static MODCMD_FUNC(chan_opt_giveops)
5577 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5580 static MODCMD_FUNC(chan_opt_enfmodes)
5582 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5585 static MODCMD_FUNC(chan_opt_enftopic)
5587 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5590 static MODCMD_FUNC(chan_opt_pubcmd)
5592 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5595 static MODCMD_FUNC(chan_opt_setters)
5597 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5600 static MODCMD_FUNC(chan_opt_ctcpusers)
5602 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5605 static MODCMD_FUNC(chan_opt_userinfo)
5607 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5610 static MODCMD_FUNC(chan_opt_givevoice)
5612 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5615 static MODCMD_FUNC(chan_opt_topicsnarf)
5617 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5620 static MODCMD_FUNC(chan_opt_inviteme)
5622 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5626 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5628 struct chanData *cData = channel->channel_info;
5629 int count = charOptions[option].count, index;
5633 index = atoi(argv[1]);
5635 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5637 reply("CSMSG_INVALID_NUMERIC", index);
5638 /* Show possible values. */
5639 for(index = 0; index < count; index++)
5640 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5644 cData->chOpts[option] = charOptions[option].values[index].value;
5648 /* Find current option value. */
5651 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5655 /* Somehow, the option value is corrupt; reset it to the default. */
5656 cData->chOpts[option] = charOptions[option].default_value;
5661 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5665 static MODCMD_FUNC(chan_opt_protect)
5667 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5670 static MODCMD_FUNC(chan_opt_toys)
5672 return channel_multiple_option(chToys, CSFUNC_ARGS);
5675 static MODCMD_FUNC(chan_opt_ctcpreaction)
5677 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5680 static MODCMD_FUNC(chan_opt_topicrefresh)
5682 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5685 static struct svccmd_list set_shows_list;
5688 handle_svccmd_unbind(struct svccmd *target) {
5690 for(ii=0; ii<set_shows_list.used; ++ii)
5691 if(target == set_shows_list.list[ii])
5692 set_shows_list.used = 0;
5695 static CHANSERV_FUNC(cmd_set)
5697 struct svccmd *subcmd;
5701 /* Check if we need to (re-)initialize set_shows_list. */
5702 if(!set_shows_list.used)
5704 if(!set_shows_list.size)
5706 set_shows_list.size = chanserv_conf.set_shows->used;
5707 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5709 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5711 const char *name = chanserv_conf.set_shows->list[ii];
5712 sprintf(buf, "%s %s", argv[0], name);
5713 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5716 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5719 svccmd_list_append(&set_shows_list, subcmd);
5725 reply("CSMSG_CHANNEL_OPTIONS");
5726 for(ii = 0; ii < set_shows_list.used; ii++)
5728 subcmd = set_shows_list.list[ii];
5729 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5734 sprintf(buf, "%s %s", argv[0], argv[1]);
5735 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5738 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5741 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5743 reply("CSMSG_NO_ACCESS");
5749 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5753 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5755 struct userData *uData;
5757 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5760 reply("CSMSG_NOT_USER", channel->name);
5766 /* Just show current option value. */
5768 else if(enabled_string(argv[1]))
5770 uData->flags |= mask;
5772 else if(disabled_string(argv[1]))
5774 uData->flags &= ~mask;
5778 reply("MSG_INVALID_BINARY", argv[1]);
5782 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5786 static MODCMD_FUNC(user_opt_noautoop)
5788 struct userData *uData;
5790 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5793 reply("CSMSG_NOT_USER", channel->name);
5796 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5797 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5799 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5802 static MODCMD_FUNC(user_opt_autoinvite)
5804 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5807 static MODCMD_FUNC(user_opt_info)
5809 struct userData *uData;
5812 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5816 /* If they got past the command restrictions (which require access)
5817 * but fail this test, we have some fool with security override on.
5819 reply("CSMSG_NOT_USER", channel->name);
5826 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5827 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5829 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5832 bp = strcspn(infoline, "\001");
5835 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5840 if(infoline[0] == '*' && infoline[1] == 0)
5843 uData->info = strdup(infoline);
5846 reply("CSMSG_USET_INFO", uData->info);
5848 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5852 struct svccmd_list uset_shows_list;
5854 static CHANSERV_FUNC(cmd_uset)
5856 struct svccmd *subcmd;
5860 /* Check if we need to (re-)initialize uset_shows_list. */
5861 if(!uset_shows_list.used)
5865 "NoAutoOp", "AutoInvite", "Info"
5868 if(!uset_shows_list.size)
5870 uset_shows_list.size = ArrayLength(options);
5871 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5873 for(ii = 0; ii < ArrayLength(options); ii++)
5875 const char *name = options[ii];
5876 sprintf(buf, "%s %s", argv[0], name);
5877 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5880 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5883 svccmd_list_append(&uset_shows_list, subcmd);
5889 /* Do this so options are presented in a consistent order. */
5890 reply("CSMSG_USER_OPTIONS");
5891 for(ii = 0; ii < uset_shows_list.used; ii++)
5892 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5896 sprintf(buf, "%s %s", argv[0], argv[1]);
5897 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5900 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5904 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5907 static CHANSERV_FUNC(cmd_giveownership)
5909 struct handle_info *new_owner_hi;
5910 struct userData *new_owner;
5911 struct userData *curr_user;
5912 struct userData *invoker;
5913 struct chanData *cData = channel->channel_info;
5914 struct do_not_register *dnr;
5915 const char *confirm;
5917 unsigned short co_access;
5918 char reason[MAXLEN];
5921 curr_user = GetChannelAccess(cData, user->handle_info);
5922 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5923 if(!curr_user || (curr_user->access != UL_OWNER))
5925 struct userData *owner = NULL;
5926 for(curr_user = channel->channel_info->users;
5928 curr_user = curr_user->next)
5930 if(curr_user->access != UL_OWNER)
5934 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5941 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5943 char delay[INTERVALLEN];
5944 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5945 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5948 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5950 if(new_owner_hi == user->handle_info)
5952 reply("CSMSG_NO_TRANSFER_SELF");
5955 new_owner = GetChannelAccess(cData, new_owner_hi);
5960 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
5964 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5968 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5970 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5973 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5974 if(!IsHelping(user))
5975 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5977 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5980 invoker = GetChannelUser(cData, user->handle_info);
5981 if(invoker->access <= UL_OWNER)
5983 confirm = make_confirmation_string(curr_user);
5984 if((argc < 3) || strcmp(argv[2], confirm))
5986 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5990 if(new_owner->access >= UL_COOWNER)
5991 co_access = new_owner->access;
5993 co_access = UL_COOWNER;
5994 new_owner->access = UL_OWNER;
5996 curr_user->access = co_access;
5997 cData->ownerTransfer = now;
5998 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5999 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6000 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6004 static CHANSERV_FUNC(cmd_suspend)
6006 struct handle_info *hi;
6007 struct userData *self, *real_self, *target;
6008 unsigned int override = 0;
6011 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6012 self = GetChannelUser(channel->channel_info, user->handle_info);
6013 real_self = GetChannelAccess(channel->channel_info, 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 >= self->access)
6021 reply("MSG_USER_OUTRANKED", hi->handle);
6024 if(target->flags & USER_SUSPENDED)
6026 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6031 target->present = 0;
6034 if(!real_self || target->access >= real_self->access)
6035 override = CMD_LOG_OVERRIDE;
6036 target->flags |= USER_SUSPENDED;
6037 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6038 return 1 | override;
6041 static CHANSERV_FUNC(cmd_unsuspend)
6043 struct handle_info *hi;
6044 struct userData *self, *real_self, *target;
6045 unsigned int override = 0;
6048 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6049 self = GetChannelUser(channel->channel_info, user->handle_info);
6050 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6051 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6053 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6056 if(target->access >= self->access)
6058 reply("MSG_USER_OUTRANKED", hi->handle);
6061 if(!(target->flags & USER_SUSPENDED))
6063 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6066 if(!real_self || target->access >= real_self->access)
6067 override = CMD_LOG_OVERRIDE;
6068 target->flags &= ~USER_SUSPENDED;
6069 scan_user_presence(target, NULL);
6070 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6071 return 1 | override;
6074 static MODCMD_FUNC(cmd_deleteme)
6076 struct handle_info *hi;
6077 struct userData *target;
6078 const char *confirm_string;
6079 unsigned short access;
6082 hi = user->handle_info;
6083 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6085 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6088 if(target->access == UL_OWNER)
6090 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6093 confirm_string = make_confirmation_string(target);
6094 if((argc < 2) || strcmp(argv[1], confirm_string))
6096 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6099 access = target->access;
6100 channel_name = strdup(channel->name);
6101 del_channel_user(target, 1);
6102 reply("CSMSG_DELETED_YOU", access, channel_name);
6108 chanserv_refresh_topics(UNUSED_ARG(void *data))
6110 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6111 struct chanData *cData;
6114 for(cData = channelList; cData; cData = cData->next)
6116 if(IsSuspended(cData))
6118 opt = cData->chOpts[chTopicRefresh];
6121 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6124 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6125 cData->last_refresh = refresh_num;
6127 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6130 static CHANSERV_FUNC(cmd_unf)
6134 char response[MAXLEN];
6135 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6136 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6137 irc_privmsg(cmd->parent->bot, channel->name, response);
6140 reply("CSMSG_UNF_RESPONSE");
6144 static CHANSERV_FUNC(cmd_ping)
6148 char response[MAXLEN];
6149 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6150 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6151 irc_privmsg(cmd->parent->bot, channel->name, response);
6154 reply("CSMSG_PING_RESPONSE");
6158 static CHANSERV_FUNC(cmd_wut)
6162 char response[MAXLEN];
6163 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6164 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6165 irc_privmsg(cmd->parent->bot, channel->name, response);
6168 reply("CSMSG_WUT_RESPONSE");
6172 static CHANSERV_FUNC(cmd_8ball)
6174 unsigned int i, j, accum;
6179 for(i=1; i<argc; i++)
6180 for(j=0; argv[i][j]; j++)
6181 accum = (accum << 5) - accum + toupper(argv[i][j]);
6182 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6185 char response[MAXLEN];
6186 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6187 irc_privmsg(cmd->parent->bot, channel->name, response);
6190 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6194 static CHANSERV_FUNC(cmd_d)
6196 unsigned long sides, count, modifier, ii, total;
6197 char response[MAXLEN], *sep;
6201 if((count = strtoul(argv[1], &sep, 10)) < 1)
6211 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6212 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6216 else if((sep[0] == '-') && isdigit(sep[1]))
6217 modifier = strtoul(sep, NULL, 10);
6218 else if((sep[0] == '+') && isdigit(sep[1]))
6219 modifier = strtoul(sep+1, NULL, 10);
6226 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6231 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6234 for(total = ii = 0; ii < count; ++ii)
6235 total += (rand() % sides) + 1;
6238 if((count > 1) || modifier)
6240 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6241 sprintf(response, fmt, total, count, sides, modifier);
6245 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6246 sprintf(response, fmt, total, sides);
6249 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6251 send_message_type(4, user, cmd->parent->bot, "%s", response);
6255 static CHANSERV_FUNC(cmd_huggle)
6257 /* CTCP must be via PRIVMSG, never notice */
6259 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6261 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6266 chanserv_adjust_limit(void *data)
6268 struct mod_chanmode change;
6269 struct chanData *cData = data;
6270 struct chanNode *channel = cData->channel;
6273 if(IsSuspended(cData))
6276 cData->limitAdjusted = now;
6277 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6278 if(cData->modes.modes_set & MODE_LIMIT)
6280 if(limit > cData->modes.new_limit)
6281 limit = cData->modes.new_limit;
6282 else if(limit == cData->modes.new_limit)
6286 mod_chanmode_init(&change);
6287 change.modes_set = MODE_LIMIT;
6288 change.new_limit = limit;
6289 mod_chanmode_announce(chanserv, channel, &change);
6293 handle_new_channel(struct chanNode *channel)
6295 struct chanData *cData;
6297 if(!(cData = channel->channel_info))
6300 if(cData->modes.modes_set || cData->modes.modes_clear)
6301 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6303 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6304 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6307 /* Welcome to my worst nightmare. Warning: Read (or modify)
6308 the code below at your own risk. */
6310 handle_join(struct modeNode *mNode)
6312 struct mod_chanmode change;
6313 struct userNode *user = mNode->user;
6314 struct chanNode *channel = mNode->channel;
6315 struct chanData *cData;
6316 struct userData *uData = NULL;
6317 struct banData *bData;
6318 struct handle_info *handle;
6319 unsigned int modes = 0, info = 0;
6322 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6325 cData = channel->channel_info;
6326 if(channel->members.used > cData->max)
6327 cData->max = channel->members.used;
6329 /* Check for bans. If they're joining through a ban, one of two
6331 * 1: Join during a netburst, by riding the break. Kick them
6332 * unless they have ops or voice in the channel.
6333 * 2: They're allowed to join through the ban (an invite in
6334 * ircu2.10, or a +e on Hybrid, or something).
6335 * If they're not joining through a ban, and the banlist is not
6336 * full, see if they're on the banlist for the channel. If so,
6339 if(user->uplink->burst && !mNode->modes)
6342 for(ii = 0; ii < channel->banlist.used; ii++)
6344 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6346 /* Riding a netburst. Naughty. */
6347 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6353 mod_chanmode_init(&change);
6355 if(channel->banlist.used < MAXBANS)
6357 /* Not joining through a ban. */
6358 for(bData = cData->bans;
6359 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6360 bData = bData->next);
6364 char kick_reason[MAXLEN];
6365 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6367 bData->triggered = now;
6368 if(bData != cData->bans)
6370 /* Shuffle the ban to the head of the list. */
6372 bData->next->prev = bData->prev;
6374 bData->prev->next = bData->next;
6377 bData->next = cData->bans;
6380 cData->bans->prev = bData;
6381 cData->bans = bData;
6384 change.args[0].mode = MODE_BAN;
6385 change.args[0].u.hostmask = bData->mask;
6386 mod_chanmode_announce(chanserv, channel, &change);
6387 KickChannelUser(user, channel, chanserv, kick_reason);
6392 /* ChanServ will not modify the limits in join-flooded channels.
6393 It will also skip DynLimit processing when the user (or srvx)
6394 is bursting in, because there are likely more incoming. */
6395 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6396 && !user->uplink->burst
6397 && !channel->join_flooded
6398 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6400 /* The user count has begun "bumping" into the channel limit,
6401 so set a timer to raise the limit a bit. Any previous
6402 timers are removed so three incoming users within the delay
6403 results in one limit change, not three. */
6405 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6406 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6409 if(channel->join_flooded)
6411 /* don't automatically give ops or voice during a join flood */
6413 else if(cData->lvlOpts[lvlGiveOps] == 0)
6414 modes |= MODE_CHANOP;
6415 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6416 modes |= MODE_VOICE;
6418 greeting = cData->greeting;
6419 if(user->handle_info)
6421 handle = user->handle_info;
6423 if(IsHelper(user) && !IsHelping(user))
6426 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6428 if(channel == chanserv_conf.support_channels.list[ii])
6430 HANDLE_SET_FLAG(user->handle_info, HELPING);
6436 uData = GetTrueChannelAccess(cData, handle);
6437 if(uData && !IsUserSuspended(uData))
6439 /* Ops and above were handled by the above case. */
6440 if(IsUserAutoOp(uData))
6442 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6443 modes |= MODE_CHANOP;
6444 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6445 modes |= MODE_VOICE;
6447 if(uData->access >= UL_PRESENT)
6448 cData->visited = now;
6449 if(cData->user_greeting)
6450 greeting = cData->user_greeting;
6452 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6453 && ((now - uData->seen) >= chanserv_conf.info_delay)
6461 /* If user joining normally (not during burst), apply op or voice,
6462 * and send greeting/userinfo as appropriate.
6464 if(!user->uplink->burst)
6468 if(modes & MODE_CHANOP)
6469 modes &= ~MODE_VOICE;
6470 change.args[0].mode = modes;
6471 change.args[0].u.member = mNode;
6472 mod_chanmode_announce(chanserv, channel, &change);
6475 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6477 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6483 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6485 struct mod_chanmode change;
6486 struct userData *channel;
6487 unsigned int ii, jj;
6489 if(!user->handle_info)
6492 mod_chanmode_init(&change);
6494 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6496 struct chanNode *cn;
6497 struct modeNode *mn;
6498 if(IsUserSuspended(channel)
6499 || IsSuspended(channel->channel)
6500 || !(cn = channel->channel->channel))
6503 mn = GetUserMode(cn, user);
6506 if(!IsUserSuspended(channel)
6507 && IsUserAutoInvite(channel)
6508 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6510 && !user->uplink->burst)
6511 irc_invite(chanserv, user, cn);
6515 if(channel->access >= UL_PRESENT)
6516 channel->channel->visited = now;
6518 if(IsUserAutoOp(channel))
6520 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6521 change.args[0].mode = MODE_CHANOP;
6522 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6523 change.args[0].mode = MODE_VOICE;
6525 change.args[0].mode = 0;
6526 change.args[0].u.member = mn;
6527 if(change.args[0].mode)
6528 mod_chanmode_announce(chanserv, cn, &change);
6531 channel->seen = now;
6532 channel->present = 1;
6535 for(ii = 0; ii < user->channels.used; ++ii)
6537 struct chanNode *channel = user->channels.list[ii]->channel;
6538 struct banData *ban;
6540 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6541 || !channel->channel_info
6542 || IsSuspended(channel->channel_info))
6544 for(jj = 0; jj < channel->banlist.used; ++jj)
6545 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6547 if(jj < channel->banlist.used)
6549 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6551 char kick_reason[MAXLEN];
6552 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6554 change.args[0].mode = MODE_BAN;
6555 change.args[0].u.hostmask = ban->mask;
6556 mod_chanmode_announce(chanserv, channel, &change);
6557 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6558 KickChannelUser(user, channel, chanserv, kick_reason);
6559 ban->triggered = now;
6564 if(IsSupportHelper(user))
6566 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6568 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6570 HANDLE_SET_FLAG(user->handle_info, HELPING);
6578 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6580 struct chanData *cData;
6581 struct userData *uData;
6583 cData = mn->channel->channel_info;
6584 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6587 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6589 /* Allow for a bit of padding so that the limit doesn't
6590 track the user count exactly, which could get annoying. */
6591 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6593 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6594 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6598 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6600 scan_user_presence(uData, mn->user);
6602 if (uData->access >= UL_PRESENT)
6603 cData->visited = now;
6606 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6608 unsigned int ii, jj;
6609 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6611 for(jj = 0; jj < mn->user->channels.used; ++jj)
6612 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6614 if(jj < mn->user->channels.used)
6617 if(ii == chanserv_conf.support_channels.used)
6618 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6623 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6625 struct userData *uData;
6627 if(!channel->channel_info || !kicker || IsService(kicker)
6628 || (kicker == victim) || IsSuspended(channel->channel_info)
6629 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6632 if(protect_user(victim, kicker, channel->channel_info))
6634 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6635 KickChannelUser(kicker, channel, chanserv, reason);
6638 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6643 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6645 struct chanData *cData;
6647 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6650 cData = channel->channel_info;
6651 if(bad_topic(channel, user, channel->topic))
6653 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6654 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6655 SetChannelTopic(channel, chanserv, old_topic, 1);
6656 else if(cData->topic)
6657 SetChannelTopic(channel, chanserv, cData->topic, 1);
6660 /* With topicsnarf, grab the topic and save it as the default topic. */
6661 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6664 cData->topic = strdup(channel->topic);
6670 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6672 struct mod_chanmode *bounce = NULL;
6673 unsigned int bnc, ii;
6676 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6679 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6680 && mode_lock_violated(&channel->channel_info->modes, change))
6682 char correct[MAXLEN];
6683 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6684 mod_chanmode_format(&channel->channel_info->modes, correct);
6685 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6687 for(ii = bnc = 0; ii < change->argc; ++ii)
6689 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6691 const struct userNode *victim = change->args[ii].u.member->user;
6692 if(!protect_user(victim, user, channel->channel_info))
6695 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6698 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6699 bounce->args[bnc].u.member = GetUserMode(channel, user);
6700 if(bounce->args[bnc].u.member)
6704 bounce->args[bnc].mode = MODE_CHANOP;
6705 bounce->args[bnc].u.member = change->args[ii].u.member;
6707 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6709 else if(change->args[ii].mode & MODE_CHANOP)
6711 const struct userNode *victim = change->args[ii].u.member->user;
6712 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6715 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6716 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6717 bounce->args[bnc].u.member = change->args[ii].u.member;
6720 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6722 const char *ban = change->args[ii].u.hostmask;
6723 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6726 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6727 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6728 bounce->args[bnc].u.hostmask = strdup(ban);
6730 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6735 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6736 mod_chanmode_announce(chanserv, channel, bounce);
6737 for(ii = 0; ii < change->argc; ++ii)
6738 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6739 free((char*)bounce->args[ii].u.hostmask);
6740 mod_chanmode_free(bounce);
6745 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6747 struct chanNode *channel;
6748 struct banData *bData;
6749 struct mod_chanmode change;
6750 unsigned int ii, jj;
6751 char kick_reason[MAXLEN];
6753 mod_chanmode_init(&change);
6755 change.args[0].mode = MODE_BAN;
6756 for(ii = 0; ii < user->channels.used; ++ii)
6758 channel = user->channels.list[ii]->channel;
6759 /* Need not check for bans if they're opped or voiced. */
6760 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6762 /* Need not check for bans unless channel registration is active. */
6763 if(!channel->channel_info || IsSuspended(channel->channel_info))
6765 /* Look for a matching ban already on the channel. */
6766 for(jj = 0; jj < channel->banlist.used; ++jj)
6767 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6769 /* Need not act if we found one. */
6770 if(jj < channel->banlist.used)
6772 /* Look for a matching ban in this channel. */
6773 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6775 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6777 change.args[0].u.hostmask = bData->mask;
6778 mod_chanmode_announce(chanserv, channel, &change);
6779 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6780 KickChannelUser(user, channel, chanserv, kick_reason);
6781 bData->triggered = now;
6782 break; /* we don't need to check any more bans in the channel */
6787 static void handle_rename(struct handle_info *handle, const char *old_handle)
6789 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6793 dict_remove2(handle_dnrs, old_handle, 1);
6794 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6795 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6800 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6802 struct userNode *h_user;
6804 if(handle->channels)
6806 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6807 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6809 while(handle->channels)
6810 del_channel_user(handle->channels, 1);
6815 handle_server_link(UNUSED_ARG(struct server *server))
6817 struct chanData *cData;
6819 for(cData = channelList; cData; cData = cData->next)
6821 if(!IsSuspended(cData))
6822 cData->may_opchan = 1;
6823 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6824 && !cData->channel->join_flooded
6825 && ((cData->channel->limit - cData->channel->members.used)
6826 < chanserv_conf.adjust_threshold))
6828 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6829 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6835 chanserv_conf_read(void)
6839 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6840 struct mod_chanmode *change;
6841 struct string_list *strlist;
6842 struct chanNode *chan;
6845 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6847 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6850 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6851 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6852 chanserv_conf.support_channels.used = 0;
6853 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6855 for(ii = 0; ii < strlist->used; ++ii)
6857 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6860 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6862 channelList_append(&chanserv_conf.support_channels, chan);
6865 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6868 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6871 chan = AddChannel(str, now, str2, NULL);
6873 channelList_append(&chanserv_conf.support_channels, chan);
6875 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6876 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6877 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6878 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6879 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6880 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6881 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6882 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6883 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6884 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6885 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6886 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6887 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6888 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6889 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6890 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6891 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6892 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6893 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6894 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6895 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6896 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6897 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6898 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6899 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6901 NickChange(chanserv, str, 0);
6902 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6903 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6904 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6905 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6906 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6907 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6908 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6909 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6910 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6911 chanserv_conf.max_owned = str ? atoi(str) : 5;
6912 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6913 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6914 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6915 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6916 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6917 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6918 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6921 safestrncpy(mode_line, str, sizeof(mode_line));
6922 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6923 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6924 && (change->argc < 2))
6926 chanserv_conf.default_modes = *change;
6927 mod_chanmode_free(change);
6929 free_string_list(chanserv_conf.set_shows);
6930 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6932 strlist = string_list_copy(strlist);
6935 static const char *list[] = {
6936 /* free form text */
6937 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6938 /* options based on user level */
6939 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6940 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6941 /* multiple choice options */
6942 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6943 /* binary options */
6944 "DynLimit", "NoDelete",
6949 strlist = alloc_string_list(ArrayLength(list)-1);
6950 for(ii=0; list[ii]; ii++)
6951 string_list_append(strlist, strdup(list[ii]));
6953 chanserv_conf.set_shows = strlist;
6954 /* We don't look things up now, in case the list refers to options
6955 * defined by modules initialized after this point. Just mark the
6956 * function list as invalid, so it will be initialized.
6958 set_shows_list.used = 0;
6959 free_string_list(chanserv_conf.eightball);
6960 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6963 strlist = string_list_copy(strlist);
6967 strlist = alloc_string_list(4);
6968 string_list_append(strlist, strdup("Yes."));
6969 string_list_append(strlist, strdup("No."));
6970 string_list_append(strlist, strdup("Maybe so."));
6972 chanserv_conf.eightball = strlist;
6973 free_string_list(chanserv_conf.old_ban_names);
6974 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6976 strlist = string_list_copy(strlist);
6978 strlist = alloc_string_list(2);
6979 chanserv_conf.old_ban_names = strlist;
6980 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6981 off_channel = str ? atoi(str) : 0;
6985 chanserv_note_type_read(const char *key, struct record_data *rd)
6988 struct note_type *ntype;
6991 if(!(obj = GET_RECORD_OBJECT(rd)))
6993 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6996 if(!(ntype = chanserv_create_note_type(key)))
6998 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7002 /* Figure out set access */
7003 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7005 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7006 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7008 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7010 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7011 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7013 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7015 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7019 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7020 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7021 ntype->set_access.min_opserv = 0;
7024 /* Figure out visibility */
7025 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7026 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7027 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7028 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7029 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7030 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7031 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7032 ntype->visible_type = NOTE_VIS_ALL;
7034 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7036 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7037 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7041 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7043 struct handle_info *handle;
7044 struct userData *uData;
7045 char *seen, *inf, *flags;
7047 unsigned short access;
7049 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7051 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7055 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7056 if(access > UL_OWNER)
7058 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7062 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7063 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7064 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7065 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7066 handle = get_handle_info(key);
7069 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7073 uData = add_channel_user(chan, handle, access, last_seen, inf);
7074 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7078 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7080 struct banData *bData;
7081 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7082 time_t set_time, triggered_time, expires_time;
7084 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7086 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7090 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7091 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7092 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7093 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7094 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7095 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7096 if (!reason || !owner)
7099 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7100 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7102 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7104 expires_time = set_time + atoi(s_duration);
7108 if(!reason || (expires_time && (expires_time < now)))
7111 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7114 static struct suspended *
7115 chanserv_read_suspended(dict_t obj)
7117 struct suspended *suspended = calloc(1, sizeof(*suspended));
7121 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7122 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7123 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7124 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7125 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7126 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7127 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7128 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7129 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7130 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7135 chanserv_channel_read(const char *key, struct record_data *hir)
7137 struct suspended *suspended;
7138 struct mod_chanmode *modes;
7139 struct chanNode *cNode;
7140 struct chanData *cData;
7141 struct dict *channel, *obj;
7142 char *str, *argv[10];
7146 channel = hir->d.object;
7148 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7151 cNode = AddChannel(key, now, NULL, NULL);
7154 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7157 cData = register_channel(cNode, str);
7160 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7164 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7166 enum levelOption lvlOpt;
7167 enum charOption chOpt;
7169 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7170 cData->flags = atoi(str);
7172 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7174 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7176 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7177 else if(levelOptions[lvlOpt].old_flag)
7179 if(cData->flags & levelOptions[lvlOpt].old_flag)
7180 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7182 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7186 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7188 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7190 cData->chOpts[chOpt] = str[0];
7193 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7195 enum levelOption lvlOpt;
7196 enum charOption chOpt;
7199 cData->flags = base64toint(str, 5);
7200 count = strlen(str += 5);
7201 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7204 if(levelOptions[lvlOpt].old_flag)
7206 if(cData->flags & levelOptions[lvlOpt].old_flag)
7207 lvl = levelOptions[lvlOpt].flag_value;
7209 lvl = levelOptions[lvlOpt].default_value;
7211 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7213 case 'c': lvl = UL_COOWNER; break;
7214 case 'm': lvl = UL_MASTER; break;
7215 case 'n': lvl = UL_OWNER+1; break;
7216 case 'o': lvl = UL_OP; break;
7217 case 'p': lvl = UL_PEON; break;
7218 case 'w': lvl = UL_OWNER; break;
7219 default: lvl = 0; break;
7221 cData->lvlOpts[lvlOpt] = lvl;
7223 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7224 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7227 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7229 suspended = chanserv_read_suspended(obj);
7230 cData->suspended = suspended;
7231 suspended->cData = cData;
7232 /* We could use suspended->expires and suspended->revoked to
7233 * set the CHANNEL_SUSPENDED flag, but we don't. */
7235 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7237 suspended = calloc(1, sizeof(*suspended));
7238 suspended->issued = 0;
7239 suspended->revoked = 0;
7240 suspended->suspender = strdup(str);
7241 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7242 suspended->expires = str ? atoi(str) : 0;
7243 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7244 suspended->reason = strdup(str ? str : "No reason");
7245 suspended->previous = NULL;
7246 cData->suspended = suspended;
7247 suspended->cData = cData;
7251 cData->flags &= ~CHANNEL_SUSPENDED;
7252 suspended = NULL; /* to squelch a warning */
7255 if(IsSuspended(cData)) {
7256 if(suspended->expires > now)
7257 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7258 else if(suspended->expires)
7259 cData->flags &= ~CHANNEL_SUSPENDED;
7262 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7263 struct mod_chanmode change;
7264 mod_chanmode_init(&change);
7266 change.args[0].mode = MODE_CHANOP;
7267 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7268 mod_chanmode_announce(chanserv, cNode, &change);
7271 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7272 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7273 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7274 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7275 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7276 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7277 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7278 cData->max = str ? atoi(str) : 0;
7279 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7280 cData->greeting = str ? strdup(str) : NULL;
7281 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7282 cData->user_greeting = str ? strdup(str) : NULL;
7283 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7284 cData->topic_mask = str ? strdup(str) : NULL;
7285 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7286 cData->topic = str ? strdup(str) : NULL;
7288 if(!IsSuspended(cData)
7289 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7290 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7291 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7292 cData->modes = *modes;
7294 cData->modes.modes_set |= MODE_REGISTERED;
7295 if(cData->modes.argc > 1)
7296 cData->modes.argc = 1;
7297 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7298 mod_chanmode_free(modes);
7301 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7302 for(it = dict_first(obj); it; it = iter_next(it))
7303 user_read_helper(iter_key(it), iter_data(it), cData);
7305 if(!cData->users && !IsProtected(cData))
7307 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7308 unregister_channel(cData, "has empty user list.");
7312 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7313 for(it = dict_first(obj); it; it = iter_next(it))
7314 ban_read_helper(iter_key(it), iter_data(it), cData);
7316 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7317 for(it = dict_first(obj); it; it = iter_next(it))
7319 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7320 struct record_data *rd = iter_data(it);
7321 const char *note, *setter;
7323 if(rd->type != RECDB_OBJECT)
7325 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7329 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7331 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7333 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7337 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7338 if(!setter) setter = "<unknown>";
7339 chanserv_add_channel_note(cData, ntype, setter, note);
7347 chanserv_dnr_read(const char *key, struct record_data *hir)
7349 const char *setter, *reason, *str;
7350 struct do_not_register *dnr;
7353 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7356 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7359 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7362 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7365 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7366 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7367 if(expiry && expiry <= now)
7369 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7372 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7374 dnr->set = atoi(str);
7380 chanserv_saxdb_read(struct dict *database)
7382 struct dict *section;
7385 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7386 for(it = dict_first(section); it; it = iter_next(it))
7387 chanserv_note_type_read(iter_key(it), iter_data(it));
7389 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7390 for(it = dict_first(section); it; it = iter_next(it))
7391 chanserv_channel_read(iter_key(it), iter_data(it));
7393 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7394 for(it = dict_first(section); it; it = iter_next(it))
7395 chanserv_dnr_read(iter_key(it), iter_data(it));
7401 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7403 int high_present = 0;
7404 saxdb_start_record(ctx, KEY_USERS, 1);
7405 for(; uData; uData = uData->next)
7407 if((uData->access >= UL_PRESENT) && uData->present)
7409 saxdb_start_record(ctx, uData->handle->handle, 0);
7410 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7411 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7413 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7415 saxdb_write_string(ctx, KEY_INFO, uData->info);
7416 saxdb_end_record(ctx);
7418 saxdb_end_record(ctx);
7419 return high_present;
7423 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7427 saxdb_start_record(ctx, KEY_BANS, 1);
7428 for(; bData; bData = bData->next)
7430 saxdb_start_record(ctx, bData->mask, 0);
7431 saxdb_write_int(ctx, KEY_SET, bData->set);
7432 if(bData->triggered)
7433 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7435 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7437 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7439 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7440 saxdb_end_record(ctx);
7442 saxdb_end_record(ctx);
7446 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7448 saxdb_start_record(ctx, name, 0);
7449 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7450 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7452 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7454 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7456 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7458 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7459 saxdb_end_record(ctx);
7463 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7467 enum levelOption lvlOpt;
7468 enum charOption chOpt;
7470 saxdb_start_record(ctx, channel->channel->name, 1);
7472 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7473 saxdb_write_int(ctx, KEY_MAX, channel->max);
7475 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7476 if(channel->registrar)
7477 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7478 if(channel->greeting)
7479 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7480 if(channel->user_greeting)
7481 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7482 if(channel->topic_mask)
7483 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7484 if(channel->suspended)
7485 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7487 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7488 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7489 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7490 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7491 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7493 buf[0] = channel->chOpts[chOpt];
7495 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7497 saxdb_end_record(ctx);
7499 if(channel->modes.modes_set || channel->modes.modes_clear)
7501 mod_chanmode_format(&channel->modes, buf);
7502 saxdb_write_string(ctx, KEY_MODES, buf);
7505 high_present = chanserv_write_users(ctx, channel->users);
7506 chanserv_write_bans(ctx, channel->bans);
7508 if(dict_size(channel->notes))
7512 saxdb_start_record(ctx, KEY_NOTES, 1);
7513 for(it = dict_first(channel->notes); it; it = iter_next(it))
7515 struct note *note = iter_data(it);
7516 saxdb_start_record(ctx, iter_key(it), 0);
7517 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7518 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7519 saxdb_end_record(ctx);
7521 saxdb_end_record(ctx);
7524 if(channel->ownerTransfer)
7525 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7526 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7527 saxdb_end_record(ctx);
7531 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7535 saxdb_start_record(ctx, ntype->name, 0);
7536 switch(ntype->set_access_type)
7538 case NOTE_SET_CHANNEL_ACCESS:
7539 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7541 case NOTE_SET_CHANNEL_SETTER:
7542 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7544 case NOTE_SET_PRIVILEGED: default:
7545 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7548 switch(ntype->visible_type)
7550 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7551 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7552 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7554 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7555 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7556 saxdb_end_record(ctx);
7560 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7562 struct do_not_register *dnr;
7563 dict_iterator_t it, next;
7565 for(it = dict_first(dnrs); it; it = next)
7567 next = iter_next(it);
7568 dnr = iter_data(it);
7569 if(dnr->expires && dnr->expires <= now)
7571 dict_remove(dnrs, iter_key(it));
7574 saxdb_start_record(ctx, dnr->chan_name, 0);
7576 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7578 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7579 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7580 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7581 saxdb_end_record(ctx);
7586 chanserv_saxdb_write(struct saxdb_context *ctx)
7589 struct chanData *channel;
7592 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7593 for(it = dict_first(note_types); it; it = iter_next(it))
7594 chanserv_write_note_type(ctx, iter_data(it));
7595 saxdb_end_record(ctx);
7598 saxdb_start_record(ctx, KEY_DNR, 1);
7599 write_dnrs_helper(ctx, handle_dnrs);
7600 write_dnrs_helper(ctx, plain_dnrs);
7601 write_dnrs_helper(ctx, mask_dnrs);
7602 saxdb_end_record(ctx);
7605 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7606 for(channel = channelList; channel; channel = channel->next)
7607 chanserv_write_channel(ctx, channel);
7608 saxdb_end_record(ctx);
7614 chanserv_db_cleanup(void) {
7616 unreg_part_func(handle_part);
7618 unregister_channel(channelList, "terminating.");
7619 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7620 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7621 free(chanserv_conf.support_channels.list);
7622 dict_delete(handle_dnrs);
7623 dict_delete(plain_dnrs);
7624 dict_delete(mask_dnrs);
7625 dict_delete(note_types);
7626 free_string_list(chanserv_conf.eightball);
7627 free_string_list(chanserv_conf.old_ban_names);
7628 free_string_list(chanserv_conf.set_shows);
7629 free(set_shows_list.list);
7630 free(uset_shows_list.list);
7633 struct userData *helper = helperList;
7634 helperList = helperList->next;
7639 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7640 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7641 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7644 init_chanserv(const char *nick)
7646 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7647 conf_register_reload(chanserv_conf_read);
7651 reg_server_link_func(handle_server_link);
7652 reg_new_channel_func(handle_new_channel);
7653 reg_join_func(handle_join);
7654 reg_part_func(handle_part);
7655 reg_kick_func(handle_kick);
7656 reg_topic_func(handle_topic);
7657 reg_mode_change_func(handle_mode);
7658 reg_nick_change_func(handle_nick_change);
7659 reg_auth_func(handle_auth);
7662 reg_handle_rename_func(handle_rename);
7663 reg_unreg_func(handle_unreg);
7665 handle_dnrs = dict_new();
7666 dict_set_free_data(handle_dnrs, free);
7667 plain_dnrs = dict_new();
7668 dict_set_free_data(plain_dnrs, free);
7669 mask_dnrs = dict_new();
7670 dict_set_free_data(mask_dnrs, free);
7672 reg_svccmd_unbind_func(handle_svccmd_unbind);
7673 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7674 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7675 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7676 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7677 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7678 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7679 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7680 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7681 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7682 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7683 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7684 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7685 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7687 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7688 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7690 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7691 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7692 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7693 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7694 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7696 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7697 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7698 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7699 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7700 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7702 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7703 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7704 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7705 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7707 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7708 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7709 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7710 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7711 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7712 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7713 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7714 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7716 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7717 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7718 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7719 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7720 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7721 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7722 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7723 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7724 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7725 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7726 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7727 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7728 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7729 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7731 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7732 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7733 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7734 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7735 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7737 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7738 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7740 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7741 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7742 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7743 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7744 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7745 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7746 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7747 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7748 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7749 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7750 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7752 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7753 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7755 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7756 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7757 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7758 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7760 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7761 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7762 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7763 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7764 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7766 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7767 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7768 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7769 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7770 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7771 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7773 /* Channel options */
7774 DEFINE_CHANNEL_OPTION(defaulttopic);
7775 DEFINE_CHANNEL_OPTION(topicmask);
7776 DEFINE_CHANNEL_OPTION(greeting);
7777 DEFINE_CHANNEL_OPTION(usergreeting);
7778 DEFINE_CHANNEL_OPTION(modes);
7779 DEFINE_CHANNEL_OPTION(enfops);
7780 DEFINE_CHANNEL_OPTION(giveops);
7781 DEFINE_CHANNEL_OPTION(protect);
7782 DEFINE_CHANNEL_OPTION(enfmodes);
7783 DEFINE_CHANNEL_OPTION(enftopic);
7784 DEFINE_CHANNEL_OPTION(pubcmd);
7785 DEFINE_CHANNEL_OPTION(givevoice);
7786 DEFINE_CHANNEL_OPTION(userinfo);
7787 DEFINE_CHANNEL_OPTION(dynlimit);
7788 DEFINE_CHANNEL_OPTION(topicsnarf);
7789 DEFINE_CHANNEL_OPTION(nodelete);
7790 DEFINE_CHANNEL_OPTION(toys);
7791 DEFINE_CHANNEL_OPTION(setters);
7792 DEFINE_CHANNEL_OPTION(topicrefresh);
7793 DEFINE_CHANNEL_OPTION(ctcpusers);
7794 DEFINE_CHANNEL_OPTION(ctcpreaction);
7795 DEFINE_CHANNEL_OPTION(inviteme);
7796 DEFINE_CHANNEL_OPTION(unreviewed);
7797 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7798 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7800 DEFINE_CHANNEL_OPTION(offchannel);
7801 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7803 /* Alias set topic to set defaulttopic for compatibility. */
7804 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7807 DEFINE_USER_OPTION(noautoop);
7808 DEFINE_USER_OPTION(autoinvite);
7809 DEFINE_USER_OPTION(info);
7811 /* Alias uset autovoice to uset autoop. */
7812 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7814 note_types = dict_new();
7815 dict_set_free_data(note_types, chanserv_deref_note_type);
7818 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7819 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7820 service_register(chanserv)->trigger = '!';
7821 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7823 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7825 if(chanserv_conf.channel_expire_frequency)
7826 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7828 if(chanserv_conf.dnr_expire_frequency)
7829 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7831 if(chanserv_conf.refresh_period)
7833 time_t next_refresh;
7834 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7835 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7838 reg_exit_func(chanserv_db_cleanup);
7839 message_register_table(msgtab);