1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 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() */
26 #include "nickserv.h" /* for oper_outranks() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_CHANNELS "channels"
62 #define KEY_NOTE_TYPES "note_types"
64 /* Note type parameters */
65 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
66 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
67 #define KEY_NOTE_SETTER_ACCESS "setter_access"
68 #define KEY_NOTE_VISIBILITY "visibility"
69 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
70 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
71 #define KEY_NOTE_VIS_ALL "all"
72 #define KEY_NOTE_MAX_LENGTH "max_length"
73 #define KEY_NOTE_SETTER "setter"
74 #define KEY_NOTE_NOTE "note"
76 /* Do-not-register channels */
78 #define KEY_DNR_SET "set"
79 #define KEY_DNR_SETTER "setter"
80 #define KEY_DNR_REASON "reason"
83 #define KEY_REGISTERED "registered"
84 #define KEY_REGISTRAR "registrar"
85 #define KEY_SUSPENDED "suspended"
86 #define KEY_PREVIOUS "previous"
87 #define KEY_SUSPENDER "suspender"
88 #define KEY_ISSUED "issued"
89 #define KEY_REVOKED "revoked"
90 #define KEY_SUSPEND_EXPIRES "suspend_expires"
91 #define KEY_SUSPEND_REASON "suspend_reason"
92 #define KEY_VISITED "visited"
93 #define KEY_TOPIC "topic"
94 #define KEY_GREETING "greeting"
95 #define KEY_USER_GREETING "user_greeting"
96 #define KEY_MODES "modes"
97 #define KEY_FLAGS "flags"
98 #define KEY_OPTIONS "options"
99 #define KEY_USERS "users"
100 #define KEY_BANS "bans"
101 #define KEY_MAX "max"
102 #define KEY_NOTES "notes"
103 #define KEY_TOPIC_MASK "topic_mask"
104 #define KEY_OWNER_TRANSFER "owner_transfer"
107 #define KEY_LEVEL "level"
108 #define KEY_INFO "info"
109 #define KEY_SEEN "seen"
112 #define KEY_OWNER "owner"
113 #define KEY_REASON "reason"
114 #define KEY_SET "set"
115 #define KEY_DURATION "duration"
116 #define KEY_EXPIRES "expires"
117 #define KEY_TRIGGERED "triggered"
119 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
120 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
121 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
123 /* Administrative messages */
124 static const struct message_entry msgtab[] = {
125 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
127 /* Channel registration */
128 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
129 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
130 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
131 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
132 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
133 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
135 /* Do-not-register channels */
136 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
137 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
138 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
140 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
141 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
142 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
143 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
144 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
145 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
146 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
147 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
148 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
151 /* Channel unregistration */
152 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
153 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
154 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
155 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
158 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
159 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161 /* Channel merging */
162 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
163 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
164 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
165 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
166 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168 /* Handle unregistration */
169 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
172 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
173 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
174 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
175 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
176 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
177 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
178 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
179 { "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." },
180 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
181 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
182 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
183 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
184 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
185 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187 /* Removing yourself from a channel. */
188 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
189 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
190 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192 /* User management */
193 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
194 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
195 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
196 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
197 { "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." },
198 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
199 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
200 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
202 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
203 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
204 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
205 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
206 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
207 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
208 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
211 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
212 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
213 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
214 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
215 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
216 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
217 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
218 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
219 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
220 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
221 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
222 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
223 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
224 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
225 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
226 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
228 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
230 /* Channel management */
231 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
232 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
233 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
235 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
236 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
237 { "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" },
238 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
239 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
240 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
241 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
243 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
244 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
245 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
246 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
247 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
248 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
249 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
250 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
251 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
252 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
253 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
254 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
255 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
256 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
257 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
258 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
259 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
260 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
261 { "CSMSG_SET_MODES", "$bModes $b %s" },
262 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
263 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
264 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
265 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
266 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
267 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
268 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
269 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
270 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
271 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
272 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
273 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
274 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
275 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
276 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
277 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
278 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
279 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
280 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
281 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
282 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
283 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
284 { "CSMSG_USET_INFO", "$bInfo $b %s" },
286 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
287 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
288 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
289 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
290 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
291 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
292 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
293 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
294 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
295 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
296 { "CSMSG_PROTECT_NONE", "No users will be protected." },
297 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
298 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
299 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
300 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
301 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
302 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
303 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
304 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
305 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
307 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
308 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
310 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
311 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
312 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
313 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
314 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
315 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
316 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
317 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
319 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
320 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
321 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
323 /* Channel userlist */
324 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
325 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
326 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
327 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
329 /* Channel note list */
330 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
331 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
332 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
333 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
334 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
335 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
336 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
337 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
338 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
339 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
340 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
341 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
342 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
343 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
344 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
346 /* Channel [un]suspension */
347 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
348 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
349 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
350 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
351 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
352 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
353 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
355 /* Access information */
356 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
357 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
358 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
359 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
360 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
361 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
362 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
363 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
364 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
365 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
366 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
367 { "CSMSG_UC_H_TITLE", "network helper" },
368 { "CSMSG_LC_H_TITLE", "support helper" },
369 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
371 /* Seen information */
372 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
373 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
374 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
375 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
377 /* Names information */
378 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
379 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
381 /* Channel information */
382 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
383 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
384 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
385 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
386 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
387 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
388 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
389 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
390 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
391 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
392 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
393 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
401 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
402 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
404 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
405 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
406 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
407 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
408 { "CSMSG_PEEK_OPS", "$bOps:$b" },
409 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
411 /* Network information */
412 { "CSMSG_NETWORK_INFO", "Network Information:" },
413 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
414 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
415 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
416 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
417 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
418 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
419 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
420 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
423 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
424 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
425 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
427 /* Channel searches */
428 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
429 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
430 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
431 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
433 /* Channel configuration */
434 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
435 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
436 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
437 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
440 { "CSMSG_USER_OPTIONS", "User Options:" },
441 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
444 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
445 { "CSMSG_PING_RESPONSE", "Pong!" },
446 { "CSMSG_WUT_RESPONSE", "wut" },
447 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
448 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
449 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
450 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
451 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
452 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
453 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
456 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
460 /* eject_user and unban_user flags */
461 #define ACTION_KICK 0x0001
462 #define ACTION_BAN 0x0002
463 #define ACTION_ADD_BAN 0x0004
464 #define ACTION_ADD_TIMED_BAN 0x0008
465 #define ACTION_UNBAN 0x0010
466 #define ACTION_DEL_BAN 0x0020
468 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
469 #define MODELEN 40 + KEYLEN
473 #define CSFUNC_ARGS user, channel, argc, argv, cmd
475 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
476 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
477 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
478 reply("MSG_MISSING_PARAMS", argv[0]); \
482 DECLARE_LIST(dnrList, struct do_not_register *);
483 DEFINE_LIST(dnrList, struct do_not_register *)
485 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
487 struct userNode *chanserv;
490 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
491 static struct log_type *CS_LOG;
495 struct channelList support_channels;
496 struct mod_chanmode default_modes;
498 unsigned long db_backup_frequency;
499 unsigned long channel_expire_frequency;
500 unsigned long dnr_expire_frequency;
502 unsigned long info_delay;
503 unsigned long adjust_delay;
504 unsigned long channel_expire_delay;
505 unsigned int nodelete_level;
507 unsigned int adjust_threshold;
508 int join_flood_threshold;
510 unsigned int greeting_length;
511 unsigned int refresh_period;
512 unsigned int giveownership_period;
514 unsigned int max_owned;
515 unsigned int max_chan_users;
516 unsigned int max_chan_bans;
517 unsigned int max_userinfo_length;
519 struct string_list *set_shows;
520 struct string_list *eightball;
521 struct string_list *old_ban_names;
523 const char *ctcp_short_ban_duration;
524 const char *ctcp_long_ban_duration;
526 const char *irc_operator_epithet;
527 const char *network_helper_epithet;
528 const char *support_helper_epithet;
533 struct userNode *user;
534 struct userNode *bot;
535 struct chanNode *channel;
537 unsigned short lowest;
538 unsigned short highest;
539 struct userData **users;
540 struct helpfile_table table;
543 enum note_access_type
545 NOTE_SET_CHANNEL_ACCESS,
546 NOTE_SET_CHANNEL_SETTER,
550 enum note_visible_type
553 NOTE_VIS_CHANNEL_USERS,
559 enum note_access_type set_access_type;
561 unsigned int min_opserv;
562 unsigned short min_ulevel;
564 enum note_visible_type visible_type;
565 unsigned int max_length;
572 struct note_type *type;
573 char setter[NICKSERV_HANDLE_LEN+1];
577 static unsigned int registered_channels;
578 static unsigned int banCount;
580 static const struct {
583 unsigned short level;
586 { "peon", "Peon", UL_PEON, '+' },
587 { "op", "Op", UL_OP, '@' },
588 { "master", "Master", UL_MASTER, '%' },
589 { "coowner", "Coowner", UL_COOWNER, '*' },
590 { "owner", "Owner", UL_OWNER, '!' },
591 { "helper", "BUG:", UL_HELPER, 'X' }
594 static const struct {
597 unsigned short default_value;
598 unsigned int old_idx;
599 unsigned int old_flag;
600 unsigned short flag_value;
602 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
603 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
604 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
605 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
606 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
607 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
608 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
609 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
610 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
611 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
612 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
615 struct charOptionValues {
618 } protectValues[] = {
619 { 'a', "CSMSG_PROTECT_ALL" },
620 { 'e', "CSMSG_PROTECT_EQUAL" },
621 { 'l', "CSMSG_PROTECT_LOWER" },
622 { 'n', "CSMSG_PROTECT_NONE" }
624 { 'd', "CSMSG_TOYS_DISABLED" },
625 { 'n', "CSMSG_TOYS_PRIVATE" },
626 { 'p', "CSMSG_TOYS_PUBLIC" }
627 }, topicRefreshValues[] = {
628 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
629 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
630 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
631 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
632 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
633 }, ctcpReactionValues[] = {
634 { 'k', "CSMSG_CTCPREACTION_KICK" },
635 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
636 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
637 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
640 static const struct {
644 unsigned int old_idx;
646 struct charOptionValues *values;
648 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
649 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
650 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
651 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
654 struct userData *helperList;
655 struct chanData *channelList;
656 static struct module *chanserv_module;
657 static unsigned int userCount;
659 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
660 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
661 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
664 user_level_from_name(const char *name, unsigned short clamp_level)
666 unsigned int level = 0, ii;
668 level = strtoul(name, NULL, 10);
669 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
670 if(!irccasecmp(name, accessLevels[ii].name))
671 level = accessLevels[ii].level;
672 if(level > clamp_level)
678 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
681 *minl = strtoul(arg, &sep, 10);
689 *maxl = strtoul(sep+1, &sep, 10);
697 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
699 struct userData *uData, **head;
701 if(!channel || !handle)
704 if(override && HANDLE_FLAGGED(handle, HELPING)
705 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
707 for(uData = helperList;
708 uData && uData->handle != handle;
709 uData = uData->next);
713 uData = calloc(1, sizeof(struct userData));
714 uData->handle = handle;
716 uData->access = UL_HELPER;
722 uData->next = helperList;
724 helperList->prev = uData;
732 for(uData = channel->users; uData; uData = uData->next)
733 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
736 head = &(channel->users);
739 if(uData && (uData != *head))
741 /* Shuffle the user to the head of whatever list he was in. */
743 uData->next->prev = uData->prev;
745 uData->prev->next = uData->next;
751 (**head).prev = uData;
758 /* Returns non-zero if user has at least the minimum access.
759 * exempt_owner is set when handling !set, so the owner can set things
762 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
764 struct userData *uData;
765 struct chanData *cData = channel->channel_info;
766 unsigned short minimum = cData->lvlOpts[opt];
769 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
772 if(minimum <= uData->access)
774 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
779 /* Scan for other users authenticated to the same handle
780 still in the channel. If so, keep them listed as present.
782 user is optional, if not null, it skips checking that userNode
783 (for the handle_part function) */
785 scan_user_presence(struct userData *uData, struct userNode *user)
789 if(IsSuspended(uData->channel)
790 || IsUserSuspended(uData)
791 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
803 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
805 unsigned int eflags, argc;
807 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
809 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
810 if(!channel->channel_info
811 || IsSuspended(channel->channel_info)
813 || !ircncasecmp(text, "ACTION ", 7))
815 /* Figure out the minimum level needed to CTCP the channel */
816 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
818 /* We need to enforce against them; do so. */
820 argv[0] = (char*)text;
821 argv[1] = user->nick;
823 if(GetUserMode(channel, user))
824 eflags |= ACTION_KICK;
825 switch(channel->channel_info->chOpts[chCTCPReaction]) {
826 default: case 'k': /* just do the kick */ break;
828 eflags |= ACTION_BAN;
831 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
832 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
835 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
836 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
839 argv[argc++] = bad_ctcp_reason;
840 eject_user(chanserv, channel, argc, argv, NULL, eflags);
844 chanserv_create_note_type(const char *name)
846 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
847 strcpy(ntype->name, name);
849 dict_insert(note_types, ntype->name, ntype);
854 chanserv_deref_note_type(void *data)
856 struct note_type *ntype = data;
858 if(--ntype->refs > 0)
864 chanserv_flush_note_type(struct note_type *ntype)
866 struct chanData *cData;
867 for(cData = channelList; cData; cData = cData->next)
868 dict_remove(cData->notes, ntype->name);
872 chanserv_truncate_notes(struct note_type *ntype)
874 struct chanData *cData;
876 unsigned int size = sizeof(*note) + ntype->max_length;
878 for(cData = channelList; cData; cData = cData->next) {
879 note = dict_find(cData->notes, ntype->name, NULL);
882 if(strlen(note->note) <= ntype->max_length)
884 dict_remove2(cData->notes, ntype->name, 1);
885 note = realloc(note, size);
886 note->note[ntype->max_length] = 0;
887 dict_insert(cData->notes, ntype->name, note);
891 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
894 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
897 unsigned int len = strlen(text);
899 if(len > type->max_length) len = type->max_length;
900 note = calloc(1, sizeof(*note) + len);
902 strncpy(note->setter, setter, sizeof(note->setter)-1);
903 memcpy(note->note, text, len);
905 dict_insert(channel->notes, type->name, note);
911 chanserv_free_note(void *data)
913 struct note *note = data;
915 chanserv_deref_note_type(note->type);
916 assert(note->type->refs > 0); /* must use delnote to remove the type */
920 static MODCMD_FUNC(cmd_createnote) {
921 struct note_type *ntype;
922 unsigned int arg = 1, existed = 0, max_length;
924 if((ntype = dict_find(note_types, argv[1], NULL)))
927 ntype = chanserv_create_note_type(argv[arg]);
928 if(!irccasecmp(argv[++arg], "privileged"))
931 ntype->set_access_type = NOTE_SET_PRIVILEGED;
932 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
934 else if(!irccasecmp(argv[arg], "channel"))
936 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
939 reply("CSMSG_INVALID_ACCESS", argv[arg]);
942 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
943 ntype->set_access.min_ulevel = ulvl;
945 else if(!irccasecmp(argv[arg], "setter"))
947 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
951 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
955 if(!irccasecmp(argv[++arg], "privileged"))
956 ntype->visible_type = NOTE_VIS_PRIVILEGED;
957 else if(!irccasecmp(argv[arg], "channel_users"))
958 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
959 else if(!irccasecmp(argv[arg], "all"))
960 ntype->visible_type = NOTE_VIS_ALL;
962 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
966 if((arg+1) >= argc) {
967 reply("MSG_MISSING_PARAMS", argv[0]);
970 max_length = strtoul(argv[++arg], NULL, 0);
971 if(max_length < 20 || max_length > 450)
973 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
976 if(existed && (max_length < ntype->max_length))
978 ntype->max_length = max_length;
979 chanserv_truncate_notes(ntype);
981 ntype->max_length = max_length;
984 reply("CSMSG_NOTE_MODIFIED", ntype->name);
986 reply("CSMSG_NOTE_CREATED", ntype->name);
991 dict_remove(note_types, ntype->name);
995 static MODCMD_FUNC(cmd_removenote) {
996 struct note_type *ntype;
999 ntype = dict_find(note_types, argv[1], NULL);
1000 force = (argc > 2) && !irccasecmp(argv[2], "force");
1003 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1010 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1013 chanserv_flush_note_type(ntype);
1015 dict_remove(note_types, argv[1]);
1016 reply("CSMSG_NOTE_DELETED", argv[1]);
1021 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1025 if(orig->modes_set & change->modes_clear)
1027 if(orig->modes_clear & change->modes_set)
1029 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1030 && strcmp(orig->new_key, change->new_key))
1032 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1033 && (orig->new_limit != change->new_limit))
1038 static char max_length_text[MAXLEN+1][16];
1040 static struct helpfile_expansion
1041 chanserv_expand_variable(const char *variable)
1043 struct helpfile_expansion exp;
1045 if(!irccasecmp(variable, "notes"))
1048 exp.type = HF_TABLE;
1049 exp.value.table.length = 1;
1050 exp.value.table.width = 3;
1051 exp.value.table.flags = 0;
1052 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1053 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1054 exp.value.table.contents[0][0] = "Note Type";
1055 exp.value.table.contents[0][1] = "Visibility";
1056 exp.value.table.contents[0][2] = "Max Length";
1057 for(it=dict_first(note_types); it; it=iter_next(it))
1059 struct note_type *ntype = iter_data(it);
1062 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1063 row = exp.value.table.length++;
1064 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1065 exp.value.table.contents[row][0] = ntype->name;
1066 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1067 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1069 if(!max_length_text[ntype->max_length][0])
1070 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1071 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1076 exp.type = HF_STRING;
1077 exp.value.str = NULL;
1081 static struct chanData*
1082 register_channel(struct chanNode *cNode, char *registrar)
1084 struct chanData *channel;
1085 enum levelOption lvlOpt;
1086 enum charOption chOpt;
1088 channel = calloc(1, sizeof(struct chanData));
1090 channel->notes = dict_new();
1091 dict_set_free_data(channel->notes, chanserv_free_note);
1093 channel->registrar = strdup(registrar);
1094 channel->registered = now;
1095 channel->visited = now;
1096 channel->limitAdjusted = now;
1097 channel->ownerTransfer = now;
1098 channel->flags = CHANNEL_DEFAULT_FLAGS;
1099 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1100 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1101 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1102 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1104 channel->prev = NULL;
1105 channel->next = channelList;
1108 channelList->prev = channel;
1109 channelList = channel;
1110 registered_channels++;
1112 channel->channel = cNode;
1114 cNode->channel_info = channel;
1119 static struct userData*
1120 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1122 struct userData *ud;
1124 if(access_level > UL_OWNER)
1127 ud = calloc(1, sizeof(*ud));
1128 ud->channel = channel;
1129 ud->handle = handle;
1131 ud->access = access_level;
1132 ud->info = info ? strdup(info) : NULL;
1135 ud->next = channel->users;
1137 channel->users->prev = ud;
1138 channel->users = ud;
1140 channel->userCount++;
1144 ud->u_next = ud->handle->channels;
1146 ud->u_next->u_prev = ud;
1147 ud->handle->channels = ud;
1152 static void unregister_channel(struct chanData *channel, const char *reason);
1155 del_channel_user(struct userData *user, int do_gc)
1157 struct chanData *channel = user->channel;
1159 channel->userCount--;
1163 user->prev->next = user->next;
1165 channel->users = user->next;
1167 user->next->prev = user->prev;
1170 user->u_prev->u_next = user->u_next;
1172 user->handle->channels = user->u_next;
1174 user->u_next->u_prev = user->u_prev;
1178 if(do_gc && !channel->users && !IsProtected(channel))
1179 unregister_channel(channel, "lost all users.");
1182 static void expire_ban(void *data);
1184 static struct banData*
1185 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1188 unsigned int ii, l1, l2;
1193 bd = malloc(sizeof(struct banData));
1195 bd->channel = channel;
1197 bd->triggered = triggered;
1198 bd->expires = expires;
1200 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1202 extern const char *hidden_host_suffix;
1203 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1207 l2 = strlen(old_name);
1210 if(irccasecmp(mask + l1 - l2, old_name))
1212 new_mask = alloca(MAXLEN);
1213 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1216 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1218 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1219 bd->reason = strdup(reason);
1222 timeq_add(expires, expire_ban, bd);
1225 bd->next = channel->bans;
1227 channel->bans->prev = bd;
1229 channel->banCount++;
1236 del_channel_ban(struct banData *ban)
1238 ban->channel->banCount--;
1242 ban->prev->next = ban->next;
1244 ban->channel->bans = ban->next;
1247 ban->next->prev = ban->prev;
1250 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1259 expire_ban(void *data)
1261 struct banData *bd = data;
1262 if(!IsSuspended(bd->channel))
1264 struct banList bans;
1265 struct mod_chanmode change;
1267 bans = bd->channel->channel->banlist;
1268 mod_chanmode_init(&change);
1269 for(ii=0; ii<bans.used; ii++)
1271 if(!strcmp(bans.list[ii]->ban, bd->mask))
1274 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1275 change.args[0].u.hostmask = bd->mask;
1276 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1282 del_channel_ban(bd);
1285 static void chanserv_expire_suspension(void *data);
1288 unregister_channel(struct chanData *channel, const char *reason)
1290 struct mod_chanmode change;
1291 char msgbuf[MAXLEN];
1293 /* After channel unregistration, the following must be cleaned
1295 - Channel information.
1298 - Channel suspension data.
1299 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1305 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1309 mod_chanmode_init(&change);
1310 change.modes_clear |= MODE_REGISTERED;
1311 mod_chanmode_announce(chanserv, channel->channel, &change);
1314 while(channel->users)
1315 del_channel_user(channel->users, 0);
1317 while(channel->bans)
1318 del_channel_ban(channel->bans);
1320 free(channel->topic);
1321 free(channel->registrar);
1322 free(channel->greeting);
1323 free(channel->user_greeting);
1324 free(channel->topic_mask);
1327 channel->prev->next = channel->next;
1329 channelList = channel->next;
1332 channel->next->prev = channel->prev;
1334 if(channel->suspended)
1336 struct chanNode *cNode = channel->channel;
1337 struct suspended *suspended, *next_suspended;
1339 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1341 next_suspended = suspended->previous;
1342 free(suspended->suspender);
1343 free(suspended->reason);
1344 if(suspended->expires)
1345 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1350 cNode->channel_info = NULL;
1352 channel->channel->channel_info = NULL;
1354 dict_delete(channel->notes);
1355 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1356 if(!IsSuspended(channel))
1357 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1358 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1359 UnlockChannel(channel->channel);
1361 registered_channels--;
1365 expire_channels(UNUSED_ARG(void *data))
1367 struct chanData *channel, *next;
1368 struct userData *user;
1369 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1371 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1372 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1374 for(channel = channelList; channel; channel = next)
1376 next = channel->next;
1378 /* See if the channel can be expired. */
1379 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1380 || IsProtected(channel))
1383 /* Make sure there are no high-ranking users still in the channel. */
1384 for(user=channel->users; user; user=user->next)
1385 if(user->present && (user->access >= UL_PRESENT))
1390 /* Unregister the channel */
1391 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1392 unregister_channel(channel, "registration expired.");
1395 if(chanserv_conf.channel_expire_frequency)
1396 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1400 expire_dnrs(UNUSED_ARG(void *data))
1402 dict_iterator_t it, next;
1403 struct do_not_register *dnr;
1405 for(it = dict_first(handle_dnrs); it; it = next)
1407 dnr = iter_data(it);
1408 next = iter_next(it);
1409 if(dnr->expires && dnr->expires <= now)
1410 dict_remove(handle_dnrs, dnr->chan_name + 1);
1412 for(it = dict_first(plain_dnrs); it; it = next)
1414 dnr = iter_data(it);
1415 next = iter_next(it);
1416 if(dnr->expires && dnr->expires <= now)
1417 dict_remove(plain_dnrs, dnr->chan_name + 1);
1419 for(it = dict_first(mask_dnrs); it; it = next)
1421 dnr = iter_data(it);
1422 next = iter_next(it);
1423 if(dnr->expires && dnr->expires <= now)
1424 dict_remove(mask_dnrs, dnr->chan_name + 1);
1427 if(chanserv_conf.dnr_expire_frequency)
1428 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1432 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1434 char protect = channel->chOpts[chProtect];
1435 struct userData *cs_victim, *cs_aggressor;
1437 /* Don't protect if no one is to be protected, someone is attacking
1438 himself, or if the aggressor is an IRC Operator. */
1439 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1442 /* Don't protect if the victim isn't authenticated (because they
1443 can't be a channel user), unless we are to protect non-users
1445 cs_victim = GetChannelAccess(channel, victim->handle_info);
1446 if(protect != 'a' && !cs_victim)
1449 /* Protect if the aggressor isn't a user because at this point,
1450 the aggressor can only be less than or equal to the victim. */
1451 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1455 /* If the aggressor was a user, then the victim can't be helped. */
1462 if(cs_victim->access > cs_aggressor->access)
1467 if(cs_victim->access >= cs_aggressor->access)
1476 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1478 struct chanData *cData = channel->channel_info;
1479 struct userData *cs_victim;
1481 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1482 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1483 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1485 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1493 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1495 if(IsService(victim))
1497 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1501 if(protect_user(victim, user, channel->channel_info))
1503 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1510 static struct do_not_register *
1511 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1513 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1514 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1515 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1516 strcpy(dnr->reason, reason);
1518 dnr->expires = expires;
1519 if(dnr->chan_name[0] == '*')
1520 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1521 else if(strpbrk(dnr->chan_name, "*?"))
1522 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1524 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1528 static struct dnrList
1529 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1531 struct dnrList list;
1532 dict_iterator_t it, next;
1533 struct do_not_register *dnr;
1535 dnrList_init(&list);
1537 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1539 if(dnr->expires && dnr->expires <= now)
1540 dict_remove(handle_dnrs, handle);
1541 else if(list.used < max)
1542 dnrList_append(&list, dnr);
1545 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1547 if(dnr->expires && dnr->expires <= now)
1548 dict_remove(plain_dnrs, chan_name);
1549 else if(list.used < max)
1550 dnrList_append(&list, dnr);
1555 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1557 next = iter_next(it);
1558 if(!match_ircglob(chan_name, iter_key(it)))
1560 dnr = iter_data(it);
1561 if(dnr->expires && dnr->expires <= now)
1562 dict_remove(mask_dnrs, iter_key(it));
1564 dnrList_append(&list, dnr);
1571 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1573 struct userNode *user;
1574 char buf1[INTERVALLEN];
1575 char buf2[INTERVALLEN];
1582 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1587 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1588 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1592 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1595 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1600 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1602 struct dnrList list;
1605 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1606 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1607 dnr_print_func(list.list[ii], user);
1609 reply("CSMSG_MORE_DNRS", list.used - ii);
1614 struct do_not_register *
1615 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1617 struct dnrList list;
1618 struct do_not_register *dnr;
1620 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1621 dnr = list.used ? list.list[0] : NULL;
1626 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1628 struct do_not_register *dnr;
1629 dict_iterator_t it, next;
1630 unsigned int matches = 0;
1632 for(it = dict_first(dict); it; it = next)
1634 dnr = iter_data(it);
1635 next = iter_next(it);
1636 if(dnr->expires && dnr->expires <= now)
1638 dict_remove(dict, iter_key(it));
1641 dnr_print_func(dnr, user);
1648 static CHANSERV_FUNC(cmd_noregister)
1652 unsigned long expiry, duration;
1653 unsigned int matches;
1657 reply("CSMSG_DNR_SEARCH_RESULTS");
1658 matches = send_dnrs(user, handle_dnrs);
1659 matches += send_dnrs(user, plain_dnrs);
1660 matches += send_dnrs(user, mask_dnrs);
1662 reply("MSG_MATCH_COUNT", matches);
1664 reply("MSG_NO_MATCHES");
1670 if(!IsChannelName(target) && (*target != '*'))
1672 reply("CSMSG_NOT_DNR", target);
1680 reply("MSG_INVALID_DURATION", argv[2]);
1684 if(!strcmp(argv[2], "0"))
1686 else if((duration = ParseInterval(argv[2])))
1687 expiry = now + duration;
1690 reply("MSG_INVALID_DURATION", argv[2]);
1694 reason = unsplit_string(argv + 3, argc - 3, NULL);
1695 if((*target == '*') && !get_handle_info(target + 1))
1697 reply("MSG_HANDLE_UNKNOWN", target + 1);
1700 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1701 reply("CSMSG_NOREGISTER_CHANNEL", target);
1705 reply("CSMSG_DNR_SEARCH_RESULTS");
1707 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1709 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1711 reply("MSG_NO_MATCHES");
1715 static CHANSERV_FUNC(cmd_allowregister)
1717 const char *chan_name = argv[1];
1719 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1720 || dict_remove(plain_dnrs, chan_name)
1721 || dict_remove(mask_dnrs, chan_name))
1723 reply("CSMSG_DNR_REMOVED", chan_name);
1726 reply("CSMSG_NO_SUCH_DNR", chan_name);
1731 struct userNode *source;
1735 unsigned long min_set, max_set;
1736 unsigned long min_expires, max_expires;
1741 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1743 return !((dnr->set < search->min_set)
1744 || (dnr->set > search->max_set)
1745 || (dnr->expires < search->min_expires)
1746 || (search->max_expires
1747 && ((dnr->expires == 0)
1748 || (dnr->expires > search->max_expires)))
1749 || (search->chan_mask
1750 && !match_ircglob(dnr->chan_name, search->chan_mask))
1751 || (search->setter_mask
1752 && !match_ircglob(dnr->setter, search->setter_mask))
1753 || (search->reason_mask
1754 && !match_ircglob(dnr->reason, search->reason_mask)));
1757 static struct dnr_search *
1758 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1760 struct dnr_search *discrim;
1763 discrim = calloc(1, sizeof(*discrim));
1764 discrim->source = user;
1765 discrim->chan_mask = NULL;
1766 discrim->setter_mask = NULL;
1767 discrim->reason_mask = NULL;
1768 discrim->max_set = INT_MAX;
1769 discrim->limit = 50;
1771 for(ii=0; ii<argc; ++ii)
1775 reply("MSG_MISSING_PARAMS", argv[ii]);
1778 else if(0 == irccasecmp(argv[ii], "channel"))
1780 discrim->chan_mask = argv[++ii];
1782 else if(0 == irccasecmp(argv[ii], "setter"))
1784 discrim->setter_mask = argv[++ii];
1786 else if(0 == irccasecmp(argv[ii], "reason"))
1788 discrim->reason_mask = argv[++ii];
1790 else if(0 == irccasecmp(argv[ii], "limit"))
1792 discrim->limit = strtoul(argv[++ii], NULL, 0);
1794 else if(0 == irccasecmp(argv[ii], "set"))
1796 const char *cmp = argv[++ii];
1799 discrim->min_set = now - ParseInterval(cmp + 2);
1801 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1802 } else if(cmp[0] == '=') {
1803 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1804 } else if(cmp[0] == '>') {
1806 discrim->max_set = now - ParseInterval(cmp + 2);
1808 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1810 discrim->max_set = now - (ParseInterval(cmp) - 1);
1813 else if(0 == irccasecmp(argv[ii], "expires"))
1815 const char *cmp = argv[++ii];
1818 discrim->max_expires = now + ParseInterval(cmp + 2);
1820 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1821 } else if(cmp[0] == '=') {
1822 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1823 } else if(cmp[0] == '>') {
1825 discrim->min_expires = now + ParseInterval(cmp + 2);
1827 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1829 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1834 reply("MSG_INVALID_CRITERIA", argv[ii]);
1845 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1848 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1850 struct do_not_register *dnr;
1851 dict_iterator_t next;
1856 /* Initialize local variables. */
1859 if(discrim->chan_mask)
1861 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1862 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1866 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1868 /* Check against account-based DNRs. */
1869 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1870 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1873 else if(target_fixed)
1875 /* Check against channel-based DNRs. */
1876 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1877 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1882 /* Exhaustively search account DNRs. */
1883 for(it = dict_first(handle_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 channel DNRs. */
1892 for(it = dict_first(plain_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))
1900 /* Do the same for wildcarded channel DNRs. */
1901 for(it = dict_first(mask_dnrs); it; it = next)
1903 next = iter_next(it);
1904 dnr = iter_data(it);
1905 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1913 dnr_remove_func(struct do_not_register *match, void *extra)
1915 struct userNode *user;
1918 chan_name = alloca(strlen(match->chan_name) + 1);
1919 strcpy(chan_name, match->chan_name);
1921 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1922 || dict_remove(plain_dnrs, chan_name)
1923 || dict_remove(mask_dnrs, chan_name))
1925 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1931 dnr_count_func(struct do_not_register *match, void *extra)
1933 return 0; (void)match; (void)extra;
1936 static MODCMD_FUNC(cmd_dnrsearch)
1938 struct dnr_search *discrim;
1939 dnr_search_func action;
1940 struct svccmd *subcmd;
1941 unsigned int matches;
1944 sprintf(buf, "dnrsearch %s", argv[1]);
1945 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1948 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1951 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1953 if(!irccasecmp(argv[1], "print"))
1954 action = dnr_print_func;
1955 else if(!irccasecmp(argv[1], "remove"))
1956 action = dnr_remove_func;
1957 else if(!irccasecmp(argv[1], "count"))
1958 action = dnr_count_func;
1961 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1965 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1969 if(action == dnr_print_func)
1970 reply("CSMSG_DNR_SEARCH_RESULTS");
1971 matches = dnr_search(discrim, action, user);
1973 reply("MSG_MATCH_COUNT", matches);
1975 reply("MSG_NO_MATCHES");
1981 chanserv_get_owned_count(struct handle_info *hi)
1983 struct userData *cList;
1986 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1987 if(cList->access == UL_OWNER)
1992 static CHANSERV_FUNC(cmd_register)
1994 struct handle_info *handle;
1995 struct chanData *cData;
1996 struct modeNode *mn;
1997 char reason[MAXLEN];
1999 unsigned int new_channel, force=0;
2000 struct do_not_register *dnr;
2004 if(channel->channel_info)
2006 reply("CSMSG_ALREADY_REGGED", channel->name);
2010 if(channel->bad_channel)
2012 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2017 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2019 reply("CSMSG_MUST_BE_OPPED", channel->name);
2024 chan_name = channel->name;
2028 if((argc < 2) || !IsChannelName(argv[1]))
2030 reply("MSG_NOT_CHANNEL_NAME");
2034 if(opserv_bad_channel(argv[1]))
2036 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2041 chan_name = argv[1];
2044 if(argc >= (new_channel+2))
2046 if(!IsHelping(user))
2048 reply("CSMSG_PROXY_FORBIDDEN");
2052 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2054 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2055 dnr = chanserv_is_dnr(chan_name, handle);
2059 handle = user->handle_info;
2060 dnr = chanserv_is_dnr(chan_name, handle);
2064 if(!IsHelping(user))
2065 reply("CSMSG_DNR_CHANNEL", chan_name);
2067 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2071 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2073 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2078 channel = AddChannel(argv[1], now, NULL, NULL);
2080 cData = register_channel(channel, user->handle_info->handle);
2081 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2082 cData->modes = chanserv_conf.default_modes;
2084 cData->modes.modes_set |= MODE_REGISTERED;
2085 if (IsOffChannel(cData))
2087 mod_chanmode_announce(chanserv, channel, &cData->modes);
2091 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2092 change->args[change->argc].mode = MODE_CHANOP;
2093 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2095 mod_chanmode_announce(chanserv, channel, change);
2096 mod_chanmode_free(change);
2099 /* Initialize the channel's max user record. */
2100 cData->max = channel->members.used;
2102 if(handle != user->handle_info)
2103 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2105 reply("CSMSG_REG_SUCCESS", channel->name);
2107 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2108 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2113 make_confirmation_string(struct userData *uData)
2115 static char strbuf[16];
2120 for(src = uData->handle->handle; *src; )
2121 accum = accum * 31 + toupper(*src++);
2123 for(src = uData->channel->channel->name; *src; )
2124 accum = accum * 31 + toupper(*src++);
2125 sprintf(strbuf, "%08x", accum);
2129 static CHANSERV_FUNC(cmd_unregister)
2132 char reason[MAXLEN];
2133 struct chanData *cData;
2134 struct userData *uData;
2136 cData = channel->channel_info;
2139 reply("CSMSG_NOT_REGISTERED", channel->name);
2143 uData = GetChannelUser(cData, user->handle_info);
2144 if(!uData || (uData->access < UL_OWNER))
2146 reply("CSMSG_NO_ACCESS");
2150 if(IsProtected(cData))
2152 reply("CSMSG_UNREG_NODELETE", channel->name);
2156 if(!IsHelping(user))
2158 const char *confirm_string;
2159 if(IsSuspended(cData))
2161 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2164 confirm_string = make_confirmation_string(uData);
2165 if((argc < 2) || strcmp(argv[1], confirm_string))
2167 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2172 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2173 name = strdup(channel->name);
2174 unregister_channel(cData, reason);
2175 reply("CSMSG_UNREG_SUCCESS", name);
2180 static CHANSERV_FUNC(cmd_move)
2182 struct mod_chanmode change;
2183 struct chanNode *target;
2184 struct modeNode *mn;
2185 struct userData *uData;
2186 char reason[MAXLEN];
2187 struct do_not_register *dnr;
2191 if(IsProtected(channel->channel_info))
2193 reply("CSMSG_MOVE_NODELETE", channel->name);
2197 if(!IsChannelName(argv[1]))
2199 reply("MSG_NOT_CHANNEL_NAME");
2203 if(opserv_bad_channel(argv[1]))
2205 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2209 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2211 for(uData = channel->channel_info->users; uData; uData = uData->next)
2213 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2215 if(!IsHelping(user))
2216 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2218 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2224 mod_chanmode_init(&change);
2225 if(!(target = GetChannel(argv[1])))
2227 target = AddChannel(argv[1], now, NULL, NULL);
2228 if(!IsSuspended(channel->channel_info))
2229 AddChannelUser(chanserv, target);
2231 else if(target->channel_info)
2233 reply("CSMSG_ALREADY_REGGED", target->name);
2236 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2237 && !IsHelping(user))
2239 reply("CSMSG_MUST_BE_OPPED", target->name);
2242 else if(!IsSuspended(channel->channel_info))
2245 change.args[0].mode = MODE_CHANOP;
2246 change.args[0].u.member = AddChannelUser(chanserv, target);
2247 mod_chanmode_announce(chanserv, target, &change);
2252 /* Clear MODE_REGISTERED from old channel, add it to new. */
2254 change.modes_clear = MODE_REGISTERED;
2255 mod_chanmode_announce(chanserv, channel, &change);
2256 change.modes_clear = 0;
2257 change.modes_set = MODE_REGISTERED;
2258 mod_chanmode_announce(chanserv, target, &change);
2261 /* Move the channel_info to the target channel; it
2262 shouldn't be necessary to clear timeq callbacks
2263 for the old channel. */
2264 target->channel_info = channel->channel_info;
2265 target->channel_info->channel = target;
2266 channel->channel_info = NULL;
2268 /* Check whether users are present in the new channel. */
2269 for(uData = target->channel_info->users; uData; uData = uData->next)
2270 scan_user_presence(uData, NULL);
2272 reply("CSMSG_MOVE_SUCCESS", target->name);
2274 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2275 if(!IsSuspended(target->channel_info))
2277 char reason2[MAXLEN];
2278 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2279 DelChannelUser(chanserv, channel, reason2, 0);
2281 UnlockChannel(channel);
2282 LockChannel(target);
2283 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2288 merge_users(struct chanData *source, struct chanData *target)
2290 struct userData *suData, *tuData, *next;
2296 /* Insert the source's users into the scratch area. */
2297 for(suData = source->users; suData; suData = suData->next)
2298 dict_insert(merge, suData->handle->handle, suData);
2300 /* Iterate through the target's users, looking for
2301 users common to both channels. The lower access is
2302 removed from either the scratch area or target user
2304 for(tuData = target->users; tuData; tuData = next)
2306 struct userData *choice;
2308 next = tuData->next;
2310 /* If a source user exists with the same handle as a target
2311 channel's user, resolve the conflict by removing one. */
2312 suData = dict_find(merge, tuData->handle->handle, NULL);
2316 /* Pick the data we want to keep. */
2317 /* If the access is the same, use the later seen time. */
2318 if(suData->access == tuData->access)
2319 choice = (suData->seen > tuData->seen) ? suData : tuData;
2320 else /* Otherwise, keep the higher access level. */
2321 choice = (suData->access > tuData->access) ? suData : tuData;
2322 /* Use the later seen time. */
2323 if(suData->seen < tuData->seen)
2324 suData->seen = tuData->seen;
2326 tuData->seen = suData->seen;
2328 /* Remove the user that wasn't picked. */
2329 if(choice == tuData)
2331 dict_remove(merge, suData->handle->handle);
2332 del_channel_user(suData, 0);
2335 del_channel_user(tuData, 0);
2338 /* Move the remaining users to the target channel. */
2339 for(it = dict_first(merge); it; it = iter_next(it))
2341 suData = iter_data(it);
2343 /* Insert the user into the target channel's linked list. */
2344 suData->prev = NULL;
2345 suData->next = target->users;
2346 suData->channel = target;
2349 target->users->prev = suData;
2350 target->users = suData;
2352 /* Update the user counts for the target channel; the
2353 source counts are left alone. */
2354 target->userCount++;
2356 /* Check whether the user is in the target channel. */
2357 scan_user_presence(suData, NULL);
2360 /* Possible to assert (source->users == NULL) here. */
2361 source->users = NULL;
2366 merge_bans(struct chanData *source, struct chanData *target)
2368 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2370 /* Hold on to the original head of the target ban list
2371 to avoid comparing source bans with source bans. */
2372 tFront = target->bans;
2374 /* Perform a totally expensive O(n*m) merge, ick. */
2375 for(sbData = source->bans; sbData; sbData = sNext)
2377 /* Flag to track whether the ban's been moved
2378 to the destination yet. */
2381 /* Possible to assert (sbData->prev == NULL) here. */
2382 sNext = sbData->next;
2384 for(tbData = tFront; tbData; tbData = tNext)
2386 tNext = tbData->next;
2388 /* Perform two comparisons between each source
2389 and target ban, conflicts are resolved by
2390 keeping the broader ban and copying the later
2391 expiration and triggered time. */
2392 if(match_ircglobs(tbData->mask, sbData->mask))
2394 /* There is a broader ban in the target channel that
2395 overrides one in the source channel; remove the
2396 source ban and break. */
2397 if(sbData->expires > tbData->expires)
2398 tbData->expires = sbData->expires;
2399 if(sbData->triggered > tbData->triggered)
2400 tbData->triggered = sbData->triggered;
2401 del_channel_ban(sbData);
2404 else if(match_ircglobs(sbData->mask, tbData->mask))
2406 /* There is a broader ban in the source channel that
2407 overrides one in the target channel; remove the
2408 target ban, fall through and move the source over. */
2409 if(tbData->expires > sbData->expires)
2410 sbData->expires = tbData->expires;
2411 if(tbData->triggered > sbData->triggered)
2412 sbData->triggered = tbData->triggered;
2413 if(tbData == tFront)
2415 del_channel_ban(tbData);
2418 /* Source bans can override multiple target bans, so
2419 we allow a source to run through this loop multiple
2420 times, but we can only move it once. */
2425 /* Remove the source ban from the source ban list. */
2427 sbData->next->prev = sbData->prev;
2429 /* Modify the source ban's associated channel. */
2430 sbData->channel = target;
2432 /* Insert the ban into the target channel's linked list. */
2433 sbData->prev = NULL;
2434 sbData->next = target->bans;
2437 target->bans->prev = sbData;
2438 target->bans = sbData;
2440 /* Update the user counts for the target channel. */
2445 /* Possible to assert (source->bans == NULL) here. */
2446 source->bans = NULL;
2450 merge_data(struct chanData *source, struct chanData *target)
2452 /* Use more recent visited and owner-transfer time; use older
2453 * registered time. Bitwise or may_opchan. Use higher max.
2454 * Do not touch last_refresh, ban count or user counts.
2456 if(source->visited > target->visited)
2457 target->visited = source->visited;
2458 if(source->registered < target->registered)
2459 target->registered = source->registered;
2460 if(source->ownerTransfer > target->ownerTransfer)
2461 target->ownerTransfer = source->ownerTransfer;
2462 if(source->may_opchan)
2463 target->may_opchan = 1;
2464 if(source->max > target->max)
2465 target->max = source->max;
2469 merge_channel(struct chanData *source, struct chanData *target)
2471 merge_users(source, target);
2472 merge_bans(source, target);
2473 merge_data(source, target);
2476 static CHANSERV_FUNC(cmd_merge)
2478 struct userData *target_user;
2479 struct chanNode *target;
2480 char reason[MAXLEN];
2484 /* Make sure the target channel exists and is registered to the user
2485 performing the command. */
2486 if(!(target = GetChannel(argv[1])))
2488 reply("MSG_INVALID_CHANNEL");
2492 if(!target->channel_info)
2494 reply("CSMSG_NOT_REGISTERED", target->name);
2498 if(IsProtected(channel->channel_info))
2500 reply("CSMSG_MERGE_NODELETE");
2504 if(IsSuspended(target->channel_info))
2506 reply("CSMSG_MERGE_SUSPENDED");
2510 if(channel == target)
2512 reply("CSMSG_MERGE_SELF");
2516 target_user = GetChannelUser(target->channel_info, user->handle_info);
2517 if(!target_user || (target_user->access < UL_OWNER))
2519 reply("CSMSG_MERGE_NOT_OWNER");
2523 /* Merge the channel structures and associated data. */
2524 merge_channel(channel->channel_info, target->channel_info);
2525 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2526 unregister_channel(channel->channel_info, reason);
2527 reply("CSMSG_MERGE_SUCCESS", target->name);
2531 static CHANSERV_FUNC(cmd_opchan)
2533 struct mod_chanmode change;
2534 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2536 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2539 channel->channel_info->may_opchan = 0;
2540 mod_chanmode_init(&change);
2542 change.args[0].mode = MODE_CHANOP;
2543 change.args[0].u.member = GetUserMode(channel, chanserv);
2544 if(!change.args[0].u.member)
2546 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2549 mod_chanmode_announce(chanserv, channel, &change);
2550 reply("CSMSG_OPCHAN_DONE", channel->name);
2554 static CHANSERV_FUNC(cmd_adduser)
2556 struct userData *actee;
2557 struct userData *actor, *real_actor;
2558 struct handle_info *handle;
2559 unsigned short access_level, override = 0;
2563 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2565 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2569 access_level = user_level_from_name(argv[2], UL_OWNER);
2572 reply("CSMSG_INVALID_ACCESS", argv[2]);
2576 actor = GetChannelUser(channel->channel_info, user->handle_info);
2577 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2579 if(actor->access <= access_level)
2581 reply("CSMSG_NO_BUMP_ACCESS");
2585 /* Trying to add someone with equal/more access? */
2586 if (!real_actor || real_actor->access <= access_level)
2587 override = CMD_LOG_OVERRIDE;
2589 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2592 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2594 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2598 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2599 scan_user_presence(actee, NULL);
2600 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2601 return 1 | override;
2604 static CHANSERV_FUNC(cmd_clvl)
2606 struct handle_info *handle;
2607 struct userData *victim;
2608 struct userData *actor, *real_actor;
2609 unsigned short new_access, override = 0;
2610 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2614 actor = GetChannelUser(channel->channel_info, user->handle_info);
2615 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2617 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2620 if(handle == user->handle_info && !privileged)
2622 reply("CSMSG_NO_SELF_CLVL");
2626 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2628 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2632 if(actor->access <= victim->access && !privileged)
2634 reply("MSG_USER_OUTRANKED", handle->handle);
2638 new_access = user_level_from_name(argv[2], UL_OWNER);
2642 reply("CSMSG_INVALID_ACCESS", argv[2]);
2646 if(new_access >= actor->access && !privileged)
2648 reply("CSMSG_NO_BUMP_ACCESS");
2652 /* Trying to clvl a equal/higher user? */
2653 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2654 override = CMD_LOG_OVERRIDE;
2655 /* Trying to clvl someone to equal/higher access? */
2656 if(!real_actor || new_access >= real_actor->access)
2657 override = CMD_LOG_OVERRIDE;
2658 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2659 * If they lower their own access it's not a big problem.
2662 victim->access = new_access;
2663 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2664 return 1 | override;
2667 static CHANSERV_FUNC(cmd_deluser)
2669 struct handle_info *handle;
2670 struct userData *victim;
2671 struct userData *actor, *real_actor;
2672 unsigned short access_level, override = 0;
2677 actor = GetChannelUser(channel->channel_info, user->handle_info);
2678 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2680 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2683 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2685 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2691 access_level = user_level_from_name(argv[1], UL_OWNER);
2694 reply("CSMSG_INVALID_ACCESS", argv[1]);
2697 if(access_level != victim->access)
2699 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2705 access_level = victim->access;
2708 if((actor->access <= victim->access) && !IsHelping(user))
2710 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2714 /* If people delete themselves it is an override, but they
2715 * could've used deleteme so we don't log it as an override
2717 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2718 override = CMD_LOG_OVERRIDE;
2720 chan_name = strdup(channel->name);
2721 del_channel_user(victim, 1);
2722 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2724 return 1 | override;
2728 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2730 struct userData *actor, *real_actor, *uData, *next;
2731 unsigned int override = 0;
2733 actor = GetChannelUser(channel->channel_info, user->handle_info);
2734 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2736 if(min_access > max_access)
2738 reply("CSMSG_BAD_RANGE", min_access, max_access);
2742 if(actor->access <= max_access)
2744 reply("CSMSG_NO_ACCESS");
2748 if(!real_actor || real_actor->access <= max_access)
2749 override = CMD_LOG_OVERRIDE;
2751 for(uData = channel->channel_info->users; uData; uData = next)
2755 if((uData->access >= min_access)
2756 && (uData->access <= max_access)
2757 && match_ircglob(uData->handle->handle, mask))
2758 del_channel_user(uData, 1);
2761 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2762 return 1 | override;
2765 static CHANSERV_FUNC(cmd_mdelowner)
2767 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2770 static CHANSERV_FUNC(cmd_mdelcoowner)
2772 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2775 static CHANSERV_FUNC(cmd_mdelmaster)
2777 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2780 static CHANSERV_FUNC(cmd_mdelop)
2782 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2785 static CHANSERV_FUNC(cmd_mdelpeon)
2787 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2791 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2793 struct banData *bData, *next;
2794 char interval[INTERVALLEN];
2796 unsigned long limit;
2799 limit = now - duration;
2800 for(bData = channel->channel_info->bans; bData; bData = next)
2804 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2807 del_channel_ban(bData);
2811 intervalString(interval, duration, user->handle_info);
2812 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2817 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2819 struct userData *actor, *uData, *next;
2820 char interval[INTERVALLEN];
2822 unsigned long limit;
2824 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2825 if(min_access > max_access)
2827 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2831 if(!actor || actor->access <= max_access)
2833 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2838 limit = now - duration;
2839 for(uData = channel->channel_info->users; uData; uData = next)
2843 if((uData->seen > limit)
2845 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2848 if(((uData->access >= min_access) && (uData->access <= max_access))
2849 || (!max_access && (uData->access < actor->access)))
2851 del_channel_user(uData, 1);
2859 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2861 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2865 static CHANSERV_FUNC(cmd_trim)
2867 unsigned long duration;
2868 unsigned short min_level, max_level;
2873 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2874 duration = ParseInterval(argv[2]);
2877 reply("CSMSG_CANNOT_TRIM");
2881 if(!irccasecmp(argv[1], "bans"))
2883 cmd_trim_bans(user, channel, duration);
2886 else if(!irccasecmp(argv[1], "users"))
2888 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2891 else if(parse_level_range(&min_level, &max_level, argv[1]))
2893 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2896 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2898 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2903 reply("CSMSG_INVALID_TRIM", argv[1]);
2908 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2909 to the user. cmd_all takes advantage of this. */
2910 static CHANSERV_FUNC(cmd_up)
2912 struct mod_chanmode change;
2913 struct userData *uData;
2916 mod_chanmode_init(&change);
2918 change.args[0].u.member = GetUserMode(channel, user);
2919 if(!change.args[0].u.member)
2922 reply("MSG_CHANNEL_ABSENT", channel->name);
2926 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2930 reply("CSMSG_GODMODE_UP", argv[0]);
2933 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2935 change.args[0].mode = MODE_CHANOP;
2936 errmsg = "CSMSG_ALREADY_OPPED";
2938 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2940 change.args[0].mode = MODE_VOICE;
2941 errmsg = "CSMSG_ALREADY_VOICED";
2946 reply("CSMSG_NO_ACCESS");
2949 change.args[0].mode &= ~change.args[0].u.member->modes;
2950 if(!change.args[0].mode)
2953 reply(errmsg, channel->name);
2956 modcmd_chanmode_announce(&change);
2960 static CHANSERV_FUNC(cmd_down)
2962 struct mod_chanmode change;
2964 mod_chanmode_init(&change);
2966 change.args[0].u.member = GetUserMode(channel, user);
2967 if(!change.args[0].u.member)
2970 reply("MSG_CHANNEL_ABSENT", channel->name);
2974 if(!change.args[0].u.member->modes)
2977 reply("CSMSG_ALREADY_DOWN", channel->name);
2981 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2982 modcmd_chanmode_announce(&change);
2986 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)
2988 struct userData *cList;
2990 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2992 if(IsSuspended(cList->channel)
2993 || IsUserSuspended(cList)
2994 || !GetUserMode(cList->channel->channel, user))
2997 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3003 static CHANSERV_FUNC(cmd_upall)
3005 return cmd_all(CSFUNC_ARGS, cmd_up);
3008 static CHANSERV_FUNC(cmd_downall)
3010 return cmd_all(CSFUNC_ARGS, cmd_down);
3013 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3014 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3017 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)
3019 unsigned int ii, valid;
3020 struct userNode *victim;
3021 struct mod_chanmode *change;
3023 change = mod_chanmode_alloc(argc - 1);
3025 for(ii=valid=0; ++ii < argc; )
3027 if(!(victim = GetUserH(argv[ii])))
3029 change->args[valid].mode = mode;
3030 change->args[valid].u.member = GetUserMode(channel, victim);
3031 if(!change->args[valid].u.member)
3033 if(validate && !validate(user, channel, victim))
3038 change->argc = valid;
3039 if(valid < (argc-1))
3040 reply("CSMSG_PROCESS_FAILED");
3043 modcmd_chanmode_announce(change);
3044 reply(action, channel->name);
3046 mod_chanmode_free(change);
3050 static CHANSERV_FUNC(cmd_op)
3052 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3055 static CHANSERV_FUNC(cmd_deop)
3057 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3060 static CHANSERV_FUNC(cmd_voice)
3062 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3065 static CHANSERV_FUNC(cmd_devoice)
3067 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3071 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3077 for(ii=0; ii<channel->members.used; ii++)
3079 struct modeNode *mn = channel->members.list[ii];
3081 if(IsService(mn->user))
3084 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3087 if(protect_user(mn->user, user, channel->channel_info))
3091 victims[(*victimCount)++] = mn;
3097 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3099 struct userNode *victim;
3100 struct modeNode **victims;
3101 unsigned int offset, n, victimCount, duration = 0;
3102 char *reason = "Bye.", *ban, *name;
3103 char interval[INTERVALLEN];
3105 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3106 REQUIRE_PARAMS(offset);
3109 reason = unsplit_string(argv + offset, argc - offset, NULL);
3110 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3112 /* Truncate the reason to a length of TOPICLEN, as
3113 the ircd does; however, leave room for an ellipsis
3114 and the kicker's nick. */
3115 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3119 if((victim = GetUserH(argv[1])))
3121 victims = alloca(sizeof(victims[0]));
3122 victims[0] = GetUserMode(channel, victim);
3123 /* XXX: The comparison with ACTION_KICK is just because all
3124 * other actions can work on users outside the channel, and we
3125 * want to allow those (e.g. unbans) in that case. If we add
3126 * some other ejection action for in-channel users, change
3128 victimCount = victims[0] ? 1 : 0;
3130 if(IsService(victim))
3132 reply("MSG_SERVICE_IMMUNE", victim->nick);
3136 if((action == ACTION_KICK) && !victimCount)
3138 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3142 if(protect_user(victim, user, channel->channel_info))
3144 reply("CSMSG_USER_PROTECTED", victim->nick);
3148 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3149 name = victim->nick;
3151 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3153 struct handle_info *hi;
3154 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3155 const char *accountname = argv[1] + 1;
3157 if(!(hi = get_handle_info(accountname)))
3159 reply("MSG_HANDLE_UNKNOWN", accountname);
3163 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3164 victims = alloca(sizeof(victims[0]) * channel->members.used);
3166 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3168 reply("CSMSG_MASK_PROTECTED", banmask);
3172 if((action == ACTION_KICK) && (victimCount == 0))
3174 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3178 name = ban = strdup(banmask);
3182 if(!is_ircmask(argv[1]))
3184 reply("MSG_NICK_UNKNOWN", argv[1]);
3188 victims = alloca(sizeof(victims[0]) * channel->members.used);
3190 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3192 reply("CSMSG_MASK_PROTECTED", argv[1]);
3196 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3198 reply("CSMSG_LAME_MASK", argv[1]);
3202 if((action == ACTION_KICK) && (victimCount == 0))
3204 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3208 name = ban = strdup(argv[1]);
3211 /* Truncate the ban in place if necessary; we must ensure
3212 that 'ban' is a valid ban mask before sanitizing it. */
3213 sanitize_ircmask(ban);
3215 if(action & ACTION_ADD_BAN)
3217 struct banData *bData, *next;
3219 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3221 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3226 if(action & ACTION_ADD_TIMED_BAN)
3228 duration = ParseInterval(argv[2]);
3232 reply("CSMSG_DURATION_TOO_LOW");
3236 else if(duration > (86400 * 365 * 2))
3238 reply("CSMSG_DURATION_TOO_HIGH");
3244 for(bData = channel->channel_info->bans; bData; bData = next)
3246 if(match_ircglobs(bData->mask, ban))
3248 int exact = !irccasecmp(bData->mask, ban);
3250 /* The ban is redundant; there is already a ban
3251 with the same effect in place. */
3255 free(bData->reason);
3256 bData->reason = strdup(reason);
3257 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3259 reply("CSMSG_REASON_CHANGE", ban);
3263 if(exact && bData->expires)
3267 /* If the ban matches an existing one exactly,
3268 extend the expiration time if the provided
3269 duration is longer. */
3270 if(duration && (now + duration > bData->expires))
3272 bData->expires = now + duration;
3283 /* Delete the expiration timeq entry and
3284 requeue if necessary. */
3285 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3288 timeq_add(bData->expires, expire_ban, bData);
3292 /* automated kickban */
3295 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3297 reply("CSMSG_BAN_ADDED", name, channel->name);
3303 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3310 if(match_ircglobs(ban, bData->mask))
3312 /* The ban we are adding makes previously existing
3313 bans redundant; silently remove them. */
3314 del_channel_ban(bData);
3318 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);
3320 name = ban = strdup(bData->mask);
3324 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3326 extern const char *hidden_host_suffix;
3327 const char *old_name = chanserv_conf.old_ban_names->list[n];
3329 unsigned int l1, l2;
3332 l2 = strlen(old_name);
3335 if(irccasecmp(ban + l1 - l2, old_name))
3337 new_mask = malloc(MAXLEN);
3338 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3340 name = ban = new_mask;
3345 if(action & ACTION_BAN)
3347 unsigned int exists;
3348 struct mod_chanmode *change;
3350 if(channel->banlist.used >= MAXBANS)
3353 reply("CSMSG_BANLIST_FULL", channel->name);
3358 exists = ChannelBanExists(channel, ban);
3359 change = mod_chanmode_alloc(victimCount + 1);
3360 for(n = 0; n < victimCount; ++n)
3362 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3363 change->args[n].u.member = victims[n];
3367 change->args[n].mode = MODE_BAN;
3368 change->args[n++].u.hostmask = ban;
3372 modcmd_chanmode_announce(change);
3374 mod_chanmode_announce(chanserv, channel, change);
3375 mod_chanmode_free(change);
3377 if(exists && (action == ACTION_BAN))
3380 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3386 if(action & ACTION_KICK)
3388 char kick_reason[MAXLEN];
3389 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3391 for(n = 0; n < victimCount; n++)
3392 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3397 /* No response, since it was automated. */
3399 else if(action & ACTION_ADD_BAN)
3402 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3404 reply("CSMSG_BAN_ADDED", name, channel->name);
3406 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3407 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3408 else if(action & ACTION_BAN)
3409 reply("CSMSG_BAN_DONE", name, channel->name);
3410 else if(action & ACTION_KICK && victimCount)
3411 reply("CSMSG_KICK_DONE", name, channel->name);
3417 static CHANSERV_FUNC(cmd_kickban)
3419 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3422 static CHANSERV_FUNC(cmd_kick)
3424 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3427 static CHANSERV_FUNC(cmd_ban)
3429 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3432 static CHANSERV_FUNC(cmd_addban)
3434 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3437 static CHANSERV_FUNC(cmd_addtimedban)
3439 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3442 static struct mod_chanmode *
3443 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3445 struct mod_chanmode *change;
3446 unsigned char *match;
3447 unsigned int ii, count;
3449 match = alloca(bans->used);
3452 for(ii = count = 0; ii < bans->used; ++ii)
3454 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3455 MATCH_USENICK | MATCH_VISIBLE);
3462 for(ii = count = 0; ii < bans->used; ++ii)
3464 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3471 change = mod_chanmode_alloc(count);
3472 for(ii = count = 0; ii < bans->used; ++ii)
3476 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3477 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3479 assert(count == change->argc);
3484 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3486 struct userNode *actee;
3492 /* may want to allow a comma delimited list of users... */
3493 if(!(actee = GetUserH(argv[1])))
3495 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3497 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3498 const char *accountname = argv[1] + 1;
3500 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3501 mask = strdup(banmask);
3503 else if(!is_ircmask(argv[1]))
3505 reply("MSG_NICK_UNKNOWN", argv[1]);
3510 mask = strdup(argv[1]);
3514 /* We don't sanitize the mask here because ircu
3516 if(action & ACTION_UNBAN)
3518 struct mod_chanmode *change;
3519 change = find_matching_bans(&channel->banlist, actee, mask);
3524 modcmd_chanmode_announce(change);
3525 for(ii = 0; ii < change->argc; ++ii)
3526 free((char*)change->args[ii].u.hostmask);
3527 mod_chanmode_free(change);
3532 if(action & ACTION_DEL_BAN)
3534 struct banData *ban, *next;
3536 ban = channel->channel_info->bans;
3540 for( ; ban && !user_matches_glob(actee, ban->mask,
3541 MATCH_USENICK | MATCH_VISIBLE);
3544 for( ; ban && !match_ircglobs(mask, ban->mask);
3549 del_channel_ban(ban);
3556 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3558 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3564 static CHANSERV_FUNC(cmd_unban)
3566 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3569 static CHANSERV_FUNC(cmd_delban)
3571 /* it doesn't necessarily have to remove the channel ban - may want
3572 to make that an option. */
3573 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3576 static CHANSERV_FUNC(cmd_unbanme)
3578 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3579 long flags = ACTION_UNBAN;
3581 /* remove permanent bans if the user has the proper access. */
3582 if(uData->access >= UL_MASTER)
3583 flags |= ACTION_DEL_BAN;
3585 argv[1] = user->nick;
3586 return unban_user(user, channel, 2, argv, cmd, flags);
3589 static CHANSERV_FUNC(cmd_unbanall)
3591 struct mod_chanmode *change;
3594 if(!channel->banlist.used)
3596 reply("CSMSG_NO_BANS", channel->name);
3600 change = mod_chanmode_alloc(channel->banlist.used);
3601 for(ii=0; ii<channel->banlist.used; ii++)
3603 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3604 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3606 modcmd_chanmode_announce(change);
3607 for(ii = 0; ii < change->argc; ++ii)
3608 free((char*)change->args[ii].u.hostmask);
3609 mod_chanmode_free(change);
3610 reply("CSMSG_BANS_REMOVED", channel->name);
3614 static CHANSERV_FUNC(cmd_open)
3616 struct mod_chanmode *change;
3619 change = find_matching_bans(&channel->banlist, user, NULL);
3621 change = mod_chanmode_alloc(0);
3622 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3623 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3624 && channel->channel_info->modes.modes_set)
3625 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3626 modcmd_chanmode_announce(change);
3627 reply("CSMSG_CHANNEL_OPENED", channel->name);
3628 for(ii = 0; ii < change->argc; ++ii)
3629 free((char*)change->args[ii].u.hostmask);
3630 mod_chanmode_free(change);
3634 static CHANSERV_FUNC(cmd_myaccess)
3636 static struct string_buffer sbuf;
3637 struct handle_info *target_handle;
3638 struct userData *uData;
3641 target_handle = user->handle_info;
3642 else if(!IsStaff(user))
3644 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3647 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3650 if(!oper_outranks(user, target_handle))
3653 if(!target_handle->channels)
3655 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3659 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3660 for(uData = target_handle->channels; uData; uData = uData->u_next)
3662 struct chanData *cData = uData->channel;
3664 if(uData->access > UL_OWNER)
3666 if(IsProtected(cData)
3667 && (target_handle != user->handle_info)
3668 && !GetTrueChannelAccess(cData, user->handle_info))
3671 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3672 if(uData->flags != USER_AUTO_OP)
3673 string_buffer_append(&sbuf, ',');
3674 if(IsUserSuspended(uData))
3675 string_buffer_append(&sbuf, 's');
3676 if(IsUserAutoOp(uData))
3678 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3679 string_buffer_append(&sbuf, 'o');
3680 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3681 string_buffer_append(&sbuf, 'v');
3683 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3684 string_buffer_append(&sbuf, 'i');
3686 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3688 string_buffer_append_string(&sbuf, ")]");
3689 string_buffer_append(&sbuf, '\0');
3690 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3696 static CHANSERV_FUNC(cmd_access)
3698 struct userNode *target;
3699 struct handle_info *target_handle;
3700 struct userData *uData;
3702 char prefix[MAXLEN];
3707 target_handle = target->handle_info;
3709 else if((target = GetUserH(argv[1])))
3711 target_handle = target->handle_info;
3713 else if(argv[1][0] == '*')
3715 if(!(target_handle = get_handle_info(argv[1]+1)))
3717 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3723 reply("MSG_NICK_UNKNOWN", argv[1]);
3727 assert(target || target_handle);
3729 if(target == chanserv)
3731 reply("CSMSG_IS_CHANSERV");
3739 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3744 reply("MSG_USER_AUTHENTICATE", target->nick);
3747 reply("MSG_AUTHENTICATE");
3753 const char *epithet = NULL, *type = NULL;
3756 epithet = chanserv_conf.irc_operator_epithet;
3757 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3759 else if(IsNetworkHelper(target))
3761 epithet = chanserv_conf.network_helper_epithet;
3762 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3764 else if(IsSupportHelper(target))
3766 epithet = chanserv_conf.support_helper_epithet;
3767 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3771 if(target_handle->epithet)
3772 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3774 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3776 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3780 sprintf(prefix, "%s", target_handle->handle);
3783 if(!channel->channel_info)
3785 reply("CSMSG_NOT_REGISTERED", channel->name);
3789 helping = HANDLE_FLAGGED(target_handle, HELPING)
3790 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3791 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3793 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3794 /* To prevent possible information leaks, only show infolines
3795 * if the requestor is in the channel or it's their own
3797 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3799 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3801 /* Likewise, only say it's suspended if the user has active
3802 * access in that channel or it's their own entry. */
3803 if(IsUserSuspended(uData)
3804 && (GetChannelUser(channel->channel_info, user->handle_info)
3805 || (user->handle_info == uData->handle)))
3807 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3812 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3819 zoot_list(struct listData *list)
3821 struct userData *uData;
3822 unsigned int start, curr, highest, lowest;
3823 struct helpfile_table tmp_table;
3824 const char **temp, *msg;
3826 if(list->table.length == 1)
3829 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3831 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3832 msg = user_find_message(list->user, "MSG_NONE");
3833 send_message_type(4, list->user, list->bot, " %s", msg);
3835 tmp_table.width = list->table.width;
3836 tmp_table.flags = list->table.flags;
3837 list->table.contents[0][0] = " ";
3838 highest = list->highest;
3839 if(list->lowest != 0)
3840 lowest = list->lowest;
3841 else if(highest < 100)
3844 lowest = highest - 100;
3845 for(start = curr = 1; curr < list->table.length; )
3847 uData = list->users[curr-1];
3848 list->table.contents[curr++][0] = " ";
3849 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3852 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3854 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3855 temp = list->table.contents[--start];
3856 list->table.contents[start] = list->table.contents[0];
3857 tmp_table.contents = list->table.contents + start;
3858 tmp_table.length = curr - start;
3859 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3860 list->table.contents[start] = temp;
3862 highest = lowest - 1;
3863 lowest = (highest < 100) ? 0 : (highest - 99);
3869 def_list(struct listData *list)
3873 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3875 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3876 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3877 if(list->table.length == 1)
3879 msg = user_find_message(list->user, "MSG_NONE");
3880 send_message_type(4, list->user, list->bot, " %s", msg);
3885 userData_access_comp(const void *arg_a, const void *arg_b)
3887 const struct userData *a = *(struct userData**)arg_a;
3888 const struct userData *b = *(struct userData**)arg_b;
3890 if(a->access != b->access)
3891 res = b->access - a->access;
3893 res = irccasecmp(a->handle->handle, b->handle->handle);
3898 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3900 void (*send_list)(struct listData *);
3901 struct userData *uData;
3902 struct listData lData;
3903 unsigned int matches;
3907 lData.bot = cmd->parent->bot;
3908 lData.channel = channel;
3909 lData.lowest = lowest;
3910 lData.highest = highest;
3911 lData.search = (argc > 1) ? argv[1] : NULL;
3912 send_list = def_list;
3913 (void)zoot_list; /* since it doesn't show user levels */
3915 if(user->handle_info)
3917 switch(user->handle_info->userlist_style)
3919 case HI_STYLE_DEF: send_list = def_list; break;
3920 case HI_STYLE_ZOOT: send_list = def_list; break;
3924 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3926 for(uData = channel->channel_info->users; uData; uData = uData->next)
3928 if((uData->access < lowest)
3929 || (uData->access > highest)
3930 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3932 lData.users[matches++] = uData;
3934 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3936 lData.table.length = matches+1;
3937 lData.table.width = 4;
3938 lData.table.flags = TABLE_NO_FREE;
3939 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3940 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3941 lData.table.contents[0] = ary;
3944 ary[2] = "Last Seen";
3946 for(matches = 1; matches < lData.table.length; ++matches)
3948 char seen[INTERVALLEN];
3950 uData = lData.users[matches-1];
3951 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3952 lData.table.contents[matches] = ary;
3953 ary[0] = strtab(uData->access);
3954 ary[1] = uData->handle->handle;
3957 else if(!uData->seen)
3960 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3961 ary[2] = strdup(ary[2]);
3962 if(IsUserSuspended(uData))
3963 ary[3] = "Suspended";
3964 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3965 ary[3] = "Vacation";
3970 for(matches = 1; matches < lData.table.length; ++matches)
3972 free((char*)lData.table.contents[matches][2]);
3973 free(lData.table.contents[matches]);
3975 free(lData.table.contents[0]);
3976 free(lData.table.contents);
3980 static CHANSERV_FUNC(cmd_users)
3982 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3985 static CHANSERV_FUNC(cmd_wlist)
3987 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3990 static CHANSERV_FUNC(cmd_clist)
3992 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3995 static CHANSERV_FUNC(cmd_mlist)
3997 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4000 static CHANSERV_FUNC(cmd_olist)
4002 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4005 static CHANSERV_FUNC(cmd_plist)
4007 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4010 static CHANSERV_FUNC(cmd_bans)
4012 struct userNode *search_u = NULL;
4013 struct helpfile_table tbl;
4014 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4015 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4016 const char *msg_never, *triggered, *expires;
4017 struct banData *ban, **bans;
4021 else if(strchr(search = argv[1], '!'))
4024 search_wilds = search[strcspn(search, "?*")];
4026 else if(!(search_u = GetUserH(search)))
4027 reply("MSG_NICK_UNKNOWN", search);
4029 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4031 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4035 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4040 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4043 bans[matches++] = ban;
4048 tbl.length = matches + 1;
4049 tbl.width = 4 + timed;
4051 tbl.flags = TABLE_NO_FREE;
4052 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4053 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4054 tbl.contents[0][0] = "Mask";
4055 tbl.contents[0][1] = "Set By";
4056 tbl.contents[0][2] = "Triggered";
4059 tbl.contents[0][3] = "Expires";
4060 tbl.contents[0][4] = "Reason";
4063 tbl.contents[0][3] = "Reason";
4066 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4068 free(tbl.contents[0]);
4073 msg_never = user_find_message(user, "MSG_NEVER");
4074 for(ii = 0; ii < matches; )
4080 else if(ban->expires)
4081 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4083 expires = msg_never;
4086 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4088 triggered = msg_never;
4090 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4091 tbl.contents[ii][0] = ban->mask;
4092 tbl.contents[ii][1] = ban->owner;
4093 tbl.contents[ii][2] = strdup(triggered);
4096 tbl.contents[ii][3] = strdup(expires);
4097 tbl.contents[ii][4] = ban->reason;
4100 tbl.contents[ii][3] = ban->reason;
4102 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4103 reply("MSG_MATCH_COUNT", matches);
4104 for(ii = 1; ii < tbl.length; ++ii)
4106 free((char*)tbl.contents[ii][2]);
4108 free((char*)tbl.contents[ii][3]);
4109 free(tbl.contents[ii]);
4111 free(tbl.contents[0]);
4117 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4119 struct chanData *cData = channel->channel_info;
4120 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4122 if(cData->topic_mask)
4123 return !match_ircglob(new_topic, cData->topic_mask);
4124 else if(cData->topic)
4125 return irccasecmp(new_topic, cData->topic);
4130 static CHANSERV_FUNC(cmd_topic)
4132 struct chanData *cData;
4135 cData = channel->channel_info;
4140 SetChannelTopic(channel, chanserv, cData->topic, 1);
4141 reply("CSMSG_TOPIC_SET", cData->topic);
4145 reply("CSMSG_NO_TOPIC", channel->name);
4149 topic = unsplit_string(argv + 1, argc - 1, NULL);
4150 /* If they say "!topic *", use an empty topic. */
4151 if((topic[0] == '*') && (topic[1] == 0))
4153 if(bad_topic(channel, user, topic))
4155 char *topic_mask = cData->topic_mask;
4158 char new_topic[TOPICLEN+1], tchar;
4159 int pos=0, starpos=-1, dpos=0, len;
4161 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4168 len = strlen(topic);
4169 if((dpos + len) > TOPICLEN)
4170 len = TOPICLEN + 1 - dpos;
4171 memcpy(new_topic+dpos, topic, len);
4175 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4176 default: new_topic[dpos++] = tchar; break;
4179 if((dpos > TOPICLEN) || tchar)
4182 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4183 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4186 new_topic[dpos] = 0;
4187 SetChannelTopic(channel, chanserv, new_topic, 1);
4189 reply("CSMSG_TOPIC_LOCKED", channel->name);
4194 SetChannelTopic(channel, chanserv, topic, 1);
4196 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4198 /* Grab the topic and save it as the default topic. */
4200 cData->topic = strdup(channel->topic);
4206 static CHANSERV_FUNC(cmd_mode)
4208 struct userData *uData;
4209 struct mod_chanmode *change;
4214 change = &channel->channel_info->modes;
4215 if(change->modes_set || change->modes_clear) {
4216 modcmd_chanmode_announce(change);
4217 reply("CSMSG_DEFAULTED_MODES", channel->name);
4219 reply("CSMSG_NO_MODES", channel->name);
4223 uData = GetChannelUser(channel->channel_info, user->handle_info);
4225 base_oplevel = MAXOPLEVEL;
4226 else if (uData->access >= UL_OWNER)
4229 base_oplevel = 1 + UL_OWNER - uData->access;
4230 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4233 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4237 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4238 && mode_lock_violated(&channel->channel_info->modes, change))
4241 mod_chanmode_format(&channel->channel_info->modes, modes);
4242 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4246 modcmd_chanmode_announce(change);
4247 mod_chanmode_free(change);
4248 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4252 static CHANSERV_FUNC(cmd_invite)
4254 struct userData *uData;
4255 struct userNode *invite;
4257 uData = GetChannelUser(channel->channel_info, user->handle_info);
4261 if(!(invite = GetUserH(argv[1])))
4263 reply("MSG_NICK_UNKNOWN", argv[1]);
4270 if(GetUserMode(channel, invite))
4272 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4280 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4281 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4284 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4286 irc_invite(chanserv, invite, channel);
4288 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4293 static CHANSERV_FUNC(cmd_inviteme)
4295 if(GetUserMode(channel, user))
4297 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4300 if(channel->channel_info
4301 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4303 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4306 irc_invite(cmd->parent->bot, user, channel);
4311 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4314 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4316 /* We display things based on two dimensions:
4317 * - Issue time: present or absent
4318 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4319 * (in order of precedence, so something both expired and revoked
4320 * only counts as revoked)
4322 combo = (suspended->issued ? 4 : 0)
4323 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4325 case 0: /* no issue time, indefinite expiration */
4326 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4328 case 1: /* no issue time, expires in future */
4329 intervalString(buf1, suspended->expires-now, user->handle_info);
4330 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4332 case 2: /* no issue time, expired */
4333 intervalString(buf1, now-suspended->expires, user->handle_info);
4334 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4336 case 3: /* no issue time, revoked */
4337 intervalString(buf1, now-suspended->revoked, user->handle_info);
4338 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4340 case 4: /* issue time set, indefinite expiration */
4341 intervalString(buf1, now-suspended->issued, user->handle_info);
4342 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4344 case 5: /* issue time set, expires in future */
4345 intervalString(buf1, now-suspended->issued, user->handle_info);
4346 intervalString(buf2, suspended->expires-now, user->handle_info);
4347 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4349 case 6: /* issue time set, expired */
4350 intervalString(buf1, now-suspended->issued, user->handle_info);
4351 intervalString(buf2, now-suspended->expires, user->handle_info);
4352 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4354 case 7: /* issue time set, revoked */
4355 intervalString(buf1, now-suspended->issued, user->handle_info);
4356 intervalString(buf2, now-suspended->revoked, user->handle_info);
4357 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4360 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4365 static CHANSERV_FUNC(cmd_info)
4367 char modes[MAXLEN], buffer[INTERVALLEN];
4368 struct userData *uData, *owner;
4369 struct chanData *cData;
4370 struct do_not_register *dnr;
4375 cData = channel->channel_info;
4376 reply("CSMSG_CHANNEL_INFO", channel->name);
4378 uData = GetChannelUser(cData, user->handle_info);
4379 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4381 mod_chanmode_format(&cData->modes, modes);
4382 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4383 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4386 for(it = dict_first(cData->notes); it; it = iter_next(it))
4390 note = iter_data(it);
4391 if(!note_type_visible_to_user(cData, note->type, user))
4394 padding = PADLEN - 1 - strlen(iter_key(it));
4395 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4398 reply("CSMSG_CHANNEL_MAX", cData->max);
4399 for(owner = cData->users; owner; owner = owner->next)
4400 if(owner->access == UL_OWNER)
4401 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4402 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4403 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4404 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4406 privileged = IsStaff(user);
4408 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4409 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4410 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4412 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4413 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4415 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4417 struct suspended *suspended;
4418 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4419 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4420 show_suspension_info(cmd, user, suspended);
4422 else if(IsSuspended(cData))
4424 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4425 show_suspension_info(cmd, user, cData->suspended);
4430 static CHANSERV_FUNC(cmd_netinfo)
4432 extern unsigned long boot_time;
4433 extern unsigned long burst_length;
4434 char interval[INTERVALLEN];
4436 reply("CSMSG_NETWORK_INFO");
4437 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4438 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4439 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4440 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4441 reply("CSMSG_NETWORK_BANS", banCount);
4442 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4443 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4444 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4449 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4451 struct helpfile_table table;
4453 struct userNode *user;
4458 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4459 table.contents = alloca(list->used*sizeof(*table.contents));
4460 for(nn=0; nn<list->used; nn++)
4462 user = list->list[nn];
4463 if(user->modes & skip_flags)
4467 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4470 nick = alloca(strlen(user->nick)+3);
4471 sprintf(nick, "(%s)", user->nick);
4475 table.contents[table.length][0] = nick;
4478 table_send(chanserv, to->nick, 0, NULL, table);
4481 static CHANSERV_FUNC(cmd_ircops)
4483 reply("CSMSG_STAFF_OPERS");
4484 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4488 static CHANSERV_FUNC(cmd_helpers)
4490 reply("CSMSG_STAFF_HELPERS");
4491 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4495 static CHANSERV_FUNC(cmd_staff)
4497 reply("CSMSG_NETWORK_STAFF");
4498 cmd_ircops(CSFUNC_ARGS);
4499 cmd_helpers(CSFUNC_ARGS);
4503 static CHANSERV_FUNC(cmd_peek)
4505 struct modeNode *mn;
4506 char modes[MODELEN];
4508 struct helpfile_table table;
4510 irc_make_chanmode(channel, modes);
4512 reply("CSMSG_PEEK_INFO", channel->name);
4513 reply("CSMSG_PEEK_TOPIC", channel->topic);
4514 reply("CSMSG_PEEK_MODES", modes);
4515 reply("CSMSG_PEEK_USERS", channel->members.used);
4519 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4520 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4521 for(n = 0; n < channel->members.used; n++)
4523 mn = channel->members.list[n];
4524 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4526 table.contents[table.length] = alloca(sizeof(**table.contents));
4527 table.contents[table.length][0] = mn->user->nick;
4532 reply("CSMSG_PEEK_OPS");
4533 table_send(chanserv, user->nick, 0, NULL, table);
4536 reply("CSMSG_PEEK_NO_OPS");
4540 static MODCMD_FUNC(cmd_wipeinfo)
4542 struct handle_info *victim;
4543 struct userData *ud, *actor, *real_actor;
4544 unsigned int override = 0;
4547 actor = GetChannelUser(channel->channel_info, user->handle_info);
4548 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4549 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4551 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4553 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4556 if((ud->access >= actor->access) && (ud != actor))
4558 reply("MSG_USER_OUTRANKED", victim->handle);
4561 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4562 override = CMD_LOG_OVERRIDE;
4566 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4567 return 1 | override;
4570 static CHANSERV_FUNC(cmd_resync)
4572 struct mod_chanmode *changes;
4573 struct chanData *cData = channel->channel_info;
4574 unsigned int ii, used;
4576 changes = mod_chanmode_alloc(channel->members.used * 2);
4577 for(ii = used = 0; ii < channel->members.used; ++ii)
4579 struct modeNode *mn = channel->members.list[ii];
4580 struct userData *uData;
4582 if(IsService(mn->user))
4585 uData = GetChannelAccess(cData, mn->user->handle_info);
4586 if(!cData->lvlOpts[lvlGiveOps]
4587 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4589 if(!(mn->modes & MODE_CHANOP))
4591 changes->args[used].mode = MODE_CHANOP;
4592 changes->args[used++].u.member = mn;
4595 else if(!cData->lvlOpts[lvlGiveVoice]
4596 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4598 if(mn->modes & MODE_CHANOP)
4600 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4601 changes->args[used++].u.member = mn;
4603 if(!(mn->modes & MODE_VOICE))
4605 changes->args[used].mode = MODE_VOICE;
4606 changes->args[used++].u.member = mn;
4613 changes->args[used].mode = MODE_REMOVE | mn->modes;
4614 changes->args[used++].u.member = mn;
4618 changes->argc = used;
4619 modcmd_chanmode_announce(changes);
4620 mod_chanmode_free(changes);
4621 reply("CSMSG_RESYNCED_USERS", channel->name);
4625 static CHANSERV_FUNC(cmd_seen)
4627 struct userData *uData;
4628 struct handle_info *handle;
4629 char seen[INTERVALLEN];
4633 if(!irccasecmp(argv[1], chanserv->nick))
4635 reply("CSMSG_IS_CHANSERV");
4639 if(!(handle = get_handle_info(argv[1])))
4641 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4645 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4647 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4652 reply("CSMSG_USER_PRESENT", handle->handle);
4653 else if(uData->seen)
4654 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4656 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4658 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4659 reply("CSMSG_USER_VACATION", handle->handle);
4664 static MODCMD_FUNC(cmd_names)
4666 struct userNode *targ;
4667 struct userData *targData;
4668 unsigned int ii, pos;
4671 for(ii=pos=0; ii<channel->members.used; ++ii)
4673 targ = channel->members.list[ii]->user;
4674 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4677 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4680 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4684 if(IsUserSuspended(targData))
4686 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4689 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4690 reply("CSMSG_END_NAMES", channel->name);
4695 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4697 switch(ntype->visible_type)
4699 case NOTE_VIS_ALL: return 1;
4700 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4701 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4706 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4708 struct userData *uData;
4710 switch(ntype->set_access_type)
4712 case NOTE_SET_CHANNEL_ACCESS:
4713 if(!user->handle_info)
4715 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4717 return uData->access >= ntype->set_access.min_ulevel;
4718 case NOTE_SET_CHANNEL_SETTER:
4719 return check_user_level(channel, user, lvlSetters, 1, 0);
4720 case NOTE_SET_PRIVILEGED: default:
4721 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4725 static CHANSERV_FUNC(cmd_note)
4727 struct chanData *cData;
4729 struct note_type *ntype;
4731 cData = channel->channel_info;
4734 reply("CSMSG_NOT_REGISTERED", channel->name);
4738 /* If no arguments, show all visible notes for the channel. */
4744 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4746 note = iter_data(it);
4747 if(!note_type_visible_to_user(cData, note->type, user))
4750 reply("CSMSG_NOTELIST_HEADER", channel->name);
4751 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4754 reply("CSMSG_NOTELIST_END", channel->name);
4756 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4758 /* If one argument, show the named note. */
4761 if((note = dict_find(cData->notes, argv[1], NULL))
4762 && note_type_visible_to_user(cData, note->type, user))
4764 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4766 else if((ntype = dict_find(note_types, argv[1], NULL))
4767 && note_type_visible_to_user(NULL, ntype, user))
4769 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4774 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4778 /* Assume they're trying to set a note. */
4782 ntype = dict_find(note_types, argv[1], NULL);
4785 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4788 else if(note_type_settable_by_user(channel, ntype, user))
4790 note_text = unsplit_string(argv+2, argc-2, NULL);
4791 if((note = dict_find(cData->notes, argv[1], NULL)))
4792 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4793 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4794 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4796 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4798 /* The note is viewable to staff only, so return 0
4799 to keep the invocation from getting logged (or
4800 regular users can see it in !events). */
4806 reply("CSMSG_NO_ACCESS");
4813 static CHANSERV_FUNC(cmd_delnote)
4818 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4819 || !note_type_settable_by_user(channel, note->type, user))
4821 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4824 dict_remove(channel->channel_info->notes, note->type->name);
4825 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4829 static CHANSERV_FUNC(cmd_events)
4831 struct logSearch discrim;
4832 struct logReport report;
4833 unsigned int matches, limit;
4835 limit = (argc > 1) ? atoi(argv[1]) : 10;
4836 if(limit < 1 || limit > 200)
4839 memset(&discrim, 0, sizeof(discrim));
4840 discrim.masks.bot = chanserv;
4841 discrim.masks.channel_name = channel->name;
4843 discrim.masks.command = argv[2];
4844 discrim.limit = limit;
4845 discrim.max_time = INT_MAX;
4846 discrim.severities = 1 << LOG_COMMAND;
4847 report.reporter = chanserv;
4849 reply("CSMSG_EVENT_SEARCH_RESULTS");
4850 matches = log_entry_search(&discrim, log_report_entry, &report);
4852 reply("MSG_MATCH_COUNT", matches);
4854 reply("MSG_NO_MATCHES");
4858 static CHANSERV_FUNC(cmd_say)
4864 msg = unsplit_string(argv + 1, argc - 1, NULL);
4865 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4867 else if(*argv[1] == '*' && argv[1][1] != '\0')
4869 struct handle_info *hi;
4870 struct userNode *authed;
4873 msg = unsplit_string(argv + 2, argc - 2, NULL);
4875 if (!(hi = get_handle_info(argv[1] + 1)))
4877 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4881 for (authed = hi->users; authed; authed = authed->next_authed)
4882 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4884 else if(GetUserH(argv[1]))
4887 msg = unsplit_string(argv + 2, argc - 2, NULL);
4888 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4892 reply("MSG_NOT_TARGET_NAME");
4898 static CHANSERV_FUNC(cmd_emote)
4904 /* CTCP is so annoying. */
4905 msg = unsplit_string(argv + 1, argc - 1, NULL);
4906 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4908 else if(*argv[1] == '*' && argv[1][1] != '\0')
4910 struct handle_info *hi;
4911 struct userNode *authed;
4914 msg = unsplit_string(argv + 2, argc - 2, NULL);
4916 if (!(hi = get_handle_info(argv[1] + 1)))
4918 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4922 for (authed = hi->users; authed; authed = authed->next_authed)
4923 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4925 else if(GetUserH(argv[1]))
4927 msg = unsplit_string(argv + 2, argc - 2, NULL);
4928 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4932 reply("MSG_NOT_TARGET_NAME");
4938 struct channelList *
4939 chanserv_support_channels(void)
4941 return &chanserv_conf.support_channels;
4944 static CHANSERV_FUNC(cmd_expire)
4946 int channel_count = registered_channels;
4947 expire_channels(NULL);
4948 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4953 chanserv_expire_suspension(void *data)
4955 struct suspended *suspended = data;
4956 struct chanNode *channel;
4959 /* Update the channel registration data structure. */
4960 if(!suspended->expires || (now < suspended->expires))
4961 suspended->revoked = now;
4962 channel = suspended->cData->channel;
4963 suspended->cData->channel = channel;
4964 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4966 /* If appropriate, re-join ChanServ to the channel. */
4967 if(!IsOffChannel(suspended->cData))
4969 struct mod_chanmode change;
4970 mod_chanmode_init(&change);
4972 change.args[0].mode = MODE_CHANOP;
4973 change.args[0].u.member = AddChannelUser(chanserv, channel);
4974 mod_chanmode_announce(chanserv, channel, &change);
4977 /* Mark everyone currently in the channel as present. */
4978 for(ii = 0; ii < channel->members.used; ++ii)
4980 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
4989 static CHANSERV_FUNC(cmd_csuspend)
4991 struct suspended *suspended;
4992 char reason[MAXLEN];
4993 unsigned long expiry, duration;
4994 struct userData *uData;
4998 if(IsProtected(channel->channel_info))
5000 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5004 if(argv[1][0] == '!')
5006 else if(IsSuspended(channel->channel_info))
5008 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5009 show_suspension_info(cmd, user, channel->channel_info->suspended);
5013 if(!strcmp(argv[1], "0"))
5015 else if((duration = ParseInterval(argv[1])))
5016 expiry = now + duration;
5019 reply("MSG_INVALID_DURATION", argv[1]);
5023 unsplit_string(argv + 2, argc - 2, reason);
5025 suspended = calloc(1, sizeof(*suspended));
5026 suspended->revoked = 0;
5027 suspended->issued = now;
5028 suspended->suspender = strdup(user->handle_info->handle);
5029 suspended->expires = expiry;
5030 suspended->reason = strdup(reason);
5031 suspended->cData = channel->channel_info;
5032 suspended->previous = suspended->cData->suspended;
5033 suspended->cData->suspended = suspended;
5035 if(suspended->expires)
5036 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5038 if(IsSuspended(channel->channel_info))
5040 suspended->previous->revoked = now;
5041 if(suspended->previous->expires)
5042 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5043 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5044 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5048 /* Mark all users in channel as absent. */
5049 for(uData = channel->channel_info->users; uData; uData = uData->next)
5058 /* Mark the channel as suspended, then part. */
5059 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5060 DelChannelUser(chanserv, channel, suspended->reason, 0);
5061 reply("CSMSG_SUSPENDED", channel->name);
5062 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5063 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5068 static CHANSERV_FUNC(cmd_cunsuspend)
5070 struct suspended *suspended;
5071 char message[MAXLEN];
5073 if(!IsSuspended(channel->channel_info))
5075 reply("CSMSG_NOT_SUSPENDED", channel->name);
5079 suspended = channel->channel_info->suspended;
5081 /* Expire the suspension and join ChanServ to the channel. */
5082 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5083 chanserv_expire_suspension(suspended);
5084 reply("CSMSG_UNSUSPENDED", channel->name);
5085 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5086 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5090 typedef struct chanservSearch
5095 unsigned long unvisited;
5096 unsigned long registered;
5098 unsigned long flags;
5102 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5105 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5110 search = malloc(sizeof(struct chanservSearch));
5111 memset(search, 0, sizeof(*search));
5114 for(i = 0; i < argc; i++)
5116 /* Assume all criteria require arguments. */
5119 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5123 if(!irccasecmp(argv[i], "name"))
5124 search->name = argv[++i];
5125 else if(!irccasecmp(argv[i], "registrar"))
5126 search->registrar = argv[++i];
5127 else if(!irccasecmp(argv[i], "unvisited"))
5128 search->unvisited = ParseInterval(argv[++i]);
5129 else if(!irccasecmp(argv[i], "registered"))
5130 search->registered = ParseInterval(argv[++i]);
5131 else if(!irccasecmp(argv[i], "flags"))
5134 if(!irccasecmp(argv[i], "nodelete"))
5135 search->flags |= CHANNEL_NODELETE;
5136 else if(!irccasecmp(argv[i], "suspended"))
5137 search->flags |= CHANNEL_SUSPENDED;
5138 else if(!irccasecmp(argv[i], "unreviewed"))
5139 search->flags |= CHANNEL_UNREVIEWED;
5142 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5146 else if(!irccasecmp(argv[i], "limit"))
5147 search->limit = strtoul(argv[++i], NULL, 10);
5150 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5155 if(search->name && !strcmp(search->name, "*"))
5157 if(search->registrar && !strcmp(search->registrar, "*"))
5158 search->registrar = 0;
5167 chanserv_channel_match(struct chanData *channel, search_t search)
5169 const char *name = channel->channel->name;
5170 if((search->name && !match_ircglob(name, search->name)) ||
5171 (search->registrar && !channel->registrar) ||
5172 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5173 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5174 (search->registered && (now - channel->registered) > search->registered) ||
5175 (search->flags && ((search->flags & channel->flags) != search->flags)))
5182 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5184 struct chanData *channel;
5185 unsigned int matches = 0;
5187 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5189 if(!chanserv_channel_match(channel, search))
5199 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5204 search_print(struct chanData *channel, void *data)
5206 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5209 static CHANSERV_FUNC(cmd_search)
5212 unsigned int matches;
5213 channel_search_func action;
5217 if(!irccasecmp(argv[1], "count"))
5218 action = search_count;
5219 else if(!irccasecmp(argv[1], "print"))
5220 action = search_print;
5223 reply("CSMSG_ACTION_INVALID", argv[1]);
5227 search = chanserv_search_create(user, argc - 2, argv + 2);
5231 if(action == search_count)
5232 search->limit = INT_MAX;
5234 if(action == search_print)
5235 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5237 matches = chanserv_channel_search(search, action, user);
5240 reply("MSG_MATCH_COUNT", matches);
5242 reply("MSG_NO_MATCHES");
5248 static CHANSERV_FUNC(cmd_unvisited)
5250 struct chanData *cData;
5251 unsigned long interval = chanserv_conf.channel_expire_delay;
5252 char buffer[INTERVALLEN];
5253 unsigned int limit = 25, matches = 0;
5257 interval = ParseInterval(argv[1]);
5259 limit = atoi(argv[2]);
5262 intervalString(buffer, interval, user->handle_info);
5263 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5265 for(cData = channelList; cData && matches < limit; cData = cData->next)
5267 if((now - cData->visited) < interval)
5270 intervalString(buffer, now - cData->visited, user->handle_info);
5271 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5278 static MODCMD_FUNC(chan_opt_defaulttopic)
5284 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5286 reply("CSMSG_TOPIC_LOCKED", channel->name);
5290 topic = unsplit_string(argv+1, argc-1, NULL);
5292 free(channel->channel_info->topic);
5293 if(topic[0] == '*' && topic[1] == 0)
5295 topic = channel->channel_info->topic = NULL;
5299 topic = channel->channel_info->topic = strdup(topic);
5300 if(channel->channel_info->topic_mask
5301 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5302 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5304 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5307 if(channel->channel_info->topic)
5308 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5310 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5314 static MODCMD_FUNC(chan_opt_topicmask)
5318 struct chanData *cData = channel->channel_info;
5321 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5323 reply("CSMSG_TOPIC_LOCKED", channel->name);
5327 mask = unsplit_string(argv+1, argc-1, NULL);
5329 if(cData->topic_mask)
5330 free(cData->topic_mask);
5331 if(mask[0] == '*' && mask[1] == 0)
5333 cData->topic_mask = 0;
5337 cData->topic_mask = strdup(mask);
5339 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5340 else if(!match_ircglob(cData->topic, cData->topic_mask))
5341 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5345 if(channel->channel_info->topic_mask)
5346 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5348 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5352 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5356 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5360 if(greeting[0] == '*' && greeting[1] == 0)
5364 unsigned int length = strlen(greeting);
5365 if(length > chanserv_conf.greeting_length)
5367 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5370 *data = strdup(greeting);
5379 reply(name, user_find_message(user, "MSG_NONE"));
5383 static MODCMD_FUNC(chan_opt_greeting)
5385 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5388 static MODCMD_FUNC(chan_opt_usergreeting)
5390 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5393 static MODCMD_FUNC(chan_opt_modes)
5395 struct mod_chanmode *new_modes;
5396 char modes[MODELEN];
5400 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5402 reply("CSMSG_NO_ACCESS");
5405 if(argv[1][0] == '*' && argv[1][1] == 0)
5407 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5409 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5411 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5414 else if(new_modes->argc > 1)
5416 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5417 mod_chanmode_free(new_modes);
5422 channel->channel_info->modes = *new_modes;
5423 modcmd_chanmode_announce(new_modes);
5424 mod_chanmode_free(new_modes);
5428 mod_chanmode_format(&channel->channel_info->modes, modes);
5430 reply("CSMSG_SET_MODES", modes);
5432 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5436 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5438 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5440 struct chanData *cData = channel->channel_info;
5445 /* Set flag according to value. */
5446 if(enabled_string(argv[1]))
5448 cData->flags |= mask;
5451 else if(disabled_string(argv[1]))
5453 cData->flags &= ~mask;
5458 reply("MSG_INVALID_BINARY", argv[1]);
5464 /* Find current option value. */
5465 value = (cData->flags & mask) ? 1 : 0;
5469 reply(name, user_find_message(user, "MSG_ON"));
5471 reply(name, user_find_message(user, "MSG_OFF"));
5475 static MODCMD_FUNC(chan_opt_nodelete)
5477 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5479 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5483 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5486 static MODCMD_FUNC(chan_opt_dynlimit)
5488 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5491 static MODCMD_FUNC(chan_opt_offchannel)
5493 struct chanData *cData = channel->channel_info;
5498 /* Set flag according to value. */
5499 if(enabled_string(argv[1]))
5501 if(!IsOffChannel(cData))
5502 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5503 cData->flags |= CHANNEL_OFFCHANNEL;
5506 else if(disabled_string(argv[1]))
5508 if(IsOffChannel(cData))
5510 struct mod_chanmode change;
5511 mod_chanmode_init(&change);
5513 change.args[0].mode = MODE_CHANOP;
5514 change.args[0].u.member = AddChannelUser(chanserv, channel);
5515 mod_chanmode_announce(chanserv, channel, &change);
5517 cData->flags &= ~CHANNEL_OFFCHANNEL;
5522 reply("MSG_INVALID_BINARY", argv[1]);
5528 /* Find current option value. */
5529 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5533 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5535 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5539 static MODCMD_FUNC(chan_opt_unreviewed)
5541 struct chanData *cData = channel->channel_info;
5542 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5548 /* The two directions can have different ACLs. */
5549 if(enabled_string(argv[1]))
5551 else if(disabled_string(argv[1]))
5555 reply("MSG_INVALID_BINARY", argv[1]);
5559 if (new_value != value)
5561 struct svccmd *subcmd;
5562 char subcmd_name[32];
5564 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5565 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5568 reply("MSG_COMMAND_DISABLED", subcmd_name);
5571 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5575 cData->flags |= CHANNEL_UNREVIEWED;
5578 free(cData->registrar);
5579 cData->registrar = strdup(user->handle_info->handle);
5580 cData->flags &= ~CHANNEL_UNREVIEWED;
5587 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5589 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5593 static MODCMD_FUNC(chan_opt_defaults)
5595 struct userData *uData;
5596 struct chanData *cData;
5597 const char *confirm;
5598 enum levelOption lvlOpt;
5599 enum charOption chOpt;
5601 cData = channel->channel_info;
5602 uData = GetChannelUser(cData, user->handle_info);
5603 if(!uData || (uData->access < UL_OWNER))
5605 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5608 confirm = make_confirmation_string(uData);
5609 if((argc < 2) || strcmp(argv[1], confirm))
5611 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5614 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5615 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5616 cData->modes = chanserv_conf.default_modes;
5617 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5618 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5619 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5620 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5621 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5626 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5628 struct chanData *cData = channel->channel_info;
5629 struct userData *uData;
5630 unsigned short value;
5634 if(!check_user_level(channel, user, option, 1, 1))
5636 reply("CSMSG_CANNOT_SET");
5639 value = user_level_from_name(argv[1], UL_OWNER+1);
5640 if(!value && strcmp(argv[1], "0"))
5642 reply("CSMSG_INVALID_ACCESS", argv[1]);
5645 uData = GetChannelUser(cData, user->handle_info);
5646 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5648 reply("CSMSG_BAD_SETLEVEL");
5654 if(value > cData->lvlOpts[lvlGiveOps])
5656 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5661 if(value < cData->lvlOpts[lvlGiveVoice])
5663 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5668 /* This test only applies to owners, since non-owners
5669 * trying to set an option to above their level get caught
5670 * by the CSMSG_BAD_SETLEVEL test above.
5672 if(value > uData->access)
5674 reply("CSMSG_BAD_SETTERS");
5681 cData->lvlOpts[option] = value;
5683 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5687 static MODCMD_FUNC(chan_opt_enfops)
5689 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5692 static MODCMD_FUNC(chan_opt_giveops)
5694 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5697 static MODCMD_FUNC(chan_opt_enfmodes)
5699 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5702 static MODCMD_FUNC(chan_opt_enftopic)
5704 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5707 static MODCMD_FUNC(chan_opt_pubcmd)
5709 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5712 static MODCMD_FUNC(chan_opt_setters)
5714 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5717 static MODCMD_FUNC(chan_opt_ctcpusers)
5719 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5722 static MODCMD_FUNC(chan_opt_userinfo)
5724 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5727 static MODCMD_FUNC(chan_opt_givevoice)
5729 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5732 static MODCMD_FUNC(chan_opt_topicsnarf)
5734 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5737 static MODCMD_FUNC(chan_opt_inviteme)
5739 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5743 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5745 struct chanData *cData = channel->channel_info;
5746 int count = charOptions[option].count, idx;
5750 idx = atoi(argv[1]);
5752 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5754 reply("CSMSG_INVALID_NUMERIC", idx);
5755 /* Show possible values. */
5756 for(idx = 0; idx < count; idx++)
5757 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5761 cData->chOpts[option] = charOptions[option].values[idx].value;
5765 /* Find current option value. */
5768 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5772 /* Somehow, the option value is corrupt; reset it to the default. */
5773 cData->chOpts[option] = charOptions[option].default_value;
5778 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5782 static MODCMD_FUNC(chan_opt_protect)
5784 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5787 static MODCMD_FUNC(chan_opt_toys)
5789 return channel_multiple_option(chToys, CSFUNC_ARGS);
5792 static MODCMD_FUNC(chan_opt_ctcpreaction)
5794 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5797 static MODCMD_FUNC(chan_opt_topicrefresh)
5799 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5802 static struct svccmd_list set_shows_list;
5805 handle_svccmd_unbind(struct svccmd *target) {
5807 for(ii=0; ii<set_shows_list.used; ++ii)
5808 if(target == set_shows_list.list[ii])
5809 set_shows_list.used = 0;
5812 static CHANSERV_FUNC(cmd_set)
5814 struct svccmd *subcmd;
5818 /* Check if we need to (re-)initialize set_shows_list. */
5819 if(!set_shows_list.used)
5821 if(!set_shows_list.size)
5823 set_shows_list.size = chanserv_conf.set_shows->used;
5824 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5826 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5828 const char *name = chanserv_conf.set_shows->list[ii];
5829 sprintf(buf, "%s %s", argv[0], name);
5830 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5833 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5836 svccmd_list_append(&set_shows_list, subcmd);
5842 reply("CSMSG_CHANNEL_OPTIONS");
5843 for(ii = 0; ii < set_shows_list.used; ii++)
5845 subcmd = set_shows_list.list[ii];
5846 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5851 sprintf(buf, "%s %s", argv[0], argv[1]);
5852 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5855 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5858 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5860 reply("CSMSG_NO_ACCESS");
5866 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5870 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5872 struct userData *uData;
5874 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5877 reply("CSMSG_NOT_USER", channel->name);
5883 /* Just show current option value. */
5885 else if(enabled_string(argv[1]))
5887 uData->flags |= mask;
5889 else if(disabled_string(argv[1]))
5891 uData->flags &= ~mask;
5895 reply("MSG_INVALID_BINARY", argv[1]);
5899 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5903 static MODCMD_FUNC(user_opt_noautoop)
5905 struct userData *uData;
5907 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5910 reply("CSMSG_NOT_USER", channel->name);
5913 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5914 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5916 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5919 static MODCMD_FUNC(user_opt_autoinvite)
5921 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5923 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5925 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5928 static MODCMD_FUNC(user_opt_info)
5930 struct userData *uData;
5933 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5937 /* If they got past the command restrictions (which require access)
5938 * but fail this test, we have some fool with security override on.
5940 reply("CSMSG_NOT_USER", channel->name);
5947 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5948 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5950 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5953 bp = strcspn(infoline, "\001");
5956 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5961 if(infoline[0] == '*' && infoline[1] == 0)
5964 uData->info = strdup(infoline);
5967 reply("CSMSG_USET_INFO", uData->info);
5969 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5973 struct svccmd_list uset_shows_list;
5975 static CHANSERV_FUNC(cmd_uset)
5977 struct svccmd *subcmd;
5981 /* Check if we need to (re-)initialize uset_shows_list. */
5982 if(!uset_shows_list.used)
5986 "NoAutoOp", "AutoInvite", "Info"
5989 if(!uset_shows_list.size)
5991 uset_shows_list.size = ArrayLength(options);
5992 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5994 for(ii = 0; ii < ArrayLength(options); ii++)
5996 const char *name = options[ii];
5997 sprintf(buf, "%s %s", argv[0], name);
5998 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6001 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6004 svccmd_list_append(&uset_shows_list, subcmd);
6010 /* Do this so options are presented in a consistent order. */
6011 reply("CSMSG_USER_OPTIONS");
6012 for(ii = 0; ii < uset_shows_list.used; ii++)
6013 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6017 sprintf(buf, "%s %s", argv[0], argv[1]);
6018 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6021 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6025 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6028 static CHANSERV_FUNC(cmd_giveownership)
6030 struct handle_info *new_owner_hi;
6031 struct userData *new_owner;
6032 struct userData *curr_user;
6033 struct userData *invoker;
6034 struct chanData *cData = channel->channel_info;
6035 struct do_not_register *dnr;
6036 const char *confirm;
6038 unsigned short co_access;
6039 char reason[MAXLEN];
6042 curr_user = GetChannelAccess(cData, user->handle_info);
6043 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6044 if(!curr_user || (curr_user->access != UL_OWNER))
6046 struct userData *owner = NULL;
6047 for(curr_user = channel->channel_info->users;
6049 curr_user = curr_user->next)
6051 if(curr_user->access != UL_OWNER)
6055 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6062 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6064 char delay[INTERVALLEN];
6065 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6066 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6069 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6071 if(new_owner_hi == user->handle_info)
6073 reply("CSMSG_NO_TRANSFER_SELF");
6076 new_owner = GetChannelAccess(cData, new_owner_hi);
6081 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6085 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6089 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6091 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6094 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6095 if(!IsHelping(user))
6096 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6098 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6101 invoker = GetChannelUser(cData, user->handle_info);
6102 if(invoker->access <= UL_OWNER)
6104 confirm = make_confirmation_string(curr_user);
6105 if((argc < 3) || strcmp(argv[2], confirm))
6107 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6111 if(new_owner->access >= UL_COOWNER)
6112 co_access = new_owner->access;
6114 co_access = UL_COOWNER;
6115 new_owner->access = UL_OWNER;
6117 curr_user->access = co_access;
6118 cData->ownerTransfer = now;
6119 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6120 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6121 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6125 static CHANSERV_FUNC(cmd_suspend)
6127 struct handle_info *hi;
6128 struct userData *actor, *real_actor, *target;
6129 unsigned int override = 0;
6132 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6133 actor = GetChannelUser(channel->channel_info, user->handle_info);
6134 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6135 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6137 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6140 if(target->access >= actor->access)
6142 reply("MSG_USER_OUTRANKED", hi->handle);
6145 if(target->flags & USER_SUSPENDED)
6147 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6152 target->present = 0;
6155 if(!real_actor || target->access >= real_actor->access)
6156 override = CMD_LOG_OVERRIDE;
6157 target->flags |= USER_SUSPENDED;
6158 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6159 return 1 | override;
6162 static CHANSERV_FUNC(cmd_unsuspend)
6164 struct handle_info *hi;
6165 struct userData *actor, *real_actor, *target;
6166 unsigned int override = 0;
6169 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6170 actor = GetChannelUser(channel->channel_info, user->handle_info);
6171 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6172 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6174 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6177 if(target->access >= actor->access)
6179 reply("MSG_USER_OUTRANKED", hi->handle);
6182 if(!(target->flags & USER_SUSPENDED))
6184 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6187 if(!real_actor || target->access >= real_actor->access)
6188 override = CMD_LOG_OVERRIDE;
6189 target->flags &= ~USER_SUSPENDED;
6190 scan_user_presence(target, NULL);
6191 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6192 return 1 | override;
6195 static MODCMD_FUNC(cmd_deleteme)
6197 struct handle_info *hi;
6198 struct userData *target;
6199 const char *confirm_string;
6200 unsigned short access_level;
6203 hi = user->handle_info;
6204 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6206 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6209 if(target->access == UL_OWNER)
6211 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6214 confirm_string = make_confirmation_string(target);
6215 if((argc < 2) || strcmp(argv[1], confirm_string))
6217 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6220 access_level = target->access;
6221 channel_name = strdup(channel->name);
6222 del_channel_user(target, 1);
6223 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6229 chanserv_refresh_topics(UNUSED_ARG(void *data))
6231 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6232 struct chanData *cData;
6235 for(cData = channelList; cData; cData = cData->next)
6237 if(IsSuspended(cData))
6239 opt = cData->chOpts[chTopicRefresh];
6242 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6245 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6246 cData->last_refresh = refresh_num;
6248 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6251 static CHANSERV_FUNC(cmd_unf)
6255 char response[MAXLEN];
6256 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6257 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6258 irc_privmsg(cmd->parent->bot, channel->name, response);
6261 reply("CSMSG_UNF_RESPONSE");
6265 static CHANSERV_FUNC(cmd_ping)
6269 char response[MAXLEN];
6270 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6271 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6272 irc_privmsg(cmd->parent->bot, channel->name, response);
6275 reply("CSMSG_PING_RESPONSE");
6279 static CHANSERV_FUNC(cmd_wut)
6283 char response[MAXLEN];
6284 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6285 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6286 irc_privmsg(cmd->parent->bot, channel->name, response);
6289 reply("CSMSG_WUT_RESPONSE");
6293 static CHANSERV_FUNC(cmd_8ball)
6295 unsigned int i, j, accum;
6300 for(i=1; i<argc; i++)
6301 for(j=0; argv[i][j]; j++)
6302 accum = (accum << 5) - accum + toupper(argv[i][j]);
6303 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6306 char response[MAXLEN];
6307 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6308 irc_privmsg(cmd->parent->bot, channel->name, response);
6311 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6315 static CHANSERV_FUNC(cmd_d)
6317 unsigned long sides, count, modifier, ii, total;
6318 char response[MAXLEN], *sep;
6322 if((count = strtoul(argv[1], &sep, 10)) < 1)
6332 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6333 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6337 else if((sep[0] == '-') && isdigit(sep[1]))
6338 modifier = strtoul(sep, NULL, 10);
6339 else if((sep[0] == '+') && isdigit(sep[1]))
6340 modifier = strtoul(sep+1, NULL, 10);
6347 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6352 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6355 for(total = ii = 0; ii < count; ++ii)
6356 total += (rand() % sides) + 1;
6359 if((count > 1) || modifier)
6361 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6362 sprintf(response, fmt, total, count, sides, modifier);
6366 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6367 sprintf(response, fmt, total, sides);
6370 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6372 send_message_type(4, user, cmd->parent->bot, "%s", response);
6376 static CHANSERV_FUNC(cmd_huggle)
6378 /* CTCP must be via PRIVMSG, never notice */
6380 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6382 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6387 chanserv_adjust_limit(void *data)
6389 struct mod_chanmode change;
6390 struct chanData *cData = data;
6391 struct chanNode *channel = cData->channel;
6394 if(IsSuspended(cData))
6397 cData->limitAdjusted = now;
6398 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6399 if(cData->modes.modes_set & MODE_LIMIT)
6401 if(limit > cData->modes.new_limit)
6402 limit = cData->modes.new_limit;
6403 else if(limit == cData->modes.new_limit)
6407 mod_chanmode_init(&change);
6408 change.modes_set = MODE_LIMIT;
6409 change.new_limit = limit;
6410 mod_chanmode_announce(chanserv, channel, &change);
6414 handle_new_channel(struct chanNode *channel)
6416 struct chanData *cData;
6418 if(!(cData = channel->channel_info))
6421 if(cData->modes.modes_set || cData->modes.modes_clear)
6422 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6424 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6425 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6428 /* Welcome to my worst nightmare. Warning: Read (or modify)
6429 the code below at your own risk. */
6431 handle_join(struct modeNode *mNode)
6433 struct mod_chanmode change;
6434 struct userNode *user = mNode->user;
6435 struct chanNode *channel = mNode->channel;
6436 struct chanData *cData;
6437 struct userData *uData = NULL;
6438 struct banData *bData;
6439 struct handle_info *handle;
6440 unsigned int modes = 0, info = 0;
6443 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6446 cData = channel->channel_info;
6447 if(channel->members.used > cData->max)
6448 cData->max = channel->members.used;
6450 /* Check for bans. If they're joining through a ban, one of two
6452 * 1: Join during a netburst, by riding the break. Kick them
6453 * unless they have ops or voice in the channel.
6454 * 2: They're allowed to join through the ban (an invite in
6455 * ircu2.10, or a +e on Hybrid, or something).
6456 * If they're not joining through a ban, and the banlist is not
6457 * full, see if they're on the banlist for the channel. If so,
6460 if(user->uplink->burst && !mNode->modes)
6463 for(ii = 0; ii < channel->banlist.used; ii++)
6465 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6467 /* Riding a netburst. Naughty. */
6468 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6474 mod_chanmode_init(&change);
6476 if(channel->banlist.used < MAXBANS)
6478 /* Not joining through a ban. */
6479 for(bData = cData->bans;
6480 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6481 bData = bData->next);
6485 char kick_reason[MAXLEN];
6486 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6488 bData->triggered = now;
6489 if(bData != cData->bans)
6491 /* Shuffle the ban to the head of the list. */
6493 bData->next->prev = bData->prev;
6495 bData->prev->next = bData->next;
6498 bData->next = cData->bans;
6501 cData->bans->prev = bData;
6502 cData->bans = bData;
6505 change.args[0].mode = MODE_BAN;
6506 change.args[0].u.hostmask = bData->mask;
6507 mod_chanmode_announce(chanserv, channel, &change);
6508 KickChannelUser(user, channel, chanserv, kick_reason);
6513 /* ChanServ will not modify the limits in join-flooded channels,
6514 or when there are enough slots left below the limit. */
6515 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6516 && !channel->join_flooded
6517 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6519 /* The user count has begun "bumping" into the channel limit,
6520 so set a timer to raise the limit a bit. Any previous
6521 timers are removed so three incoming users within the delay
6522 results in one limit change, not three. */
6524 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6525 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6528 if(channel->join_flooded)
6530 /* don't automatically give ops or voice during a join flood */
6532 else if(cData->lvlOpts[lvlGiveOps] == 0)
6533 modes |= MODE_CHANOP;
6534 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6535 modes |= MODE_VOICE;
6537 greeting = cData->greeting;
6538 if(user->handle_info)
6540 handle = user->handle_info;
6542 if(IsHelper(user) && !IsHelping(user))
6545 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6547 if(channel == chanserv_conf.support_channels.list[ii])
6549 HANDLE_SET_FLAG(user->handle_info, HELPING);
6555 uData = GetTrueChannelAccess(cData, handle);
6556 if(uData && !IsUserSuspended(uData))
6558 /* Ops and above were handled by the above case. */
6559 if(IsUserAutoOp(uData))
6561 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6562 modes |= MODE_CHANOP;
6563 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6564 modes |= MODE_VOICE;
6566 if(uData->access >= UL_PRESENT)
6567 cData->visited = now;
6568 if(cData->user_greeting)
6569 greeting = cData->user_greeting;
6571 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6572 && ((now - uData->seen) >= chanserv_conf.info_delay)
6580 /* If user joining normally (not during burst), apply op or voice,
6581 * and send greeting/userinfo as appropriate.
6583 if(!user->uplink->burst)
6587 if(modes & MODE_CHANOP)
6588 modes &= ~MODE_VOICE;
6589 change.args[0].mode = modes;
6590 change.args[0].u.member = mNode;
6591 mod_chanmode_announce(chanserv, channel, &change);
6594 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6596 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6602 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6604 struct mod_chanmode change;
6605 struct userData *channel;
6606 unsigned int ii, jj;
6608 if(!user->handle_info)
6611 mod_chanmode_init(&change);
6613 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6615 struct chanNode *cn;
6616 struct modeNode *mn;
6617 if(IsUserSuspended(channel)
6618 || IsSuspended(channel->channel)
6619 || !(cn = channel->channel->channel))
6622 mn = GetUserMode(cn, user);
6625 if(!IsUserSuspended(channel)
6626 && IsUserAutoInvite(channel)
6627 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6629 && !user->uplink->burst)
6630 irc_invite(chanserv, user, cn);
6634 if(channel->access >= UL_PRESENT)
6635 channel->channel->visited = now;
6637 if(IsUserAutoOp(channel))
6639 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6640 change.args[0].mode = MODE_CHANOP;
6641 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6642 change.args[0].mode = MODE_VOICE;
6644 change.args[0].mode = 0;
6645 change.args[0].u.member = mn;
6646 if(change.args[0].mode)
6647 mod_chanmode_announce(chanserv, cn, &change);
6650 channel->seen = now;
6651 channel->present = 1;
6654 for(ii = 0; ii < user->channels.used; ++ii)
6656 struct chanNode *chan = user->channels.list[ii]->channel;
6657 struct banData *ban;
6659 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6660 || !chan->channel_info
6661 || IsSuspended(chan->channel_info))
6663 for(jj = 0; jj < chan->banlist.used; ++jj)
6664 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6666 if(jj < chan->banlist.used)
6668 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6670 char kick_reason[MAXLEN];
6671 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6673 change.args[0].mode = MODE_BAN;
6674 change.args[0].u.hostmask = ban->mask;
6675 mod_chanmode_announce(chanserv, chan, &change);
6676 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6677 KickChannelUser(user, chan, chanserv, kick_reason);
6678 ban->triggered = now;
6683 if(IsSupportHelper(user))
6685 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6687 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6689 HANDLE_SET_FLAG(user->handle_info, HELPING);
6697 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6699 struct chanData *cData;
6700 struct userData *uData;
6702 cData = mn->channel->channel_info;
6703 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6706 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6708 /* Allow for a bit of padding so that the limit doesn't
6709 track the user count exactly, which could get annoying. */
6710 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6712 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6713 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6717 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6719 scan_user_presence(uData, mn->user);
6721 if (uData->access >= UL_PRESENT)
6722 cData->visited = now;
6725 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6728 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6729 struct chanNode *channel;
6730 struct userNode *exclude;
6731 /* When looking at the channel that is being /part'ed, we
6732 * have to skip over the client that is leaving. For
6733 * other channels, we must not do that.
6735 channel = chanserv_conf.support_channels.list[ii];
6736 exclude = (channel == mn->channel) ? mn->user : NULL;
6737 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6740 if(ii == chanserv_conf.support_channels.used)
6741 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6746 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6748 struct userData *uData;
6750 if(!channel->channel_info || !kicker || IsService(kicker)
6751 || (kicker == victim) || IsSuspended(channel->channel_info)
6752 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6755 if(protect_user(victim, kicker, channel->channel_info))
6757 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6758 KickChannelUser(kicker, channel, chanserv, reason);
6761 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6766 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6768 struct chanData *cData;
6770 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6773 cData = channel->channel_info;
6774 if(bad_topic(channel, user, channel->topic))
6776 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6777 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6778 SetChannelTopic(channel, chanserv, old_topic, 1);
6779 else if(cData->topic)
6780 SetChannelTopic(channel, chanserv, cData->topic, 1);
6783 /* With topicsnarf, grab the topic and save it as the default topic. */
6784 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6787 cData->topic = strdup(channel->topic);
6793 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6795 struct mod_chanmode *bounce = NULL;
6796 unsigned int bnc, ii;
6799 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6802 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6803 && mode_lock_violated(&channel->channel_info->modes, change))
6805 char correct[MAXLEN];
6806 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6807 mod_chanmode_format(&channel->channel_info->modes, correct);
6808 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6810 for(ii = bnc = 0; ii < change->argc; ++ii)
6812 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6814 const struct userNode *victim = change->args[ii].u.member->user;
6815 if(!protect_user(victim, user, channel->channel_info))
6818 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6821 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6822 bounce->args[bnc].u.member = GetUserMode(channel, user);
6823 if(bounce->args[bnc].u.member)
6827 bounce->args[bnc].mode = MODE_CHANOP;
6828 bounce->args[bnc].u.member = change->args[ii].u.member;
6830 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6832 else if(change->args[ii].mode & MODE_CHANOP)
6834 const struct userNode *victim = change->args[ii].u.member->user;
6835 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6838 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6839 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6840 bounce->args[bnc].u.member = change->args[ii].u.member;
6843 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6845 const char *ban = change->args[ii].u.hostmask;
6846 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6849 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6850 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6851 bounce->args[bnc].u.hostmask = strdup(ban);
6853 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6858 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6859 mod_chanmode_announce(chanserv, channel, bounce);
6860 for(ii = 0; ii < change->argc; ++ii)
6861 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6862 free((char*)bounce->args[ii].u.hostmask);
6863 mod_chanmode_free(bounce);
6868 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6870 struct chanNode *channel;
6871 struct banData *bData;
6872 struct mod_chanmode change;
6873 unsigned int ii, jj;
6874 char kick_reason[MAXLEN];
6876 mod_chanmode_init(&change);
6878 change.args[0].mode = MODE_BAN;
6879 for(ii = 0; ii < user->channels.used; ++ii)
6881 channel = user->channels.list[ii]->channel;
6882 /* Need not check for bans if they're opped or voiced. */
6883 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6885 /* Need not check for bans unless channel registration is active. */
6886 if(!channel->channel_info || IsSuspended(channel->channel_info))
6888 /* Look for a matching ban already on the channel. */
6889 for(jj = 0; jj < channel->banlist.used; ++jj)
6890 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6892 /* Need not act if we found one. */
6893 if(jj < channel->banlist.used)
6895 /* Look for a matching ban in this channel. */
6896 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6898 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6900 change.args[0].u.hostmask = bData->mask;
6901 mod_chanmode_announce(chanserv, channel, &change);
6902 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6903 KickChannelUser(user, channel, chanserv, kick_reason);
6904 bData->triggered = now;
6905 break; /* we don't need to check any more bans in the channel */
6910 static void handle_rename(struct handle_info *handle, const char *old_handle)
6912 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6916 dict_remove2(handle_dnrs, old_handle, 1);
6917 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6918 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6923 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6925 struct userNode *h_user;
6927 if(handle->channels)
6929 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6930 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6932 while(handle->channels)
6933 del_channel_user(handle->channels, 1);
6938 handle_server_link(UNUSED_ARG(struct server *server))
6940 struct chanData *cData;
6942 for(cData = channelList; cData; cData = cData->next)
6944 if(!IsSuspended(cData))
6945 cData->may_opchan = 1;
6946 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6947 && !cData->channel->join_flooded
6948 && ((cData->channel->limit - cData->channel->members.used)
6949 < chanserv_conf.adjust_threshold))
6951 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6952 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6958 chanserv_conf_read(void)
6962 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6963 struct mod_chanmode *change;
6964 struct string_list *strlist;
6965 struct chanNode *chan;
6968 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6970 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6973 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6974 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6975 chanserv_conf.support_channels.used = 0;
6976 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6978 for(ii = 0; ii < strlist->used; ++ii)
6980 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6983 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6985 channelList_append(&chanserv_conf.support_channels, chan);
6988 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6991 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6994 chan = AddChannel(str, now, str2, NULL);
6996 channelList_append(&chanserv_conf.support_channels, chan);
6998 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6999 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7000 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7001 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7002 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7003 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7004 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7005 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7006 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7007 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7008 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7009 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7010 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7011 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7012 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7013 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7014 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7015 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7016 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7017 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7018 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7019 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7020 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7021 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7022 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7024 NickChange(chanserv, str, 0);
7025 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7026 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7027 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7028 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7029 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7030 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7031 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7032 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7033 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7034 chanserv_conf.max_owned = str ? atoi(str) : 5;
7035 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7036 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7037 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7038 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7039 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7040 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7041 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7044 safestrncpy(mode_line, str, sizeof(mode_line));
7045 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7046 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7047 && (change->argc < 2))
7049 chanserv_conf.default_modes = *change;
7050 mod_chanmode_free(change);
7052 free_string_list(chanserv_conf.set_shows);
7053 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7055 strlist = string_list_copy(strlist);
7058 static const char *list[] = {
7059 /* free form text */
7060 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7061 /* options based on user level */
7062 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7063 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7064 /* multiple choice options */
7065 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7066 /* binary options */
7067 "DynLimit", "NoDelete",
7071 strlist = alloc_string_list(ArrayLength(list)-1);
7072 for(ii=0; list[ii]; ii++)
7073 string_list_append(strlist, strdup(list[ii]));
7075 chanserv_conf.set_shows = strlist;
7076 /* We don't look things up now, in case the list refers to options
7077 * defined by modules initialized after this point. Just mark the
7078 * function list as invalid, so it will be initialized.
7080 set_shows_list.used = 0;
7081 free_string_list(chanserv_conf.eightball);
7082 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7085 strlist = string_list_copy(strlist);
7089 strlist = alloc_string_list(4);
7090 string_list_append(strlist, strdup("Yes."));
7091 string_list_append(strlist, strdup("No."));
7092 string_list_append(strlist, strdup("Maybe so."));
7094 chanserv_conf.eightball = strlist;
7095 free_string_list(chanserv_conf.old_ban_names);
7096 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7098 strlist = string_list_copy(strlist);
7100 strlist = alloc_string_list(2);
7101 chanserv_conf.old_ban_names = strlist;
7102 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7103 off_channel = str ? atoi(str) : 0;
7107 chanserv_note_type_read(const char *key, struct record_data *rd)
7110 struct note_type *ntype;
7113 if(!(obj = GET_RECORD_OBJECT(rd)))
7115 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7118 if(!(ntype = chanserv_create_note_type(key)))
7120 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7124 /* Figure out set access */
7125 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7127 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7128 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7130 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7132 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7133 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7135 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7137 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7141 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7142 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7143 ntype->set_access.min_opserv = 0;
7146 /* Figure out visibility */
7147 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7148 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7149 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7150 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7151 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7152 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7153 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7154 ntype->visible_type = NOTE_VIS_ALL;
7156 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7158 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7159 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7163 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7165 struct handle_info *handle;
7166 struct userData *uData;
7167 char *seen, *inf, *flags;
7168 unsigned long last_seen;
7169 unsigned short access_level;
7171 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7173 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7177 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7178 if(access_level > UL_OWNER)
7180 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7184 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7185 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7186 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7187 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7188 handle = get_handle_info(key);
7191 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7195 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7196 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7200 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7202 struct banData *bData;
7203 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7204 unsigned long set_time, triggered_time, expires_time;
7206 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7208 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7212 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7213 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7214 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7215 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7216 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7217 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7218 if (!reason || !owner)
7221 set_time = set ? strtoul(set, NULL, 0) : now;
7222 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7224 expires_time = strtoul(s_expires, NULL, 0);
7226 expires_time = set_time + atoi(s_duration);
7230 if(!reason || (expires_time && (expires_time < now)))
7233 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7236 static struct suspended *
7237 chanserv_read_suspended(dict_t obj)
7239 struct suspended *suspended = calloc(1, sizeof(*suspended));
7243 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7244 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7245 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7246 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7247 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7248 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7249 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7250 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7251 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7252 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7257 chanserv_channel_read(const char *key, struct record_data *hir)
7259 struct suspended *suspended;
7260 struct mod_chanmode *modes;
7261 struct chanNode *cNode;
7262 struct chanData *cData;
7263 struct dict *channel, *obj;
7264 char *str, *argv[10];
7268 channel = hir->d.object;
7270 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7273 cNode = AddChannel(key, now, NULL, NULL);
7276 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7279 cData = register_channel(cNode, str);
7282 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7286 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7288 enum levelOption lvlOpt;
7289 enum charOption chOpt;
7291 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7292 cData->flags = atoi(str);
7294 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7296 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7298 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7299 else if(levelOptions[lvlOpt].old_flag)
7301 if(cData->flags & levelOptions[lvlOpt].old_flag)
7302 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7304 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7308 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7310 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7312 cData->chOpts[chOpt] = str[0];
7315 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7317 enum levelOption lvlOpt;
7318 enum charOption chOpt;
7321 cData->flags = base64toint(str, 5);
7322 count = strlen(str += 5);
7323 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7326 if(levelOptions[lvlOpt].old_flag)
7328 if(cData->flags & levelOptions[lvlOpt].old_flag)
7329 lvl = levelOptions[lvlOpt].flag_value;
7331 lvl = levelOptions[lvlOpt].default_value;
7333 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7335 case 'c': lvl = UL_COOWNER; break;
7336 case 'm': lvl = UL_MASTER; break;
7337 case 'n': lvl = UL_OWNER+1; break;
7338 case 'o': lvl = UL_OP; break;
7339 case 'p': lvl = UL_PEON; break;
7340 case 'w': lvl = UL_OWNER; break;
7341 default: lvl = 0; break;
7343 cData->lvlOpts[lvlOpt] = lvl;
7345 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7346 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7349 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7351 suspended = chanserv_read_suspended(obj);
7352 cData->suspended = suspended;
7353 suspended->cData = cData;
7354 /* We could use suspended->expires and suspended->revoked to
7355 * set the CHANNEL_SUSPENDED flag, but we don't. */
7357 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7359 suspended = calloc(1, sizeof(*suspended));
7360 suspended->issued = 0;
7361 suspended->revoked = 0;
7362 suspended->suspender = strdup(str);
7363 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7364 suspended->expires = str ? atoi(str) : 0;
7365 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7366 suspended->reason = strdup(str ? str : "No reason");
7367 suspended->previous = NULL;
7368 cData->suspended = suspended;
7369 suspended->cData = cData;
7373 cData->flags &= ~CHANNEL_SUSPENDED;
7374 suspended = NULL; /* to squelch a warning */
7377 if(IsSuspended(cData)) {
7378 if(suspended->expires > now)
7379 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7380 else if(suspended->expires)
7381 cData->flags &= ~CHANNEL_SUSPENDED;
7384 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7385 struct mod_chanmode change;
7386 mod_chanmode_init(&change);
7388 change.args[0].mode = MODE_CHANOP;
7389 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7390 mod_chanmode_announce(chanserv, cNode, &change);
7393 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7394 cData->registered = str ? strtoul(str, NULL, 0) : now;
7395 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7396 cData->visited = str ? strtoul(str, NULL, 0) : now;
7397 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7398 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7399 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7400 cData->max = str ? atoi(str) : 0;
7401 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7402 cData->greeting = str ? strdup(str) : NULL;
7403 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7404 cData->user_greeting = str ? strdup(str) : NULL;
7405 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7406 cData->topic_mask = str ? strdup(str) : NULL;
7407 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7408 cData->topic = str ? strdup(str) : NULL;
7410 if(!IsSuspended(cData)
7411 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7412 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7413 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7414 cData->modes = *modes;
7416 cData->modes.modes_set |= MODE_REGISTERED;
7417 if(cData->modes.argc > 1)
7418 cData->modes.argc = 1;
7419 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7420 mod_chanmode_free(modes);
7423 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7424 for(it = dict_first(obj); it; it = iter_next(it))
7425 user_read_helper(iter_key(it), iter_data(it), cData);
7427 if(!cData->users && !IsProtected(cData))
7429 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7430 unregister_channel(cData, "has empty user list.");
7434 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7435 for(it = dict_first(obj); it; it = iter_next(it))
7436 ban_read_helper(iter_key(it), iter_data(it), cData);
7438 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7439 for(it = dict_first(obj); it; it = iter_next(it))
7441 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7442 struct record_data *rd = iter_data(it);
7443 const char *note, *setter;
7445 if(rd->type != RECDB_OBJECT)
7447 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7451 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7453 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7455 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7459 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7460 if(!setter) setter = "<unknown>";
7461 chanserv_add_channel_note(cData, ntype, setter, note);
7469 chanserv_dnr_read(const char *key, struct record_data *hir)
7471 const char *setter, *reason, *str;
7472 struct do_not_register *dnr;
7473 unsigned long expiry;
7475 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7478 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7481 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7484 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7487 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7488 expiry = str ? strtoul(str, NULL, 0) : 0;
7489 if(expiry && expiry <= now)
7491 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7494 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7496 dnr->set = atoi(str);
7502 chanserv_saxdb_read(struct dict *database)
7504 struct dict *section;
7507 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7508 for(it = dict_first(section); it; it = iter_next(it))
7509 chanserv_note_type_read(iter_key(it), iter_data(it));
7511 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7512 for(it = dict_first(section); it; it = iter_next(it))
7513 chanserv_channel_read(iter_key(it), iter_data(it));
7515 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7516 for(it = dict_first(section); it; it = iter_next(it))
7517 chanserv_dnr_read(iter_key(it), iter_data(it));
7523 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7525 int high_present = 0;
7526 saxdb_start_record(ctx, KEY_USERS, 1);
7527 for(; uData; uData = uData->next)
7529 if((uData->access >= UL_PRESENT) && uData->present)
7531 saxdb_start_record(ctx, uData->handle->handle, 0);
7532 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7533 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7535 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7537 saxdb_write_string(ctx, KEY_INFO, uData->info);
7538 saxdb_end_record(ctx);
7540 saxdb_end_record(ctx);
7541 return high_present;
7545 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7549 saxdb_start_record(ctx, KEY_BANS, 1);
7550 for(; bData; bData = bData->next)
7552 saxdb_start_record(ctx, bData->mask, 0);
7553 saxdb_write_int(ctx, KEY_SET, bData->set);
7554 if(bData->triggered)
7555 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7557 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7559 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7561 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7562 saxdb_end_record(ctx);
7564 saxdb_end_record(ctx);
7568 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7570 saxdb_start_record(ctx, name, 0);
7571 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7572 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7574 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7576 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7578 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7580 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7581 saxdb_end_record(ctx);
7585 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7589 enum levelOption lvlOpt;
7590 enum charOption chOpt;
7592 saxdb_start_record(ctx, channel->channel->name, 1);
7594 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7595 saxdb_write_int(ctx, KEY_MAX, channel->max);
7597 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7598 if(channel->registrar)
7599 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7600 if(channel->greeting)
7601 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7602 if(channel->user_greeting)
7603 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7604 if(channel->topic_mask)
7605 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7606 if(channel->suspended)
7607 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7609 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7610 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7611 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7612 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7613 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7615 buf[0] = channel->chOpts[chOpt];
7617 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7619 saxdb_end_record(ctx);
7621 if(channel->modes.modes_set || channel->modes.modes_clear)
7623 mod_chanmode_format(&channel->modes, buf);
7624 saxdb_write_string(ctx, KEY_MODES, buf);
7627 high_present = chanserv_write_users(ctx, channel->users);
7628 chanserv_write_bans(ctx, channel->bans);
7630 if(dict_size(channel->notes))
7634 saxdb_start_record(ctx, KEY_NOTES, 1);
7635 for(it = dict_first(channel->notes); it; it = iter_next(it))
7637 struct note *note = iter_data(it);
7638 saxdb_start_record(ctx, iter_key(it), 0);
7639 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7640 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7641 saxdb_end_record(ctx);
7643 saxdb_end_record(ctx);
7646 if(channel->ownerTransfer)
7647 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7648 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7649 saxdb_end_record(ctx);
7653 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7657 saxdb_start_record(ctx, ntype->name, 0);
7658 switch(ntype->set_access_type)
7660 case NOTE_SET_CHANNEL_ACCESS:
7661 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7663 case NOTE_SET_CHANNEL_SETTER:
7664 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7666 case NOTE_SET_PRIVILEGED: default:
7667 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7670 switch(ntype->visible_type)
7672 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7673 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7674 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7676 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7677 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7678 saxdb_end_record(ctx);
7682 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7684 struct do_not_register *dnr;
7685 dict_iterator_t it, next;
7687 for(it = dict_first(dnrs); it; it = next)
7689 next = iter_next(it);
7690 dnr = iter_data(it);
7691 if(dnr->expires && dnr->expires <= now)
7693 dict_remove(dnrs, iter_key(it));
7696 saxdb_start_record(ctx, dnr->chan_name, 0);
7698 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7700 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7701 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7702 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7703 saxdb_end_record(ctx);
7708 chanserv_saxdb_write(struct saxdb_context *ctx)
7711 struct chanData *channel;
7714 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7715 for(it = dict_first(note_types); it; it = iter_next(it))
7716 chanserv_write_note_type(ctx, iter_data(it));
7717 saxdb_end_record(ctx);
7720 saxdb_start_record(ctx, KEY_DNR, 1);
7721 write_dnrs_helper(ctx, handle_dnrs);
7722 write_dnrs_helper(ctx, plain_dnrs);
7723 write_dnrs_helper(ctx, mask_dnrs);
7724 saxdb_end_record(ctx);
7727 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7728 for(channel = channelList; channel; channel = channel->next)
7729 chanserv_write_channel(ctx, channel);
7730 saxdb_end_record(ctx);
7736 chanserv_db_cleanup(void) {
7738 unreg_part_func(handle_part);
7740 unregister_channel(channelList, "terminating.");
7741 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7742 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7743 free(chanserv_conf.support_channels.list);
7744 dict_delete(handle_dnrs);
7745 dict_delete(plain_dnrs);
7746 dict_delete(mask_dnrs);
7747 dict_delete(note_types);
7748 free_string_list(chanserv_conf.eightball);
7749 free_string_list(chanserv_conf.old_ban_names);
7750 free_string_list(chanserv_conf.set_shows);
7751 free(set_shows_list.list);
7752 free(uset_shows_list.list);
7755 struct userData *helper = helperList;
7756 helperList = helperList->next;
7761 #if defined(GCC_VARMACROS)
7762 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7763 #elif defined(C99_VARMACROS)
7764 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7766 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7767 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7770 init_chanserv(const char *nick)
7772 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7773 conf_register_reload(chanserv_conf_read);
7777 reg_server_link_func(handle_server_link);
7778 reg_new_channel_func(handle_new_channel);
7779 reg_join_func(handle_join);
7780 reg_part_func(handle_part);
7781 reg_kick_func(handle_kick);
7782 reg_topic_func(handle_topic);
7783 reg_mode_change_func(handle_mode);
7784 reg_nick_change_func(handle_nick_change);
7785 reg_auth_func(handle_auth);
7788 reg_handle_rename_func(handle_rename);
7789 reg_unreg_func(handle_unreg);
7791 handle_dnrs = dict_new();
7792 dict_set_free_data(handle_dnrs, free);
7793 plain_dnrs = dict_new();
7794 dict_set_free_data(plain_dnrs, free);
7795 mask_dnrs = dict_new();
7796 dict_set_free_data(mask_dnrs, free);
7798 reg_svccmd_unbind_func(handle_svccmd_unbind);
7799 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7800 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7801 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7802 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7803 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7804 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7805 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7806 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7807 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7808 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7809 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7810 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7811 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7813 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7814 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7816 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7817 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7818 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7819 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7820 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7822 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7823 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7824 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7825 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7826 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7828 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7829 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7830 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7831 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7833 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7834 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7835 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7836 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7837 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7838 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7839 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7840 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7842 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7843 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7844 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7845 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7846 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7847 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7848 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7849 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7850 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7851 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7852 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7853 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7854 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7855 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7857 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7858 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7859 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7860 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7861 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7863 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7864 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7866 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7867 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7868 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7869 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7870 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7871 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7872 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7873 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7874 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7875 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7876 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7878 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7879 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7881 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7882 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7883 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7884 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7886 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7887 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7888 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7889 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7890 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7892 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7893 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7894 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7895 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7896 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7897 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7899 /* Channel options */
7900 DEFINE_CHANNEL_OPTION(defaulttopic);
7901 DEFINE_CHANNEL_OPTION(topicmask);
7902 DEFINE_CHANNEL_OPTION(greeting);
7903 DEFINE_CHANNEL_OPTION(usergreeting);
7904 DEFINE_CHANNEL_OPTION(modes);
7905 DEFINE_CHANNEL_OPTION(enfops);
7906 DEFINE_CHANNEL_OPTION(giveops);
7907 DEFINE_CHANNEL_OPTION(protect);
7908 DEFINE_CHANNEL_OPTION(enfmodes);
7909 DEFINE_CHANNEL_OPTION(enftopic);
7910 DEFINE_CHANNEL_OPTION(pubcmd);
7911 DEFINE_CHANNEL_OPTION(givevoice);
7912 DEFINE_CHANNEL_OPTION(userinfo);
7913 DEFINE_CHANNEL_OPTION(dynlimit);
7914 DEFINE_CHANNEL_OPTION(topicsnarf);
7915 DEFINE_CHANNEL_OPTION(nodelete);
7916 DEFINE_CHANNEL_OPTION(toys);
7917 DEFINE_CHANNEL_OPTION(setters);
7918 DEFINE_CHANNEL_OPTION(topicrefresh);
7919 DEFINE_CHANNEL_OPTION(ctcpusers);
7920 DEFINE_CHANNEL_OPTION(ctcpreaction);
7921 DEFINE_CHANNEL_OPTION(inviteme);
7922 DEFINE_CHANNEL_OPTION(unreviewed);
7923 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7924 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7926 DEFINE_CHANNEL_OPTION(offchannel);
7927 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7929 /* Alias set topic to set defaulttopic for compatibility. */
7930 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7933 DEFINE_USER_OPTION(noautoop);
7934 DEFINE_USER_OPTION(autoinvite);
7935 DEFINE_USER_OPTION(info);
7937 /* Alias uset autovoice to uset autoop. */
7938 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7940 note_types = dict_new();
7941 dict_set_free_data(note_types, chanserv_deref_note_type);
7944 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7945 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7946 service_register(chanserv)->trigger = '!';
7947 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7949 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7951 if(chanserv_conf.channel_expire_frequency)
7952 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7954 if(chanserv_conf.dnr_expire_frequency)
7955 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7957 if(chanserv_conf.refresh_period)
7959 unsigned long next_refresh;
7960 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7961 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7964 reg_exit_func(chanserv_db_cleanup);
7965 message_register_table(msgtab);