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 (%d ops, %d voices, %d regulars)" },
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(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) && !HANDLE_FLAGGED(user->handle, BOT))
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 && !data)
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 = GetChannelUser(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 extern const char *titlehost_suffix;
3155 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3156 const char *accountname = argv[1] + 1;
3158 if(!(hi = get_handle_info(accountname)))
3160 reply("MSG_HANDLE_UNKNOWN", accountname);
3164 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3165 victims = alloca(sizeof(victims[0]) * channel->members.used);
3167 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3169 reply("CSMSG_MASK_PROTECTED", banmask);
3173 if((action == ACTION_KICK) && (victimCount == 0))
3175 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3179 name = ban = strdup(banmask);
3183 if(!is_ircmask(argv[1]))
3185 reply("MSG_NICK_UNKNOWN", argv[1]);
3189 victims = alloca(sizeof(victims[0]) * channel->members.used);
3191 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3193 reply("CSMSG_MASK_PROTECTED", argv[1]);
3197 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3199 reply("CSMSG_LAME_MASK", argv[1]);
3203 if((action == ACTION_KICK) && (victimCount == 0))
3205 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3209 name = ban = strdup(argv[1]);
3212 /* Truncate the ban in place if necessary; we must ensure
3213 that 'ban' is a valid ban mask before sanitizing it. */
3214 sanitize_ircmask(ban);
3216 if(action & ACTION_ADD_BAN)
3218 struct banData *bData, *next;
3220 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3222 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3227 if(action & ACTION_ADD_TIMED_BAN)
3229 duration = ParseInterval(argv[2]);
3233 reply("CSMSG_DURATION_TOO_LOW");
3237 else if(duration > (86400 * 365 * 2))
3239 reply("CSMSG_DURATION_TOO_HIGH");
3245 for(bData = channel->channel_info->bans; bData; bData = next)
3247 if(match_ircglobs(bData->mask, ban))
3249 int exact = !irccasecmp(bData->mask, ban);
3251 /* The ban is redundant; there is already a ban
3252 with the same effect in place. */
3256 free(bData->reason);
3257 bData->reason = strdup(reason);
3258 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3260 reply("CSMSG_REASON_CHANGE", ban);
3264 if(exact && bData->expires)
3268 /* If the ban matches an existing one exactly,
3269 extend the expiration time if the provided
3270 duration is longer. */
3271 if(duration && (now + duration > bData->expires))
3273 bData->expires = now + duration;
3284 /* Delete the expiration timeq entry and
3285 requeue if necessary. */
3286 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3289 timeq_add(bData->expires, expire_ban, bData);
3293 /* automated kickban */
3296 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3298 reply("CSMSG_BAN_ADDED", name, channel->name);
3304 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3311 if(match_ircglobs(ban, bData->mask))
3313 /* The ban we are adding makes previously existing
3314 bans redundant; silently remove them. */
3315 del_channel_ban(bData);
3319 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);
3321 name = ban = strdup(bData->mask);
3325 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3327 extern const char *hidden_host_suffix;
3328 const char *old_name = chanserv_conf.old_ban_names->list[n];
3330 unsigned int l1, l2;
3333 l2 = strlen(old_name);
3336 if(irccasecmp(ban + l1 - l2, old_name))
3338 new_mask = malloc(MAXLEN);
3339 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3341 name = ban = new_mask;
3346 if(action & ACTION_BAN)
3348 unsigned int exists;
3349 struct mod_chanmode *change;
3351 if(channel->banlist.used >= MAXBANS)
3354 reply("CSMSG_BANLIST_FULL", channel->name);
3359 exists = ChannelBanExists(channel, ban);
3360 change = mod_chanmode_alloc(victimCount + 1);
3361 for(n = 0; n < victimCount; ++n)
3363 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3364 change->args[n].u.member = victims[n];
3368 change->args[n].mode = MODE_BAN;
3369 change->args[n++].u.hostmask = ban;
3373 modcmd_chanmode_announce(change);
3375 mod_chanmode_announce(chanserv, channel, change);
3376 mod_chanmode_free(change);
3378 if(exists && (action == ACTION_BAN))
3381 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3387 if(action & ACTION_KICK)
3389 char kick_reason[MAXLEN];
3390 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3392 for(n = 0; n < victimCount; n++)
3393 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3398 /* No response, since it was automated. */
3400 else if(action & ACTION_ADD_BAN)
3403 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3405 reply("CSMSG_BAN_ADDED", name, channel->name);
3407 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3408 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3409 else if(action & ACTION_BAN)
3410 reply("CSMSG_BAN_DONE", name, channel->name);
3411 else if(action & ACTION_KICK && victimCount)
3412 reply("CSMSG_KICK_DONE", name, channel->name);
3418 static CHANSERV_FUNC(cmd_kickban)
3420 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3423 static CHANSERV_FUNC(cmd_kick)
3425 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3428 static CHANSERV_FUNC(cmd_ban)
3430 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3433 static CHANSERV_FUNC(cmd_addban)
3435 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3438 static CHANSERV_FUNC(cmd_addtimedban)
3440 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3443 static struct mod_chanmode *
3444 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3446 struct mod_chanmode *change;
3447 unsigned char *match;
3448 unsigned int ii, count;
3450 match = alloca(bans->used);
3453 for(ii = count = 0; ii < bans->used; ++ii)
3455 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3456 MATCH_USENICK | MATCH_VISIBLE);
3463 for(ii = count = 0; ii < bans->used; ++ii)
3465 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3472 change = mod_chanmode_alloc(count);
3473 for(ii = count = 0; ii < bans->used; ++ii)
3477 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3478 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3480 assert(count == change->argc);
3485 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3487 struct userNode *actee;
3493 /* may want to allow a comma delimited list of users... */
3494 if(!(actee = GetUserH(argv[1])))
3496 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3498 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3499 const char *accountname = argv[1] + 1;
3501 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3502 mask = strdup(banmask);
3504 else if(!is_ircmask(argv[1]))
3506 reply("MSG_NICK_UNKNOWN", argv[1]);
3511 mask = strdup(argv[1]);
3515 /* We don't sanitize the mask here because ircu
3517 if(action & ACTION_UNBAN)
3519 struct mod_chanmode *change;
3520 change = find_matching_bans(&channel->banlist, actee, mask);
3525 modcmd_chanmode_announce(change);
3526 for(ii = 0; ii < change->argc; ++ii)
3527 free((char*)change->args[ii].u.hostmask);
3528 mod_chanmode_free(change);
3533 if(action & ACTION_DEL_BAN)
3535 struct banData *ban, *next;
3537 ban = channel->channel_info->bans;
3541 for( ; ban && !user_matches_glob(actee, ban->mask,
3542 MATCH_USENICK | MATCH_VISIBLE);
3545 for( ; ban && !match_ircglobs(mask, ban->mask);
3550 del_channel_ban(ban);
3557 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3559 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3565 static CHANSERV_FUNC(cmd_unban)
3567 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3570 static CHANSERV_FUNC(cmd_delban)
3572 /* it doesn't necessarily have to remove the channel ban - may want
3573 to make that an option. */
3574 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3577 static CHANSERV_FUNC(cmd_unbanme)
3579 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3580 long flags = ACTION_UNBAN;
3582 /* remove permanent bans if the user has the proper access. */
3583 if(uData->access >= UL_MASTER)
3584 flags |= ACTION_DEL_BAN;
3586 argv[1] = user->nick;
3587 return unban_user(user, channel, 2, argv, cmd, flags);
3590 static CHANSERV_FUNC(cmd_unbanall)
3592 struct mod_chanmode *change;
3595 if(!channel->banlist.used)
3597 reply("CSMSG_NO_BANS", channel->name);
3601 change = mod_chanmode_alloc(channel->banlist.used);
3602 for(ii=0; ii<channel->banlist.used; ii++)
3604 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3605 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3607 modcmd_chanmode_announce(change);
3608 for(ii = 0; ii < change->argc; ++ii)
3609 free((char*)change->args[ii].u.hostmask);
3610 mod_chanmode_free(change);
3611 reply("CSMSG_BANS_REMOVED", channel->name);
3615 static CHANSERV_FUNC(cmd_open)
3617 struct mod_chanmode *change;
3620 change = find_matching_bans(&channel->banlist, user, NULL);
3622 change = mod_chanmode_alloc(0);
3623 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3624 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3625 && channel->channel_info->modes.modes_set)
3626 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3627 modcmd_chanmode_announce(change);
3628 reply("CSMSG_CHANNEL_OPENED", channel->name);
3629 for(ii = 0; ii < change->argc; ++ii)
3630 free((char*)change->args[ii].u.hostmask);
3631 mod_chanmode_free(change);
3635 static CHANSERV_FUNC(cmd_myaccess)
3637 static struct string_buffer sbuf;
3638 struct handle_info *target_handle;
3639 struct userData *uData;
3642 target_handle = user->handle_info;
3643 else if(!IsStaff(user))
3645 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3648 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3651 if(!oper_outranks(user, target_handle))
3654 if(!target_handle->channels)
3656 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3660 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3661 for(uData = target_handle->channels; uData; uData = uData->u_next)
3663 struct chanData *cData = uData->channel;
3664 unsigned int base_len;
3666 if(uData->access > UL_OWNER)
3668 if(IsProtected(cData)
3669 && (target_handle != user->handle_info)
3670 && !GetTrueChannelAccess(cData, user->handle_info))
3673 string_buffer_append_printf(&sbuf, "[%s (%d,", cData->channel->name, uData->access);
3674 base_len = sbuf.used;
3675 if(IsUserSuspended(uData))
3676 string_buffer_append(&sbuf, 's');
3677 if(IsUserAutoOp(uData))
3679 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3680 string_buffer_append(&sbuf, 'o');
3681 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3682 string_buffer_append(&sbuf, 'v');
3684 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3685 string_buffer_append(&sbuf, 'i');
3686 if(sbuf.used==base_len)
3689 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3691 string_buffer_append_string(&sbuf, ")]");
3692 string_buffer_append(&sbuf, '\0');
3693 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3699 static CHANSERV_FUNC(cmd_access)
3701 struct userNode *target;
3702 struct handle_info *target_handle;
3703 struct userData *uData;
3705 char prefix[MAXLEN];
3710 target_handle = target->handle_info;
3712 else if((target = GetUserH(argv[1])))
3714 target_handle = target->handle_info;
3716 else if(argv[1][0] == '*')
3718 if(!(target_handle = get_handle_info(argv[1]+1)))
3720 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3726 reply("MSG_NICK_UNKNOWN", argv[1]);
3730 assert(target || target_handle);
3732 if(target == chanserv)
3734 reply("CSMSG_IS_CHANSERV");
3742 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3747 reply("MSG_USER_AUTHENTICATE", target->nick);
3750 reply("MSG_AUTHENTICATE");
3756 const char *epithet = NULL, *type = NULL;
3759 epithet = chanserv_conf.irc_operator_epithet;
3760 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3762 else if(IsNetworkHelper(target))
3764 epithet = chanserv_conf.network_helper_epithet;
3765 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3767 else if(IsSupportHelper(target))
3769 epithet = chanserv_conf.support_helper_epithet;
3770 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3774 if(target_handle->epithet)
3775 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3777 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3779 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3783 sprintf(prefix, "%s", target_handle->handle);
3786 if(!channel->channel_info)
3788 reply("CSMSG_NOT_REGISTERED", channel->name);
3792 helping = HANDLE_FLAGGED(target_handle, HELPING)
3793 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3794 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3796 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3797 /* To prevent possible information leaks, only show infolines
3798 * if the requestor is in the channel or it's their own
3800 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3802 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3804 /* Likewise, only say it's suspended if the user has active
3805 * access in that channel or it's their own entry. */
3806 if(IsUserSuspended(uData)
3807 && (GetChannelUser(channel->channel_info, user->handle_info)
3808 || (user->handle_info == uData->handle)))
3810 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3815 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3822 def_list(struct listData *list)
3826 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3828 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3829 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3830 if(list->table.length == 1)
3832 msg = user_find_message(list->user, "MSG_NONE");
3833 send_message_type(4, list->user, list->bot, " %s", msg);
3838 userData_access_comp(const void *arg_a, const void *arg_b)
3840 const struct userData *a = *(struct userData**)arg_a;
3841 const struct userData *b = *(struct userData**)arg_b;
3843 if(a->access != b->access)
3844 res = b->access - a->access;
3846 res = irccasecmp(a->handle->handle, b->handle->handle);
3851 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3853 void (*send_list)(struct listData *);
3854 struct userData *uData;
3855 struct listData lData;
3856 unsigned int matches;
3860 lData.bot = cmd->parent->bot;
3861 lData.channel = channel;
3862 lData.lowest = lowest;
3863 lData.highest = highest;
3864 lData.search = (argc > 1) ? argv[1] : NULL;
3865 send_list = def_list;
3867 if(user->handle_info)
3869 switch(user->handle_info->userlist_style)
3871 case HI_STYLE_DEF: send_list = def_list; break;
3872 case HI_STYLE_ZOOT: send_list = def_list; break;
3876 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3878 for(uData = channel->channel_info->users; uData; uData = uData->next)
3880 if((uData->access < lowest)
3881 || (uData->access > highest)
3882 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3884 lData.users[matches++] = uData;
3886 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3888 lData.table.length = matches+1;
3889 lData.table.width = 4;
3890 lData.table.flags = TABLE_NO_FREE;
3891 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3892 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3893 lData.table.contents[0] = ary;
3896 ary[2] = "Last Seen";
3898 for(matches = 1; matches < lData.table.length; ++matches)
3900 char seen[INTERVALLEN];
3902 uData = lData.users[matches-1];
3903 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3904 lData.table.contents[matches] = ary;
3905 ary[0] = strtab(uData->access);
3906 ary[1] = uData->handle->handle;
3909 else if(!uData->seen)
3912 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3913 ary[2] = strdup(ary[2]);
3914 if(IsUserSuspended(uData))
3915 ary[3] = "Suspended";
3916 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3917 ary[3] = "Vacation";
3918 else if(HANDLE_FLAGGED(uData->handle, BOT))
3924 for(matches = 1; matches < lData.table.length; ++matches)
3926 free((char*)lData.table.contents[matches][2]);
3927 free(lData.table.contents[matches]);
3929 free(lData.table.contents[0]);
3930 free(lData.table.contents);
3934 static CHANSERV_FUNC(cmd_users)
3936 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3939 static CHANSERV_FUNC(cmd_wlist)
3941 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3944 static CHANSERV_FUNC(cmd_clist)
3946 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3949 static CHANSERV_FUNC(cmd_mlist)
3951 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3954 static CHANSERV_FUNC(cmd_olist)
3956 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3959 static CHANSERV_FUNC(cmd_plist)
3961 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3964 static CHANSERV_FUNC(cmd_bans)
3966 struct userNode *search_u = NULL;
3967 struct helpfile_table tbl;
3968 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3969 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
3970 const char *msg_never, *triggered, *expires;
3971 struct banData *ban, **bans;
3975 else if(strchr(search = argv[1], '!'))
3978 search_wilds = search[strcspn(search, "?*")];
3980 else if(!(search_u = GetUserH(search)))
3981 reply("MSG_NICK_UNKNOWN", search);
3983 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
3985 for(ban = channel->channel_info->bans; ban; ban = ban->next)
3989 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
3994 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
3997 bans[matches++] = ban;
4002 tbl.length = matches + 1;
4003 tbl.width = 4 + timed;
4005 tbl.flags = TABLE_NO_FREE;
4006 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4007 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4008 tbl.contents[0][0] = "Mask";
4009 tbl.contents[0][1] = "Set By";
4010 tbl.contents[0][2] = "Triggered";
4013 tbl.contents[0][3] = "Expires";
4014 tbl.contents[0][4] = "Reason";
4017 tbl.contents[0][3] = "Reason";
4020 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4022 free(tbl.contents[0]);
4027 msg_never = user_find_message(user, "MSG_NEVER");
4028 for(ii = 0; ii < matches; )
4034 else if(ban->expires)
4035 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4037 expires = msg_never;
4040 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4042 triggered = msg_never;
4044 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4045 tbl.contents[ii][0] = ban->mask;
4046 tbl.contents[ii][1] = ban->owner;
4047 tbl.contents[ii][2] = strdup(triggered);
4050 tbl.contents[ii][3] = strdup(expires);
4051 tbl.contents[ii][4] = ban->reason;
4054 tbl.contents[ii][3] = ban->reason;
4056 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4057 reply("MSG_MATCH_COUNT", matches);
4058 for(ii = 1; ii < tbl.length; ++ii)
4060 free((char*)tbl.contents[ii][2]);
4062 free((char*)tbl.contents[ii][3]);
4063 free(tbl.contents[ii]);
4065 free(tbl.contents[0]);
4071 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4073 struct chanData *cData = channel->channel_info;
4074 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4076 if(cData->topic_mask)
4077 return !match_ircglob(new_topic, cData->topic_mask);
4078 else if(cData->topic)
4079 return irccasecmp(new_topic, cData->topic);
4084 static CHANSERV_FUNC(cmd_topic)
4086 struct chanData *cData;
4089 cData = channel->channel_info;
4094 SetChannelTopic(channel, chanserv, cData->topic, 1);
4095 reply("CSMSG_TOPIC_SET", cData->topic);
4099 reply("CSMSG_NO_TOPIC", channel->name);
4103 topic = unsplit_string(argv + 1, argc - 1, NULL);
4104 /* If they say "!topic *", use an empty topic. */
4105 if((topic[0] == '*') && (topic[1] == 0))
4107 if(bad_topic(channel, user, topic))
4109 char *topic_mask = cData->topic_mask;
4112 char new_topic[TOPICLEN+1], tchar;
4113 int pos=0, starpos=-1, dpos=0, len;
4115 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4122 len = strlen(topic);
4123 if((dpos + len) > TOPICLEN)
4124 len = TOPICLEN + 1 - dpos;
4125 memcpy(new_topic+dpos, topic, len);
4129 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4130 default: new_topic[dpos++] = tchar; break;
4133 if((dpos > TOPICLEN) || tchar)
4136 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4137 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4140 new_topic[dpos] = 0;
4141 SetChannelTopic(channel, chanserv, new_topic, 1);
4143 reply("CSMSG_TOPIC_LOCKED", channel->name);
4148 SetChannelTopic(channel, chanserv, topic, 1);
4150 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4152 /* Grab the topic and save it as the default topic. */
4154 cData->topic = strdup(channel->topic);
4160 static CHANSERV_FUNC(cmd_mode)
4162 struct userData *uData;
4163 struct mod_chanmode *change;
4169 change = &channel->channel_info->modes;
4170 if(change->modes_set || change->modes_clear) {
4171 modcmd_chanmode_announce(change);
4172 reply("CSMSG_DEFAULTED_MODES", channel->name);
4174 reply("CSMSG_NO_MODES", channel->name);
4178 uData = GetChannelUser(channel->channel_info, user->handle_info);
4180 base_oplevel = MAXOPLEVEL;
4181 else if (uData->access >= UL_OWNER)
4184 base_oplevel = 1 + UL_OWNER - uData->access;
4185 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4188 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4192 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4193 && mode_lock_violated(&channel->channel_info->modes, change))
4196 mod_chanmode_format(&channel->channel_info->modes, modes);
4197 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4201 modcmd_chanmode_announce(change);
4202 mod_chanmode_format(change, fmt);
4203 mod_chanmode_free(change);
4204 reply("CSMSG_MODES_SET", fmt);
4208 static CHANSERV_FUNC(cmd_invite)
4210 struct userNode *invite;
4214 if(!(invite = GetUserH(argv[1])))
4216 reply("MSG_NICK_UNKNOWN", argv[1]);
4223 if(GetUserMode(channel, invite))
4225 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4233 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4234 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4237 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4239 irc_invite(chanserv, invite, channel);
4241 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4246 static CHANSERV_FUNC(cmd_inviteme)
4248 if(GetUserMode(channel, user))
4250 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4253 if(channel->channel_info
4254 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4256 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4259 irc_invite(cmd->parent->bot, user, channel);
4264 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4267 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4269 /* We display things based on two dimensions:
4270 * - Issue time: present or absent
4271 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4272 * (in order of precedence, so something both expired and revoked
4273 * only counts as revoked)
4275 combo = (suspended->issued ? 4 : 0)
4276 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4278 case 0: /* no issue time, indefinite expiration */
4279 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4281 case 1: /* no issue time, expires in future */
4282 intervalString(buf1, suspended->expires-now, user->handle_info);
4283 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4285 case 2: /* no issue time, expired */
4286 intervalString(buf1, now-suspended->expires, user->handle_info);
4287 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4289 case 3: /* no issue time, revoked */
4290 intervalString(buf1, now-suspended->revoked, user->handle_info);
4291 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4293 case 4: /* issue time set, indefinite expiration */
4294 intervalString(buf1, now-suspended->issued, user->handle_info);
4295 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4297 case 5: /* issue time set, expires in future */
4298 intervalString(buf1, now-suspended->issued, user->handle_info);
4299 intervalString(buf2, suspended->expires-now, user->handle_info);
4300 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4302 case 6: /* issue time set, expired */
4303 intervalString(buf1, now-suspended->issued, user->handle_info);
4304 intervalString(buf2, now-suspended->expires, user->handle_info);
4305 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4307 case 7: /* issue time set, revoked */
4308 intervalString(buf1, now-suspended->issued, user->handle_info);
4309 intervalString(buf2, now-suspended->revoked, user->handle_info);
4310 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4313 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4318 static CHANSERV_FUNC(cmd_info)
4320 char modes[MAXLEN], buffer[INTERVALLEN];
4321 struct userData *uData, *owner;
4322 struct chanData *cData;
4323 struct do_not_register *dnr;
4328 cData = channel->channel_info;
4329 reply("CSMSG_CHANNEL_INFO", channel->name);
4331 uData = GetChannelUser(cData, user->handle_info);
4332 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4334 mod_chanmode_format(&cData->modes, modes);
4335 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4336 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4339 for(it = dict_first(cData->notes); it; it = iter_next(it))
4343 note = iter_data(it);
4344 if(!note_type_visible_to_user(cData, note->type, user))
4347 padding = PADLEN - 1 - strlen(iter_key(it));
4348 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4351 reply("CSMSG_CHANNEL_MAX", cData->max);
4352 for(owner = cData->users; owner; owner = owner->next)
4353 if(owner->access == UL_OWNER)
4354 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4355 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4356 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4357 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4359 privileged = IsStaff(user);
4361 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4362 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4363 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4365 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4366 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4368 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4370 struct suspended *suspended;
4371 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4372 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4373 show_suspension_info(cmd, user, suspended);
4375 else if(IsSuspended(cData))
4377 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4378 show_suspension_info(cmd, user, cData->suspended);
4383 static CHANSERV_FUNC(cmd_netinfo)
4385 extern unsigned long boot_time;
4386 extern unsigned long burst_length;
4387 char interval[INTERVALLEN];
4389 reply("CSMSG_NETWORK_INFO");
4390 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4391 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4392 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4393 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4394 reply("CSMSG_NETWORK_BANS", banCount);
4395 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4396 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4397 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4402 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4404 struct helpfile_table table;
4406 struct userNode *user;
4411 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4412 table.contents = alloca(list->used*sizeof(*table.contents));
4413 for(nn=0; nn<list->used; nn++)
4415 user = list->list[nn];
4416 if(user->modes & skip_flags)
4420 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4423 nick = alloca(strlen(user->nick)+3);
4424 sprintf(nick, "(%s)", user->nick);
4428 table.contents[table.length][0] = nick;
4431 table_send(chanserv, to->nick, 0, NULL, table);
4434 static CHANSERV_FUNC(cmd_ircops)
4436 reply("CSMSG_STAFF_OPERS");
4437 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4441 static CHANSERV_FUNC(cmd_helpers)
4443 reply("CSMSG_STAFF_HELPERS");
4444 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4448 static CHANSERV_FUNC(cmd_staff)
4450 reply("CSMSG_NETWORK_STAFF");
4451 cmd_ircops(CSFUNC_ARGS);
4452 cmd_helpers(CSFUNC_ARGS);
4456 static CHANSERV_FUNC(cmd_peek)
4458 struct modeNode *mn;
4459 char modes[MODELEN];
4461 struct helpfile_table table;
4462 int opcount = 0, voicecount = 0, srvcount = 0;
4464 irc_make_chanmode(channel, modes);
4466 reply("CSMSG_PEEK_INFO", channel->name);
4467 reply("CSMSG_PEEK_TOPIC", channel->topic);
4468 reply("CSMSG_PEEK_MODES", modes);
4472 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4473 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4474 for(n = 0; n < channel->members.used; n++)
4476 mn = channel->members.list[n];
4477 if(IsLocal(mn->user))
4479 else if(mn->modes & MODE_CHANOP)
4481 else if(mn->modes & MODE_VOICE)
4484 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4486 table.contents[table.length] = alloca(sizeof(**table.contents));
4487 table.contents[table.length][0] = mn->user->nick;
4491 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4492 (channel->members.used - opcount - voicecount - srvcount));
4496 reply("CSMSG_PEEK_OPS");
4497 table_send(chanserv, user->nick, 0, NULL, table);
4500 reply("CSMSG_PEEK_NO_OPS");
4504 static MODCMD_FUNC(cmd_wipeinfo)
4506 struct handle_info *victim;
4507 struct userData *ud, *actor, *real_actor;
4508 unsigned int override = 0;
4511 actor = GetChannelUser(channel->channel_info, user->handle_info);
4512 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4513 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4515 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4517 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4520 if((ud->access >= actor->access) && (ud != actor))
4522 reply("MSG_USER_OUTRANKED", victim->handle);
4525 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4526 override = CMD_LOG_OVERRIDE;
4530 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4531 return 1 | override;
4534 static CHANSERV_FUNC(cmd_resync)
4536 struct mod_chanmode *changes;
4537 struct chanData *cData = channel->channel_info;
4538 unsigned int ii, used;
4540 changes = mod_chanmode_alloc(channel->members.used * 2);
4541 for(ii = used = 0; ii < channel->members.used; ++ii)
4543 struct modeNode *mn = channel->members.list[ii];
4544 struct userData *uData;
4546 if(IsService(mn->user))
4549 uData = GetChannelAccess(cData, mn->user->handle_info);
4550 if(!cData->lvlOpts[lvlGiveOps]
4551 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4553 if(!(mn->modes & MODE_CHANOP))
4555 changes->args[used].mode = MODE_CHANOP;
4556 changes->args[used++].u.member = mn;
4559 else if(!cData->lvlOpts[lvlGiveVoice]
4560 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4562 if(mn->modes & MODE_CHANOP)
4564 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4565 changes->args[used++].u.member = mn;
4567 if(!(mn->modes & MODE_VOICE))
4569 changes->args[used].mode = MODE_VOICE;
4570 changes->args[used++].u.member = mn;
4577 changes->args[used].mode = MODE_REMOVE | mn->modes;
4578 changes->args[used++].u.member = mn;
4582 changes->argc = used;
4583 modcmd_chanmode_announce(changes);
4584 mod_chanmode_free(changes);
4585 reply("CSMSG_RESYNCED_USERS", channel->name);
4589 static CHANSERV_FUNC(cmd_seen)
4591 struct userData *uData;
4592 struct handle_info *handle;
4593 char seen[INTERVALLEN];
4597 if(!irccasecmp(argv[1], chanserv->nick))
4599 reply("CSMSG_IS_CHANSERV");
4603 if(!(handle = get_handle_info(argv[1])))
4605 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4609 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4611 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4616 reply("CSMSG_USER_PRESENT", handle->handle);
4617 else if(uData->seen)
4618 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4620 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4622 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4623 reply("CSMSG_USER_VACATION", handle->handle);
4628 static MODCMD_FUNC(cmd_names)
4630 struct userNode *targ;
4631 struct userData *targData;
4632 unsigned int ii, pos;
4635 for(ii=pos=0; ii<channel->members.used; ++ii)
4637 targ = channel->members.list[ii]->user;
4638 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4641 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4644 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4648 if(IsUserSuspended(targData))
4650 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4653 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4654 reply("CSMSG_END_NAMES", channel->name);
4659 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4661 switch(ntype->visible_type)
4663 case NOTE_VIS_ALL: return 1;
4664 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4665 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4670 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4672 struct userData *uData;
4674 switch(ntype->set_access_type)
4676 case NOTE_SET_CHANNEL_ACCESS:
4677 if(!user->handle_info)
4679 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4681 return uData->access >= ntype->set_access.min_ulevel;
4682 case NOTE_SET_CHANNEL_SETTER:
4683 return check_user_level(channel, user, lvlSetters, 1, 0);
4684 case NOTE_SET_PRIVILEGED: default:
4685 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4689 static CHANSERV_FUNC(cmd_note)
4691 struct chanData *cData;
4693 struct note_type *ntype;
4695 cData = channel->channel_info;
4698 reply("CSMSG_NOT_REGISTERED", channel->name);
4702 /* If no arguments, show all visible notes for the channel. */
4708 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4710 note = iter_data(it);
4711 if(!note_type_visible_to_user(cData, note->type, user))
4714 reply("CSMSG_NOTELIST_HEADER", channel->name);
4715 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4718 reply("CSMSG_NOTELIST_END", channel->name);
4720 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4722 /* If one argument, show the named note. */
4725 if((note = dict_find(cData->notes, argv[1], NULL))
4726 && note_type_visible_to_user(cData, note->type, user))
4728 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4730 else if((ntype = dict_find(note_types, argv[1], NULL))
4731 && note_type_visible_to_user(NULL, ntype, user))
4733 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4738 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4742 /* Assume they're trying to set a note. */
4746 ntype = dict_find(note_types, argv[1], NULL);
4749 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4752 else if(note_type_settable_by_user(channel, ntype, user))
4754 note_text = unsplit_string(argv+2, argc-2, NULL);
4755 if((note = dict_find(cData->notes, argv[1], NULL)))
4756 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4757 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4758 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4760 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4762 /* The note is viewable to staff only, so return 0
4763 to keep the invocation from getting logged (or
4764 regular users can see it in !events). */
4770 reply("CSMSG_NO_ACCESS");
4777 static CHANSERV_FUNC(cmd_delnote)
4782 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4783 || !note_type_settable_by_user(channel, note->type, user))
4785 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4788 dict_remove(channel->channel_info->notes, note->type->name);
4789 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4793 static CHANSERV_FUNC(cmd_events)
4795 struct logSearch discrim;
4796 struct logReport report;
4797 unsigned int matches, limit;
4799 limit = (argc > 1) ? atoi(argv[1]) : 10;
4800 if(limit < 1 || limit > 200)
4803 memset(&discrim, 0, sizeof(discrim));
4804 discrim.masks.bot = chanserv;
4805 discrim.masks.channel_name = channel->name;
4807 discrim.masks.command = argv[2];
4808 discrim.limit = limit;
4809 discrim.max_time = INT_MAX;
4810 discrim.severities = 1 << LOG_COMMAND;
4811 report.reporter = chanserv;
4813 reply("CSMSG_EVENT_SEARCH_RESULTS");
4814 matches = log_entry_search(&discrim, log_report_entry, &report);
4816 reply("MSG_MATCH_COUNT", matches);
4818 reply("MSG_NO_MATCHES");
4822 static CHANSERV_FUNC(cmd_say)
4828 msg = unsplit_string(argv + 1, argc - 1, NULL);
4829 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4831 else if(*argv[1] == '*' && argv[1][1] != '\0')
4833 struct handle_info *hi;
4834 struct userNode *authed;
4837 msg = unsplit_string(argv + 2, argc - 2, NULL);
4839 if (!(hi = get_handle_info(argv[1] + 1)))
4841 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4845 for (authed = hi->users; authed; authed = authed->next_authed)
4846 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4848 else if(GetUserH(argv[1]))
4851 msg = unsplit_string(argv + 2, argc - 2, NULL);
4852 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4856 reply("MSG_NOT_TARGET_NAME");
4862 static CHANSERV_FUNC(cmd_emote)
4868 /* CTCP is so annoying. */
4869 msg = unsplit_string(argv + 1, argc - 1, NULL);
4870 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4872 else if(*argv[1] == '*' && argv[1][1] != '\0')
4874 struct handle_info *hi;
4875 struct userNode *authed;
4878 msg = unsplit_string(argv + 2, argc - 2, NULL);
4880 if (!(hi = get_handle_info(argv[1] + 1)))
4882 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4886 for (authed = hi->users; authed; authed = authed->next_authed)
4887 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4889 else if(GetUserH(argv[1]))
4891 msg = unsplit_string(argv + 2, argc - 2, NULL);
4892 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4896 reply("MSG_NOT_TARGET_NAME");
4902 struct channelList *
4903 chanserv_support_channels(void)
4905 return &chanserv_conf.support_channels;
4908 static CHANSERV_FUNC(cmd_expire)
4910 int channel_count = registered_channels;
4911 expire_channels(chanserv);
4912 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4917 chanserv_expire_suspension(void *data)
4919 struct suspended *suspended = data;
4920 struct chanNode *channel;
4923 /* Update the channel registration data structure. */
4924 if(!suspended->expires || (now < suspended->expires))
4925 suspended->revoked = now;
4926 channel = suspended->cData->channel;
4927 suspended->cData->channel = channel;
4928 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4930 /* If appropriate, re-join ChanServ to the channel. */
4931 if(!IsOffChannel(suspended->cData))
4933 struct mod_chanmode change;
4934 mod_chanmode_init(&change);
4936 change.args[0].mode = MODE_CHANOP;
4937 change.args[0].u.member = AddChannelUser(chanserv, channel);
4938 mod_chanmode_announce(chanserv, channel, &change);
4941 /* Mark everyone currently in the channel as present. */
4942 for(ii = 0; ii < channel->members.used; ++ii)
4944 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
4953 static CHANSERV_FUNC(cmd_csuspend)
4955 struct suspended *suspended;
4956 char reason[MAXLEN];
4957 unsigned long expiry, duration;
4958 struct userData *uData;
4962 if(IsProtected(channel->channel_info))
4964 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4968 if(argv[1][0] == '!')
4970 else if(IsSuspended(channel->channel_info))
4972 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4973 show_suspension_info(cmd, user, channel->channel_info->suspended);
4977 if(!strcmp(argv[1], "0"))
4979 else if((duration = ParseInterval(argv[1])))
4980 expiry = now + duration;
4983 reply("MSG_INVALID_DURATION", argv[1]);
4987 unsplit_string(argv + 2, argc - 2, reason);
4989 suspended = calloc(1, sizeof(*suspended));
4990 suspended->revoked = 0;
4991 suspended->issued = now;
4992 suspended->suspender = strdup(user->handle_info->handle);
4993 suspended->expires = expiry;
4994 suspended->reason = strdup(reason);
4995 suspended->cData = channel->channel_info;
4996 suspended->previous = suspended->cData->suspended;
4997 suspended->cData->suspended = suspended;
4999 if(suspended->expires)
5000 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5002 if(IsSuspended(channel->channel_info))
5004 suspended->previous->revoked = now;
5005 if(suspended->previous->expires)
5006 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5007 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5008 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5012 /* Mark all users in channel as absent. */
5013 for(uData = channel->channel_info->users; uData; uData = uData->next)
5022 /* Mark the channel as suspended, then part. */
5023 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5024 DelChannelUser(chanserv, channel, suspended->reason, 0);
5025 reply("CSMSG_SUSPENDED", channel->name);
5026 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5027 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5032 static CHANSERV_FUNC(cmd_cunsuspend)
5034 struct suspended *suspended;
5035 char message[MAXLEN];
5037 if(!IsSuspended(channel->channel_info))
5039 reply("CSMSG_NOT_SUSPENDED", channel->name);
5043 suspended = channel->channel_info->suspended;
5045 /* Expire the suspension and join ChanServ to the channel. */
5046 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5047 chanserv_expire_suspension(suspended);
5048 reply("CSMSG_UNSUSPENDED", channel->name);
5049 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5050 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5054 typedef struct chanservSearch
5059 unsigned long unvisited;
5060 unsigned long registered;
5062 unsigned long flags;
5066 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5069 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5074 search = malloc(sizeof(struct chanservSearch));
5075 memset(search, 0, sizeof(*search));
5078 for(i = 0; i < argc; i++)
5080 /* Assume all criteria require arguments. */
5083 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5087 if(!irccasecmp(argv[i], "name"))
5088 search->name = argv[++i];
5089 else if(!irccasecmp(argv[i], "registrar"))
5090 search->registrar = argv[++i];
5091 else if(!irccasecmp(argv[i], "unvisited"))
5092 search->unvisited = ParseInterval(argv[++i]);
5093 else if(!irccasecmp(argv[i], "registered"))
5094 search->registered = ParseInterval(argv[++i]);
5095 else if(!irccasecmp(argv[i], "flags"))
5098 if(!irccasecmp(argv[i], "nodelete"))
5099 search->flags |= CHANNEL_NODELETE;
5100 else if(!irccasecmp(argv[i], "suspended"))
5101 search->flags |= CHANNEL_SUSPENDED;
5102 else if(!irccasecmp(argv[i], "unreviewed"))
5103 search->flags |= CHANNEL_UNREVIEWED;
5106 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5110 else if(!irccasecmp(argv[i], "limit"))
5111 search->limit = strtoul(argv[++i], NULL, 10);
5114 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5119 if(search->name && !strcmp(search->name, "*"))
5121 if(search->registrar && !strcmp(search->registrar, "*"))
5122 search->registrar = 0;
5131 chanserv_channel_match(struct chanData *channel, search_t search)
5133 const char *name = channel->channel->name;
5134 if((search->name && !match_ircglob(name, search->name)) ||
5135 (search->registrar && !channel->registrar) ||
5136 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5137 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5138 (search->registered && (now - channel->registered) > search->registered) ||
5139 (search->flags && ((search->flags & channel->flags) != search->flags)))
5146 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5148 struct chanData *channel;
5149 unsigned int matches = 0;
5151 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5153 if(!chanserv_channel_match(channel, search))
5163 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5168 search_print(struct chanData *channel, void *data)
5170 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5173 static CHANSERV_FUNC(cmd_search)
5176 unsigned int matches;
5177 channel_search_func action;
5181 if(!irccasecmp(argv[1], "count"))
5182 action = search_count;
5183 else if(!irccasecmp(argv[1], "print"))
5184 action = search_print;
5187 reply("CSMSG_ACTION_INVALID", argv[1]);
5191 search = chanserv_search_create(user, argc - 2, argv + 2);
5195 if(action == search_count)
5196 search->limit = INT_MAX;
5198 if(action == search_print)
5199 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5201 matches = chanserv_channel_search(search, action, user);
5204 reply("MSG_MATCH_COUNT", matches);
5206 reply("MSG_NO_MATCHES");
5212 static CHANSERV_FUNC(cmd_unvisited)
5214 struct chanData *cData;
5215 unsigned long interval = chanserv_conf.channel_expire_delay;
5216 char buffer[INTERVALLEN];
5217 unsigned int limit = 25, matches = 0;
5221 interval = ParseInterval(argv[1]);
5223 limit = atoi(argv[2]);
5226 intervalString(buffer, interval, user->handle_info);
5227 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5229 for(cData = channelList; cData && matches < limit; cData = cData->next)
5231 if((now - cData->visited) < interval)
5234 intervalString(buffer, now - cData->visited, user->handle_info);
5235 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5242 static MODCMD_FUNC(chan_opt_defaulttopic)
5248 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5250 reply("CSMSG_TOPIC_LOCKED", channel->name);
5254 topic = unsplit_string(argv+1, argc-1, NULL);
5256 free(channel->channel_info->topic);
5257 if(topic[0] == '*' && topic[1] == 0)
5259 topic = channel->channel_info->topic = NULL;
5263 topic = channel->channel_info->topic = strdup(topic);
5264 if(channel->channel_info->topic_mask
5265 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5266 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5268 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5271 if(channel->channel_info->topic)
5272 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5274 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5278 static MODCMD_FUNC(chan_opt_topicmask)
5282 struct chanData *cData = channel->channel_info;
5285 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5287 reply("CSMSG_TOPIC_LOCKED", channel->name);
5291 mask = unsplit_string(argv+1, argc-1, NULL);
5293 if(cData->topic_mask)
5294 free(cData->topic_mask);
5295 if(mask[0] == '*' && mask[1] == 0)
5297 cData->topic_mask = 0;
5301 cData->topic_mask = strdup(mask);
5303 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5304 else if(!match_ircglob(cData->topic, cData->topic_mask))
5305 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5309 if(channel->channel_info->topic_mask)
5310 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5312 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5316 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5320 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5324 if(greeting[0] == '*' && greeting[1] == 0)
5328 unsigned int length = strlen(greeting);
5329 if(length > chanserv_conf.greeting_length)
5331 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5334 *data = strdup(greeting);
5343 reply(name, user_find_message(user, "MSG_NONE"));
5347 static MODCMD_FUNC(chan_opt_greeting)
5349 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5352 static MODCMD_FUNC(chan_opt_usergreeting)
5354 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5357 static MODCMD_FUNC(chan_opt_modes)
5359 struct mod_chanmode *new_modes;
5364 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5366 reply("CSMSG_NO_ACCESS");
5369 if(argv[1][0] == '*' && argv[1][1] == 0)
5371 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5373 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5375 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5378 else if(new_modes->argc > 1)
5380 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5381 mod_chanmode_free(new_modes);
5386 channel->channel_info->modes = *new_modes;
5387 modcmd_chanmode_announce(new_modes);
5388 mod_chanmode_free(new_modes);
5392 mod_chanmode_format(&channel->channel_info->modes, modes);
5394 reply("CSMSG_SET_MODES", modes);
5396 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5400 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5402 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5404 struct chanData *cData = channel->channel_info;
5409 /* Set flag according to value. */
5410 if(enabled_string(argv[1]))
5412 cData->flags |= mask;
5415 else if(disabled_string(argv[1]))
5417 cData->flags &= ~mask;
5422 reply("MSG_INVALID_BINARY", argv[1]);
5428 /* Find current option value. */
5429 value = (cData->flags & mask) ? 1 : 0;
5433 reply(name, user_find_message(user, "MSG_ON"));
5435 reply(name, user_find_message(user, "MSG_OFF"));
5439 static MODCMD_FUNC(chan_opt_nodelete)
5441 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5443 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5447 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5450 static MODCMD_FUNC(chan_opt_dynlimit)
5452 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5455 static MODCMD_FUNC(chan_opt_offchannel)
5457 struct chanData *cData = channel->channel_info;
5462 /* Set flag according to value. */
5463 if(enabled_string(argv[1]))
5465 if(!IsOffChannel(cData))
5466 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5467 cData->flags |= CHANNEL_OFFCHANNEL;
5470 else if(disabled_string(argv[1]))
5472 if(IsOffChannel(cData))
5474 struct mod_chanmode change;
5475 mod_chanmode_init(&change);
5477 change.args[0].mode = MODE_CHANOP;
5478 change.args[0].u.member = AddChannelUser(chanserv, channel);
5479 mod_chanmode_announce(chanserv, channel, &change);
5481 cData->flags &= ~CHANNEL_OFFCHANNEL;
5486 reply("MSG_INVALID_BINARY", argv[1]);
5492 /* Find current option value. */
5493 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5497 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5499 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5503 static MODCMD_FUNC(chan_opt_unreviewed)
5505 struct chanData *cData = channel->channel_info;
5506 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5512 /* The two directions can have different ACLs. */
5513 if(enabled_string(argv[1]))
5515 else if(disabled_string(argv[1]))
5519 reply("MSG_INVALID_BINARY", argv[1]);
5523 if (new_value != value)
5525 struct svccmd *subcmd;
5526 char subcmd_name[32];
5528 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5529 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5532 reply("MSG_COMMAND_DISABLED", subcmd_name);
5535 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5539 cData->flags |= CHANNEL_UNREVIEWED;
5542 free(cData->registrar);
5543 cData->registrar = strdup(user->handle_info->handle);
5544 cData->flags &= ~CHANNEL_UNREVIEWED;
5551 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5553 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5557 static MODCMD_FUNC(chan_opt_defaults)
5559 struct userData *uData;
5560 struct chanData *cData;
5561 const char *confirm;
5562 enum levelOption lvlOpt;
5563 enum charOption chOpt;
5565 cData = channel->channel_info;
5566 uData = GetChannelUser(cData, user->handle_info);
5567 if(!uData || (uData->access < UL_OWNER))
5569 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5572 confirm = make_confirmation_string(uData);
5573 if((argc < 2) || strcmp(argv[1], confirm))
5575 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5578 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5579 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5580 cData->modes = chanserv_conf.default_modes;
5581 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5582 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5583 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5584 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5585 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5590 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5592 struct chanData *cData = channel->channel_info;
5593 struct userData *uData;
5594 unsigned short value;
5598 if(!check_user_level(channel, user, option, 1, 1))
5600 reply("CSMSG_CANNOT_SET");
5603 value = user_level_from_name(argv[1], UL_OWNER+1);
5604 if(!value && strcmp(argv[1], "0"))
5606 reply("CSMSG_INVALID_ACCESS", argv[1]);
5609 uData = GetChannelUser(cData, user->handle_info);
5610 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5612 reply("CSMSG_BAD_SETLEVEL");
5618 if(value > cData->lvlOpts[lvlGiveOps])
5620 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5625 if(value < cData->lvlOpts[lvlGiveVoice])
5627 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5632 /* This test only applies to owners, since non-owners
5633 * trying to set an option to above their level get caught
5634 * by the CSMSG_BAD_SETLEVEL test above.
5636 if(value > uData->access)
5638 reply("CSMSG_BAD_SETTERS");
5645 cData->lvlOpts[option] = value;
5647 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5651 static MODCMD_FUNC(chan_opt_enfops)
5653 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5656 static MODCMD_FUNC(chan_opt_giveops)
5658 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5661 static MODCMD_FUNC(chan_opt_enfmodes)
5663 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5666 static MODCMD_FUNC(chan_opt_enftopic)
5668 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5671 static MODCMD_FUNC(chan_opt_pubcmd)
5673 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5676 static MODCMD_FUNC(chan_opt_setters)
5678 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5681 static MODCMD_FUNC(chan_opt_ctcpusers)
5683 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5686 static MODCMD_FUNC(chan_opt_userinfo)
5688 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5691 static MODCMD_FUNC(chan_opt_givevoice)
5693 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5696 static MODCMD_FUNC(chan_opt_topicsnarf)
5698 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5701 static MODCMD_FUNC(chan_opt_inviteme)
5703 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5707 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5709 struct chanData *cData = channel->channel_info;
5710 int count = charOptions[option].count, idx;
5714 idx = atoi(argv[1]);
5716 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5718 reply("CSMSG_INVALID_NUMERIC", idx);
5719 /* Show possible values. */
5720 for(idx = 0; idx < count; idx++)
5721 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5725 cData->chOpts[option] = charOptions[option].values[idx].value;
5729 /* Find current option value. */
5732 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5736 /* Somehow, the option value is corrupt; reset it to the default. */
5737 cData->chOpts[option] = charOptions[option].default_value;
5742 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5746 static MODCMD_FUNC(chan_opt_protect)
5748 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5751 static MODCMD_FUNC(chan_opt_toys)
5753 return channel_multiple_option(chToys, CSFUNC_ARGS);
5756 static MODCMD_FUNC(chan_opt_ctcpreaction)
5758 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5761 static MODCMD_FUNC(chan_opt_topicrefresh)
5763 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5766 static struct svccmd_list set_shows_list;
5769 handle_svccmd_unbind(struct svccmd *target) {
5771 for(ii=0; ii<set_shows_list.used; ++ii)
5772 if(target == set_shows_list.list[ii])
5773 set_shows_list.used = 0;
5776 static CHANSERV_FUNC(cmd_set)
5778 struct svccmd *subcmd;
5782 /* Check if we need to (re-)initialize set_shows_list. */
5783 if(!set_shows_list.used)
5785 if(!set_shows_list.size)
5787 set_shows_list.size = chanserv_conf.set_shows->used;
5788 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5790 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5792 const char *name = chanserv_conf.set_shows->list[ii];
5793 sprintf(buf, "%s %s", argv[0], name);
5794 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5797 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5800 svccmd_list_append(&set_shows_list, subcmd);
5806 reply("CSMSG_CHANNEL_OPTIONS");
5807 for(ii = 0; ii < set_shows_list.used; ii++)
5809 subcmd = set_shows_list.list[ii];
5810 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5815 sprintf(buf, "%s %s", argv[0], argv[1]);
5816 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5819 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5822 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5824 reply("CSMSG_NO_ACCESS");
5830 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5834 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5836 struct userData *uData;
5838 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5841 reply("CSMSG_NOT_USER", channel->name);
5847 /* Just show current option value. */
5849 else if(enabled_string(argv[1]))
5851 uData->flags |= mask;
5853 else if(disabled_string(argv[1]))
5855 uData->flags &= ~mask;
5859 reply("MSG_INVALID_BINARY", argv[1]);
5863 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5867 static MODCMD_FUNC(user_opt_noautoop)
5869 struct userData *uData;
5871 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5874 reply("CSMSG_NOT_USER", channel->name);
5877 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5878 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5880 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5883 static MODCMD_FUNC(user_opt_autoinvite)
5885 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5887 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5889 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5892 static MODCMD_FUNC(user_opt_info)
5894 struct userData *uData;
5897 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5901 /* If they got past the command restrictions (which require access)
5902 * but fail this test, we have some fool with security override on.
5904 reply("CSMSG_NOT_USER", channel->name);
5911 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5912 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5914 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5917 bp = strcspn(infoline, "\001");
5920 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5925 if(infoline[0] == '*' && infoline[1] == 0)
5928 uData->info = strdup(infoline);
5931 reply("CSMSG_USET_INFO", uData->info);
5933 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5937 struct svccmd_list uset_shows_list;
5939 static CHANSERV_FUNC(cmd_uset)
5941 struct svccmd *subcmd;
5945 /* Check if we need to (re-)initialize uset_shows_list. */
5946 if(!uset_shows_list.used)
5950 "NoAutoOp", "AutoInvite", "Info"
5953 if(!uset_shows_list.size)
5955 uset_shows_list.size = ArrayLength(options);
5956 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5958 for(ii = 0; ii < ArrayLength(options); ii++)
5960 const char *name = options[ii];
5961 sprintf(buf, "%s %s", argv[0], name);
5962 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5965 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5968 svccmd_list_append(&uset_shows_list, subcmd);
5974 /* Do this so options are presented in a consistent order. */
5975 reply("CSMSG_USER_OPTIONS");
5976 for(ii = 0; ii < uset_shows_list.used; ii++)
5977 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5981 sprintf(buf, "%s %s", argv[0], argv[1]);
5982 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5985 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5989 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5992 static CHANSERV_FUNC(cmd_giveownership)
5994 struct handle_info *new_owner_hi;
5995 struct userData *new_owner;
5996 struct userData *curr_user;
5997 struct userData *invoker;
5998 struct chanData *cData = channel->channel_info;
5999 struct do_not_register *dnr;
6000 const char *confirm;
6002 unsigned short co_access;
6003 char reason[MAXLEN];
6006 curr_user = GetChannelAccess(cData, user->handle_info);
6007 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6008 if(!curr_user || (curr_user->access != UL_OWNER))
6010 struct userData *owner = NULL;
6011 for(curr_user = channel->channel_info->users;
6013 curr_user = curr_user->next)
6015 if(curr_user->access != UL_OWNER)
6019 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6026 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6028 char delay[INTERVALLEN];
6029 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6030 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6033 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6035 if(new_owner_hi == user->handle_info)
6037 reply("CSMSG_NO_TRANSFER_SELF");
6040 new_owner = GetChannelAccess(cData, new_owner_hi);
6045 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6049 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6053 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6055 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6058 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6059 if(!IsHelping(user))
6060 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6062 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6065 invoker = GetChannelUser(cData, user->handle_info);
6066 if(invoker->access <= UL_OWNER)
6068 confirm = make_confirmation_string(curr_user);
6069 if((argc < 3) || strcmp(argv[2], confirm))
6071 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6075 if(new_owner->access >= UL_COOWNER)
6076 co_access = new_owner->access;
6078 co_access = UL_COOWNER;
6079 new_owner->access = UL_OWNER;
6081 curr_user->access = co_access;
6082 cData->ownerTransfer = now;
6083 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6084 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6085 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6089 static CHANSERV_FUNC(cmd_suspend)
6091 struct handle_info *hi;
6092 struct userData *actor, *real_actor, *target;
6093 unsigned int override = 0;
6096 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6097 actor = GetChannelUser(channel->channel_info, user->handle_info);
6098 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6099 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6101 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6104 if(target->access >= actor->access)
6106 reply("MSG_USER_OUTRANKED", hi->handle);
6109 if(target->flags & USER_SUSPENDED)
6111 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6116 target->present = 0;
6119 if(!real_actor || target->access >= real_actor->access)
6120 override = CMD_LOG_OVERRIDE;
6121 target->flags |= USER_SUSPENDED;
6122 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6123 return 1 | override;
6126 static CHANSERV_FUNC(cmd_unsuspend)
6128 struct handle_info *hi;
6129 struct userData *actor, *real_actor, *target;
6130 unsigned int override = 0;
6133 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6134 actor = GetChannelUser(channel->channel_info, user->handle_info);
6135 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6136 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6138 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6141 if(target->access >= actor->access)
6143 reply("MSG_USER_OUTRANKED", hi->handle);
6146 if(!(target->flags & USER_SUSPENDED))
6148 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6151 if(!real_actor || target->access >= real_actor->access)
6152 override = CMD_LOG_OVERRIDE;
6153 target->flags &= ~USER_SUSPENDED;
6154 scan_user_presence(target, NULL);
6155 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6156 return 1 | override;
6159 static MODCMD_FUNC(cmd_deleteme)
6161 struct handle_info *hi;
6162 struct userData *target;
6163 const char *confirm_string;
6164 unsigned short access_level;
6167 hi = user->handle_info;
6168 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6170 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6173 if(target->access == UL_OWNER)
6175 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6178 confirm_string = make_confirmation_string(target);
6179 if((argc < 2) || strcmp(argv[1], confirm_string))
6181 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6184 access_level = target->access;
6185 channel_name = strdup(channel->name);
6186 del_channel_user(target, 1);
6187 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6193 chanserv_refresh_topics(UNUSED_ARG(void *data))
6195 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6196 struct chanData *cData;
6199 for(cData = channelList; cData; cData = cData->next)
6201 if(IsSuspended(cData))
6203 opt = cData->chOpts[chTopicRefresh];
6206 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6209 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6210 cData->last_refresh = refresh_num;
6212 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6215 static CHANSERV_FUNC(cmd_unf)
6219 char response[MAXLEN];
6220 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6221 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6222 irc_privmsg(cmd->parent->bot, channel->name, response);
6225 reply("CSMSG_UNF_RESPONSE");
6229 static CHANSERV_FUNC(cmd_ping)
6233 char response[MAXLEN];
6234 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6235 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6236 irc_privmsg(cmd->parent->bot, channel->name, response);
6239 reply("CSMSG_PING_RESPONSE");
6243 static CHANSERV_FUNC(cmd_wut)
6247 char response[MAXLEN];
6248 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6249 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6250 irc_privmsg(cmd->parent->bot, channel->name, response);
6253 reply("CSMSG_WUT_RESPONSE");
6257 static CHANSERV_FUNC(cmd_8ball)
6259 unsigned int i, j, accum;
6264 for(i=1; i<argc; i++)
6265 for(j=0; argv[i][j]; j++)
6266 accum = (accum << 5) - accum + toupper(argv[i][j]);
6267 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6270 char response[MAXLEN];
6271 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6272 irc_privmsg(cmd->parent->bot, channel->name, response);
6275 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6279 static CHANSERV_FUNC(cmd_d)
6281 unsigned long sides, count, modifier, ii, total;
6282 char response[MAXLEN], *sep;
6286 if((count = strtoul(argv[1], &sep, 10)) < 1)
6296 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6297 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6301 else if((sep[0] == '-') && isdigit(sep[1]))
6302 modifier = strtoul(sep, NULL, 10);
6303 else if((sep[0] == '+') && isdigit(sep[1]))
6304 modifier = strtoul(sep+1, NULL, 10);
6311 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6316 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6319 for(total = ii = 0; ii < count; ++ii)
6320 total += (rand() % sides) + 1;
6323 if((count > 1) || modifier)
6325 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6326 sprintf(response, fmt, total, count, sides, modifier);
6330 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6331 sprintf(response, fmt, total, sides);
6334 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6336 send_message_type(4, user, cmd->parent->bot, "%s", response);
6340 static CHANSERV_FUNC(cmd_huggle)
6342 /* CTCP must be via PRIVMSG, never notice */
6344 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6346 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6351 chanserv_adjust_limit(void *data)
6353 struct mod_chanmode change;
6354 struct chanData *cData = data;
6355 struct chanNode *channel = cData->channel;
6358 if(IsSuspended(cData))
6361 cData->limitAdjusted = now;
6362 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6363 if(cData->modes.modes_set & MODE_LIMIT)
6365 if(limit > cData->modes.new_limit)
6366 limit = cData->modes.new_limit;
6367 else if(limit == cData->modes.new_limit)
6371 mod_chanmode_init(&change);
6372 change.modes_set = MODE_LIMIT;
6373 change.new_limit = limit;
6374 mod_chanmode_announce(chanserv, channel, &change);
6378 handle_new_channel(struct chanNode *channel)
6380 struct chanData *cData;
6382 if(!(cData = channel->channel_info))
6385 if(cData->modes.modes_set || cData->modes.modes_clear)
6386 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6388 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6389 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6392 /* Welcome to my worst nightmare. Warning: Read (or modify)
6393 the code below at your own risk. */
6395 handle_join(struct modeNode *mNode)
6397 struct mod_chanmode change;
6398 struct userNode *user = mNode->user;
6399 struct chanNode *channel = mNode->channel;
6400 struct chanData *cData;
6401 struct userData *uData = NULL;
6402 struct banData *bData;
6403 struct handle_info *handle;
6404 unsigned int modes = 0, info = 0;
6407 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6410 cData = channel->channel_info;
6411 if(channel->members.used > cData->max)
6412 cData->max = channel->members.used;
6414 /* Check for bans. If they're joining through a ban, one of two
6416 * 1: Join during a netburst, by riding the break. Kick them
6417 * unless they have ops or voice in the channel.
6418 * 2: They're allowed to join through the ban (an invite in
6419 * ircu2.10, or a +e on Hybrid, or something).
6420 * If they're not joining through a ban, and the banlist is not
6421 * full, see if they're on the banlist for the channel. If so,
6424 if(user->uplink->burst && !mNode->modes)
6427 for(ii = 0; ii < channel->banlist.used; ii++)
6429 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6431 /* Riding a netburst. Naughty. */
6432 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6438 mod_chanmode_init(&change);
6440 if(channel->banlist.used < MAXBANS)
6442 /* Not joining through a ban. */
6443 for(bData = cData->bans;
6444 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6445 bData = bData->next);
6449 char kick_reason[MAXLEN];
6450 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6452 bData->triggered = now;
6453 if(bData != cData->bans)
6455 /* Shuffle the ban to the head of the list. */
6457 bData->next->prev = bData->prev;
6459 bData->prev->next = bData->next;
6462 bData->next = cData->bans;
6465 cData->bans->prev = bData;
6466 cData->bans = bData;
6469 change.args[0].mode = MODE_BAN;
6470 change.args[0].u.hostmask = bData->mask;
6471 mod_chanmode_announce(chanserv, channel, &change);
6472 KickChannelUser(user, channel, chanserv, kick_reason);
6477 /* ChanServ will not modify the limits in join-flooded channels,
6478 or when there are enough slots left below the limit. */
6479 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6480 && !channel->join_flooded
6481 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6483 /* The user count has begun "bumping" into the channel limit,
6484 so set a timer to raise the limit a bit. Any previous
6485 timers are removed so three incoming users within the delay
6486 results in one limit change, not three. */
6488 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6489 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6492 if(channel->join_flooded)
6494 /* don't automatically give ops or voice during a join flood */
6496 else if(cData->lvlOpts[lvlGiveOps] == 0)
6497 modes |= MODE_CHANOP;
6498 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6499 modes |= MODE_VOICE;
6501 greeting = cData->greeting;
6502 if(user->handle_info)
6504 handle = user->handle_info;
6506 if(IsHelper(user) && !IsHelping(user))
6509 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6511 if(channel == chanserv_conf.support_channels.list[ii])
6513 HANDLE_SET_FLAG(user->handle_info, HELPING);
6519 uData = GetTrueChannelAccess(cData, handle);
6520 if(uData && !IsUserSuspended(uData))
6522 /* Ops and above were handled by the above case. */
6523 if(IsUserAutoOp(uData))
6525 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6526 modes |= MODE_CHANOP;
6527 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6528 modes |= MODE_VOICE;
6530 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6531 cData->visited = now;
6532 if(cData->user_greeting)
6533 greeting = cData->user_greeting;
6535 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6536 && ((now - uData->seen) >= chanserv_conf.info_delay)
6544 /* If user joining normally (not during burst), apply op or voice,
6545 * and send greeting/userinfo as appropriate.
6547 if(!user->uplink->burst)
6551 if(modes & MODE_CHANOP)
6552 modes &= ~MODE_VOICE;
6553 change.args[0].mode = modes;
6554 change.args[0].u.member = mNode;
6555 mod_chanmode_announce(chanserv, channel, &change);
6558 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6559 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6560 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6566 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6568 struct mod_chanmode change;
6569 struct userData *channel;
6570 unsigned int ii, jj;
6572 if(!user->handle_info)
6575 mod_chanmode_init(&change);
6577 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6579 struct chanNode *cn;
6580 struct modeNode *mn;
6581 if(IsUserSuspended(channel)
6582 || IsSuspended(channel->channel)
6583 || !(cn = channel->channel->channel))
6586 mn = GetUserMode(cn, user);
6589 if(!IsUserSuspended(channel)
6590 && IsUserAutoInvite(channel)
6591 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6593 && !user->uplink->burst)
6594 irc_invite(chanserv, user, cn);
6598 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6599 channel->channel->visited = now;
6601 if(IsUserAutoOp(channel))
6603 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6604 change.args[0].mode = MODE_CHANOP;
6605 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6606 change.args[0].mode = MODE_VOICE;
6608 change.args[0].mode = 0;
6609 change.args[0].u.member = mn;
6610 if(change.args[0].mode)
6611 mod_chanmode_announce(chanserv, cn, &change);
6614 channel->seen = now;
6615 channel->present = 1;
6618 for(ii = 0; ii < user->channels.used; ++ii)
6620 struct chanNode *chan = user->channels.list[ii]->channel;
6621 struct banData *ban;
6623 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6624 || !chan->channel_info
6625 || IsSuspended(chan->channel_info))
6627 for(jj = 0; jj < chan->banlist.used; ++jj)
6628 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6630 if(jj < chan->banlist.used)
6632 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6634 char kick_reason[MAXLEN];
6635 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6637 change.args[0].mode = MODE_BAN;
6638 change.args[0].u.hostmask = ban->mask;
6639 mod_chanmode_announce(chanserv, chan, &change);
6640 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6641 KickChannelUser(user, chan, chanserv, kick_reason);
6642 ban->triggered = now;
6647 if(IsSupportHelper(user))
6649 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6651 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6653 HANDLE_SET_FLAG(user->handle_info, HELPING);
6661 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6663 struct chanData *cData;
6664 struct userData *uData;
6666 cData = mn->channel->channel_info;
6667 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6670 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6672 /* Allow for a bit of padding so that the limit doesn't
6673 track the user count exactly, which could get annoying. */
6674 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6676 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6677 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6681 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6683 scan_user_presence(uData, mn->user);
6685 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6686 cData->visited = now;
6689 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6692 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6693 struct chanNode *channel;
6694 struct userNode *exclude;
6695 /* When looking at the channel that is being /part'ed, we
6696 * have to skip over the client that is leaving. For
6697 * other channels, we must not do that.
6699 channel = chanserv_conf.support_channels.list[ii];
6700 exclude = (channel == mn->channel) ? mn->user : NULL;
6701 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6704 if(ii == chanserv_conf.support_channels.used)
6705 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6710 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6712 struct userData *uData;
6714 if(!channel->channel_info || !kicker || IsService(kicker)
6715 || (kicker == victim) || IsSuspended(channel->channel_info)
6716 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6719 if(protect_user(victim, kicker, channel->channel_info))
6721 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6722 KickChannelUser(kicker, channel, chanserv, reason);
6725 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6730 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6732 struct chanData *cData;
6734 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6737 cData = channel->channel_info;
6738 if(bad_topic(channel, user, channel->topic))
6740 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6741 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6742 SetChannelTopic(channel, chanserv, old_topic, 1);
6743 else if(cData->topic)
6744 SetChannelTopic(channel, chanserv, cData->topic, 1);
6747 /* With topicsnarf, grab the topic and save it as the default topic. */
6748 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6751 cData->topic = strdup(channel->topic);
6757 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6759 struct mod_chanmode *bounce = NULL;
6760 unsigned int bnc, ii;
6763 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6766 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6767 && mode_lock_violated(&channel->channel_info->modes, change))
6769 char correct[MAXLEN];
6770 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6771 mod_chanmode_format(&channel->channel_info->modes, correct);
6772 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6774 for(ii = bnc = 0; ii < change->argc; ++ii)
6776 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6778 const struct userNode *victim = change->args[ii].u.member->user;
6779 if(!protect_user(victim, user, channel->channel_info))
6782 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6785 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6786 bounce->args[bnc].u.member = GetUserMode(channel, user);
6787 if(bounce->args[bnc].u.member)
6791 bounce->args[bnc].mode = MODE_CHANOP;
6792 bounce->args[bnc].u.member = change->args[ii].u.member;
6794 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6796 else if(change->args[ii].mode & MODE_CHANOP)
6798 const struct userNode *victim = change->args[ii].u.member->user;
6799 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6802 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6803 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6804 bounce->args[bnc].u.member = change->args[ii].u.member;
6807 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6809 const char *ban = change->args[ii].u.hostmask;
6810 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6813 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6814 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6815 bounce->args[bnc].u.hostmask = strdup(ban);
6817 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6822 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6823 mod_chanmode_announce(chanserv, channel, bounce);
6824 for(ii = 0; ii < change->argc; ++ii)
6825 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6826 free((char*)bounce->args[ii].u.hostmask);
6827 mod_chanmode_free(bounce);
6832 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6834 struct chanNode *channel;
6835 struct banData *bData;
6836 struct mod_chanmode change;
6837 unsigned int ii, jj;
6838 char kick_reason[MAXLEN];
6840 mod_chanmode_init(&change);
6842 change.args[0].mode = MODE_BAN;
6843 for(ii = 0; ii < user->channels.used; ++ii)
6845 channel = user->channels.list[ii]->channel;
6846 /* Need not check for bans if they're opped or voiced. */
6847 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6849 /* Need not check for bans unless channel registration is active. */
6850 if(!channel->channel_info || IsSuspended(channel->channel_info))
6852 /* Look for a matching ban already on the channel. */
6853 for(jj = 0; jj < channel->banlist.used; ++jj)
6854 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6856 /* Need not act if we found one. */
6857 if(jj < channel->banlist.used)
6859 /* Look for a matching ban in this channel. */
6860 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6862 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6864 change.args[0].u.hostmask = bData->mask;
6865 mod_chanmode_announce(chanserv, channel, &change);
6866 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6867 KickChannelUser(user, channel, chanserv, kick_reason);
6868 bData->triggered = now;
6869 break; /* we don't need to check any more bans in the channel */
6874 static void handle_rename(struct handle_info *handle, const char *old_handle)
6876 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6880 dict_remove2(handle_dnrs, old_handle, 1);
6881 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6882 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6887 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6889 struct userNode *h_user;
6891 if(handle->channels)
6893 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6894 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6896 while(handle->channels)
6897 del_channel_user(handle->channels, 1);
6902 handle_server_link(UNUSED_ARG(struct server *server))
6904 struct chanData *cData;
6906 for(cData = channelList; cData; cData = cData->next)
6908 if(!IsSuspended(cData))
6909 cData->may_opchan = 1;
6910 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6911 && !cData->channel->join_flooded
6912 && ((cData->channel->limit - cData->channel->members.used)
6913 < chanserv_conf.adjust_threshold))
6915 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6916 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6922 chanserv_conf_read(void)
6926 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6927 struct mod_chanmode *change;
6928 struct string_list *strlist;
6929 struct chanNode *chan;
6932 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6934 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6937 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6938 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6939 chanserv_conf.support_channels.used = 0;
6940 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6942 for(ii = 0; ii < strlist->used; ++ii)
6944 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6947 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6949 channelList_append(&chanserv_conf.support_channels, chan);
6952 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6955 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6958 chan = AddChannel(str, now, str2, NULL);
6960 channelList_append(&chanserv_conf.support_channels, chan);
6962 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6963 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6964 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6965 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6966 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6967 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6968 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6969 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6970 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6971 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6972 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6973 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6974 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6975 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6976 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6977 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6978 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6979 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6980 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6981 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6982 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6983 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6984 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6985 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6986 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6988 NickChange(chanserv, str, 0);
6989 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6990 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6991 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6992 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6993 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6994 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6995 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6996 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6997 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
6998 chanserv_conf.max_owned = str ? atoi(str) : 5;
6999 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7000 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7001 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7002 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7003 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7004 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7005 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7008 safestrncpy(mode_line, str, sizeof(mode_line));
7009 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7010 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7011 && (change->argc < 2))
7013 chanserv_conf.default_modes = *change;
7014 mod_chanmode_free(change);
7016 free_string_list(chanserv_conf.set_shows);
7017 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7019 strlist = string_list_copy(strlist);
7022 static const char *list[] = {
7023 /* free form text */
7024 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7025 /* options based on user level */
7026 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7027 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7028 /* multiple choice options */
7029 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7030 /* binary options */
7031 "DynLimit", "NoDelete",
7035 strlist = alloc_string_list(ArrayLength(list)-1);
7036 for(ii=0; list[ii]; ii++)
7037 string_list_append(strlist, strdup(list[ii]));
7039 chanserv_conf.set_shows = strlist;
7040 /* We don't look things up now, in case the list refers to options
7041 * defined by modules initialized after this point. Just mark the
7042 * function list as invalid, so it will be initialized.
7044 set_shows_list.used = 0;
7045 free_string_list(chanserv_conf.eightball);
7046 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7049 strlist = string_list_copy(strlist);
7053 strlist = alloc_string_list(4);
7054 string_list_append(strlist, strdup("Yes."));
7055 string_list_append(strlist, strdup("No."));
7056 string_list_append(strlist, strdup("Maybe so."));
7058 chanserv_conf.eightball = strlist;
7059 free_string_list(chanserv_conf.old_ban_names);
7060 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7062 strlist = string_list_copy(strlist);
7064 strlist = alloc_string_list(2);
7065 chanserv_conf.old_ban_names = strlist;
7066 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7067 off_channel = str ? atoi(str) : 0;
7071 chanserv_note_type_read(const char *key, struct record_data *rd)
7074 struct note_type *ntype;
7077 if(!(obj = GET_RECORD_OBJECT(rd)))
7079 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7082 if(!(ntype = chanserv_create_note_type(key)))
7084 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7088 /* Figure out set access */
7089 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7091 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7092 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7094 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7096 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7097 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7099 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7101 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7105 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7106 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7107 ntype->set_access.min_opserv = 0;
7110 /* Figure out visibility */
7111 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7112 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7113 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7114 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7115 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7116 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7117 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7118 ntype->visible_type = NOTE_VIS_ALL;
7120 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7122 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7123 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7127 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7129 struct handle_info *handle;
7130 struct userData *uData;
7131 char *seen, *inf, *flags;
7132 unsigned long last_seen;
7133 unsigned short access_level;
7135 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7137 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7141 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7142 if(access_level > UL_OWNER)
7144 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7148 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7149 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7150 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7151 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7152 handle = get_handle_info(key);
7155 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7159 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7160 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7164 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7166 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7167 unsigned long set_time, triggered_time, expires_time;
7169 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7171 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7175 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7176 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7177 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7178 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7179 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7180 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7181 if (!reason || !owner)
7184 set_time = set ? strtoul(set, NULL, 0) : now;
7185 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7187 expires_time = strtoul(s_expires, NULL, 0);
7189 expires_time = set_time + atoi(s_duration);
7193 if(!reason || (expires_time && (expires_time < now)))
7196 add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7199 static struct suspended *
7200 chanserv_read_suspended(dict_t obj)
7202 struct suspended *suspended = calloc(1, sizeof(*suspended));
7206 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7207 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7208 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7209 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7210 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7211 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7212 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7213 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7214 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7215 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7220 chanserv_channel_read(const char *key, struct record_data *hir)
7222 struct suspended *suspended;
7223 struct mod_chanmode *modes;
7224 struct chanNode *cNode;
7225 struct chanData *cData;
7226 struct dict *channel, *obj;
7227 char *str, *argv[10];
7231 channel = hir->d.object;
7233 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7236 cNode = AddChannel(key, now, NULL, NULL);
7239 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7242 cData = register_channel(cNode, str);
7245 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7249 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7251 enum levelOption lvlOpt;
7252 enum charOption chOpt;
7254 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7255 cData->flags = atoi(str);
7257 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7259 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7261 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7262 else if(levelOptions[lvlOpt].old_flag)
7264 if(cData->flags & levelOptions[lvlOpt].old_flag)
7265 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7267 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7271 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7273 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7275 cData->chOpts[chOpt] = str[0];
7278 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7280 enum levelOption lvlOpt;
7281 enum charOption chOpt;
7284 cData->flags = base64toint(str, 5);
7285 count = strlen(str += 5);
7286 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7289 if(levelOptions[lvlOpt].old_flag)
7291 if(cData->flags & levelOptions[lvlOpt].old_flag)
7292 lvl = levelOptions[lvlOpt].flag_value;
7294 lvl = levelOptions[lvlOpt].default_value;
7296 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7298 case 'c': lvl = UL_COOWNER; break;
7299 case 'm': lvl = UL_MASTER; break;
7300 case 'n': lvl = UL_OWNER+1; break;
7301 case 'o': lvl = UL_OP; break;
7302 case 'p': lvl = UL_PEON; break;
7303 case 'w': lvl = UL_OWNER; break;
7304 default: lvl = 0; break;
7306 cData->lvlOpts[lvlOpt] = lvl;
7308 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7309 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7312 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7314 suspended = chanserv_read_suspended(obj);
7315 cData->suspended = suspended;
7316 suspended->cData = cData;
7317 /* We could use suspended->expires and suspended->revoked to
7318 * set the CHANNEL_SUSPENDED flag, but we don't. */
7320 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7322 suspended = calloc(1, sizeof(*suspended));
7323 suspended->issued = 0;
7324 suspended->revoked = 0;
7325 suspended->suspender = strdup(str);
7326 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7327 suspended->expires = str ? atoi(str) : 0;
7328 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7329 suspended->reason = strdup(str ? str : "No reason");
7330 suspended->previous = NULL;
7331 cData->suspended = suspended;
7332 suspended->cData = cData;
7336 cData->flags &= ~CHANNEL_SUSPENDED;
7337 suspended = NULL; /* to squelch a warning */
7340 if(IsSuspended(cData)) {
7341 if(suspended->expires > now)
7342 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7343 else if(suspended->expires)
7344 cData->flags &= ~CHANNEL_SUSPENDED;
7347 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7348 struct mod_chanmode change;
7349 mod_chanmode_init(&change);
7351 change.args[0].mode = MODE_CHANOP;
7352 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7353 mod_chanmode_announce(chanserv, cNode, &change);
7356 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7357 cData->registered = str ? strtoul(str, NULL, 0) : now;
7358 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7359 cData->visited = str ? strtoul(str, NULL, 0) : now;
7360 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7361 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7362 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7363 cData->max = str ? atoi(str) : 0;
7364 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7365 cData->greeting = str ? strdup(str) : NULL;
7366 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7367 cData->user_greeting = str ? strdup(str) : NULL;
7368 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7369 cData->topic_mask = str ? strdup(str) : NULL;
7370 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7371 cData->topic = str ? strdup(str) : NULL;
7373 if(!IsSuspended(cData)
7374 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7375 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7376 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7377 cData->modes = *modes;
7379 cData->modes.modes_set |= MODE_REGISTERED;
7380 if(cData->modes.argc > 1)
7381 cData->modes.argc = 1;
7382 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7383 mod_chanmode_free(modes);
7386 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7387 for(it = dict_first(obj); it; it = iter_next(it))
7388 user_read_helper(iter_key(it), iter_data(it), cData);
7390 if(!cData->users && !IsProtected(cData))
7392 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7393 unregister_channel(cData, "has empty user list.");
7397 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7398 for(it = dict_first(obj); it; it = iter_next(it))
7399 ban_read_helper(iter_key(it), iter_data(it), cData);
7401 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7402 for(it = dict_first(obj); it; it = iter_next(it))
7404 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7405 struct record_data *rd = iter_data(it);
7406 const char *note, *setter;
7408 if(rd->type != RECDB_OBJECT)
7410 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7414 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7416 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7418 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7422 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7423 if(!setter) setter = "<unknown>";
7424 chanserv_add_channel_note(cData, ntype, setter, note);
7432 chanserv_dnr_read(const char *key, struct record_data *hir)
7434 const char *setter, *reason, *str;
7435 struct do_not_register *dnr;
7436 unsigned long expiry;
7438 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7441 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7444 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7447 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7450 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7451 expiry = str ? strtoul(str, NULL, 0) : 0;
7452 if(expiry && expiry <= now)
7454 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7457 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7459 dnr->set = atoi(str);
7465 chanserv_saxdb_read(struct dict *database)
7467 struct dict *section;
7470 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7471 for(it = dict_first(section); it; it = iter_next(it))
7472 chanserv_note_type_read(iter_key(it), iter_data(it));
7474 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7475 for(it = dict_first(section); it; it = iter_next(it))
7476 chanserv_channel_read(iter_key(it), iter_data(it));
7478 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7479 for(it = dict_first(section); it; it = iter_next(it))
7480 chanserv_dnr_read(iter_key(it), iter_data(it));
7486 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7488 int high_present = 0;
7489 saxdb_start_record(ctx, KEY_USERS, 1);
7490 for(; uData; uData = uData->next)
7492 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7494 saxdb_start_record(ctx, uData->handle->handle, 0);
7495 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7496 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7498 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7500 saxdb_write_string(ctx, KEY_INFO, uData->info);
7501 saxdb_end_record(ctx);
7503 saxdb_end_record(ctx);
7504 return high_present;
7508 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7512 saxdb_start_record(ctx, KEY_BANS, 1);
7513 for(; bData; bData = bData->next)
7515 saxdb_start_record(ctx, bData->mask, 0);
7516 saxdb_write_int(ctx, KEY_SET, bData->set);
7517 if(bData->triggered)
7518 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7520 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7522 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7524 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7525 saxdb_end_record(ctx);
7527 saxdb_end_record(ctx);
7531 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7533 saxdb_start_record(ctx, name, 0);
7534 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7535 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7537 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7539 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7541 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7543 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7544 saxdb_end_record(ctx);
7548 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7552 enum levelOption lvlOpt;
7553 enum charOption chOpt;
7555 saxdb_start_record(ctx, channel->channel->name, 1);
7557 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7558 saxdb_write_int(ctx, KEY_MAX, channel->max);
7560 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7561 if(channel->registrar)
7562 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7563 if(channel->greeting)
7564 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7565 if(channel->user_greeting)
7566 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7567 if(channel->topic_mask)
7568 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7569 if(channel->suspended)
7570 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7572 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7573 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7574 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7575 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7576 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7578 buf[0] = channel->chOpts[chOpt];
7580 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7582 saxdb_end_record(ctx);
7584 if(channel->modes.modes_set || channel->modes.modes_clear)
7586 mod_chanmode_format(&channel->modes, buf);
7587 saxdb_write_string(ctx, KEY_MODES, buf);
7590 high_present = chanserv_write_users(ctx, channel->users);
7591 chanserv_write_bans(ctx, channel->bans);
7593 if(dict_size(channel->notes))
7597 saxdb_start_record(ctx, KEY_NOTES, 1);
7598 for(it = dict_first(channel->notes); it; it = iter_next(it))
7600 struct note *note = iter_data(it);
7601 saxdb_start_record(ctx, iter_key(it), 0);
7602 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7603 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7604 saxdb_end_record(ctx);
7606 saxdb_end_record(ctx);
7609 if(channel->ownerTransfer)
7610 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7611 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7612 saxdb_end_record(ctx);
7616 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7620 saxdb_start_record(ctx, ntype->name, 0);
7621 switch(ntype->set_access_type)
7623 case NOTE_SET_CHANNEL_ACCESS:
7624 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7626 case NOTE_SET_CHANNEL_SETTER:
7627 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7629 case NOTE_SET_PRIVILEGED: default:
7630 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7633 switch(ntype->visible_type)
7635 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7636 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7637 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7639 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7640 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7641 saxdb_end_record(ctx);
7645 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7647 struct do_not_register *dnr;
7648 dict_iterator_t it, next;
7650 for(it = dict_first(dnrs); it; it = next)
7652 next = iter_next(it);
7653 dnr = iter_data(it);
7654 if(dnr->expires && dnr->expires <= now)
7656 dict_remove(dnrs, iter_key(it));
7659 saxdb_start_record(ctx, dnr->chan_name, 0);
7661 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7663 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7664 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7665 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7666 saxdb_end_record(ctx);
7671 chanserv_saxdb_write(struct saxdb_context *ctx)
7674 struct chanData *channel;
7677 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7678 for(it = dict_first(note_types); it; it = iter_next(it))
7679 chanserv_write_note_type(ctx, iter_data(it));
7680 saxdb_end_record(ctx);
7683 saxdb_start_record(ctx, KEY_DNR, 1);
7684 write_dnrs_helper(ctx, handle_dnrs);
7685 write_dnrs_helper(ctx, plain_dnrs);
7686 write_dnrs_helper(ctx, mask_dnrs);
7687 saxdb_end_record(ctx);
7690 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7691 for(channel = channelList; channel; channel = channel->next)
7692 chanserv_write_channel(ctx, channel);
7693 saxdb_end_record(ctx);
7699 chanserv_db_cleanup(void) {
7701 unreg_part_func(handle_part);
7703 unregister_channel(channelList, "terminating.");
7704 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7705 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7706 free(chanserv_conf.support_channels.list);
7707 dict_delete(handle_dnrs);
7708 dict_delete(plain_dnrs);
7709 dict_delete(mask_dnrs);
7710 dict_delete(note_types);
7711 free_string_list(chanserv_conf.eightball);
7712 free_string_list(chanserv_conf.old_ban_names);
7713 free_string_list(chanserv_conf.set_shows);
7714 free(set_shows_list.list);
7715 free(uset_shows_list.list);
7718 struct userData *helper = helperList;
7719 helperList = helperList->next;
7724 #if defined(GCC_VARMACROS)
7725 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7726 #elif defined(C99_VARMACROS)
7727 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7729 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7730 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7733 init_chanserv(const char *nick)
7735 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7736 conf_register_reload(chanserv_conf_read);
7740 reg_server_link_func(handle_server_link);
7741 reg_new_channel_func(handle_new_channel);
7742 reg_join_func(handle_join);
7743 reg_part_func(handle_part);
7744 reg_kick_func(handle_kick);
7745 reg_topic_func(handle_topic);
7746 reg_mode_change_func(handle_mode);
7747 reg_nick_change_func(handle_nick_change);
7748 reg_auth_func(handle_auth);
7751 reg_handle_rename_func(handle_rename);
7752 reg_unreg_func(handle_unreg);
7754 handle_dnrs = dict_new();
7755 dict_set_free_data(handle_dnrs, free);
7756 plain_dnrs = dict_new();
7757 dict_set_free_data(plain_dnrs, free);
7758 mask_dnrs = dict_new();
7759 dict_set_free_data(mask_dnrs, free);
7761 reg_svccmd_unbind_func(handle_svccmd_unbind);
7762 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7763 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7764 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7765 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7766 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7767 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7768 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7769 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7770 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7771 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7772 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7773 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7774 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7776 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7777 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7779 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7780 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7781 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7782 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7783 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7785 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7786 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7787 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7788 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7789 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7791 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7792 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7793 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7794 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7796 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7797 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7798 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7799 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7800 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7801 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7802 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7803 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7805 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7806 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7807 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7808 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7809 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7810 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7811 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7812 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7813 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7814 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7815 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7816 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7817 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7818 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7820 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7821 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7822 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7823 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7824 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7826 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7827 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7829 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7830 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7831 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7832 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7833 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7834 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7835 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7836 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7837 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7838 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7839 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7841 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7842 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7844 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7845 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7846 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7847 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7849 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7850 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7851 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7852 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7853 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7855 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7856 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7857 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7858 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7859 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7860 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7862 /* Channel options */
7863 DEFINE_CHANNEL_OPTION(defaulttopic);
7864 DEFINE_CHANNEL_OPTION(topicmask);
7865 DEFINE_CHANNEL_OPTION(greeting);
7866 DEFINE_CHANNEL_OPTION(usergreeting);
7867 DEFINE_CHANNEL_OPTION(modes);
7868 DEFINE_CHANNEL_OPTION(enfops);
7869 DEFINE_CHANNEL_OPTION(giveops);
7870 DEFINE_CHANNEL_OPTION(protect);
7871 DEFINE_CHANNEL_OPTION(enfmodes);
7872 DEFINE_CHANNEL_OPTION(enftopic);
7873 DEFINE_CHANNEL_OPTION(pubcmd);
7874 DEFINE_CHANNEL_OPTION(givevoice);
7875 DEFINE_CHANNEL_OPTION(userinfo);
7876 DEFINE_CHANNEL_OPTION(dynlimit);
7877 DEFINE_CHANNEL_OPTION(topicsnarf);
7878 DEFINE_CHANNEL_OPTION(nodelete);
7879 DEFINE_CHANNEL_OPTION(toys);
7880 DEFINE_CHANNEL_OPTION(setters);
7881 DEFINE_CHANNEL_OPTION(topicrefresh);
7882 DEFINE_CHANNEL_OPTION(ctcpusers);
7883 DEFINE_CHANNEL_OPTION(ctcpreaction);
7884 DEFINE_CHANNEL_OPTION(inviteme);
7885 DEFINE_CHANNEL_OPTION(unreviewed);
7886 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7887 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7889 DEFINE_CHANNEL_OPTION(offchannel);
7890 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7892 /* Alias set topic to set defaulttopic for compatibility. */
7893 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7896 DEFINE_USER_OPTION(noautoop);
7897 DEFINE_USER_OPTION(autoinvite);
7898 DEFINE_USER_OPTION(info);
7900 /* Alias uset autovoice to uset autoop. */
7901 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7903 note_types = dict_new();
7904 dict_set_free_data(note_types, chanserv_deref_note_type);
7907 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7908 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7909 service_register(chanserv)->trigger = '!';
7910 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7912 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7914 if(chanserv_conf.channel_expire_frequency)
7915 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7917 if(chanserv_conf.dnr_expire_frequency)
7918 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7920 if(chanserv_conf.refresh_period)
7922 unsigned long next_refresh;
7923 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7924 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7927 reg_exit_func(chanserv_db_cleanup);
7928 message_register_table(msgtab);