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, 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(search->chan_mask, dnr->chan_name))
1741 || (search->setter_mask
1742 && !match_ircglob(search->setter_mask, dnr->setter))
1743 || (search->reason_mask
1744 && !match_ircglob(search->reason_mask, dnr->reason)));
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);
1921 static MODCMD_FUNC(cmd_dnrsearch)
1923 struct dnr_search *discrim;
1924 dnr_search_func action;
1925 struct svccmd *subcmd;
1926 unsigned int matches;
1929 sprintf(buf, "dnrsearch %s", argv[1]);
1930 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1933 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1936 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1938 if(!irccasecmp(argv[1], "print"))
1939 action = dnr_print_func;
1940 else if(!irccasecmp(argv[1], "remove"))
1941 action = dnr_remove_func;
1944 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1948 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1952 if(action == dnr_print_func)
1953 reply("CSMSG_DNR_SEARCH_RESULTS");
1954 matches = dnr_search(discrim, action, user);
1956 reply("MSG_MATCH_COUNT", matches);
1958 reply("MSG_NO_MATCHES");
1964 chanserv_get_owned_count(struct handle_info *hi)
1966 struct userData *cList;
1969 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1970 if(cList->access == UL_OWNER)
1975 static CHANSERV_FUNC(cmd_register)
1977 struct handle_info *handle;
1978 struct chanData *cData;
1979 struct modeNode *mn;
1980 char reason[MAXLEN];
1982 unsigned int new_channel, force=0;
1983 struct do_not_register *dnr;
1987 if(channel->channel_info)
1989 reply("CSMSG_ALREADY_REGGED", channel->name);
1993 if(channel->bad_channel)
1995 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2000 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2002 reply("CSMSG_MUST_BE_OPPED", channel->name);
2007 chan_name = channel->name;
2011 if((argc < 2) || !IsChannelName(argv[1]))
2013 reply("MSG_NOT_CHANNEL_NAME");
2017 if(opserv_bad_channel(argv[1]))
2019 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2024 chan_name = argv[1];
2027 if(argc >= (new_channel+2))
2029 if(!IsHelping(user))
2031 reply("CSMSG_PROXY_FORBIDDEN");
2035 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2037 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2038 dnr = chanserv_is_dnr(chan_name, handle);
2042 handle = user->handle_info;
2043 dnr = chanserv_is_dnr(chan_name, handle);
2047 if(!IsHelping(user))
2048 reply("CSMSG_DNR_CHANNEL", chan_name);
2050 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2054 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2056 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2061 channel = AddChannel(argv[1], now, NULL, NULL);
2063 cData = register_channel(channel, user->handle_info->handle);
2064 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2065 cData->modes = chanserv_conf.default_modes;
2067 cData->modes.modes_set |= MODE_REGISTERED;
2068 if (IsOffChannel(cData))
2070 mod_chanmode_announce(chanserv, channel, &cData->modes);
2074 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2075 change->args[change->argc].mode = MODE_CHANOP;
2076 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2078 mod_chanmode_announce(chanserv, channel, change);
2079 mod_chanmode_free(change);
2082 /* Initialize the channel's max user record. */
2083 cData->max = channel->members.used;
2085 if(handle != user->handle_info)
2086 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2088 reply("CSMSG_REG_SUCCESS", channel->name);
2090 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2091 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2096 make_confirmation_string(struct userData *uData)
2098 static char strbuf[16];
2103 for(src = uData->handle->handle; *src; )
2104 accum = accum * 31 + toupper(*src++);
2106 for(src = uData->channel->channel->name; *src; )
2107 accum = accum * 31 + toupper(*src++);
2108 sprintf(strbuf, "%08x", accum);
2112 static CHANSERV_FUNC(cmd_unregister)
2115 char reason[MAXLEN];
2116 struct chanData *cData;
2117 struct userData *uData;
2119 cData = channel->channel_info;
2122 reply("CSMSG_NOT_REGISTERED", channel->name);
2126 uData = GetChannelUser(cData, user->handle_info);
2127 if(!uData || (uData->access < UL_OWNER))
2129 reply("CSMSG_NO_ACCESS");
2133 if(IsProtected(cData))
2135 reply("CSMSG_UNREG_NODELETE", channel->name);
2139 if(!IsHelping(user))
2141 const char *confirm_string;
2142 if(IsSuspended(cData))
2144 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2147 confirm_string = make_confirmation_string(uData);
2148 if((argc < 2) || strcmp(argv[1], confirm_string))
2150 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2155 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2156 name = strdup(channel->name);
2157 unregister_channel(cData, reason);
2158 reply("CSMSG_UNREG_SUCCESS", name);
2163 static CHANSERV_FUNC(cmd_move)
2165 struct mod_chanmode change;
2166 struct chanNode *target;
2167 struct modeNode *mn;
2168 struct userData *uData;
2169 char reason[MAXLEN];
2170 struct do_not_register *dnr;
2174 if(IsProtected(channel->channel_info))
2176 reply("CSMSG_MOVE_NODELETE", channel->name);
2180 if(!IsChannelName(argv[1]))
2182 reply("MSG_NOT_CHANNEL_NAME");
2186 if(opserv_bad_channel(argv[1]))
2188 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2192 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2194 for(uData = channel->channel_info->users; uData; uData = uData->next)
2196 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2198 if(!IsHelping(user))
2199 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2201 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2207 mod_chanmode_init(&change);
2208 if(!(target = GetChannel(argv[1])))
2210 target = AddChannel(argv[1], now, NULL, NULL);
2211 if(!IsSuspended(channel->channel_info))
2212 AddChannelUser(chanserv, target);
2214 else if(target->channel_info)
2216 reply("CSMSG_ALREADY_REGGED", target->name);
2219 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2220 && !IsHelping(user))
2222 reply("CSMSG_MUST_BE_OPPED", target->name);
2225 else if(!IsSuspended(channel->channel_info))
2228 change.args[0].mode = MODE_CHANOP;
2229 change.args[0].u.member = AddChannelUser(chanserv, target);
2230 mod_chanmode_announce(chanserv, target, &change);
2235 /* Clear MODE_REGISTERED from old channel, add it to new. */
2237 change.modes_clear = MODE_REGISTERED;
2238 mod_chanmode_announce(chanserv, channel, &change);
2239 change.modes_clear = 0;
2240 change.modes_set = MODE_REGISTERED;
2241 mod_chanmode_announce(chanserv, target, &change);
2244 /* Move the channel_info to the target channel; it
2245 shouldn't be necessary to clear timeq callbacks
2246 for the old channel. */
2247 target->channel_info = channel->channel_info;
2248 target->channel_info->channel = target;
2249 channel->channel_info = NULL;
2251 reply("CSMSG_MOVE_SUCCESS", target->name);
2253 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2254 if(!IsSuspended(target->channel_info))
2256 char reason2[MAXLEN];
2257 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2258 DelChannelUser(chanserv, channel, reason2, 0);
2260 UnlockChannel(channel);
2261 LockChannel(target);
2262 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2267 merge_users(struct chanData *source, struct chanData *target)
2269 struct userData *suData, *tuData, *next;
2275 /* Insert the source's users into the scratch area. */
2276 for(suData = source->users; suData; suData = suData->next)
2277 dict_insert(merge, suData->handle->handle, suData);
2279 /* Iterate through the target's users, looking for
2280 users common to both channels. The lower access is
2281 removed from either the scratch area or target user
2283 for(tuData = target->users; tuData; tuData = next)
2285 struct userData *choice;
2287 next = tuData->next;
2289 /* If a source user exists with the same handle as a target
2290 channel's user, resolve the conflict by removing one. */
2291 suData = dict_find(merge, tuData->handle->handle, NULL);
2295 /* Pick the data we want to keep. */
2296 /* If the access is the same, use the later seen time. */
2297 if(suData->access == tuData->access)
2298 choice = (suData->seen > tuData->seen) ? suData : tuData;
2299 else /* Otherwise, keep the higher access level. */
2300 choice = (suData->access > tuData->access) ? suData : tuData;
2302 /* Remove the user that wasn't picked. */
2303 if(choice == tuData)
2305 dict_remove(merge, suData->handle->handle);
2306 del_channel_user(suData, 0);
2309 del_channel_user(tuData, 0);
2312 /* Move the remaining users to the target channel. */
2313 for(it = dict_first(merge); it; it = iter_next(it))
2315 suData = iter_data(it);
2317 /* Insert the user into the target channel's linked list. */
2318 suData->prev = NULL;
2319 suData->next = target->users;
2320 suData->channel = target;
2323 target->users->prev = suData;
2324 target->users = suData;
2326 /* Update the user counts for the target channel; the
2327 source counts are left alone. */
2328 target->userCount++;
2331 /* Possible to assert (source->users == NULL) here. */
2332 source->users = NULL;
2337 merge_bans(struct chanData *source, struct chanData *target)
2339 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2341 /* Hold on to the original head of the target ban list
2342 to avoid comparing source bans with source bans. */
2343 tFront = target->bans;
2345 /* Perform a totally expensive O(n*m) merge, ick. */
2346 for(sbData = source->bans; sbData; sbData = sNext)
2348 /* Flag to track whether the ban's been moved
2349 to the destination yet. */
2352 /* Possible to assert (sbData->prev == NULL) here. */
2353 sNext = sbData->next;
2355 for(tbData = tFront; tbData; tbData = tNext)
2357 tNext = tbData->next;
2359 /* Perform two comparisons between each source
2360 and target ban, conflicts are resolved by
2361 keeping the broader ban and copying the later
2362 expiration and triggered time. */
2363 if(match_ircglobs(tbData->mask, sbData->mask))
2365 /* There is a broader ban in the target channel that
2366 overrides one in the source channel; remove the
2367 source ban and break. */
2368 if(sbData->expires > tbData->expires)
2369 tbData->expires = sbData->expires;
2370 if(sbData->triggered > tbData->triggered)
2371 tbData->triggered = sbData->triggered;
2372 del_channel_ban(sbData);
2375 else if(match_ircglobs(sbData->mask, tbData->mask))
2377 /* There is a broader ban in the source channel that
2378 overrides one in the target channel; remove the
2379 target ban, fall through and move the source over. */
2380 if(tbData->expires > sbData->expires)
2381 sbData->expires = tbData->expires;
2382 if(tbData->triggered > sbData->triggered)
2383 sbData->triggered = tbData->triggered;
2384 if(tbData == tFront)
2386 del_channel_ban(tbData);
2389 /* Source bans can override multiple target bans, so
2390 we allow a source to run through this loop multiple
2391 times, but we can only move it once. */
2396 /* Remove the source ban from the source ban list. */
2398 sbData->next->prev = sbData->prev;
2400 /* Modify the source ban's associated channel. */
2401 sbData->channel = target;
2403 /* Insert the ban into the target channel's linked list. */
2404 sbData->prev = NULL;
2405 sbData->next = target->bans;
2408 target->bans->prev = sbData;
2409 target->bans = sbData;
2411 /* Update the user counts for the target channel. */
2416 /* Possible to assert (source->bans == NULL) here. */
2417 source->bans = NULL;
2421 merge_data(struct chanData *source, struct chanData *target)
2423 /* Use more recent visited and owner-transfer time; use older
2424 * registered time. Bitwise or may_opchan. Use higher max.
2425 * Do not touch last_refresh, ban count or user counts.
2427 if(source->visited > target->visited)
2428 target->visited = source->visited;
2429 if(source->registered < target->registered)
2430 target->registered = source->registered;
2431 if(source->ownerTransfer > target->ownerTransfer)
2432 target->ownerTransfer = source->ownerTransfer;
2433 if(source->may_opchan)
2434 target->may_opchan = 1;
2435 if(source->max > target->max)
2436 target->max = source->max;
2440 merge_channel(struct chanData *source, struct chanData *target)
2442 merge_users(source, target);
2443 merge_bans(source, target);
2444 merge_data(source, target);
2447 static CHANSERV_FUNC(cmd_merge)
2449 struct userData *target_user;
2450 struct chanNode *target;
2451 char reason[MAXLEN];
2455 /* Make sure the target channel exists and is registered to the user
2456 performing the command. */
2457 if(!(target = GetChannel(argv[1])))
2459 reply("MSG_INVALID_CHANNEL");
2463 if(!target->channel_info)
2465 reply("CSMSG_NOT_REGISTERED", target->name);
2469 if(IsProtected(channel->channel_info))
2471 reply("CSMSG_MERGE_NODELETE");
2475 if(IsSuspended(target->channel_info))
2477 reply("CSMSG_MERGE_SUSPENDED");
2481 if(channel == target)
2483 reply("CSMSG_MERGE_SELF");
2487 target_user = GetChannelUser(target->channel_info, user->handle_info);
2488 if(!target_user || (target_user->access < UL_OWNER))
2490 reply("CSMSG_MERGE_NOT_OWNER");
2494 /* Merge the channel structures and associated data. */
2495 merge_channel(channel->channel_info, target->channel_info);
2496 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2497 unregister_channel(channel->channel_info, reason);
2498 reply("CSMSG_MERGE_SUCCESS", target->name);
2502 static CHANSERV_FUNC(cmd_opchan)
2504 struct mod_chanmode change;
2505 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2507 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2510 channel->channel_info->may_opchan = 0;
2511 mod_chanmode_init(&change);
2513 change.args[0].mode = MODE_CHANOP;
2514 change.args[0].u.member = GetUserMode(channel, chanserv);
2515 mod_chanmode_announce(chanserv, channel, &change);
2516 reply("CSMSG_OPCHAN_DONE", channel->name);
2520 static CHANSERV_FUNC(cmd_adduser)
2522 struct userData *actee;
2523 struct userData *actor, *real_actor;
2524 struct handle_info *handle;
2525 unsigned short access, override = 0;
2529 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2531 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2535 access = user_level_from_name(argv[2], UL_OWNER);
2538 reply("CSMSG_INVALID_ACCESS", argv[2]);
2542 actor = GetChannelUser(channel->channel_info, user->handle_info);
2543 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2545 if(actor->access <= access)
2547 reply("CSMSG_NO_BUMP_ACCESS");
2551 /* Trying to add someone with equal/more access? */
2552 if (!real_actor || real_actor->access <= access)
2553 override = CMD_LOG_OVERRIDE;
2555 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2558 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2560 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2564 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2565 scan_user_presence(actee, NULL);
2566 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2567 return 1 | override;
2570 static CHANSERV_FUNC(cmd_clvl)
2572 struct handle_info *handle;
2573 struct userData *victim;
2574 struct userData *actor, *real_actor;
2575 unsigned short new_access, override = 0;
2576 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2580 actor = GetChannelUser(channel->channel_info, user->handle_info);
2581 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2583 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2586 if(handle == user->handle_info && !privileged)
2588 reply("CSMSG_NO_SELF_CLVL");
2592 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2594 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2598 if(actor->access <= victim->access && !privileged)
2600 reply("MSG_USER_OUTRANKED", handle->handle);
2604 new_access = user_level_from_name(argv[2], UL_OWNER);
2608 reply("CSMSG_INVALID_ACCESS", argv[2]);
2612 if(new_access >= actor->access && !privileged)
2614 reply("CSMSG_NO_BUMP_ACCESS");
2618 /* Trying to clvl a equal/higher user? */
2619 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2620 override = CMD_LOG_OVERRIDE;
2621 /* Trying to clvl someone to equal/higher access? */
2622 if(!real_actor || new_access >= real_actor->access)
2623 override = CMD_LOG_OVERRIDE;
2624 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2625 * If they lower their own access it's not a big problem.
2628 victim->access = new_access;
2629 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2630 return 1 | override;
2633 static CHANSERV_FUNC(cmd_deluser)
2635 struct handle_info *handle;
2636 struct userData *victim;
2637 struct userData *actor, *real_actor;
2638 unsigned short access, override = 0;
2643 actor = GetChannelUser(channel->channel_info, user->handle_info);
2644 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2646 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2649 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2651 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2657 access = user_level_from_name(argv[1], UL_OWNER);
2660 reply("CSMSG_INVALID_ACCESS", argv[1]);
2663 if(access != victim->access)
2665 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2671 access = victim->access;
2674 if((actor->access <= victim->access) && !IsHelping(user))
2676 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2680 /* If people delete themselves it is an override, but they
2681 * could've used deleteme so we don't log it as an override
2683 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2684 override = CMD_LOG_OVERRIDE;
2686 chan_name = strdup(channel->name);
2687 del_channel_user(victim, 1);
2688 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2690 return 1 | override;
2694 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2696 struct userData *actor, *real_actor, *uData, *next;
2697 unsigned int override = 0;
2699 actor = GetChannelUser(channel->channel_info, user->handle_info);
2700 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2702 if(min_access > max_access)
2704 reply("CSMSG_BAD_RANGE", min_access, max_access);
2708 if((actor->access <= max_access) && !IsHelping(user))
2710 reply("CSMSG_NO_ACCESS");
2714 if(!real_actor || real_actor->access <= max_access)
2715 override = CMD_LOG_OVERRIDE;
2717 for(uData = channel->channel_info->users; uData; uData = next)
2721 if((uData->access >= min_access)
2722 && (uData->access <= max_access)
2723 && match_ircglob(uData->handle->handle, mask))
2724 del_channel_user(uData, 1);
2727 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2728 return 1 | override;
2731 static CHANSERV_FUNC(cmd_mdelowner)
2733 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2736 static CHANSERV_FUNC(cmd_mdelcoowner)
2738 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2741 static CHANSERV_FUNC(cmd_mdelmaster)
2743 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2746 static CHANSERV_FUNC(cmd_mdelop)
2748 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2751 static CHANSERV_FUNC(cmd_mdelpeon)
2753 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2757 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2759 struct banData *bData, *next;
2760 char interval[INTERVALLEN];
2765 limit = now - duration;
2766 for(bData = channel->channel_info->bans; bData; bData = next)
2770 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2773 del_channel_ban(bData);
2777 intervalString(interval, duration, user->handle_info);
2778 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2783 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2785 struct userData *actor, *uData, *next;
2786 char interval[INTERVALLEN];
2790 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2791 if(min_access > max_access)
2793 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2797 if(!actor || actor->access <= max_access)
2799 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2804 limit = now - duration;
2805 for(uData = channel->channel_info->users; uData; uData = next)
2809 if((uData->seen > limit)
2811 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2814 if(((uData->access >= min_access) && (uData->access <= max_access))
2815 || (!max_access && (uData->access < actor->access)))
2817 del_channel_user(uData, 1);
2825 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2827 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2831 static CHANSERV_FUNC(cmd_trim)
2833 unsigned long duration;
2834 unsigned short min_level, max_level;
2839 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2840 duration = ParseInterval(argv[2]);
2843 reply("CSMSG_CANNOT_TRIM");
2847 if(!irccasecmp(argv[1], "bans"))
2849 cmd_trim_bans(user, channel, duration);
2852 else if(!irccasecmp(argv[1], "users"))
2854 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2857 else if(parse_level_range(&min_level, &max_level, argv[1]))
2859 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2862 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2864 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2869 reply("CSMSG_INVALID_TRIM", argv[1]);
2874 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2875 to the user. cmd_all takes advantage of this. */
2876 static CHANSERV_FUNC(cmd_up)
2878 struct mod_chanmode change;
2879 struct userData *uData;
2882 mod_chanmode_init(&change);
2884 change.args[0].u.member = GetUserMode(channel, user);
2885 if(!change.args[0].u.member)
2888 reply("MSG_CHANNEL_ABSENT", channel->name);
2892 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2896 reply("CSMSG_GODMODE_UP", argv[0]);
2899 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2901 change.args[0].mode = MODE_CHANOP;
2902 errmsg = "CSMSG_ALREADY_OPPED";
2904 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2906 change.args[0].mode = MODE_VOICE;
2907 errmsg = "CSMSG_ALREADY_VOICED";
2912 reply("CSMSG_NO_ACCESS");
2915 change.args[0].mode &= ~change.args[0].u.member->modes;
2916 if(!change.args[0].mode)
2919 reply(errmsg, channel->name);
2922 modcmd_chanmode_announce(&change);
2926 static CHANSERV_FUNC(cmd_down)
2928 struct mod_chanmode change;
2930 mod_chanmode_init(&change);
2932 change.args[0].u.member = GetUserMode(channel, user);
2933 if(!change.args[0].u.member)
2936 reply("MSG_CHANNEL_ABSENT", channel->name);
2940 if(!change.args[0].u.member->modes)
2943 reply("CSMSG_ALREADY_DOWN", channel->name);
2947 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2948 modcmd_chanmode_announce(&change);
2952 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)
2954 struct userData *cList;
2956 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2958 if(IsSuspended(cList->channel)
2959 || IsUserSuspended(cList)
2960 || !GetUserMode(cList->channel->channel, user))
2963 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2969 static CHANSERV_FUNC(cmd_upall)
2971 return cmd_all(CSFUNC_ARGS, cmd_up);
2974 static CHANSERV_FUNC(cmd_downall)
2976 return cmd_all(CSFUNC_ARGS, cmd_down);
2979 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
2980 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
2983 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)
2985 unsigned int ii, valid;
2986 struct userNode *victim;
2987 struct mod_chanmode *change;
2989 change = mod_chanmode_alloc(argc - 1);
2991 for(ii=valid=0; ++ii < argc; )
2993 if(!(victim = GetUserH(argv[ii])))
2995 change->args[valid].mode = mode;
2996 change->args[valid].u.member = GetUserMode(channel, victim);
2997 if(!change->args[valid].u.member)
2999 if(validate && !validate(user, channel, victim))
3004 change->argc = valid;
3005 if(valid < (argc-1))
3006 reply("CSMSG_PROCESS_FAILED");
3009 modcmd_chanmode_announce(change);
3010 reply(action, channel->name);
3012 mod_chanmode_free(change);
3016 static CHANSERV_FUNC(cmd_op)
3018 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3021 static CHANSERV_FUNC(cmd_deop)
3023 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3026 static CHANSERV_FUNC(cmd_voice)
3028 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3031 static CHANSERV_FUNC(cmd_devoice)
3033 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3037 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3043 for(ii=0; ii<channel->members.used; ii++)
3045 struct modeNode *mn = channel->members.list[ii];
3047 if(IsService(mn->user))
3050 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3053 if(protect_user(mn->user, user, channel->channel_info))
3057 victims[(*victimCount)++] = mn;
3063 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3065 struct userNode *victim;
3066 struct modeNode **victims;
3067 unsigned int offset, n, victimCount, duration = 0;
3068 char *reason = "Bye.", *ban, *name;
3069 char interval[INTERVALLEN];
3071 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3072 REQUIRE_PARAMS(offset);
3075 reason = unsplit_string(argv + offset, argc - offset, NULL);
3076 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3078 /* Truncate the reason to a length of TOPICLEN, as
3079 the ircd does; however, leave room for an ellipsis
3080 and the kicker's nick. */
3081 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3085 if((victim = GetUserH(argv[1])))
3087 victims = alloca(sizeof(victims[0]));
3088 victims[0] = GetUserMode(channel, victim);
3089 /* XXX: The comparison with ACTION_KICK is just because all
3090 * other actions can work on users outside the channel, and we
3091 * want to allow those (e.g. unbans) in that case. If we add
3092 * some other ejection action for in-channel users, change
3094 victimCount = victims[0] ? 1 : 0;
3096 if(IsService(victim))
3098 reply("MSG_SERVICE_IMMUNE", victim->nick);
3102 if((action == ACTION_KICK) && !victimCount)
3104 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3108 if(protect_user(victim, user, channel->channel_info))
3110 reply("CSMSG_USER_PROTECTED", victim->nick);
3114 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3115 name = victim->nick;
3119 if(!is_ircmask(argv[1]))
3121 reply("MSG_NICK_UNKNOWN", argv[1]);
3125 victims = alloca(sizeof(victims[0]) * channel->members.used);
3127 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3129 reply("CSMSG_MASK_PROTECTED", argv[1]);
3133 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3135 reply("CSMSG_LAME_MASK", argv[1]);
3139 if((action == ACTION_KICK) && (victimCount == 0))
3141 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3145 name = ban = strdup(argv[1]);
3148 /* Truncate the ban in place if necessary; we must ensure
3149 that 'ban' is a valid ban mask before sanitizing it. */
3150 sanitize_ircmask(ban);
3152 if(action & ACTION_ADD_BAN)
3154 struct banData *bData, *next;
3156 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3158 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3163 if(action & ACTION_ADD_TIMED_BAN)
3165 duration = ParseInterval(argv[2]);
3169 reply("CSMSG_DURATION_TOO_LOW");
3173 else if(duration > (86400 * 365 * 2))
3175 reply("CSMSG_DURATION_TOO_HIGH");
3181 for(bData = channel->channel_info->bans; bData; bData = next)
3183 if(match_ircglobs(bData->mask, ban))
3185 int exact = !irccasecmp(bData->mask, ban);
3187 /* The ban is redundant; there is already a ban
3188 with the same effect in place. */
3192 free(bData->reason);
3193 bData->reason = strdup(reason);
3194 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3196 reply("CSMSG_REASON_CHANGE", ban);
3200 if(exact && bData->expires)
3204 /* If the ban matches an existing one exactly,
3205 extend the expiration time if the provided
3206 duration is longer. */
3207 if(duration && ((time_t)(now + duration) > bData->expires))
3209 bData->expires = now + duration;
3220 /* Delete the expiration timeq entry and
3221 requeue if necessary. */
3222 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3225 timeq_add(bData->expires, expire_ban, bData);
3229 /* automated kickban */
3232 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3234 reply("CSMSG_BAN_ADDED", name, channel->name);
3240 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3247 if(match_ircglobs(ban, bData->mask))
3249 /* The ban we are adding makes previously existing
3250 bans redundant; silently remove them. */
3251 del_channel_ban(bData);
3255 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);
3257 name = ban = strdup(bData->mask);
3261 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3263 extern const char *hidden_host_suffix;
3264 const char *old_name = chanserv_conf.old_ban_names->list[n];
3266 unsigned int l1, l2;
3269 l2 = strlen(old_name);
3272 if(irccasecmp(ban + l1 - l2, old_name))
3274 new_mask = malloc(MAXLEN);
3275 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3277 name = ban = new_mask;
3282 if(action & ACTION_BAN)
3284 unsigned int exists;
3285 struct mod_chanmode *change;
3287 if(channel->banlist.used >= MAXBANS)
3290 reply("CSMSG_BANLIST_FULL", channel->name);
3295 exists = ChannelBanExists(channel, ban);
3296 change = mod_chanmode_alloc(victimCount + 1);
3297 for(n = 0; n < victimCount; ++n)
3299 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3300 change->args[n].u.member = victims[n];
3304 change->args[n].mode = MODE_BAN;
3305 change->args[n++].u.hostmask = ban;
3309 modcmd_chanmode_announce(change);
3311 mod_chanmode_announce(chanserv, channel, change);
3312 mod_chanmode_free(change);
3314 if(exists && (action == ACTION_BAN))
3317 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3323 if(action & ACTION_KICK)
3325 char kick_reason[MAXLEN];
3326 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3328 for(n = 0; n < victimCount; n++)
3329 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3334 /* No response, since it was automated. */
3336 else if(action & ACTION_ADD_BAN)
3339 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3341 reply("CSMSG_BAN_ADDED", name, channel->name);
3343 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3344 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3345 else if(action & ACTION_BAN)
3346 reply("CSMSG_BAN_DONE", name, channel->name);
3347 else if(action & ACTION_KICK && victimCount)
3348 reply("CSMSG_KICK_DONE", name, channel->name);
3354 static CHANSERV_FUNC(cmd_kickban)
3356 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3359 static CHANSERV_FUNC(cmd_kick)
3361 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3364 static CHANSERV_FUNC(cmd_ban)
3366 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3369 static CHANSERV_FUNC(cmd_addban)
3371 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3374 static CHANSERV_FUNC(cmd_addtimedban)
3376 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3379 static struct mod_chanmode *
3380 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3382 struct mod_chanmode *change;
3383 unsigned char *match;
3384 unsigned int ii, count;
3386 match = alloca(bans->used);
3389 for(ii = count = 0; ii < bans->used; ++ii)
3391 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3392 MATCH_USENICK | MATCH_VISIBLE);
3399 for(ii = count = 0; ii < bans->used; ++ii)
3401 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3408 change = mod_chanmode_alloc(count);
3409 for(ii = count = 0; ii < bans->used; ++ii)
3413 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3414 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3416 assert(count == change->argc);
3421 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3423 struct userNode *actee;
3429 /* may want to allow a comma delimited list of users... */
3430 if(!(actee = GetUserH(argv[1])))
3432 if(!is_ircmask(argv[1]))
3434 reply("MSG_NICK_UNKNOWN", argv[1]);
3438 mask = strdup(argv[1]);
3441 /* We don't sanitize the mask here because ircu
3443 if(action & ACTION_UNBAN)
3445 struct mod_chanmode *change;
3446 change = find_matching_bans(&channel->banlist, actee, mask);
3451 modcmd_chanmode_announce(change);
3452 for(ii = 0; ii < change->argc; ++ii)
3453 free((char*)change->args[ii].u.hostmask);
3454 mod_chanmode_free(change);
3459 if(action & ACTION_DEL_BAN)
3461 struct banData *ban, *next;
3463 ban = channel->channel_info->bans;
3467 for( ; ban && !user_matches_glob(actee, ban->mask,
3468 MATCH_USENICK | MATCH_VISIBLE);
3471 for( ; ban && !match_ircglobs(mask, ban->mask);
3476 del_channel_ban(ban);
3483 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3485 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3491 static CHANSERV_FUNC(cmd_unban)
3493 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3496 static CHANSERV_FUNC(cmd_delban)
3498 /* it doesn't necessarily have to remove the channel ban - may want
3499 to make that an option. */
3500 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3503 static CHANSERV_FUNC(cmd_unbanme)
3505 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3506 long flags = ACTION_UNBAN;
3508 /* remove permanent bans if the user has the proper access. */
3509 if(uData->access >= UL_MASTER)
3510 flags |= ACTION_DEL_BAN;
3512 argv[1] = user->nick;
3513 return unban_user(user, channel, 2, argv, cmd, flags);
3516 static CHANSERV_FUNC(cmd_unbanall)
3518 struct mod_chanmode *change;
3521 if(!channel->banlist.used)
3523 reply("CSMSG_NO_BANS", channel->name);
3527 change = mod_chanmode_alloc(channel->banlist.used);
3528 for(ii=0; ii<channel->banlist.used; ii++)
3530 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3531 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3533 modcmd_chanmode_announce(change);
3534 for(ii = 0; ii < change->argc; ++ii)
3535 free((char*)change->args[ii].u.hostmask);
3536 mod_chanmode_free(change);
3537 reply("CSMSG_BANS_REMOVED", channel->name);
3541 static CHANSERV_FUNC(cmd_open)
3543 struct mod_chanmode *change;
3546 change = find_matching_bans(&channel->banlist, user, NULL);
3548 change = mod_chanmode_alloc(0);
3549 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3550 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3551 && channel->channel_info->modes.modes_set)
3552 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3553 modcmd_chanmode_announce(change);
3554 reply("CSMSG_CHANNEL_OPENED", channel->name);
3555 for(ii = 0; ii < change->argc; ++ii)
3556 free((char*)change->args[ii].u.hostmask);
3557 mod_chanmode_free(change);
3561 static CHANSERV_FUNC(cmd_myaccess)
3563 static struct string_buffer sbuf;
3564 struct handle_info *target_handle;
3565 struct userData *uData;
3568 target_handle = user->handle_info;
3569 else if(!IsHelping(user))
3571 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3574 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3577 if(!target_handle->channels)
3579 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3583 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3584 for(uData = target_handle->channels; uData; uData = uData->u_next)
3586 struct chanData *cData = uData->channel;
3588 if(uData->access > UL_OWNER)
3590 if(IsProtected(cData)
3591 && (target_handle != user->handle_info)
3592 && !GetTrueChannelAccess(cData, user->handle_info))
3595 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3596 if(uData->flags != USER_AUTO_OP)
3597 string_buffer_append(&sbuf, ',');
3598 if(IsUserSuspended(uData))
3599 string_buffer_append(&sbuf, 's');
3600 if(IsUserAutoOp(uData))
3602 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3603 string_buffer_append(&sbuf, 'o');
3604 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3605 string_buffer_append(&sbuf, 'v');
3607 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3608 string_buffer_append(&sbuf, 'i');
3610 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3612 string_buffer_append_string(&sbuf, ")]");
3613 string_buffer_append(&sbuf, '\0');
3614 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3620 static CHANSERV_FUNC(cmd_access)
3622 struct userNode *target;
3623 struct handle_info *target_handle;
3624 struct userData *uData;
3626 char prefix[MAXLEN];
3631 target_handle = target->handle_info;
3633 else if((target = GetUserH(argv[1])))
3635 target_handle = target->handle_info;
3637 else if(argv[1][0] == '*')
3639 if(!(target_handle = get_handle_info(argv[1]+1)))
3641 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3647 reply("MSG_NICK_UNKNOWN", argv[1]);
3651 assert(target || target_handle);
3653 if(target == chanserv)
3655 reply("CSMSG_IS_CHANSERV");
3663 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3668 reply("MSG_USER_AUTHENTICATE", target->nick);
3671 reply("MSG_AUTHENTICATE");
3677 const char *epithet = NULL, *type = NULL;
3680 epithet = chanserv_conf.irc_operator_epithet;
3681 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3683 else if(IsNetworkHelper(target))
3685 epithet = chanserv_conf.network_helper_epithet;
3686 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3688 else if(IsSupportHelper(target))
3690 epithet = chanserv_conf.support_helper_epithet;
3691 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3695 if(target_handle->epithet)
3696 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3698 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3700 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3704 sprintf(prefix, "%s", target_handle->handle);
3707 if(!channel->channel_info)
3709 reply("CSMSG_NOT_REGISTERED", channel->name);
3713 helping = HANDLE_FLAGGED(target_handle, HELPING)
3714 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3715 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3717 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3718 /* To prevent possible information leaks, only show infolines
3719 * if the requestor is in the channel or it's their own
3721 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3723 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3725 /* Likewise, only say it's suspended if the user has active
3726 * access in that channel or it's their own entry. */
3727 if(IsUserSuspended(uData)
3728 && (GetChannelUser(channel->channel_info, user->handle_info)
3729 || (user->handle_info == uData->handle)))
3731 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3736 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3743 zoot_list(struct listData *list)
3745 struct userData *uData;
3746 unsigned int start, curr, highest, lowest;
3747 struct helpfile_table tmp_table;
3748 const char **temp, *msg;
3750 if(list->table.length == 1)
3753 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3755 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3756 msg = user_find_message(list->user, "MSG_NONE");
3757 send_message_type(4, list->user, list->bot, " %s", msg);
3759 tmp_table.width = list->table.width;
3760 tmp_table.flags = list->table.flags;
3761 list->table.contents[0][0] = " ";
3762 highest = list->highest;
3763 if(list->lowest != 0)
3764 lowest = list->lowest;
3765 else if(highest < 100)
3768 lowest = highest - 100;
3769 for(start = curr = 1; curr < list->table.length; )
3771 uData = list->users[curr-1];
3772 list->table.contents[curr++][0] = " ";
3773 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3776 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3778 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3779 temp = list->table.contents[--start];
3780 list->table.contents[start] = list->table.contents[0];
3781 tmp_table.contents = list->table.contents + start;
3782 tmp_table.length = curr - start;
3783 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3784 list->table.contents[start] = temp;
3786 highest = lowest - 1;
3787 lowest = (highest < 100) ? 0 : (highest - 99);
3793 def_list(struct listData *list)
3797 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3799 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3800 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3801 if(list->table.length == 1)
3803 msg = user_find_message(list->user, "MSG_NONE");
3804 send_message_type(4, list->user, list->bot, " %s", msg);
3809 userData_access_comp(const void *arg_a, const void *arg_b)
3811 const struct userData *a = *(struct userData**)arg_a;
3812 const struct userData *b = *(struct userData**)arg_b;
3814 if(a->access != b->access)
3815 res = b->access - a->access;
3817 res = irccasecmp(a->handle->handle, b->handle->handle);
3822 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3824 void (*send_list)(struct listData *);
3825 struct userData *uData;
3826 struct listData lData;
3827 unsigned int matches;
3831 lData.bot = cmd->parent->bot;
3832 lData.channel = channel;
3833 lData.lowest = lowest;
3834 lData.highest = highest;
3835 lData.search = (argc > 1) ? argv[1] : NULL;
3836 send_list = def_list;
3837 (void)zoot_list; /* since it doesn't show user levels */
3839 if(user->handle_info)
3841 switch(user->handle_info->userlist_style)
3843 case HI_STYLE_DEF: send_list = def_list; break;
3844 case HI_STYLE_ZOOT: send_list = def_list; break;
3848 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3850 for(uData = channel->channel_info->users; uData; uData = uData->next)
3852 if((uData->access < lowest)
3853 || (uData->access > highest)
3854 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3856 lData.users[matches++] = uData;
3858 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3860 lData.table.length = matches+1;
3861 lData.table.width = 4;
3862 lData.table.flags = TABLE_NO_FREE;
3863 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3864 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3865 lData.table.contents[0] = ary;
3868 ary[2] = "Last Seen";
3870 for(matches = 1; matches < lData.table.length; ++matches)
3872 struct userData *uData = lData.users[matches-1];
3873 char seen[INTERVALLEN];
3875 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3876 lData.table.contents[matches] = ary;
3877 ary[0] = strtab(uData->access);
3878 ary[1] = uData->handle->handle;
3881 else if(!uData->seen)
3884 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3885 ary[2] = strdup(ary[2]);
3886 if(IsUserSuspended(uData))
3887 ary[3] = "Suspended";
3888 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3889 ary[3] = "Vacation";
3894 for(matches = 1; matches < lData.table.length; ++matches)
3896 free((char*)lData.table.contents[matches][2]);
3897 free(lData.table.contents[matches]);
3899 free(lData.table.contents[0]);
3900 free(lData.table.contents);
3904 static CHANSERV_FUNC(cmd_users)
3906 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3909 static CHANSERV_FUNC(cmd_wlist)
3911 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3914 static CHANSERV_FUNC(cmd_clist)
3916 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3919 static CHANSERV_FUNC(cmd_mlist)
3921 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3924 static CHANSERV_FUNC(cmd_olist)
3926 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3929 static CHANSERV_FUNC(cmd_plist)
3931 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3934 static CHANSERV_FUNC(cmd_bans)
3936 struct userNode *search_u = NULL;
3937 struct helpfile_table tbl;
3938 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3939 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3940 const char *msg_never, *triggered, *expires;
3941 struct banData *ban, **bans;
3945 else if(strchr(search = argv[1], '!'))
3948 search_wilds = search[strcspn(search, "?*")];
3950 else if(!(search_u = GetUserH(search)))
3951 reply("MSG_NICK_UNKNOWN", search);
3953 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3955 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3959 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3964 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3967 bans[matches++] = ban;
3972 tbl.length = matches + 1;
3973 tbl.width = 4 + timed;
3975 tbl.flags = TABLE_NO_FREE;
3976 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
3977 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
3978 tbl.contents[0][0] = "Mask";
3979 tbl.contents[0][1] = "Set By";
3980 tbl.contents[0][2] = "Triggered";
3983 tbl.contents[0][3] = "Expires";
3984 tbl.contents[0][4] = "Reason";
3987 tbl.contents[0][3] = "Reason";
3990 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
3992 free(tbl.contents[0]);
3997 msg_never = user_find_message(user, "MSG_NEVER");
3998 for(ii = 0; ii < matches; )
4004 else if(ban->expires)
4005 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4007 expires = msg_never;
4010 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4012 triggered = msg_never;
4014 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4015 tbl.contents[ii][0] = ban->mask;
4016 tbl.contents[ii][1] = ban->owner;
4017 tbl.contents[ii][2] = strdup(triggered);
4020 tbl.contents[ii][3] = strdup(expires);
4021 tbl.contents[ii][4] = ban->reason;
4024 tbl.contents[ii][3] = ban->reason;
4026 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4027 reply("MSG_MATCH_COUNT", matches);
4028 for(ii = 1; ii < tbl.length; ++ii)
4030 free((char*)tbl.contents[ii][2]);
4032 free((char*)tbl.contents[ii][3]);
4033 free(tbl.contents[ii]);
4035 free(tbl.contents[0]);
4041 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4043 struct chanData *cData = channel->channel_info;
4044 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4046 if(cData->topic_mask)
4047 return !match_ircglob(new_topic, cData->topic_mask);
4048 else if(cData->topic)
4049 return irccasecmp(new_topic, cData->topic);
4054 static CHANSERV_FUNC(cmd_topic)
4056 struct chanData *cData;
4059 cData = channel->channel_info;
4064 SetChannelTopic(channel, chanserv, cData->topic, 1);
4065 reply("CSMSG_TOPIC_SET", cData->topic);
4069 reply("CSMSG_NO_TOPIC", channel->name);
4073 topic = unsplit_string(argv + 1, argc - 1, NULL);
4074 /* If they say "!topic *", use an empty topic. */
4075 if((topic[0] == '*') && (topic[1] == 0))
4077 if(bad_topic(channel, user, topic))
4079 char *topic_mask = cData->topic_mask;
4082 char new_topic[TOPICLEN+1], tchar;
4083 int pos=0, starpos=-1, dpos=0, len;
4085 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4092 len = strlen(topic);
4093 if((dpos + len) > TOPICLEN)
4094 len = TOPICLEN + 1 - dpos;
4095 memcpy(new_topic+dpos, topic, len);
4099 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4100 default: new_topic[dpos++] = tchar; break;
4103 if((dpos > TOPICLEN) || tchar)
4106 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4107 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4110 new_topic[dpos] = 0;
4111 SetChannelTopic(channel, chanserv, new_topic, 1);
4113 reply("CSMSG_TOPIC_LOCKED", channel->name);
4118 SetChannelTopic(channel, chanserv, topic, 1);
4120 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4122 /* Grab the topic and save it as the default topic. */
4124 cData->topic = strdup(channel->topic);
4130 static CHANSERV_FUNC(cmd_mode)
4132 struct userData *uData;
4133 struct mod_chanmode *change;
4138 change = &channel->channel_info->modes;
4139 if(change->modes_set || change->modes_clear) {
4140 modcmd_chanmode_announce(change);
4141 reply("CSMSG_DEFAULTED_MODES", channel->name);
4143 reply("CSMSG_NO_MODES", channel->name);
4147 uData = GetChannelUser(channel->channel_info, user->handle_info);
4149 base_oplevel = MAXOPLEVEL;
4150 else if (uData->access >= UL_OWNER)
4153 base_oplevel = 1 + UL_OWNER - uData->access;
4154 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4157 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4161 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4162 && mode_lock_violated(&channel->channel_info->modes, change))
4165 mod_chanmode_format(&channel->channel_info->modes, modes);
4166 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4170 modcmd_chanmode_announce(change);
4171 mod_chanmode_free(change);
4172 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4176 static CHANSERV_FUNC(cmd_invite)
4178 struct userData *uData;
4179 struct userNode *invite;
4181 uData = GetChannelUser(channel->channel_info, user->handle_info);
4185 if(!(invite = GetUserH(argv[1])))
4187 reply("MSG_NICK_UNKNOWN", argv[1]);
4194 if(GetUserMode(channel, invite))
4196 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4204 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4205 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4208 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4210 irc_invite(chanserv, invite, channel);
4212 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4217 static CHANSERV_FUNC(cmd_inviteme)
4219 if(GetUserMode(channel, user))
4221 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4224 if(channel->channel_info
4225 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4227 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4230 irc_invite(cmd->parent->bot, user, channel);
4235 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4238 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4240 /* We display things based on two dimensions:
4241 * - Issue time: present or absent
4242 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4243 * (in order of precedence, so something both expired and revoked
4244 * only counts as revoked)
4246 combo = (suspended->issued ? 4 : 0)
4247 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4249 case 0: /* no issue time, indefinite expiration */
4250 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4252 case 1: /* no issue time, expires in future */
4253 intervalString(buf1, suspended->expires-now, user->handle_info);
4254 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4256 case 2: /* no issue time, expired */
4257 intervalString(buf1, now-suspended->expires, user->handle_info);
4258 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4260 case 3: /* no issue time, revoked */
4261 intervalString(buf1, now-suspended->revoked, user->handle_info);
4262 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4264 case 4: /* issue time set, indefinite expiration */
4265 intervalString(buf1, now-suspended->issued, user->handle_info);
4266 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4268 case 5: /* issue time set, expires in future */
4269 intervalString(buf1, now-suspended->issued, user->handle_info);
4270 intervalString(buf2, suspended->expires-now, user->handle_info);
4271 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4273 case 6: /* issue time set, expired */
4274 intervalString(buf1, now-suspended->issued, user->handle_info);
4275 intervalString(buf2, now-suspended->expires, user->handle_info);
4276 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4278 case 7: /* issue time set, revoked */
4279 intervalString(buf1, now-suspended->issued, user->handle_info);
4280 intervalString(buf2, now-suspended->revoked, user->handle_info);
4281 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4284 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4289 static CHANSERV_FUNC(cmd_info)
4291 char modes[MAXLEN], buffer[INTERVALLEN];
4292 struct userData *uData, *owner;
4293 struct chanData *cData;
4294 struct do_not_register *dnr;
4299 cData = channel->channel_info;
4300 reply("CSMSG_CHANNEL_INFO", channel->name);
4302 uData = GetChannelUser(cData, user->handle_info);
4303 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4305 mod_chanmode_format(&cData->modes, modes);
4306 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4307 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4310 for(it = dict_first(cData->notes); it; it = iter_next(it))
4314 note = iter_data(it);
4315 if(!note_type_visible_to_user(cData, note->type, user))
4318 padding = PADLEN - 1 - strlen(iter_key(it));
4319 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4322 reply("CSMSG_CHANNEL_MAX", cData->max);
4323 for(owner = cData->users; owner; owner = owner->next)
4324 if(owner->access == UL_OWNER)
4325 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4326 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4327 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4328 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4330 privileged = IsStaff(user);
4332 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4333 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4334 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4336 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4337 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4339 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4341 struct suspended *suspended;
4342 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4343 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4344 show_suspension_info(cmd, user, suspended);
4346 else if(IsSuspended(cData))
4348 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4349 show_suspension_info(cmd, user, cData->suspended);
4354 static CHANSERV_FUNC(cmd_netinfo)
4356 extern time_t boot_time;
4357 extern unsigned long burst_length;
4358 char interval[INTERVALLEN];
4360 reply("CSMSG_NETWORK_INFO");
4361 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4362 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4363 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4364 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4365 reply("CSMSG_NETWORK_BANS", banCount);
4366 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4367 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4368 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4373 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4375 struct helpfile_table table;
4377 struct userNode *user;
4382 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4383 table.contents = alloca(list->used*sizeof(*table.contents));
4384 for(nn=0; nn<list->used; nn++)
4386 user = list->list[nn];
4387 if(user->modes & skip_flags)
4391 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4394 nick = alloca(strlen(user->nick)+3);
4395 sprintf(nick, "(%s)", user->nick);
4399 table.contents[table.length][0] = nick;
4402 table_send(chanserv, to->nick, 0, NULL, table);
4405 static CHANSERV_FUNC(cmd_ircops)
4407 reply("CSMSG_STAFF_OPERS");
4408 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4412 static CHANSERV_FUNC(cmd_helpers)
4414 reply("CSMSG_STAFF_HELPERS");
4415 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4419 static CHANSERV_FUNC(cmd_staff)
4421 reply("CSMSG_NETWORK_STAFF");
4422 cmd_ircops(CSFUNC_ARGS);
4423 cmd_helpers(CSFUNC_ARGS);
4427 static CHANSERV_FUNC(cmd_peek)
4429 struct modeNode *mn;
4430 char modes[MODELEN];
4432 struct helpfile_table table;
4434 irc_make_chanmode(channel, modes);
4436 reply("CSMSG_PEEK_INFO", channel->name);
4437 reply("CSMSG_PEEK_TOPIC", channel->topic);
4438 reply("CSMSG_PEEK_MODES", modes);
4439 reply("CSMSG_PEEK_USERS", channel->members.used);
4443 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4444 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4445 for(n = 0; n < channel->members.used; n++)
4447 mn = channel->members.list[n];
4448 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4450 table.contents[table.length] = alloca(sizeof(**table.contents));
4451 table.contents[table.length][0] = mn->user->nick;
4456 reply("CSMSG_PEEK_OPS");
4457 table_send(chanserv, user->nick, 0, NULL, table);
4460 reply("CSMSG_PEEK_NO_OPS");
4464 static MODCMD_FUNC(cmd_wipeinfo)
4466 struct handle_info *victim;
4467 struct userData *ud, *actor, *real_actor;
4468 unsigned int override = 0;
4471 actor = GetChannelUser(channel->channel_info, user->handle_info);
4472 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4473 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4475 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4477 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4480 if((ud->access >= actor->access) && (ud != actor))
4482 reply("MSG_USER_OUTRANKED", victim->handle);
4485 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4486 override = CMD_LOG_OVERRIDE;
4490 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4491 return 1 | override;
4494 static CHANSERV_FUNC(cmd_resync)
4496 struct mod_chanmode *changes;
4497 struct chanData *cData = channel->channel_info;
4498 unsigned int ii, used;
4500 changes = mod_chanmode_alloc(channel->members.used * 2);
4501 for(ii = used = 0; ii < channel->members.used; ++ii)
4503 struct modeNode *mn = channel->members.list[ii];
4504 struct userData *uData;
4506 if(IsService(mn->user))
4509 uData = GetChannelAccess(cData, mn->user->handle_info);
4510 if(!cData->lvlOpts[lvlGiveOps]
4511 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4513 if(!(mn->modes & MODE_CHANOP))
4515 changes->args[used].mode = MODE_CHANOP;
4516 changes->args[used++].u.member = mn;
4519 else if(!cData->lvlOpts[lvlGiveVoice]
4520 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4522 if(mn->modes & MODE_CHANOP)
4524 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4525 changes->args[used++].u.member = mn;
4527 if(!(mn->modes & MODE_VOICE))
4529 changes->args[used].mode = MODE_VOICE;
4530 changes->args[used++].u.member = mn;
4537 changes->args[used].mode = MODE_REMOVE | mn->modes;
4538 changes->args[used++].u.member = mn;
4542 changes->argc = used;
4543 modcmd_chanmode_announce(changes);
4544 mod_chanmode_free(changes);
4545 reply("CSMSG_RESYNCED_USERS", channel->name);
4549 static CHANSERV_FUNC(cmd_seen)
4551 struct userData *uData;
4552 struct handle_info *handle;
4553 char seen[INTERVALLEN];
4557 if(!irccasecmp(argv[1], chanserv->nick))
4559 reply("CSMSG_IS_CHANSERV");
4563 if(!(handle = get_handle_info(argv[1])))
4565 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4569 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4571 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4576 reply("CSMSG_USER_PRESENT", handle->handle);
4577 else if(uData->seen)
4578 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4580 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4582 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4583 reply("CSMSG_USER_VACATION", handle->handle);
4588 static MODCMD_FUNC(cmd_names)
4590 struct userNode *targ;
4591 struct userData *targData;
4592 unsigned int ii, pos;
4595 for(ii=pos=0; ii<channel->members.used; ++ii)
4597 targ = channel->members.list[ii]->user;
4598 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4601 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4604 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4608 if(IsUserSuspended(targData))
4610 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4613 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4614 reply("CSMSG_END_NAMES", channel->name);
4619 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4621 switch(ntype->visible_type)
4623 case NOTE_VIS_ALL: return 1;
4624 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4625 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4630 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4632 struct userData *uData;
4634 switch(ntype->set_access_type)
4636 case NOTE_SET_CHANNEL_ACCESS:
4637 if(!user->handle_info)
4639 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4641 return uData->access >= ntype->set_access.min_ulevel;
4642 case NOTE_SET_CHANNEL_SETTER:
4643 return check_user_level(channel, user, lvlSetters, 1, 0);
4644 case NOTE_SET_PRIVILEGED: default:
4645 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4649 static CHANSERV_FUNC(cmd_note)
4651 struct chanData *cData;
4653 struct note_type *ntype;
4655 cData = channel->channel_info;
4658 reply("CSMSG_NOT_REGISTERED", channel->name);
4662 /* If no arguments, show all visible notes for the channel. */
4668 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4670 note = iter_data(it);
4671 if(!note_type_visible_to_user(cData, note->type, user))
4674 reply("CSMSG_NOTELIST_HEADER", channel->name);
4675 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4678 reply("CSMSG_NOTELIST_END", channel->name);
4680 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4682 /* If one argument, show the named note. */
4685 if((note = dict_find(cData->notes, argv[1], NULL))
4686 && note_type_visible_to_user(cData, note->type, user))
4688 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4690 else if((ntype = dict_find(note_types, argv[1], NULL))
4691 && note_type_visible_to_user(NULL, ntype, user))
4693 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4698 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4702 /* Assume they're trying to set a note. */
4706 ntype = dict_find(note_types, argv[1], NULL);
4709 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4712 else if(note_type_settable_by_user(channel, ntype, user))
4714 note_text = unsplit_string(argv+2, argc-2, NULL);
4715 if((note = dict_find(cData->notes, argv[1], NULL)))
4716 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4717 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4718 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4720 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4722 /* The note is viewable to staff only, so return 0
4723 to keep the invocation from getting logged (or
4724 regular users can see it in !events). */
4730 reply("CSMSG_NO_ACCESS");
4737 static CHANSERV_FUNC(cmd_delnote)
4742 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4743 || !note_type_settable_by_user(channel, note->type, user))
4745 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4748 dict_remove(channel->channel_info->notes, note->type->name);
4749 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4753 static CHANSERV_FUNC(cmd_events)
4755 struct logSearch discrim;
4756 struct logReport report;
4757 unsigned int matches, limit;
4759 limit = (argc > 1) ? atoi(argv[1]) : 10;
4760 if(limit < 1 || limit > 200)
4763 memset(&discrim, 0, sizeof(discrim));
4764 discrim.masks.bot = chanserv;
4765 discrim.masks.channel_name = channel->name;
4767 discrim.masks.command = argv[2];
4768 discrim.limit = limit;
4769 discrim.max_time = INT_MAX;
4770 discrim.severities = 1 << LOG_COMMAND;
4771 report.reporter = chanserv;
4773 reply("CSMSG_EVENT_SEARCH_RESULTS");
4774 matches = log_entry_search(&discrim, log_report_entry, &report);
4776 reply("MSG_MATCH_COUNT", matches);
4778 reply("MSG_NO_MATCHES");
4782 static CHANSERV_FUNC(cmd_say)
4788 msg = unsplit_string(argv + 1, argc - 1, NULL);
4789 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4791 else if(GetUserH(argv[1]))
4794 msg = unsplit_string(argv + 2, argc - 2, NULL);
4795 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4799 reply("MSG_NOT_TARGET_NAME");
4805 static CHANSERV_FUNC(cmd_emote)
4811 /* CTCP is so annoying. */
4812 msg = unsplit_string(argv + 1, argc - 1, NULL);
4813 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4815 else if(GetUserH(argv[1]))
4817 msg = unsplit_string(argv + 2, argc - 2, NULL);
4818 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4822 reply("MSG_NOT_TARGET_NAME");
4828 struct channelList *
4829 chanserv_support_channels(void)
4831 return &chanserv_conf.support_channels;
4834 static CHANSERV_FUNC(cmd_expire)
4836 int channel_count = registered_channels;
4837 expire_channels(NULL);
4838 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4843 chanserv_expire_suspension(void *data)
4845 struct suspended *suspended = data;
4846 struct chanNode *channel;
4848 if(!suspended->expires || (now < suspended->expires))
4849 suspended->revoked = now;
4850 channel = suspended->cData->channel;
4851 suspended->cData->channel = channel;
4852 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4853 if(!IsOffChannel(suspended->cData))
4855 struct mod_chanmode change;
4856 mod_chanmode_init(&change);
4858 change.args[0].mode = MODE_CHANOP;
4859 change.args[0].u.member = AddChannelUser(chanserv, channel);
4860 mod_chanmode_announce(chanserv, channel, &change);
4864 static CHANSERV_FUNC(cmd_csuspend)
4866 struct suspended *suspended;
4867 char reason[MAXLEN];
4868 time_t expiry, duration;
4869 struct userData *uData;
4873 if(IsProtected(channel->channel_info))
4875 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4879 if(argv[1][0] == '!')
4881 else if(IsSuspended(channel->channel_info))
4883 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4884 show_suspension_info(cmd, user, channel->channel_info->suspended);
4888 if(!strcmp(argv[1], "0"))
4890 else if((duration = ParseInterval(argv[1])))
4891 expiry = now + duration;
4894 reply("MSG_INVALID_DURATION", argv[1]);
4898 unsplit_string(argv + 2, argc - 2, reason);
4900 suspended = calloc(1, sizeof(*suspended));
4901 suspended->revoked = 0;
4902 suspended->issued = now;
4903 suspended->suspender = strdup(user->handle_info->handle);
4904 suspended->expires = expiry;
4905 suspended->reason = strdup(reason);
4906 suspended->cData = channel->channel_info;
4907 suspended->previous = suspended->cData->suspended;
4908 suspended->cData->suspended = suspended;
4910 if(suspended->expires)
4911 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
4913 if(IsSuspended(channel->channel_info))
4915 suspended->previous->revoked = now;
4916 if(suspended->previous->expires)
4917 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
4918 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
4919 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4923 /* Mark all users in channel as absent. */
4924 for(uData = channel->channel_info->users; uData; uData = uData->next)
4933 /* Mark the channel as suspended, then part. */
4934 channel->channel_info->flags |= CHANNEL_SUSPENDED;
4935 DelChannelUser(chanserv, channel, suspended->reason, 0);
4936 reply("CSMSG_SUSPENDED", channel->name);
4937 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
4938 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
4943 static CHANSERV_FUNC(cmd_cunsuspend)
4945 struct suspended *suspended;
4946 char message[MAXLEN];
4948 if(!IsSuspended(channel->channel_info))
4950 reply("CSMSG_NOT_SUSPENDED", channel->name);
4954 suspended = channel->channel_info->suspended;
4956 /* Expire the suspension and join ChanServ to the channel. */
4957 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
4958 chanserv_expire_suspension(suspended);
4959 reply("CSMSG_UNSUSPENDED", channel->name);
4960 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
4961 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
4965 typedef struct chanservSearch
4973 unsigned long flags;
4977 typedef void (*channel_search_func)(struct chanData *channel, void *data);
4980 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
4985 search = malloc(sizeof(struct chanservSearch));
4986 memset(search, 0, sizeof(*search));
4989 for(i = 0; i < argc; i++)
4991 /* Assume all criteria require arguments. */
4994 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
4998 if(!irccasecmp(argv[i], "name"))
4999 search->name = argv[++i];
5000 else if(!irccasecmp(argv[i], "registrar"))
5001 search->registrar = argv[++i];
5002 else if(!irccasecmp(argv[i], "unvisited"))
5003 search->unvisited = ParseInterval(argv[++i]);
5004 else if(!irccasecmp(argv[i], "registered"))
5005 search->registered = ParseInterval(argv[++i]);
5006 else if(!irccasecmp(argv[i], "flags"))
5009 if(!irccasecmp(argv[i], "nodelete"))
5010 search->flags |= CHANNEL_NODELETE;
5011 else if(!irccasecmp(argv[i], "suspended"))
5012 search->flags |= CHANNEL_SUSPENDED;
5013 else if(!irccasecmp(argv[i], "unreviewed"))
5014 search->flags |= CHANNEL_UNREVIEWED;
5017 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5021 else if(!irccasecmp(argv[i], "limit"))
5022 search->limit = strtoul(argv[++i], NULL, 10);
5025 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5030 if(search->name && !strcmp(search->name, "*"))
5032 if(search->registrar && !strcmp(search->registrar, "*"))
5033 search->registrar = 0;
5042 chanserv_channel_match(struct chanData *channel, search_t search)
5044 const char *name = channel->channel->name;
5045 if((search->name && !match_ircglob(name, search->name)) ||
5046 (search->registrar && !channel->registrar) ||
5047 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5048 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5049 (search->registered && (now - channel->registered) > search->registered) ||
5050 (search->flags && ((search->flags & channel->flags) != search->flags)))
5057 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5059 struct chanData *channel;
5060 unsigned int matches = 0;
5062 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5064 if(!chanserv_channel_match(channel, search))
5074 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5079 search_print(struct chanData *channel, void *data)
5081 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5084 static CHANSERV_FUNC(cmd_search)
5087 unsigned int matches;
5088 channel_search_func action;
5092 if(!irccasecmp(argv[1], "count"))
5093 action = search_count;
5094 else if(!irccasecmp(argv[1], "print"))
5095 action = search_print;
5098 reply("CSMSG_ACTION_INVALID", argv[1]);
5102 search = chanserv_search_create(user, argc - 2, argv + 2);
5106 if(action == search_count)
5107 search->limit = INT_MAX;
5109 if(action == search_print)
5110 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5112 matches = chanserv_channel_search(search, action, user);
5115 reply("MSG_MATCH_COUNT", matches);
5117 reply("MSG_NO_MATCHES");
5123 static CHANSERV_FUNC(cmd_unvisited)
5125 struct chanData *cData;
5126 time_t interval = chanserv_conf.channel_expire_delay;
5127 char buffer[INTERVALLEN];
5128 unsigned int limit = 25, matches = 0;
5132 interval = ParseInterval(argv[1]);
5134 limit = atoi(argv[2]);
5137 intervalString(buffer, interval, user->handle_info);
5138 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5140 for(cData = channelList; cData && matches < limit; cData = cData->next)
5142 if((now - cData->visited) < interval)
5145 intervalString(buffer, now - cData->visited, user->handle_info);
5146 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5153 static MODCMD_FUNC(chan_opt_defaulttopic)
5159 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5161 reply("CSMSG_TOPIC_LOCKED", channel->name);
5165 topic = unsplit_string(argv+1, argc-1, NULL);
5167 free(channel->channel_info->topic);
5168 if(topic[0] == '*' && topic[1] == 0)
5170 topic = channel->channel_info->topic = NULL;
5174 topic = channel->channel_info->topic = strdup(topic);
5175 if(channel->channel_info->topic_mask
5176 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5177 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5179 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5182 if(channel->channel_info->topic)
5183 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5185 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5189 static MODCMD_FUNC(chan_opt_topicmask)
5193 struct chanData *cData = channel->channel_info;
5196 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5198 reply("CSMSG_TOPIC_LOCKED", channel->name);
5202 mask = unsplit_string(argv+1, argc-1, NULL);
5204 if(cData->topic_mask)
5205 free(cData->topic_mask);
5206 if(mask[0] == '*' && mask[1] == 0)
5208 cData->topic_mask = 0;
5212 cData->topic_mask = strdup(mask);
5214 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5215 else if(!match_ircglob(cData->topic, cData->topic_mask))
5216 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5220 if(channel->channel_info->topic_mask)
5221 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5223 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5227 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5231 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5235 if(greeting[0] == '*' && greeting[1] == 0)
5239 unsigned int length = strlen(greeting);
5240 if(length > chanserv_conf.greeting_length)
5242 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5245 *data = strdup(greeting);
5254 reply(name, user_find_message(user, "MSG_NONE"));
5258 static MODCMD_FUNC(chan_opt_greeting)
5260 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5263 static MODCMD_FUNC(chan_opt_usergreeting)
5265 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5268 static MODCMD_FUNC(chan_opt_modes)
5270 struct mod_chanmode *new_modes;
5271 char modes[MODELEN];
5275 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5277 reply("CSMSG_NO_ACCESS");
5280 if(argv[1][0] == '*' && argv[1][1] == 0)
5282 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5284 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5286 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5289 else if(new_modes->argc > 1)
5291 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5292 mod_chanmode_free(new_modes);
5297 channel->channel_info->modes = *new_modes;
5298 modcmd_chanmode_announce(new_modes);
5299 mod_chanmode_free(new_modes);
5303 mod_chanmode_format(&channel->channel_info->modes, modes);
5305 reply("CSMSG_SET_MODES", modes);
5307 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5311 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5313 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5315 struct chanData *cData = channel->channel_info;
5320 /* Set flag according to value. */
5321 if(enabled_string(argv[1]))
5323 cData->flags |= mask;
5326 else if(disabled_string(argv[1]))
5328 cData->flags &= ~mask;
5333 reply("MSG_INVALID_BINARY", argv[1]);
5339 /* Find current option value. */
5340 value = (cData->flags & mask) ? 1 : 0;
5344 reply(name, user_find_message(user, "MSG_ON"));
5346 reply(name, user_find_message(user, "MSG_OFF"));
5350 static MODCMD_FUNC(chan_opt_nodelete)
5352 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5354 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5358 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5361 static MODCMD_FUNC(chan_opt_dynlimit)
5363 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5366 static MODCMD_FUNC(chan_opt_offchannel)
5368 struct chanData *cData = channel->channel_info;
5373 /* Set flag according to value. */
5374 if(enabled_string(argv[1]))
5376 if(!IsOffChannel(cData))
5377 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5378 cData->flags |= CHANNEL_OFFCHANNEL;
5381 else if(disabled_string(argv[1]))
5383 if(IsOffChannel(cData))
5385 struct mod_chanmode change;
5386 mod_chanmode_init(&change);
5388 change.args[0].mode = MODE_CHANOP;
5389 change.args[0].u.member = AddChannelUser(chanserv, channel);
5390 mod_chanmode_announce(chanserv, channel, &change);
5392 cData->flags &= ~CHANNEL_OFFCHANNEL;
5397 reply("MSG_INVALID_BINARY", argv[1]);
5403 /* Find current option value. */
5404 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5408 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5410 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5414 static MODCMD_FUNC(chan_opt_unreviewed)
5416 struct chanData *cData = channel->channel_info;
5417 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5423 /* The two directions can have different ACLs. */
5424 if(enabled_string(argv[1]))
5426 else if(disabled_string(argv[1]))
5430 reply("MSG_INVALID_BINARY", argv[1]);
5434 if (new_value != value)
5436 struct svccmd *subcmd;
5437 char subcmd_name[32];
5439 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5440 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5443 reply("MSG_COMMAND_DISABLED", subcmd_name);
5446 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5450 cData->flags |= CHANNEL_UNREVIEWED;
5453 free(cData->registrar);
5454 cData->registrar = strdup(user->handle_info->handle);
5455 cData->flags &= ~CHANNEL_UNREVIEWED;
5462 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5464 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5468 static MODCMD_FUNC(chan_opt_defaults)
5470 struct userData *uData;
5471 struct chanData *cData;
5472 const char *confirm;
5473 enum levelOption lvlOpt;
5474 enum charOption chOpt;
5476 cData = channel->channel_info;
5477 uData = GetChannelUser(cData, user->handle_info);
5478 if(!uData || (uData->access < UL_OWNER))
5480 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5483 confirm = make_confirmation_string(uData);
5484 if((argc < 2) || strcmp(argv[1], confirm))
5486 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5489 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5490 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5491 cData->modes = chanserv_conf.default_modes;
5492 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5493 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5494 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5495 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5496 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5501 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5503 struct chanData *cData = channel->channel_info;
5504 struct userData *uData;
5505 unsigned short value;
5509 if(!check_user_level(channel, user, option, 1, 1))
5511 reply("CSMSG_CANNOT_SET");
5514 value = user_level_from_name(argv[1], UL_OWNER+1);
5515 if(!value && strcmp(argv[1], "0"))
5517 reply("CSMSG_INVALID_ACCESS", argv[1]);
5520 uData = GetChannelUser(cData, user->handle_info);
5521 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5523 reply("CSMSG_BAD_SETLEVEL");
5529 if(value > cData->lvlOpts[lvlGiveOps])
5531 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5536 if(value < cData->lvlOpts[lvlGiveVoice])
5538 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5543 /* This test only applies to owners, since non-owners
5544 * trying to set an option to above their level get caught
5545 * by the CSMSG_BAD_SETLEVEL test above.
5547 if(value > uData->access)
5549 reply("CSMSG_BAD_SETTERS");
5556 cData->lvlOpts[option] = value;
5558 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5562 static MODCMD_FUNC(chan_opt_enfops)
5564 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5567 static MODCMD_FUNC(chan_opt_giveops)
5569 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5572 static MODCMD_FUNC(chan_opt_enfmodes)
5574 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5577 static MODCMD_FUNC(chan_opt_enftopic)
5579 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5582 static MODCMD_FUNC(chan_opt_pubcmd)
5584 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5587 static MODCMD_FUNC(chan_opt_setters)
5589 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5592 static MODCMD_FUNC(chan_opt_ctcpusers)
5594 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5597 static MODCMD_FUNC(chan_opt_userinfo)
5599 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5602 static MODCMD_FUNC(chan_opt_givevoice)
5604 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5607 static MODCMD_FUNC(chan_opt_topicsnarf)
5609 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5612 static MODCMD_FUNC(chan_opt_inviteme)
5614 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5618 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5620 struct chanData *cData = channel->channel_info;
5621 int count = charOptions[option].count, index;
5625 index = atoi(argv[1]);
5627 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5629 reply("CSMSG_INVALID_NUMERIC", index);
5630 /* Show possible values. */
5631 for(index = 0; index < count; index++)
5632 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5636 cData->chOpts[option] = charOptions[option].values[index].value;
5640 /* Find current option value. */
5643 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5647 /* Somehow, the option value is corrupt; reset it to the default. */
5648 cData->chOpts[option] = charOptions[option].default_value;
5653 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5657 static MODCMD_FUNC(chan_opt_protect)
5659 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5662 static MODCMD_FUNC(chan_opt_toys)
5664 return channel_multiple_option(chToys, CSFUNC_ARGS);
5667 static MODCMD_FUNC(chan_opt_ctcpreaction)
5669 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5672 static MODCMD_FUNC(chan_opt_topicrefresh)
5674 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5677 static struct svccmd_list set_shows_list;
5680 handle_svccmd_unbind(struct svccmd *target) {
5682 for(ii=0; ii<set_shows_list.used; ++ii)
5683 if(target == set_shows_list.list[ii])
5684 set_shows_list.used = 0;
5687 static CHANSERV_FUNC(cmd_set)
5689 struct svccmd *subcmd;
5693 /* Check if we need to (re-)initialize set_shows_list. */
5694 if(!set_shows_list.used)
5696 if(!set_shows_list.size)
5698 set_shows_list.size = chanserv_conf.set_shows->used;
5699 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5701 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5703 const char *name = chanserv_conf.set_shows->list[ii];
5704 sprintf(buf, "%s %s", argv[0], name);
5705 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5708 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5711 svccmd_list_append(&set_shows_list, subcmd);
5717 reply("CSMSG_CHANNEL_OPTIONS");
5718 for(ii = 0; ii < set_shows_list.used; ii++)
5720 subcmd = set_shows_list.list[ii];
5721 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5726 sprintf(buf, "%s %s", argv[0], argv[1]);
5727 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5730 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5733 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5735 reply("CSMSG_NO_ACCESS");
5741 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5745 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5747 struct userData *uData;
5749 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5752 reply("CSMSG_NOT_USER", channel->name);
5758 /* Just show current option value. */
5760 else if(enabled_string(argv[1]))
5762 uData->flags |= mask;
5764 else if(disabled_string(argv[1]))
5766 uData->flags &= ~mask;
5770 reply("MSG_INVALID_BINARY", argv[1]);
5774 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5778 static MODCMD_FUNC(user_opt_noautoop)
5780 struct userData *uData;
5782 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5785 reply("CSMSG_NOT_USER", channel->name);
5788 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5789 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5791 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5794 static MODCMD_FUNC(user_opt_autoinvite)
5796 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5799 static MODCMD_FUNC(user_opt_info)
5801 struct userData *uData;
5804 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5808 /* If they got past the command restrictions (which require access)
5809 * but fail this test, we have some fool with security override on.
5811 reply("CSMSG_NOT_USER", channel->name);
5818 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5819 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5821 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5824 bp = strcspn(infoline, "\001");
5827 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5832 if(infoline[0] == '*' && infoline[1] == 0)
5835 uData->info = strdup(infoline);
5838 reply("CSMSG_USET_INFO", uData->info);
5840 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5844 struct svccmd_list uset_shows_list;
5846 static CHANSERV_FUNC(cmd_uset)
5848 struct svccmd *subcmd;
5852 /* Check if we need to (re-)initialize uset_shows_list. */
5853 if(!uset_shows_list.used)
5857 "NoAutoOp", "AutoInvite", "Info"
5860 if(!uset_shows_list.size)
5862 uset_shows_list.size = ArrayLength(options);
5863 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5865 for(ii = 0; ii < ArrayLength(options); ii++)
5867 const char *name = options[ii];
5868 sprintf(buf, "%s %s", argv[0], name);
5869 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5872 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5875 svccmd_list_append(&uset_shows_list, subcmd);
5881 /* Do this so options are presented in a consistent order. */
5882 reply("CSMSG_USER_OPTIONS");
5883 for(ii = 0; ii < uset_shows_list.used; ii++)
5884 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5888 sprintf(buf, "%s %s", argv[0], argv[1]);
5889 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5892 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5896 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5899 static CHANSERV_FUNC(cmd_giveownership)
5901 struct handle_info *new_owner_hi;
5902 struct userData *new_owner;
5903 struct userData *curr_user;
5904 struct userData *invoker;
5905 struct chanData *cData = channel->channel_info;
5906 struct do_not_register *dnr;
5907 const char *confirm;
5909 unsigned short co_access;
5910 char reason[MAXLEN];
5913 curr_user = GetChannelAccess(cData, user->handle_info);
5914 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
5915 if(!curr_user || (curr_user->access != UL_OWNER))
5917 struct userData *owner = NULL;
5918 for(curr_user = channel->channel_info->users;
5920 curr_user = curr_user->next)
5922 if(curr_user->access != UL_OWNER)
5926 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
5933 else if(!force && (now < (time_t)(cData->ownerTransfer + chanserv_conf.giveownership_period)))
5935 char delay[INTERVALLEN];
5936 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
5937 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
5940 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
5942 if(new_owner_hi == user->handle_info)
5944 reply("CSMSG_NO_TRANSFER_SELF");
5947 new_owner = GetChannelAccess(cData, new_owner_hi);
5952 new_owner = add_channel_user(cData, new_owner_hi, UL_COOWNER, 0, NULL);
5956 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
5960 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
5962 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
5965 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
5966 if(!IsHelping(user))
5967 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
5969 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
5972 invoker = GetChannelUser(cData, user->handle_info);
5973 if(invoker->access <= UL_OWNER)
5975 confirm = make_confirmation_string(curr_user);
5976 if((argc < 3) || strcmp(argv[2], confirm))
5978 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
5982 if(new_owner->access >= UL_COOWNER)
5983 co_access = new_owner->access;
5985 co_access = UL_COOWNER;
5986 new_owner->access = UL_OWNER;
5988 curr_user->access = co_access;
5989 cData->ownerTransfer = now;
5990 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
5991 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
5992 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5996 static CHANSERV_FUNC(cmd_suspend)
5998 struct handle_info *hi;
5999 struct userData *self, *real_self, *target;
6000 unsigned int override = 0;
6003 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6004 self = GetChannelUser(channel->channel_info, user->handle_info);
6005 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6006 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6008 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6011 if(target->access >= self->access)
6013 reply("MSG_USER_OUTRANKED", hi->handle);
6016 if(target->flags & USER_SUSPENDED)
6018 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6023 target->present = 0;
6026 if(!real_self || target->access >= real_self->access)
6027 override = CMD_LOG_OVERRIDE;
6028 target->flags |= USER_SUSPENDED;
6029 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6030 return 1 | override;
6033 static CHANSERV_FUNC(cmd_unsuspend)
6035 struct handle_info *hi;
6036 struct userData *self, *real_self, *target;
6037 unsigned int override = 0;
6040 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6041 self = GetChannelUser(channel->channel_info, user->handle_info);
6042 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6043 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6045 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6048 if(target->access >= self->access)
6050 reply("MSG_USER_OUTRANKED", hi->handle);
6053 if(!(target->flags & USER_SUSPENDED))
6055 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6058 if(!real_self || target->access >= real_self->access)
6059 override = CMD_LOG_OVERRIDE;
6060 target->flags &= ~USER_SUSPENDED;
6061 scan_user_presence(target, NULL);
6062 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6063 return 1 | override;
6066 static MODCMD_FUNC(cmd_deleteme)
6068 struct handle_info *hi;
6069 struct userData *target;
6070 const char *confirm_string;
6071 unsigned short access;
6074 hi = user->handle_info;
6075 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6077 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6080 if(target->access == UL_OWNER)
6082 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6085 confirm_string = make_confirmation_string(target);
6086 if((argc < 2) || strcmp(argv[1], confirm_string))
6088 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6091 access = target->access;
6092 channel_name = strdup(channel->name);
6093 del_channel_user(target, 1);
6094 reply("CSMSG_DELETED_YOU", access, channel_name);
6100 chanserv_refresh_topics(UNUSED_ARG(void *data))
6102 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6103 struct chanData *cData;
6106 for(cData = channelList; cData; cData = cData->next)
6108 if(IsSuspended(cData))
6110 opt = cData->chOpts[chTopicRefresh];
6113 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6116 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6117 cData->last_refresh = refresh_num;
6119 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6122 static CHANSERV_FUNC(cmd_unf)
6126 char response[MAXLEN];
6127 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6128 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6129 irc_privmsg(cmd->parent->bot, channel->name, response);
6132 reply("CSMSG_UNF_RESPONSE");
6136 static CHANSERV_FUNC(cmd_ping)
6140 char response[MAXLEN];
6141 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6142 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6143 irc_privmsg(cmd->parent->bot, channel->name, response);
6146 reply("CSMSG_PING_RESPONSE");
6150 static CHANSERV_FUNC(cmd_wut)
6154 char response[MAXLEN];
6155 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6156 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6157 irc_privmsg(cmd->parent->bot, channel->name, response);
6160 reply("CSMSG_WUT_RESPONSE");
6164 static CHANSERV_FUNC(cmd_8ball)
6166 unsigned int i, j, accum;
6171 for(i=1; i<argc; i++)
6172 for(j=0; argv[i][j]; j++)
6173 accum = (accum << 5) - accum + toupper(argv[i][j]);
6174 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6177 char response[MAXLEN];
6178 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6179 irc_privmsg(cmd->parent->bot, channel->name, response);
6182 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6186 static CHANSERV_FUNC(cmd_d)
6188 unsigned long sides, count, modifier, ii, total;
6189 char response[MAXLEN], *sep;
6193 if((count = strtoul(argv[1], &sep, 10)) < 1)
6203 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6204 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6208 else if((sep[0] == '-') && isdigit(sep[1]))
6209 modifier = strtoul(sep, NULL, 10);
6210 else if((sep[0] == '+') && isdigit(sep[1]))
6211 modifier = strtoul(sep+1, NULL, 10);
6218 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6223 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6226 for(total = ii = 0; ii < count; ++ii)
6227 total += (rand() % sides) + 1;
6230 if((count > 1) || modifier)
6232 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6233 sprintf(response, fmt, total, count, sides, modifier);
6237 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6238 sprintf(response, fmt, total, sides);
6241 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6243 send_message_type(4, user, cmd->parent->bot, "%s", response);
6247 static CHANSERV_FUNC(cmd_huggle)
6249 /* CTCP must be via PRIVMSG, never notice */
6251 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6253 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6258 chanserv_adjust_limit(void *data)
6260 struct mod_chanmode change;
6261 struct chanData *cData = data;
6262 struct chanNode *channel = cData->channel;
6265 if(IsSuspended(cData))
6268 cData->limitAdjusted = now;
6269 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6270 if(cData->modes.modes_set & MODE_LIMIT)
6272 if(limit > cData->modes.new_limit)
6273 limit = cData->modes.new_limit;
6274 else if(limit == cData->modes.new_limit)
6278 mod_chanmode_init(&change);
6279 change.modes_set = MODE_LIMIT;
6280 change.new_limit = limit;
6281 mod_chanmode_announce(chanserv, channel, &change);
6285 handle_new_channel(struct chanNode *channel)
6287 struct chanData *cData;
6289 if(!(cData = channel->channel_info))
6292 if(cData->modes.modes_set || cData->modes.modes_clear)
6293 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6295 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6296 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6299 /* Welcome to my worst nightmare. Warning: Read (or modify)
6300 the code below at your own risk. */
6302 handle_join(struct modeNode *mNode)
6304 struct mod_chanmode change;
6305 struct userNode *user = mNode->user;
6306 struct chanNode *channel = mNode->channel;
6307 struct chanData *cData;
6308 struct userData *uData = NULL;
6309 struct banData *bData;
6310 struct handle_info *handle;
6311 unsigned int modes = 0, info = 0;
6314 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6317 cData = channel->channel_info;
6318 if(channel->members.used > cData->max)
6319 cData->max = channel->members.used;
6321 /* Check for bans. If they're joining through a ban, one of two
6323 * 1: Join during a netburst, by riding the break. Kick them
6324 * unless they have ops or voice in the channel.
6325 * 2: They're allowed to join through the ban (an invite in
6326 * ircu2.10, or a +e on Hybrid, or something).
6327 * If they're not joining through a ban, and the banlist is not
6328 * full, see if they're on the banlist for the channel. If so,
6331 if(user->uplink->burst && !mNode->modes)
6334 for(ii = 0; ii < channel->banlist.used; ii++)
6336 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6338 /* Riding a netburst. Naughty. */
6339 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6345 mod_chanmode_init(&change);
6347 if(channel->banlist.used < MAXBANS)
6349 /* Not joining through a ban. */
6350 for(bData = cData->bans;
6351 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6352 bData = bData->next);
6356 char kick_reason[MAXLEN];
6357 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6359 bData->triggered = now;
6360 if(bData != cData->bans)
6362 /* Shuffle the ban to the head of the list. */
6364 bData->next->prev = bData->prev;
6366 bData->prev->next = bData->next;
6369 bData->next = cData->bans;
6372 cData->bans->prev = bData;
6373 cData->bans = bData;
6376 change.args[0].mode = MODE_BAN;
6377 change.args[0].u.hostmask = bData->mask;
6378 mod_chanmode_announce(chanserv, channel, &change);
6379 KickChannelUser(user, channel, chanserv, kick_reason);
6384 /* ChanServ will not modify the limits in join-flooded channels.
6385 It will also skip DynLimit processing when the user (or srvx)
6386 is bursting in, because there are likely more incoming. */
6387 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6388 && !user->uplink->burst
6389 && !channel->join_flooded
6390 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6392 /* The user count has begun "bumping" into the channel limit,
6393 so set a timer to raise the limit a bit. Any previous
6394 timers are removed so three incoming users within the delay
6395 results in one limit change, not three. */
6397 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6398 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6401 if(channel->join_flooded)
6403 /* don't automatically give ops or voice during a join flood */
6405 else if(cData->lvlOpts[lvlGiveOps] == 0)
6406 modes |= MODE_CHANOP;
6407 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6408 modes |= MODE_VOICE;
6410 greeting = cData->greeting;
6411 if(user->handle_info)
6413 handle = user->handle_info;
6415 if(IsHelper(user) && !IsHelping(user))
6418 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6420 if(channel == chanserv_conf.support_channels.list[ii])
6422 HANDLE_SET_FLAG(user->handle_info, HELPING);
6428 uData = GetTrueChannelAccess(cData, handle);
6429 if(uData && !IsUserSuspended(uData))
6431 /* Ops and above were handled by the above case. */
6432 if(IsUserAutoOp(uData))
6434 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6435 modes |= MODE_CHANOP;
6436 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6437 modes |= MODE_VOICE;
6439 if(uData->access >= UL_PRESENT)
6440 cData->visited = now;
6441 if(cData->user_greeting)
6442 greeting = cData->user_greeting;
6444 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6445 && ((now - uData->seen) >= chanserv_conf.info_delay)
6453 /* If user joining normally (not during burst), apply op or voice,
6454 * and send greeting/userinfo as appropriate.
6456 if(!user->uplink->burst)
6460 if(modes & MODE_CHANOP)
6461 modes &= ~MODE_VOICE;
6462 change.args[0].mode = modes;
6463 change.args[0].u.member = mNode;
6464 mod_chanmode_announce(chanserv, channel, &change);
6467 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6469 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6475 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6477 struct mod_chanmode change;
6478 struct userData *channel;
6479 unsigned int ii, jj;
6481 if(!user->handle_info)
6484 mod_chanmode_init(&change);
6486 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6488 struct chanNode *cn;
6489 struct modeNode *mn;
6490 if(IsUserSuspended(channel)
6491 || IsSuspended(channel->channel)
6492 || !(cn = channel->channel->channel))
6495 mn = GetUserMode(cn, user);
6498 if(!IsUserSuspended(channel)
6499 && IsUserAutoInvite(channel)
6500 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6502 && !user->uplink->burst)
6503 irc_invite(chanserv, user, cn);
6507 if(channel->access >= UL_PRESENT)
6508 channel->channel->visited = now;
6510 if(IsUserAutoOp(channel))
6512 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6513 change.args[0].mode = MODE_CHANOP;
6514 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6515 change.args[0].mode = MODE_VOICE;
6517 change.args[0].mode = 0;
6518 change.args[0].u.member = mn;
6519 if(change.args[0].mode)
6520 mod_chanmode_announce(chanserv, cn, &change);
6523 channel->seen = now;
6524 channel->present = 1;
6527 for(ii = 0; ii < user->channels.used; ++ii)
6529 struct chanNode *channel = user->channels.list[ii]->channel;
6530 struct banData *ban;
6532 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6533 || !channel->channel_info
6534 || IsSuspended(channel->channel_info))
6536 for(jj = 0; jj < channel->banlist.used; ++jj)
6537 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6539 if(jj < channel->banlist.used)
6541 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6543 char kick_reason[MAXLEN];
6544 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6546 change.args[0].mode = MODE_BAN;
6547 change.args[0].u.hostmask = ban->mask;
6548 mod_chanmode_announce(chanserv, channel, &change);
6549 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6550 KickChannelUser(user, channel, chanserv, kick_reason);
6551 ban->triggered = now;
6556 if(IsSupportHelper(user))
6558 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6560 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6562 HANDLE_SET_FLAG(user->handle_info, HELPING);
6570 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6572 struct chanData *cData;
6573 struct userData *uData;
6575 cData = mn->channel->channel_info;
6576 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6579 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6581 /* Allow for a bit of padding so that the limit doesn't
6582 track the user count exactly, which could get annoying. */
6583 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6585 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6586 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6590 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6592 scan_user_presence(uData, mn->user);
6594 if (uData->access >= UL_PRESENT)
6595 cData->visited = now;
6598 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6600 unsigned int ii, jj;
6601 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6603 for(jj = 0; jj < mn->user->channels.used; ++jj)
6604 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6606 if(jj < mn->user->channels.used)
6609 if(ii == chanserv_conf.support_channels.used)
6610 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6615 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6617 struct userData *uData;
6619 if(!channel->channel_info || !kicker || IsService(kicker)
6620 || (kicker == victim) || IsSuspended(channel->channel_info)
6621 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6624 if(protect_user(victim, kicker, channel->channel_info))
6626 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6627 KickChannelUser(kicker, channel, chanserv, reason);
6630 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6635 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6637 struct chanData *cData;
6639 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6642 cData = channel->channel_info;
6643 if(bad_topic(channel, user, channel->topic))
6645 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6646 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6647 SetChannelTopic(channel, chanserv, old_topic, 1);
6648 else if(cData->topic)
6649 SetChannelTopic(channel, chanserv, cData->topic, 1);
6652 /* With topicsnarf, grab the topic and save it as the default topic. */
6653 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6656 cData->topic = strdup(channel->topic);
6662 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6664 struct mod_chanmode *bounce = NULL;
6665 unsigned int bnc, ii;
6668 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6671 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6672 && mode_lock_violated(&channel->channel_info->modes, change))
6674 char correct[MAXLEN];
6675 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6676 mod_chanmode_format(&channel->channel_info->modes, correct);
6677 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6679 for(ii = bnc = 0; ii < change->argc; ++ii)
6681 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6683 const struct userNode *victim = change->args[ii].u.member->user;
6684 if(!protect_user(victim, user, channel->channel_info))
6687 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6690 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6691 bounce->args[bnc].u.member = GetUserMode(channel, user);
6692 if(bounce->args[bnc].u.member)
6696 bounce->args[bnc].mode = MODE_CHANOP;
6697 bounce->args[bnc].u.member = change->args[ii].u.member;
6699 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6701 else if(change->args[ii].mode & MODE_CHANOP)
6703 const struct userNode *victim = change->args[ii].u.member->user;
6704 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6707 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6708 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6709 bounce->args[bnc].u.member = change->args[ii].u.member;
6712 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6714 const char *ban = change->args[ii].u.hostmask;
6715 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6718 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6719 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6720 bounce->args[bnc].u.hostmask = strdup(ban);
6722 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6727 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6728 mod_chanmode_announce(chanserv, channel, bounce);
6729 for(ii = 0; ii < change->argc; ++ii)
6730 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6731 free((char*)bounce->args[ii].u.hostmask);
6732 mod_chanmode_free(bounce);
6737 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6739 struct chanNode *channel;
6740 struct banData *bData;
6741 struct mod_chanmode change;
6742 unsigned int ii, jj;
6743 char kick_reason[MAXLEN];
6745 mod_chanmode_init(&change);
6747 change.args[0].mode = MODE_BAN;
6748 for(ii = 0; ii < user->channels.used; ++ii)
6750 channel = user->channels.list[ii]->channel;
6751 /* Need not check for bans if they're opped or voiced. */
6752 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6754 /* Need not check for bans unless channel registration is active. */
6755 if(!channel->channel_info || IsSuspended(channel->channel_info))
6757 /* Look for a matching ban already on the channel. */
6758 for(jj = 0; jj < channel->banlist.used; ++jj)
6759 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6761 /* Need not act if we found one. */
6762 if(jj < channel->banlist.used)
6764 /* Look for a matching ban in this channel. */
6765 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6767 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6769 change.args[0].u.hostmask = bData->mask;
6770 mod_chanmode_announce(chanserv, channel, &change);
6771 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6772 KickChannelUser(user, channel, chanserv, kick_reason);
6773 bData->triggered = now;
6774 break; /* we don't need to check any more bans in the channel */
6779 static void handle_rename(struct handle_info *handle, const char *old_handle)
6781 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6785 dict_remove2(handle_dnrs, old_handle, 1);
6786 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6787 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6792 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6794 struct userNode *h_user;
6796 if(handle->channels)
6798 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6799 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6801 while(handle->channels)
6802 del_channel_user(handle->channels, 1);
6807 handle_server_link(UNUSED_ARG(struct server *server))
6809 struct chanData *cData;
6811 for(cData = channelList; cData; cData = cData->next)
6813 if(!IsSuspended(cData))
6814 cData->may_opchan = 1;
6815 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6816 && !cData->channel->join_flooded
6817 && ((cData->channel->limit - cData->channel->members.used)
6818 < chanserv_conf.adjust_threshold))
6820 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6821 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6827 chanserv_conf_read(void)
6831 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6832 struct mod_chanmode *change;
6833 struct string_list *strlist;
6834 struct chanNode *chan;
6837 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6839 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6842 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6843 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6844 chanserv_conf.support_channels.used = 0;
6845 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6847 for(ii = 0; ii < strlist->used; ++ii)
6849 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6852 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6854 channelList_append(&chanserv_conf.support_channels, chan);
6857 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6860 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6863 chan = AddChannel(str, now, str2, NULL);
6865 channelList_append(&chanserv_conf.support_channels, chan);
6867 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6868 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6869 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6870 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6871 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6872 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6873 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6874 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6875 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6876 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6877 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6878 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6879 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6880 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6881 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6882 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6883 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6884 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6885 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6886 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6887 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6888 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6889 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6890 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6891 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6893 NickChange(chanserv, str, 0);
6894 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6895 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6896 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6897 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6898 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6899 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6900 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6901 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6902 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6903 chanserv_conf.max_owned = str ? atoi(str) : 5;
6904 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
6905 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
6906 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
6907 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
6908 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
6909 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
6910 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
6913 safestrncpy(mode_line, str, sizeof(mode_line));
6914 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
6915 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
6916 && (change->argc < 2))
6918 chanserv_conf.default_modes = *change;
6919 mod_chanmode_free(change);
6921 free_string_list(chanserv_conf.set_shows);
6922 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
6924 strlist = string_list_copy(strlist);
6927 static const char *list[] = {
6928 /* free form text */
6929 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
6930 /* options based on user level */
6931 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
6932 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
6933 /* multiple choice options */
6934 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
6935 /* binary options */
6936 "DynLimit", "NoDelete",
6941 strlist = alloc_string_list(ArrayLength(list)-1);
6942 for(ii=0; list[ii]; ii++)
6943 string_list_append(strlist, strdup(list[ii]));
6945 chanserv_conf.set_shows = strlist;
6946 /* We don't look things up now, in case the list refers to options
6947 * defined by modules initialized after this point. Just mark the
6948 * function list as invalid, so it will be initialized.
6950 set_shows_list.used = 0;
6951 free_string_list(chanserv_conf.eightball);
6952 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
6955 strlist = string_list_copy(strlist);
6959 strlist = alloc_string_list(4);
6960 string_list_append(strlist, strdup("Yes."));
6961 string_list_append(strlist, strdup("No."));
6962 string_list_append(strlist, strdup("Maybe so."));
6964 chanserv_conf.eightball = strlist;
6965 free_string_list(chanserv_conf.old_ban_names);
6966 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
6968 strlist = string_list_copy(strlist);
6970 strlist = alloc_string_list(2);
6971 chanserv_conf.old_ban_names = strlist;
6972 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
6973 off_channel = str ? atoi(str) : 0;
6977 chanserv_note_type_read(const char *key, struct record_data *rd)
6980 struct note_type *ntype;
6983 if(!(obj = GET_RECORD_OBJECT(rd)))
6985 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
6988 if(!(ntype = chanserv_create_note_type(key)))
6990 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
6994 /* Figure out set access */
6995 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
6997 ntype->set_access_type = NOTE_SET_PRIVILEGED;
6998 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7000 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7002 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7003 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7005 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7007 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7011 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7012 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7013 ntype->set_access.min_opserv = 0;
7016 /* Figure out visibility */
7017 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7018 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7019 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7020 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7021 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7022 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7023 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7024 ntype->visible_type = NOTE_VIS_ALL;
7026 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7028 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7029 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7033 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7035 struct handle_info *handle;
7036 struct userData *uData;
7037 char *seen, *inf, *flags;
7039 unsigned short access;
7041 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7043 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7047 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7048 if(access > UL_OWNER)
7050 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7054 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7055 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7056 last_seen = seen ? (signed)strtoul(seen, NULL, 0) : now;
7057 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7058 handle = get_handle_info(key);
7061 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7065 uData = add_channel_user(chan, handle, access, last_seen, inf);
7066 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7070 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7072 struct banData *bData;
7073 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7074 time_t set_time, triggered_time, expires_time;
7076 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7078 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7082 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7083 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7084 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7085 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7086 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7087 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7088 if (!reason || !owner)
7091 set_time = set ? (time_t)strtoul(set, NULL, 0) : now;
7092 triggered_time = triggered ? (time_t)strtoul(triggered, NULL, 0) : 0;
7094 expires_time = (time_t)strtoul(s_expires, NULL, 0);
7096 expires_time = set_time + atoi(s_duration);
7100 if(!reason || (expires_time && (expires_time < now)))
7103 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7106 static struct suspended *
7107 chanserv_read_suspended(dict_t obj)
7109 struct suspended *suspended = calloc(1, sizeof(*suspended));
7113 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7114 suspended->expires = str ? (time_t)strtoul(str, NULL, 0) : 0;
7115 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7116 suspended->revoked = str ? (time_t)strtoul(str, NULL, 0) : 0;
7117 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7118 suspended->issued = str ? (time_t)strtoul(str, NULL, 0) : 0;
7119 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7120 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7121 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7122 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7127 chanserv_channel_read(const char *key, struct record_data *hir)
7129 struct suspended *suspended;
7130 struct mod_chanmode *modes;
7131 struct chanNode *cNode;
7132 struct chanData *cData;
7133 struct dict *channel, *obj;
7134 char *str, *argv[10];
7138 channel = hir->d.object;
7140 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7143 cNode = AddChannel(key, now, NULL, NULL);
7146 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7149 cData = register_channel(cNode, str);
7152 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7156 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7158 enum levelOption lvlOpt;
7159 enum charOption chOpt;
7161 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7162 cData->flags = atoi(str);
7164 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7166 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7168 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7169 else if(levelOptions[lvlOpt].old_flag)
7171 if(cData->flags & levelOptions[lvlOpt].old_flag)
7172 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7174 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7178 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7180 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7182 cData->chOpts[chOpt] = str[0];
7185 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7187 enum levelOption lvlOpt;
7188 enum charOption chOpt;
7191 cData->flags = base64toint(str, 5);
7192 count = strlen(str += 5);
7193 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7196 if(levelOptions[lvlOpt].old_flag)
7198 if(cData->flags & levelOptions[lvlOpt].old_flag)
7199 lvl = levelOptions[lvlOpt].flag_value;
7201 lvl = levelOptions[lvlOpt].default_value;
7203 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7205 case 'c': lvl = UL_COOWNER; break;
7206 case 'm': lvl = UL_MASTER; break;
7207 case 'n': lvl = UL_OWNER+1; break;
7208 case 'o': lvl = UL_OP; break;
7209 case 'p': lvl = UL_PEON; break;
7210 case 'w': lvl = UL_OWNER; break;
7211 default: lvl = 0; break;
7213 cData->lvlOpts[lvlOpt] = lvl;
7215 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7216 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7219 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7221 suspended = chanserv_read_suspended(obj);
7222 cData->suspended = suspended;
7223 suspended->cData = cData;
7224 /* We could use suspended->expires and suspended->revoked to
7225 * set the CHANNEL_SUSPENDED flag, but we don't. */
7227 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7229 suspended = calloc(1, sizeof(*suspended));
7230 suspended->issued = 0;
7231 suspended->revoked = 0;
7232 suspended->suspender = strdup(str);
7233 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7234 suspended->expires = str ? atoi(str) : 0;
7235 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7236 suspended->reason = strdup(str ? str : "No reason");
7237 suspended->previous = NULL;
7238 cData->suspended = suspended;
7239 suspended->cData = cData;
7243 cData->flags &= ~CHANNEL_SUSPENDED;
7244 suspended = NULL; /* to squelch a warning */
7247 if(IsSuspended(cData)) {
7248 if(suspended->expires > now)
7249 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7250 else if(suspended->expires)
7251 cData->flags &= ~CHANNEL_SUSPENDED;
7254 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7255 struct mod_chanmode change;
7256 mod_chanmode_init(&change);
7258 change.args[0].mode = MODE_CHANOP;
7259 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7260 mod_chanmode_announce(chanserv, cNode, &change);
7263 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7264 cData->registered = str ? (time_t)strtoul(str, NULL, 0) : now;
7265 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7266 cData->visited = str ? (time_t)strtoul(str, NULL, 0) : now;
7267 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7268 cData->ownerTransfer = str ? (time_t)strtoul(str, NULL, 0) : 0;
7269 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7270 cData->max = str ? atoi(str) : 0;
7271 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7272 cData->greeting = str ? strdup(str) : NULL;
7273 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7274 cData->user_greeting = str ? strdup(str) : NULL;
7275 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7276 cData->topic_mask = str ? strdup(str) : NULL;
7277 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7278 cData->topic = str ? strdup(str) : NULL;
7280 if(!IsSuspended(cData)
7281 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7282 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7283 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7284 cData->modes = *modes;
7286 cData->modes.modes_set |= MODE_REGISTERED;
7287 if(cData->modes.argc > 1)
7288 cData->modes.argc = 1;
7289 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7290 mod_chanmode_free(modes);
7293 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7294 for(it = dict_first(obj); it; it = iter_next(it))
7295 user_read_helper(iter_key(it), iter_data(it), cData);
7297 if(!cData->users && !IsProtected(cData))
7299 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7300 unregister_channel(cData, "has empty user list.");
7304 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7305 for(it = dict_first(obj); it; it = iter_next(it))
7306 ban_read_helper(iter_key(it), iter_data(it), cData);
7308 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7309 for(it = dict_first(obj); it; it = iter_next(it))
7311 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7312 struct record_data *rd = iter_data(it);
7313 const char *note, *setter;
7315 if(rd->type != RECDB_OBJECT)
7317 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7321 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7323 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7325 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7329 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7330 if(!setter) setter = "<unknown>";
7331 chanserv_add_channel_note(cData, ntype, setter, note);
7339 chanserv_dnr_read(const char *key, struct record_data *hir)
7341 const char *setter, *reason, *str;
7342 struct do_not_register *dnr;
7345 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7348 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7351 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7354 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7357 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7358 expiry = str ? (time_t)strtoul(str, NULL, 0) : 0;
7359 if(expiry && expiry <= now)
7361 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7364 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7366 dnr->set = atoi(str);
7372 chanserv_saxdb_read(struct dict *database)
7374 struct dict *section;
7377 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7378 for(it = dict_first(section); it; it = iter_next(it))
7379 chanserv_note_type_read(iter_key(it), iter_data(it));
7381 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7382 for(it = dict_first(section); it; it = iter_next(it))
7383 chanserv_channel_read(iter_key(it), iter_data(it));
7385 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7386 for(it = dict_first(section); it; it = iter_next(it))
7387 chanserv_dnr_read(iter_key(it), iter_data(it));
7393 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7395 int high_present = 0;
7396 saxdb_start_record(ctx, KEY_USERS, 1);
7397 for(; uData; uData = uData->next)
7399 if((uData->access >= UL_PRESENT) && uData->present)
7401 saxdb_start_record(ctx, uData->handle->handle, 0);
7402 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7403 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7405 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7407 saxdb_write_string(ctx, KEY_INFO, uData->info);
7408 saxdb_end_record(ctx);
7410 saxdb_end_record(ctx);
7411 return high_present;
7415 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7419 saxdb_start_record(ctx, KEY_BANS, 1);
7420 for(; bData; bData = bData->next)
7422 saxdb_start_record(ctx, bData->mask, 0);
7423 saxdb_write_int(ctx, KEY_SET, bData->set);
7424 if(bData->triggered)
7425 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7427 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7429 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7431 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7432 saxdb_end_record(ctx);
7434 saxdb_end_record(ctx);
7438 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7440 saxdb_start_record(ctx, name, 0);
7441 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7442 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7444 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7446 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7448 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7450 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7451 saxdb_end_record(ctx);
7455 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7459 enum levelOption lvlOpt;
7460 enum charOption chOpt;
7462 saxdb_start_record(ctx, channel->channel->name, 1);
7464 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7465 saxdb_write_int(ctx, KEY_MAX, channel->max);
7467 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7468 if(channel->registrar)
7469 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7470 if(channel->greeting)
7471 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7472 if(channel->user_greeting)
7473 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7474 if(channel->topic_mask)
7475 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7476 if(channel->suspended)
7477 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7479 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7480 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7481 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7482 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7483 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7485 buf[0] = channel->chOpts[chOpt];
7487 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7489 saxdb_end_record(ctx);
7491 if(channel->modes.modes_set || channel->modes.modes_clear)
7493 mod_chanmode_format(&channel->modes, buf);
7494 saxdb_write_string(ctx, KEY_MODES, buf);
7497 high_present = chanserv_write_users(ctx, channel->users);
7498 chanserv_write_bans(ctx, channel->bans);
7500 if(dict_size(channel->notes))
7504 saxdb_start_record(ctx, KEY_NOTES, 1);
7505 for(it = dict_first(channel->notes); it; it = iter_next(it))
7507 struct note *note = iter_data(it);
7508 saxdb_start_record(ctx, iter_key(it), 0);
7509 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7510 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7511 saxdb_end_record(ctx);
7513 saxdb_end_record(ctx);
7516 if(channel->ownerTransfer)
7517 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7518 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7519 saxdb_end_record(ctx);
7523 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7527 saxdb_start_record(ctx, ntype->name, 0);
7528 switch(ntype->set_access_type)
7530 case NOTE_SET_CHANNEL_ACCESS:
7531 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7533 case NOTE_SET_CHANNEL_SETTER:
7534 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7536 case NOTE_SET_PRIVILEGED: default:
7537 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7540 switch(ntype->visible_type)
7542 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7543 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7544 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7546 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7547 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7548 saxdb_end_record(ctx);
7552 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7554 struct do_not_register *dnr;
7555 dict_iterator_t it, next;
7557 for(it = dict_first(dnrs); it; it = next)
7559 next = iter_next(it);
7560 dnr = iter_data(it);
7561 if(dnr->expires && dnr->expires <= now)
7563 dict_remove(dnrs, iter_key(it));
7566 saxdb_start_record(ctx, dnr->chan_name, 0);
7568 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7570 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7571 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7572 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7573 saxdb_end_record(ctx);
7578 chanserv_saxdb_write(struct saxdb_context *ctx)
7581 struct chanData *channel;
7584 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7585 for(it = dict_first(note_types); it; it = iter_next(it))
7586 chanserv_write_note_type(ctx, iter_data(it));
7587 saxdb_end_record(ctx);
7590 saxdb_start_record(ctx, KEY_DNR, 1);
7591 write_dnrs_helper(ctx, handle_dnrs);
7592 write_dnrs_helper(ctx, plain_dnrs);
7593 write_dnrs_helper(ctx, mask_dnrs);
7594 saxdb_end_record(ctx);
7597 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7598 for(channel = channelList; channel; channel = channel->next)
7599 chanserv_write_channel(ctx, channel);
7600 saxdb_end_record(ctx);
7606 chanserv_db_cleanup(void) {
7608 unreg_part_func(handle_part);
7610 unregister_channel(channelList, "terminating.");
7611 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7612 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7613 free(chanserv_conf.support_channels.list);
7614 dict_delete(handle_dnrs);
7615 dict_delete(plain_dnrs);
7616 dict_delete(mask_dnrs);
7617 dict_delete(note_types);
7618 free_string_list(chanserv_conf.eightball);
7619 free_string_list(chanserv_conf.old_ban_names);
7620 free_string_list(chanserv_conf.set_shows);
7621 free(set_shows_list.list);
7622 free(uset_shows_list.list);
7625 struct userData *helper = helperList;
7626 helperList = helperList->next;
7631 #define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, OPTIONS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ## OPTIONS)
7632 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7633 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7636 init_chanserv(const char *nick)
7638 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7639 conf_register_reload(chanserv_conf_read);
7643 reg_server_link_func(handle_server_link);
7644 reg_new_channel_func(handle_new_channel);
7645 reg_join_func(handle_join);
7646 reg_part_func(handle_part);
7647 reg_kick_func(handle_kick);
7648 reg_topic_func(handle_topic);
7649 reg_mode_change_func(handle_mode);
7650 reg_nick_change_func(handle_nick_change);
7651 reg_auth_func(handle_auth);
7654 reg_handle_rename_func(handle_rename);
7655 reg_unreg_func(handle_unreg);
7657 handle_dnrs = dict_new();
7658 dict_set_free_data(handle_dnrs, free);
7659 plain_dnrs = dict_new();
7660 dict_set_free_data(plain_dnrs, free);
7661 mask_dnrs = dict_new();
7662 dict_set_free_data(mask_dnrs, free);
7664 reg_svccmd_unbind_func(handle_svccmd_unbind);
7665 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7666 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7667 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7668 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7669 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7670 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7671 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7672 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7673 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7674 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7675 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7676 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7678 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7679 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7681 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7682 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7683 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7684 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7685 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7687 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7688 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7689 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7690 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7691 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7693 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7694 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7695 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7696 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7698 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7699 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7700 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7701 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7702 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7703 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7704 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7705 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7707 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7708 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7709 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7710 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7711 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7712 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7713 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7714 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7715 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7716 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7717 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7718 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7719 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7720 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7722 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7723 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7724 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7725 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7726 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7728 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7729 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7731 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7732 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7733 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7734 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7735 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7736 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7737 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7738 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7739 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7740 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7741 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7743 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7744 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7746 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7747 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7748 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7749 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7751 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7752 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7753 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7754 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7755 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7757 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7758 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7759 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7760 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7761 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7762 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7764 /* Channel options */
7765 DEFINE_CHANNEL_OPTION(defaulttopic);
7766 DEFINE_CHANNEL_OPTION(topicmask);
7767 DEFINE_CHANNEL_OPTION(greeting);
7768 DEFINE_CHANNEL_OPTION(usergreeting);
7769 DEFINE_CHANNEL_OPTION(modes);
7770 DEFINE_CHANNEL_OPTION(enfops);
7771 DEFINE_CHANNEL_OPTION(giveops);
7772 DEFINE_CHANNEL_OPTION(protect);
7773 DEFINE_CHANNEL_OPTION(enfmodes);
7774 DEFINE_CHANNEL_OPTION(enftopic);
7775 DEFINE_CHANNEL_OPTION(pubcmd);
7776 DEFINE_CHANNEL_OPTION(givevoice);
7777 DEFINE_CHANNEL_OPTION(userinfo);
7778 DEFINE_CHANNEL_OPTION(dynlimit);
7779 DEFINE_CHANNEL_OPTION(topicsnarf);
7780 DEFINE_CHANNEL_OPTION(nodelete);
7781 DEFINE_CHANNEL_OPTION(toys);
7782 DEFINE_CHANNEL_OPTION(setters);
7783 DEFINE_CHANNEL_OPTION(topicrefresh);
7784 DEFINE_CHANNEL_OPTION(ctcpusers);
7785 DEFINE_CHANNEL_OPTION(ctcpreaction);
7786 DEFINE_CHANNEL_OPTION(inviteme);
7787 DEFINE_CHANNEL_OPTION(unreviewed);
7788 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, NULL);
7789 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, NULL);
7791 DEFINE_CHANNEL_OPTION(offchannel);
7792 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7794 /* Alias set topic to set defaulttopic for compatibility. */
7795 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7798 DEFINE_USER_OPTION(noautoop);
7799 DEFINE_USER_OPTION(autoinvite);
7800 DEFINE_USER_OPTION(info);
7802 /* Alias uset autovoice to uset autoop. */
7803 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7805 note_types = dict_new();
7806 dict_set_free_data(note_types, chanserv_deref_note_type);
7809 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7810 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7811 service_register(chanserv)->trigger = '!';
7812 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7814 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7816 if(chanserv_conf.channel_expire_frequency)
7817 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7819 if(chanserv_conf.dnr_expire_frequency)
7820 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7822 if(chanserv_conf.refresh_period)
7824 time_t next_refresh;
7825 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7826 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7829 reg_exit_func(chanserv_db_cleanup);
7830 message_register_table(msgtab);