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(UNUSED_ARG(void *data))
1367 struct chanData *channel, *next;
1368 struct userData *user;
1369 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1371 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1372 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1374 for(channel = channelList; channel; channel = next)
1376 next = channel->next;
1378 /* See if the channel can be expired. */
1379 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1380 || IsProtected(channel))
1383 /* Make sure there are no high-ranking users still in the channel. */
1384 for(user=channel->users; user; user=user->next)
1385 if(user->present && (user->access >= UL_PRESENT) && !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)
1396 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1400 expire_dnrs(UNUSED_ARG(void *data))
1402 dict_iterator_t it, next;
1403 struct do_not_register *dnr;
1405 for(it = dict_first(handle_dnrs); it; it = next)
1407 dnr = iter_data(it);
1408 next = iter_next(it);
1409 if(dnr->expires && dnr->expires <= now)
1410 dict_remove(handle_dnrs, dnr->chan_name + 1);
1412 for(it = dict_first(plain_dnrs); it; it = next)
1414 dnr = iter_data(it);
1415 next = iter_next(it);
1416 if(dnr->expires && dnr->expires <= now)
1417 dict_remove(plain_dnrs, dnr->chan_name + 1);
1419 for(it = dict_first(mask_dnrs); it; it = next)
1421 dnr = iter_data(it);
1422 next = iter_next(it);
1423 if(dnr->expires && dnr->expires <= now)
1424 dict_remove(mask_dnrs, dnr->chan_name + 1);
1427 if(chanserv_conf.dnr_expire_frequency)
1428 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1432 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1434 char protect = channel->chOpts[chProtect];
1435 struct userData *cs_victim, *cs_aggressor;
1437 /* Don't protect if no one is to be protected, someone is attacking
1438 himself, or if the aggressor is an IRC Operator. */
1439 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1442 /* Don't protect if the victim isn't authenticated (because they
1443 can't be a channel user), unless we are to protect non-users
1445 cs_victim = GetChannelAccess(channel, victim->handle_info);
1446 if(protect != 'a' && !cs_victim)
1449 /* Protect if the aggressor isn't a user because at this point,
1450 the aggressor can only be less than or equal to the victim. */
1451 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1455 /* If the aggressor was a user, then the victim can't be helped. */
1462 if(cs_victim->access > cs_aggressor->access)
1467 if(cs_victim->access >= cs_aggressor->access)
1476 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1478 struct chanData *cData = channel->channel_info;
1479 struct userData *cs_victim;
1481 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1482 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1483 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1485 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1493 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1495 if(IsService(victim))
1497 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1501 if(protect_user(victim, user, channel->channel_info))
1503 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1510 static struct do_not_register *
1511 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1513 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1514 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1515 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1516 strcpy(dnr->reason, reason);
1518 dnr->expires = expires;
1519 if(dnr->chan_name[0] == '*')
1520 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1521 else if(strpbrk(dnr->chan_name, "*?"))
1522 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1524 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1528 static struct dnrList
1529 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1531 struct dnrList list;
1532 dict_iterator_t it, next;
1533 struct do_not_register *dnr;
1535 dnrList_init(&list);
1537 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1539 if(dnr->expires && dnr->expires <= now)
1540 dict_remove(handle_dnrs, handle);
1541 else if(list.used < max)
1542 dnrList_append(&list, dnr);
1545 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1547 if(dnr->expires && dnr->expires <= now)
1548 dict_remove(plain_dnrs, chan_name);
1549 else if(list.used < max)
1550 dnrList_append(&list, dnr);
1555 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1557 next = iter_next(it);
1558 if(!match_ircglob(chan_name, iter_key(it)))
1560 dnr = iter_data(it);
1561 if(dnr->expires && dnr->expires <= now)
1562 dict_remove(mask_dnrs, iter_key(it));
1564 dnrList_append(&list, dnr);
1571 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1573 struct userNode *user;
1574 char buf1[INTERVALLEN];
1575 char buf2[INTERVALLEN];
1582 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1587 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1588 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1592 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1595 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1600 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1602 struct dnrList list;
1605 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1606 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1607 dnr_print_func(list.list[ii], user);
1609 reply("CSMSG_MORE_DNRS", list.used - ii);
1614 struct do_not_register *
1615 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1617 struct dnrList list;
1618 struct do_not_register *dnr;
1620 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1621 dnr = list.used ? list.list[0] : NULL;
1626 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1628 struct do_not_register *dnr;
1629 dict_iterator_t it, next;
1630 unsigned int matches = 0;
1632 for(it = dict_first(dict); it; it = next)
1634 dnr = iter_data(it);
1635 next = iter_next(it);
1636 if(dnr->expires && dnr->expires <= now)
1638 dict_remove(dict, iter_key(it));
1641 dnr_print_func(dnr, user);
1648 static CHANSERV_FUNC(cmd_noregister)
1652 unsigned long expiry, duration;
1653 unsigned int matches;
1657 reply("CSMSG_DNR_SEARCH_RESULTS");
1658 matches = send_dnrs(user, handle_dnrs);
1659 matches += send_dnrs(user, plain_dnrs);
1660 matches += send_dnrs(user, mask_dnrs);
1662 reply("MSG_MATCH_COUNT", matches);
1664 reply("MSG_NO_MATCHES");
1670 if(!IsChannelName(target) && (*target != '*'))
1672 reply("CSMSG_NOT_DNR", target);
1680 reply("MSG_INVALID_DURATION", argv[2]);
1684 if(!strcmp(argv[2], "0"))
1686 else if((duration = ParseInterval(argv[2])))
1687 expiry = now + duration;
1690 reply("MSG_INVALID_DURATION", argv[2]);
1694 reason = unsplit_string(argv + 3, argc - 3, NULL);
1695 if((*target == '*') && !get_handle_info(target + 1))
1697 reply("MSG_HANDLE_UNKNOWN", target + 1);
1700 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1701 reply("CSMSG_NOREGISTER_CHANNEL", target);
1705 reply("CSMSG_DNR_SEARCH_RESULTS");
1707 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1709 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1711 reply("MSG_NO_MATCHES");
1715 static CHANSERV_FUNC(cmd_allowregister)
1717 const char *chan_name = argv[1];
1719 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1720 || dict_remove(plain_dnrs, chan_name)
1721 || dict_remove(mask_dnrs, chan_name))
1723 reply("CSMSG_DNR_REMOVED", chan_name);
1726 reply("CSMSG_NO_SUCH_DNR", chan_name);
1731 struct userNode *source;
1735 unsigned long min_set, max_set;
1736 unsigned long min_expires, max_expires;
1741 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1743 return !((dnr->set < search->min_set)
1744 || (dnr->set > search->max_set)
1745 || (dnr->expires < search->min_expires)
1746 || (search->max_expires
1747 && ((dnr->expires == 0)
1748 || (dnr->expires > search->max_expires)))
1749 || (search->chan_mask
1750 && !match_ircglob(dnr->chan_name, search->chan_mask))
1751 || (search->setter_mask
1752 && !match_ircglob(dnr->setter, search->setter_mask))
1753 || (search->reason_mask
1754 && !match_ircglob(dnr->reason, search->reason_mask)));
1757 static struct dnr_search *
1758 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1760 struct dnr_search *discrim;
1763 discrim = calloc(1, sizeof(*discrim));
1764 discrim->source = user;
1765 discrim->chan_mask = NULL;
1766 discrim->setter_mask = NULL;
1767 discrim->reason_mask = NULL;
1768 discrim->max_set = INT_MAX;
1769 discrim->limit = 50;
1771 for(ii=0; ii<argc; ++ii)
1775 reply("MSG_MISSING_PARAMS", argv[ii]);
1778 else if(0 == irccasecmp(argv[ii], "channel"))
1780 discrim->chan_mask = argv[++ii];
1782 else if(0 == irccasecmp(argv[ii], "setter"))
1784 discrim->setter_mask = argv[++ii];
1786 else if(0 == irccasecmp(argv[ii], "reason"))
1788 discrim->reason_mask = argv[++ii];
1790 else if(0 == irccasecmp(argv[ii], "limit"))
1792 discrim->limit = strtoul(argv[++ii], NULL, 0);
1794 else if(0 == irccasecmp(argv[ii], "set"))
1796 const char *cmp = argv[++ii];
1799 discrim->min_set = now - ParseInterval(cmp + 2);
1801 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1802 } else if(cmp[0] == '=') {
1803 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1804 } else if(cmp[0] == '>') {
1806 discrim->max_set = now - ParseInterval(cmp + 2);
1808 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1810 discrim->max_set = now - (ParseInterval(cmp) - 1);
1813 else if(0 == irccasecmp(argv[ii], "expires"))
1815 const char *cmp = argv[++ii];
1818 discrim->max_expires = now + ParseInterval(cmp + 2);
1820 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1821 } else if(cmp[0] == '=') {
1822 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1823 } else if(cmp[0] == '>') {
1825 discrim->min_expires = now + ParseInterval(cmp + 2);
1827 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1829 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1834 reply("MSG_INVALID_CRITERIA", argv[ii]);
1845 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1848 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1850 struct do_not_register *dnr;
1851 dict_iterator_t next;
1856 /* Initialize local variables. */
1859 if(discrim->chan_mask)
1861 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1862 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1866 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1868 /* Check against account-based DNRs. */
1869 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1870 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1873 else if(target_fixed)
1875 /* Check against channel-based DNRs. */
1876 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1877 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1882 /* Exhaustively search account DNRs. */
1883 for(it = dict_first(handle_dnrs); it; it = next)
1885 next = iter_next(it);
1886 dnr = iter_data(it);
1887 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1891 /* Do the same for channel DNRs. */
1892 for(it = dict_first(plain_dnrs); it; it = next)
1894 next = iter_next(it);
1895 dnr = iter_data(it);
1896 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1900 /* Do the same for wildcarded channel DNRs. */
1901 for(it = dict_first(mask_dnrs); it; it = next)
1903 next = iter_next(it);
1904 dnr = iter_data(it);
1905 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1913 dnr_remove_func(struct do_not_register *match, void *extra)
1915 struct userNode *user;
1918 chan_name = alloca(strlen(match->chan_name) + 1);
1919 strcpy(chan_name, match->chan_name);
1921 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1922 || dict_remove(plain_dnrs, chan_name)
1923 || dict_remove(mask_dnrs, chan_name))
1925 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1931 dnr_count_func(struct do_not_register *match, void *extra)
1933 return 0; (void)match; (void)extra;
1936 static MODCMD_FUNC(cmd_dnrsearch)
1938 struct dnr_search *discrim;
1939 dnr_search_func action;
1940 struct svccmd *subcmd;
1941 unsigned int matches;
1944 sprintf(buf, "dnrsearch %s", argv[1]);
1945 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1948 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1951 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1953 if(!irccasecmp(argv[1], "print"))
1954 action = dnr_print_func;
1955 else if(!irccasecmp(argv[1], "remove"))
1956 action = dnr_remove_func;
1957 else if(!irccasecmp(argv[1], "count"))
1958 action = dnr_count_func;
1961 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1965 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1969 if(action == dnr_print_func)
1970 reply("CSMSG_DNR_SEARCH_RESULTS");
1971 matches = dnr_search(discrim, action, user);
1973 reply("MSG_MATCH_COUNT", matches);
1975 reply("MSG_NO_MATCHES");
1981 chanserv_get_owned_count(struct handle_info *hi)
1983 struct userData *cList;
1986 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1987 if(cList->access == UL_OWNER)
1992 static CHANSERV_FUNC(cmd_register)
1994 struct handle_info *handle;
1995 struct chanData *cData;
1996 struct modeNode *mn;
1997 char reason[MAXLEN];
1999 unsigned int new_channel, force=0;
2000 struct do_not_register *dnr;
2004 if(channel->channel_info)
2006 reply("CSMSG_ALREADY_REGGED", channel->name);
2010 if(channel->bad_channel)
2012 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2017 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2019 reply("CSMSG_MUST_BE_OPPED", channel->name);
2024 chan_name = channel->name;
2028 if((argc < 2) || !IsChannelName(argv[1]))
2030 reply("MSG_NOT_CHANNEL_NAME");
2034 if(opserv_bad_channel(argv[1]))
2036 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2041 chan_name = argv[1];
2044 if(argc >= (new_channel+2))
2046 if(!IsHelping(user))
2048 reply("CSMSG_PROXY_FORBIDDEN");
2052 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2054 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2055 dnr = chanserv_is_dnr(chan_name, handle);
2059 handle = user->handle_info;
2060 dnr = chanserv_is_dnr(chan_name, handle);
2064 if(!IsHelping(user))
2065 reply("CSMSG_DNR_CHANNEL", chan_name);
2067 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2071 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2073 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2078 channel = AddChannel(argv[1], now, NULL, NULL);
2080 cData = register_channel(channel, user->handle_info->handle);
2081 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2082 cData->modes = chanserv_conf.default_modes;
2084 cData->modes.modes_set |= MODE_REGISTERED;
2085 if (IsOffChannel(cData))
2087 mod_chanmode_announce(chanserv, channel, &cData->modes);
2091 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2092 change->args[change->argc].mode = MODE_CHANOP;
2093 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2095 mod_chanmode_announce(chanserv, channel, change);
2096 mod_chanmode_free(change);
2099 /* Initialize the channel's max user record. */
2100 cData->max = channel->members.used;
2102 if(handle != user->handle_info)
2103 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2105 reply("CSMSG_REG_SUCCESS", channel->name);
2107 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2108 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2113 make_confirmation_string(struct userData *uData)
2115 static char strbuf[16];
2120 for(src = uData->handle->handle; *src; )
2121 accum = accum * 31 + toupper(*src++);
2123 for(src = uData->channel->channel->name; *src; )
2124 accum = accum * 31 + toupper(*src++);
2125 sprintf(strbuf, "%08x", accum);
2129 static CHANSERV_FUNC(cmd_unregister)
2132 char reason[MAXLEN];
2133 struct chanData *cData;
2134 struct userData *uData;
2136 cData = channel->channel_info;
2139 reply("CSMSG_NOT_REGISTERED", channel->name);
2143 uData = GetChannelUser(cData, user->handle_info);
2144 if(!uData || (uData->access < UL_OWNER))
2146 reply("CSMSG_NO_ACCESS");
2150 if(IsProtected(cData))
2152 reply("CSMSG_UNREG_NODELETE", channel->name);
2156 if(!IsHelping(user))
2158 const char *confirm_string;
2159 if(IsSuspended(cData))
2161 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2164 confirm_string = make_confirmation_string(uData);
2165 if((argc < 2) || strcmp(argv[1], confirm_string))
2167 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2172 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2173 name = strdup(channel->name);
2174 unregister_channel(cData, reason);
2175 reply("CSMSG_UNREG_SUCCESS", name);
2180 static CHANSERV_FUNC(cmd_move)
2182 struct mod_chanmode change;
2183 struct chanNode *target;
2184 struct modeNode *mn;
2185 struct userData *uData;
2186 char reason[MAXLEN];
2187 struct do_not_register *dnr;
2191 if(IsProtected(channel->channel_info))
2193 reply("CSMSG_MOVE_NODELETE", channel->name);
2197 if(!IsChannelName(argv[1]))
2199 reply("MSG_NOT_CHANNEL_NAME");
2203 if(opserv_bad_channel(argv[1]))
2205 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2209 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2211 for(uData = channel->channel_info->users; uData; uData = uData->next)
2213 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2215 if(!IsHelping(user))
2216 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2218 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2224 mod_chanmode_init(&change);
2225 if(!(target = GetChannel(argv[1])))
2227 target = AddChannel(argv[1], now, NULL, NULL);
2228 if(!IsSuspended(channel->channel_info))
2229 AddChannelUser(chanserv, target);
2231 else if(target->channel_info)
2233 reply("CSMSG_ALREADY_REGGED", target->name);
2236 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2237 && !IsHelping(user))
2239 reply("CSMSG_MUST_BE_OPPED", target->name);
2242 else if(!IsSuspended(channel->channel_info))
2245 change.args[0].mode = MODE_CHANOP;
2246 change.args[0].u.member = AddChannelUser(chanserv, target);
2247 mod_chanmode_announce(chanserv, target, &change);
2252 /* Clear MODE_REGISTERED from old channel, add it to new. */
2254 change.modes_clear = MODE_REGISTERED;
2255 mod_chanmode_announce(chanserv, channel, &change);
2256 change.modes_clear = 0;
2257 change.modes_set = MODE_REGISTERED;
2258 mod_chanmode_announce(chanserv, target, &change);
2261 /* Move the channel_info to the target channel; it
2262 shouldn't be necessary to clear timeq callbacks
2263 for the old channel. */
2264 target->channel_info = channel->channel_info;
2265 target->channel_info->channel = target;
2266 channel->channel_info = NULL;
2268 /* Check whether users are present in the new channel. */
2269 for(uData = target->channel_info->users; uData; uData = uData->next)
2270 scan_user_presence(uData, NULL);
2272 reply("CSMSG_MOVE_SUCCESS", target->name);
2274 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2275 if(!IsSuspended(target->channel_info))
2277 char reason2[MAXLEN];
2278 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2279 DelChannelUser(chanserv, channel, reason2, 0);
2281 UnlockChannel(channel);
2282 LockChannel(target);
2283 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2288 merge_users(struct chanData *source, struct chanData *target)
2290 struct userData *suData, *tuData, *next;
2296 /* Insert the source's users into the scratch area. */
2297 for(suData = source->users; suData; suData = suData->next)
2298 dict_insert(merge, suData->handle->handle, suData);
2300 /* Iterate through the target's users, looking for
2301 users common to both channels. The lower access is
2302 removed from either the scratch area or target user
2304 for(tuData = target->users; tuData; tuData = next)
2306 struct userData *choice;
2308 next = tuData->next;
2310 /* If a source user exists with the same handle as a target
2311 channel's user, resolve the conflict by removing one. */
2312 suData = dict_find(merge, tuData->handle->handle, NULL);
2316 /* Pick the data we want to keep. */
2317 /* If the access is the same, use the later seen time. */
2318 if(suData->access == tuData->access)
2319 choice = (suData->seen > tuData->seen) ? suData : tuData;
2320 else /* Otherwise, keep the higher access level. */
2321 choice = (suData->access > tuData->access) ? suData : tuData;
2322 /* Use the later seen time. */
2323 if(suData->seen < tuData->seen)
2324 suData->seen = tuData->seen;
2326 tuData->seen = suData->seen;
2328 /* Remove the user that wasn't picked. */
2329 if(choice == tuData)
2331 dict_remove(merge, suData->handle->handle);
2332 del_channel_user(suData, 0);
2335 del_channel_user(tuData, 0);
2338 /* Move the remaining users to the target channel. */
2339 for(it = dict_first(merge); it; it = iter_next(it))
2341 suData = iter_data(it);
2343 /* Insert the user into the target channel's linked list. */
2344 suData->prev = NULL;
2345 suData->next = target->users;
2346 suData->channel = target;
2349 target->users->prev = suData;
2350 target->users = suData;
2352 /* Update the user counts for the target channel; the
2353 source counts are left alone. */
2354 target->userCount++;
2356 /* Check whether the user is in the target channel. */
2357 scan_user_presence(suData, NULL);
2360 /* Possible to assert (source->users == NULL) here. */
2361 source->users = NULL;
2366 merge_bans(struct chanData *source, struct chanData *target)
2368 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2370 /* Hold on to the original head of the target ban list
2371 to avoid comparing source bans with source bans. */
2372 tFront = target->bans;
2374 /* Perform a totally expensive O(n*m) merge, ick. */
2375 for(sbData = source->bans; sbData; sbData = sNext)
2377 /* Flag to track whether the ban's been moved
2378 to the destination yet. */
2381 /* Possible to assert (sbData->prev == NULL) here. */
2382 sNext = sbData->next;
2384 for(tbData = tFront; tbData; tbData = tNext)
2386 tNext = tbData->next;
2388 /* Perform two comparisons between each source
2389 and target ban, conflicts are resolved by
2390 keeping the broader ban and copying the later
2391 expiration and triggered time. */
2392 if(match_ircglobs(tbData->mask, sbData->mask))
2394 /* There is a broader ban in the target channel that
2395 overrides one in the source channel; remove the
2396 source ban and break. */
2397 if(sbData->expires > tbData->expires)
2398 tbData->expires = sbData->expires;
2399 if(sbData->triggered > tbData->triggered)
2400 tbData->triggered = sbData->triggered;
2401 del_channel_ban(sbData);
2404 else if(match_ircglobs(sbData->mask, tbData->mask))
2406 /* There is a broader ban in the source channel that
2407 overrides one in the target channel; remove the
2408 target ban, fall through and move the source over. */
2409 if(tbData->expires > sbData->expires)
2410 sbData->expires = tbData->expires;
2411 if(tbData->triggered > sbData->triggered)
2412 sbData->triggered = tbData->triggered;
2413 if(tbData == tFront)
2415 del_channel_ban(tbData);
2418 /* Source bans can override multiple target bans, so
2419 we allow a source to run through this loop multiple
2420 times, but we can only move it once. */
2425 /* Remove the source ban from the source ban list. */
2427 sbData->next->prev = sbData->prev;
2429 /* Modify the source ban's associated channel. */
2430 sbData->channel = target;
2432 /* Insert the ban into the target channel's linked list. */
2433 sbData->prev = NULL;
2434 sbData->next = target->bans;
2437 target->bans->prev = sbData;
2438 target->bans = sbData;
2440 /* Update the user counts for the target channel. */
2445 /* Possible to assert (source->bans == NULL) here. */
2446 source->bans = NULL;
2450 merge_data(struct chanData *source, struct chanData *target)
2452 /* Use more recent visited and owner-transfer time; use older
2453 * registered time. Bitwise or may_opchan. Use higher max.
2454 * Do not touch last_refresh, ban count or user counts.
2456 if(source->visited > target->visited)
2457 target->visited = source->visited;
2458 if(source->registered < target->registered)
2459 target->registered = source->registered;
2460 if(source->ownerTransfer > target->ownerTransfer)
2461 target->ownerTransfer = source->ownerTransfer;
2462 if(source->may_opchan)
2463 target->may_opchan = 1;
2464 if(source->max > target->max)
2465 target->max = source->max;
2469 merge_channel(struct chanData *source, struct chanData *target)
2471 merge_users(source, target);
2472 merge_bans(source, target);
2473 merge_data(source, target);
2476 static CHANSERV_FUNC(cmd_merge)
2478 struct userData *target_user;
2479 struct chanNode *target;
2480 char reason[MAXLEN];
2484 /* Make sure the target channel exists and is registered to the user
2485 performing the command. */
2486 if(!(target = GetChannel(argv[1])))
2488 reply("MSG_INVALID_CHANNEL");
2492 if(!target->channel_info)
2494 reply("CSMSG_NOT_REGISTERED", target->name);
2498 if(IsProtected(channel->channel_info))
2500 reply("CSMSG_MERGE_NODELETE");
2504 if(IsSuspended(target->channel_info))
2506 reply("CSMSG_MERGE_SUSPENDED");
2510 if(channel == target)
2512 reply("CSMSG_MERGE_SELF");
2516 target_user = GetChannelUser(target->channel_info, user->handle_info);
2517 if(!target_user || (target_user->access < UL_OWNER))
2519 reply("CSMSG_MERGE_NOT_OWNER");
2523 /* Merge the channel structures and associated data. */
2524 merge_channel(channel->channel_info, target->channel_info);
2525 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2526 unregister_channel(channel->channel_info, reason);
2527 reply("CSMSG_MERGE_SUCCESS", target->name);
2531 static CHANSERV_FUNC(cmd_opchan)
2533 struct mod_chanmode change;
2534 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2536 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2539 channel->channel_info->may_opchan = 0;
2540 mod_chanmode_init(&change);
2542 change.args[0].mode = MODE_CHANOP;
2543 change.args[0].u.member = GetUserMode(channel, chanserv);
2544 if(!change.args[0].u.member)
2546 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2549 mod_chanmode_announce(chanserv, channel, &change);
2550 reply("CSMSG_OPCHAN_DONE", channel->name);
2554 static CHANSERV_FUNC(cmd_adduser)
2556 struct userData *actee;
2557 struct userData *actor, *real_actor;
2558 struct handle_info *handle;
2559 unsigned short access_level, override = 0;
2563 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2565 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2569 access_level = user_level_from_name(argv[2], UL_OWNER);
2572 reply("CSMSG_INVALID_ACCESS", argv[2]);
2576 actor = GetChannelUser(channel->channel_info, user->handle_info);
2577 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2579 if(actor->access <= access_level)
2581 reply("CSMSG_NO_BUMP_ACCESS");
2585 /* Trying to add someone with equal/more access? */
2586 if (!real_actor || real_actor->access <= access_level)
2587 override = CMD_LOG_OVERRIDE;
2589 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2592 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2594 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2598 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2599 scan_user_presence(actee, NULL);
2600 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2601 return 1 | override;
2604 static CHANSERV_FUNC(cmd_clvl)
2606 struct handle_info *handle;
2607 struct userData *victim;
2608 struct userData *actor, *real_actor;
2609 unsigned short new_access, override = 0;
2610 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2614 actor = GetChannelUser(channel->channel_info, user->handle_info);
2615 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2617 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2620 if(handle == user->handle_info && !privileged)
2622 reply("CSMSG_NO_SELF_CLVL");
2626 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2628 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2632 if(actor->access <= victim->access && !privileged)
2634 reply("MSG_USER_OUTRANKED", handle->handle);
2638 new_access = user_level_from_name(argv[2], UL_OWNER);
2642 reply("CSMSG_INVALID_ACCESS", argv[2]);
2646 if(new_access >= actor->access && !privileged)
2648 reply("CSMSG_NO_BUMP_ACCESS");
2652 /* Trying to clvl a equal/higher user? */
2653 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2654 override = CMD_LOG_OVERRIDE;
2655 /* Trying to clvl someone to equal/higher access? */
2656 if(!real_actor || new_access >= real_actor->access)
2657 override = CMD_LOG_OVERRIDE;
2658 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2659 * If they lower their own access it's not a big problem.
2662 victim->access = new_access;
2663 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2664 return 1 | override;
2667 static CHANSERV_FUNC(cmd_deluser)
2669 struct handle_info *handle;
2670 struct userData *victim;
2671 struct userData *actor, *real_actor;
2672 unsigned short access_level, override = 0;
2677 actor = GetChannelUser(channel->channel_info, user->handle_info);
2678 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2680 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2683 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2685 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2691 access_level = user_level_from_name(argv[1], UL_OWNER);
2694 reply("CSMSG_INVALID_ACCESS", argv[1]);
2697 if(access_level != victim->access)
2699 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2705 access_level = victim->access;
2708 if((actor->access <= victim->access) && !IsHelping(user))
2710 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2714 /* If people delete themselves it is an override, but they
2715 * could've used deleteme so we don't log it as an override
2717 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2718 override = CMD_LOG_OVERRIDE;
2720 chan_name = strdup(channel->name);
2721 del_channel_user(victim, 1);
2722 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2724 return 1 | override;
2728 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2730 struct userData *actor, *real_actor, *uData, *next;
2731 unsigned int override = 0;
2733 actor = GetChannelUser(channel->channel_info, user->handle_info);
2734 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2736 if(min_access > max_access)
2738 reply("CSMSG_BAD_RANGE", min_access, max_access);
2742 if(actor->access <= max_access)
2744 reply("CSMSG_NO_ACCESS");
2748 if(!real_actor || real_actor->access <= max_access)
2749 override = CMD_LOG_OVERRIDE;
2751 for(uData = channel->channel_info->users; uData; uData = next)
2755 if((uData->access >= min_access)
2756 && (uData->access <= max_access)
2757 && match_ircglob(uData->handle->handle, mask))
2758 del_channel_user(uData, 1);
2761 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2762 return 1 | override;
2765 static CHANSERV_FUNC(cmd_mdelowner)
2767 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2770 static CHANSERV_FUNC(cmd_mdelcoowner)
2772 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2775 static CHANSERV_FUNC(cmd_mdelmaster)
2777 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2780 static CHANSERV_FUNC(cmd_mdelop)
2782 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2785 static CHANSERV_FUNC(cmd_mdelpeon)
2787 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2791 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2793 struct banData *bData, *next;
2794 char interval[INTERVALLEN];
2796 unsigned long limit;
2799 limit = now - duration;
2800 for(bData = channel->channel_info->bans; bData; bData = next)
2804 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2807 del_channel_ban(bData);
2811 intervalString(interval, duration, user->handle_info);
2812 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2817 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2819 struct userData *actor, *uData, *next;
2820 char interval[INTERVALLEN];
2822 unsigned long limit;
2824 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2825 if(min_access > max_access)
2827 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2831 if(!actor || actor->access <= max_access)
2833 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2838 limit = now - duration;
2839 for(uData = channel->channel_info->users; uData; uData = next)
2843 if((uData->seen > limit)
2845 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2848 if(((uData->access >= min_access) && (uData->access <= max_access))
2849 || (!max_access && (uData->access < actor->access)))
2851 del_channel_user(uData, 1);
2859 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2861 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2865 static CHANSERV_FUNC(cmd_trim)
2867 unsigned long duration;
2868 unsigned short min_level, max_level;
2873 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2874 duration = ParseInterval(argv[2]);
2877 reply("CSMSG_CANNOT_TRIM");
2881 if(!irccasecmp(argv[1], "bans"))
2883 cmd_trim_bans(user, channel, duration);
2886 else if(!irccasecmp(argv[1], "users"))
2888 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2891 else if(parse_level_range(&min_level, &max_level, argv[1]))
2893 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2896 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2898 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2903 reply("CSMSG_INVALID_TRIM", argv[1]);
2908 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2909 to the user. cmd_all takes advantage of this. */
2910 static CHANSERV_FUNC(cmd_up)
2912 struct mod_chanmode change;
2913 struct userData *uData;
2916 mod_chanmode_init(&change);
2918 change.args[0].u.member = GetUserMode(channel, user);
2919 if(!change.args[0].u.member)
2922 reply("MSG_CHANNEL_ABSENT", channel->name);
2926 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2930 reply("CSMSG_GODMODE_UP", argv[0]);
2933 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2935 change.args[0].mode = MODE_CHANOP;
2936 errmsg = "CSMSG_ALREADY_OPPED";
2938 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2940 change.args[0].mode = MODE_VOICE;
2941 errmsg = "CSMSG_ALREADY_VOICED";
2946 reply("CSMSG_NO_ACCESS");
2949 change.args[0].mode &= ~change.args[0].u.member->modes;
2950 if(!change.args[0].mode)
2953 reply(errmsg, channel->name);
2956 modcmd_chanmode_announce(&change);
2960 static CHANSERV_FUNC(cmd_down)
2962 struct mod_chanmode change;
2964 mod_chanmode_init(&change);
2966 change.args[0].u.member = GetUserMode(channel, user);
2967 if(!change.args[0].u.member)
2970 reply("MSG_CHANNEL_ABSENT", channel->name);
2974 if(!change.args[0].u.member->modes)
2977 reply("CSMSG_ALREADY_DOWN", channel->name);
2981 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2982 modcmd_chanmode_announce(&change);
2986 static int cmd_all(struct userNode *user, UNUSED_ARG(struct chanNode *channel), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char *argv[]), struct svccmd *cmd, modcmd_func_t mcmd)
2988 struct userData *cList;
2990 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2992 if(IsSuspended(cList->channel)
2993 || IsUserSuspended(cList)
2994 || !GetUserMode(cList->channel->channel, user))
2997 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3003 static CHANSERV_FUNC(cmd_upall)
3005 return cmd_all(CSFUNC_ARGS, cmd_up);
3008 static CHANSERV_FUNC(cmd_downall)
3010 return cmd_all(CSFUNC_ARGS, cmd_down);
3013 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3014 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3017 modify_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, validate_func_t validate, chan_mode_t mode, char *action)
3019 unsigned int ii, valid;
3020 struct userNode *victim;
3021 struct mod_chanmode *change;
3023 change = mod_chanmode_alloc(argc - 1);
3025 for(ii=valid=0; ++ii < argc; )
3027 if(!(victim = GetUserH(argv[ii])))
3029 change->args[valid].mode = mode;
3030 change->args[valid].u.member = GetUserMode(channel, victim);
3031 if(!change->args[valid].u.member)
3033 if(validate && !validate(user, channel, victim))
3038 change->argc = valid;
3039 if(valid < (argc-1))
3040 reply("CSMSG_PROCESS_FAILED");
3043 modcmd_chanmode_announce(change);
3044 reply(action, channel->name);
3046 mod_chanmode_free(change);
3050 static CHANSERV_FUNC(cmd_op)
3052 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3055 static CHANSERV_FUNC(cmd_deop)
3057 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3060 static CHANSERV_FUNC(cmd_voice)
3062 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3065 static CHANSERV_FUNC(cmd_devoice)
3067 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3071 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3077 for(ii=0; ii<channel->members.used; ii++)
3079 struct modeNode *mn = channel->members.list[ii];
3081 if(IsService(mn->user))
3084 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3087 if(protect_user(mn->user, user, channel->channel_info))
3091 victims[(*victimCount)++] = mn;
3097 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3099 struct userNode *victim;
3100 struct modeNode **victims;
3101 unsigned int offset, n, victimCount, duration = 0;
3102 char *reason = "Bye.", *ban, *name;
3103 char interval[INTERVALLEN];
3105 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3106 REQUIRE_PARAMS(offset);
3109 reason = unsplit_string(argv + offset, argc - offset, NULL);
3110 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3112 /* Truncate the reason to a length of TOPICLEN, as
3113 the ircd does; however, leave room for an ellipsis
3114 and the kicker's nick. */
3115 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3119 if((victim = GetUserH(argv[1])))
3121 victims = alloca(sizeof(victims[0]));
3122 victims[0] = GetUserMode(channel, victim);
3123 /* XXX: The comparison with ACTION_KICK is just because all
3124 * other actions can work on users outside the channel, and we
3125 * want to allow those (e.g. unbans) in that case. If we add
3126 * some other ejection action for in-channel users, change
3128 victimCount = victims[0] ? 1 : 0;
3130 if(IsService(victim))
3132 reply("MSG_SERVICE_IMMUNE", victim->nick);
3136 if((action == ACTION_KICK) && !victimCount)
3138 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3142 if(protect_user(victim, user, channel->channel_info))
3144 reply("CSMSG_USER_PROTECTED", victim->nick);
3148 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3149 name = victim->nick;
3151 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3153 struct handle_info *hi;
3154 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;
3665 if(uData->access > UL_OWNER)
3667 if(IsProtected(cData)
3668 && (target_handle != user->handle_info)
3669 && !GetTrueChannelAccess(cData, user->handle_info))
3672 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3673 if(uData->flags != USER_AUTO_OP)
3674 string_buffer_append(&sbuf, ',');
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');
3687 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3689 string_buffer_append_string(&sbuf, ")]");
3690 string_buffer_append(&sbuf, '\0');
3691 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3697 static CHANSERV_FUNC(cmd_access)
3699 struct userNode *target;
3700 struct handle_info *target_handle;
3701 struct userData *uData;
3703 char prefix[MAXLEN];
3708 target_handle = target->handle_info;
3710 else if((target = GetUserH(argv[1])))
3712 target_handle = target->handle_info;
3714 else if(argv[1][0] == '*')
3716 if(!(target_handle = get_handle_info(argv[1]+1)))
3718 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3724 reply("MSG_NICK_UNKNOWN", argv[1]);
3728 assert(target || target_handle);
3730 if(target == chanserv)
3732 reply("CSMSG_IS_CHANSERV");
3740 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3745 reply("MSG_USER_AUTHENTICATE", target->nick);
3748 reply("MSG_AUTHENTICATE");
3754 const char *epithet = NULL, *type = NULL;
3757 epithet = chanserv_conf.irc_operator_epithet;
3758 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3760 else if(IsNetworkHelper(target))
3762 epithet = chanserv_conf.network_helper_epithet;
3763 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3765 else if(IsSupportHelper(target))
3767 epithet = chanserv_conf.support_helper_epithet;
3768 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3772 if(target_handle->epithet)
3773 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3775 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3777 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3781 sprintf(prefix, "%s", target_handle->handle);
3784 if(!channel->channel_info)
3786 reply("CSMSG_NOT_REGISTERED", channel->name);
3790 helping = HANDLE_FLAGGED(target_handle, HELPING)
3791 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3792 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3794 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3795 /* To prevent possible information leaks, only show infolines
3796 * if the requestor is in the channel or it's their own
3798 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3800 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3802 /* Likewise, only say it's suspended if the user has active
3803 * access in that channel or it's their own entry. */
3804 if(IsUserSuspended(uData)
3805 && (GetChannelUser(channel->channel_info, user->handle_info)
3806 || (user->handle_info == uData->handle)))
3808 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3813 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3820 zoot_list(struct listData *list)
3822 struct userData *uData;
3823 unsigned int start, curr, highest, lowest;
3824 struct helpfile_table tmp_table;
3825 const char **temp, *msg;
3827 if(list->table.length == 1)
3830 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3832 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3833 msg = user_find_message(list->user, "MSG_NONE");
3834 send_message_type(4, list->user, list->bot, " %s", msg);
3836 tmp_table.width = list->table.width;
3837 tmp_table.flags = list->table.flags;
3838 list->table.contents[0][0] = " ";
3839 highest = list->highest;
3840 if(list->lowest != 0)
3841 lowest = list->lowest;
3842 else if(highest < 100)
3845 lowest = highest - 100;
3846 for(start = curr = 1; curr < list->table.length; )
3848 uData = list->users[curr-1];
3849 list->table.contents[curr++][0] = " ";
3850 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3853 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3855 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3856 temp = list->table.contents[--start];
3857 list->table.contents[start] = list->table.contents[0];
3858 tmp_table.contents = list->table.contents + start;
3859 tmp_table.length = curr - start;
3860 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3861 list->table.contents[start] = temp;
3863 highest = lowest - 1;
3864 lowest = (highest < 100) ? 0 : (highest - 99);
3870 def_list(struct listData *list)
3874 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3876 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3877 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3878 if(list->table.length == 1)
3880 msg = user_find_message(list->user, "MSG_NONE");
3881 send_message_type(4, list->user, list->bot, " %s", msg);
3886 userData_access_comp(const void *arg_a, const void *arg_b)
3888 const struct userData *a = *(struct userData**)arg_a;
3889 const struct userData *b = *(struct userData**)arg_b;
3891 if(a->access != b->access)
3892 res = b->access - a->access;
3894 res = irccasecmp(a->handle->handle, b->handle->handle);
3899 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3901 void (*send_list)(struct listData *);
3902 struct userData *uData;
3903 struct listData lData;
3904 unsigned int matches;
3908 lData.bot = cmd->parent->bot;
3909 lData.channel = channel;
3910 lData.lowest = lowest;
3911 lData.highest = highest;
3912 lData.search = (argc > 1) ? argv[1] : NULL;
3913 send_list = def_list;
3914 (void)zoot_list; /* since it doesn't show user levels */
3916 if(user->handle_info)
3918 switch(user->handle_info->userlist_style)
3920 case HI_STYLE_DEF: send_list = def_list; break;
3921 case HI_STYLE_ZOOT: send_list = def_list; break;
3925 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3927 for(uData = channel->channel_info->users; uData; uData = uData->next)
3929 if((uData->access < lowest)
3930 || (uData->access > highest)
3931 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3933 lData.users[matches++] = uData;
3935 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3937 lData.table.length = matches+1;
3938 lData.table.width = 4;
3939 lData.table.flags = TABLE_NO_FREE;
3940 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3941 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3942 lData.table.contents[0] = ary;
3945 ary[2] = "Last Seen";
3947 for(matches = 1; matches < lData.table.length; ++matches)
3949 char seen[INTERVALLEN];
3951 uData = lData.users[matches-1];
3952 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3953 lData.table.contents[matches] = ary;
3954 ary[0] = strtab(uData->access);
3955 ary[1] = uData->handle->handle;
3958 else if(!uData->seen)
3961 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3962 ary[2] = strdup(ary[2]);
3963 if(IsUserSuspended(uData))
3964 ary[3] = "Suspended";
3965 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3966 ary[3] = "Vacation";
3967 else if(HANDLE_FLAGGED(uData->handle, BOT))
3973 for(matches = 1; matches < lData.table.length; ++matches)
3975 free((char*)lData.table.contents[matches][2]);
3976 free(lData.table.contents[matches]);
3978 free(lData.table.contents[0]);
3979 free(lData.table.contents);
3983 static CHANSERV_FUNC(cmd_users)
3985 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3988 static CHANSERV_FUNC(cmd_wlist)
3990 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3993 static CHANSERV_FUNC(cmd_clist)
3995 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3998 static CHANSERV_FUNC(cmd_mlist)
4000 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4003 static CHANSERV_FUNC(cmd_olist)
4005 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4008 static CHANSERV_FUNC(cmd_plist)
4010 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4013 static CHANSERV_FUNC(cmd_bans)
4015 struct userNode *search_u = NULL;
4016 struct helpfile_table tbl;
4017 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4018 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4019 const char *msg_never, *triggered, *expires;
4020 struct banData *ban, **bans;
4024 else if(strchr(search = argv[1], '!'))
4027 search_wilds = search[strcspn(search, "?*")];
4029 else if(!(search_u = GetUserH(search)))
4030 reply("MSG_NICK_UNKNOWN", search);
4032 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4034 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4038 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4043 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4046 bans[matches++] = ban;
4051 tbl.length = matches + 1;
4052 tbl.width = 4 + timed;
4054 tbl.flags = TABLE_NO_FREE;
4055 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4056 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4057 tbl.contents[0][0] = "Mask";
4058 tbl.contents[0][1] = "Set By";
4059 tbl.contents[0][2] = "Triggered";
4062 tbl.contents[0][3] = "Expires";
4063 tbl.contents[0][4] = "Reason";
4066 tbl.contents[0][3] = "Reason";
4069 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4071 free(tbl.contents[0]);
4076 msg_never = user_find_message(user, "MSG_NEVER");
4077 for(ii = 0; ii < matches; )
4083 else if(ban->expires)
4084 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4086 expires = msg_never;
4089 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4091 triggered = msg_never;
4093 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4094 tbl.contents[ii][0] = ban->mask;
4095 tbl.contents[ii][1] = ban->owner;
4096 tbl.contents[ii][2] = strdup(triggered);
4099 tbl.contents[ii][3] = strdup(expires);
4100 tbl.contents[ii][4] = ban->reason;
4103 tbl.contents[ii][3] = ban->reason;
4105 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4106 reply("MSG_MATCH_COUNT", matches);
4107 for(ii = 1; ii < tbl.length; ++ii)
4109 free((char*)tbl.contents[ii][2]);
4111 free((char*)tbl.contents[ii][3]);
4112 free(tbl.contents[ii]);
4114 free(tbl.contents[0]);
4120 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4122 struct chanData *cData = channel->channel_info;
4123 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4125 if(cData->topic_mask)
4126 return !match_ircglob(new_topic, cData->topic_mask);
4127 else if(cData->topic)
4128 return irccasecmp(new_topic, cData->topic);
4133 static CHANSERV_FUNC(cmd_topic)
4135 struct chanData *cData;
4138 cData = channel->channel_info;
4143 SetChannelTopic(channel, chanserv, cData->topic, 1);
4144 reply("CSMSG_TOPIC_SET", cData->topic);
4148 reply("CSMSG_NO_TOPIC", channel->name);
4152 topic = unsplit_string(argv + 1, argc - 1, NULL);
4153 /* If they say "!topic *", use an empty topic. */
4154 if((topic[0] == '*') && (topic[1] == 0))
4156 if(bad_topic(channel, user, topic))
4158 char *topic_mask = cData->topic_mask;
4161 char new_topic[TOPICLEN+1], tchar;
4162 int pos=0, starpos=-1, dpos=0, len;
4164 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4171 len = strlen(topic);
4172 if((dpos + len) > TOPICLEN)
4173 len = TOPICLEN + 1 - dpos;
4174 memcpy(new_topic+dpos, topic, len);
4178 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4179 default: new_topic[dpos++] = tchar; break;
4182 if((dpos > TOPICLEN) || tchar)
4185 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4186 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4189 new_topic[dpos] = 0;
4190 SetChannelTopic(channel, chanserv, new_topic, 1);
4192 reply("CSMSG_TOPIC_LOCKED", channel->name);
4197 SetChannelTopic(channel, chanserv, topic, 1);
4199 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4201 /* Grab the topic and save it as the default topic. */
4203 cData->topic = strdup(channel->topic);
4209 static CHANSERV_FUNC(cmd_mode)
4211 struct userData *uData;
4212 struct mod_chanmode *change;
4218 change = &channel->channel_info->modes;
4219 if(change->modes_set || change->modes_clear) {
4220 modcmd_chanmode_announce(change);
4221 reply("CSMSG_DEFAULTED_MODES", channel->name);
4223 reply("CSMSG_NO_MODES", channel->name);
4227 uData = GetChannelUser(channel->channel_info, user->handle_info);
4229 base_oplevel = MAXOPLEVEL;
4230 else if (uData->access >= UL_OWNER)
4233 base_oplevel = 1 + UL_OWNER - uData->access;
4234 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4237 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4241 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4242 && mode_lock_violated(&channel->channel_info->modes, change))
4245 mod_chanmode_format(&channel->channel_info->modes, modes);
4246 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4250 modcmd_chanmode_announce(change);
4251 mod_chanmode_format(change, fmt);
4252 mod_chanmode_free(change);
4253 reply("CSMSG_MODES_SET", fmt);
4257 static CHANSERV_FUNC(cmd_invite)
4259 struct userData *uData;
4260 struct userNode *invite;
4262 uData = GetChannelUser(channel->channel_info, user->handle_info);
4266 if(!(invite = GetUserH(argv[1])))
4268 reply("MSG_NICK_UNKNOWN", argv[1]);
4275 if(GetUserMode(channel, invite))
4277 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4285 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4286 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4289 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4291 irc_invite(chanserv, invite, channel);
4293 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4298 static CHANSERV_FUNC(cmd_inviteme)
4300 if(GetUserMode(channel, user))
4302 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4305 if(channel->channel_info
4306 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4308 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4311 irc_invite(cmd->parent->bot, user, channel);
4316 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4319 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4321 /* We display things based on two dimensions:
4322 * - Issue time: present or absent
4323 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4324 * (in order of precedence, so something both expired and revoked
4325 * only counts as revoked)
4327 combo = (suspended->issued ? 4 : 0)
4328 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4330 case 0: /* no issue time, indefinite expiration */
4331 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4333 case 1: /* no issue time, expires in future */
4334 intervalString(buf1, suspended->expires-now, user->handle_info);
4335 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4337 case 2: /* no issue time, expired */
4338 intervalString(buf1, now-suspended->expires, user->handle_info);
4339 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4341 case 3: /* no issue time, revoked */
4342 intervalString(buf1, now-suspended->revoked, user->handle_info);
4343 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4345 case 4: /* issue time set, indefinite expiration */
4346 intervalString(buf1, now-suspended->issued, user->handle_info);
4347 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4349 case 5: /* issue time set, expires in future */
4350 intervalString(buf1, now-suspended->issued, user->handle_info);
4351 intervalString(buf2, suspended->expires-now, user->handle_info);
4352 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4354 case 6: /* issue time set, expired */
4355 intervalString(buf1, now-suspended->issued, user->handle_info);
4356 intervalString(buf2, now-suspended->expires, user->handle_info);
4357 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4359 case 7: /* issue time set, revoked */
4360 intervalString(buf1, now-suspended->issued, user->handle_info);
4361 intervalString(buf2, now-suspended->revoked, user->handle_info);
4362 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4365 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4370 static CHANSERV_FUNC(cmd_info)
4372 char modes[MAXLEN], buffer[INTERVALLEN];
4373 struct userData *uData, *owner;
4374 struct chanData *cData;
4375 struct do_not_register *dnr;
4380 cData = channel->channel_info;
4381 reply("CSMSG_CHANNEL_INFO", channel->name);
4383 uData = GetChannelUser(cData, user->handle_info);
4384 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4386 mod_chanmode_format(&cData->modes, modes);
4387 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4388 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4391 for(it = dict_first(cData->notes); it; it = iter_next(it))
4395 note = iter_data(it);
4396 if(!note_type_visible_to_user(cData, note->type, user))
4399 padding = PADLEN - 1 - strlen(iter_key(it));
4400 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4403 reply("CSMSG_CHANNEL_MAX", cData->max);
4404 for(owner = cData->users; owner; owner = owner->next)
4405 if(owner->access == UL_OWNER)
4406 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4407 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4408 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4409 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4411 privileged = IsStaff(user);
4413 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4414 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4415 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4417 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4418 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4420 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4422 struct suspended *suspended;
4423 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4424 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4425 show_suspension_info(cmd, user, suspended);
4427 else if(IsSuspended(cData))
4429 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4430 show_suspension_info(cmd, user, cData->suspended);
4435 static CHANSERV_FUNC(cmd_netinfo)
4437 extern unsigned long boot_time;
4438 extern unsigned long burst_length;
4439 char interval[INTERVALLEN];
4441 reply("CSMSG_NETWORK_INFO");
4442 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4443 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4444 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4445 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4446 reply("CSMSG_NETWORK_BANS", banCount);
4447 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4448 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4449 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4454 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4456 struct helpfile_table table;
4458 struct userNode *user;
4463 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4464 table.contents = alloca(list->used*sizeof(*table.contents));
4465 for(nn=0; nn<list->used; nn++)
4467 user = list->list[nn];
4468 if(user->modes & skip_flags)
4472 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4475 nick = alloca(strlen(user->nick)+3);
4476 sprintf(nick, "(%s)", user->nick);
4480 table.contents[table.length][0] = nick;
4483 table_send(chanserv, to->nick, 0, NULL, table);
4486 static CHANSERV_FUNC(cmd_ircops)
4488 reply("CSMSG_STAFF_OPERS");
4489 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4493 static CHANSERV_FUNC(cmd_helpers)
4495 reply("CSMSG_STAFF_HELPERS");
4496 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4500 static CHANSERV_FUNC(cmd_staff)
4502 reply("CSMSG_NETWORK_STAFF");
4503 cmd_ircops(CSFUNC_ARGS);
4504 cmd_helpers(CSFUNC_ARGS);
4508 static CHANSERV_FUNC(cmd_peek)
4510 struct modeNode *mn;
4511 char modes[MODELEN];
4513 struct helpfile_table table;
4514 int opcount = 0, voicecount = 0, srvcount = 0;
4516 irc_make_chanmode(channel, modes);
4518 reply("CSMSG_PEEK_INFO", channel->name);
4519 reply("CSMSG_PEEK_TOPIC", channel->topic);
4520 reply("CSMSG_PEEK_MODES", modes);
4524 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4525 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4526 for(n = 0; n < channel->members.used; n++)
4528 mn = channel->members.list[n];
4529 if(IsLocal(mn->user))
4531 else if(mn->modes & MODE_CHANOP)
4533 else if(mn->modes & MODE_VOICE)
4536 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4538 table.contents[table.length] = alloca(sizeof(**table.contents));
4539 table.contents[table.length][0] = mn->user->nick;
4543 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4544 (channel->members.used - opcount - voicecount - srvcount));
4548 reply("CSMSG_PEEK_OPS");
4549 table_send(chanserv, user->nick, 0, NULL, table);
4552 reply("CSMSG_PEEK_NO_OPS");
4556 static MODCMD_FUNC(cmd_wipeinfo)
4558 struct handle_info *victim;
4559 struct userData *ud, *actor, *real_actor;
4560 unsigned int override = 0;
4563 actor = GetChannelUser(channel->channel_info, user->handle_info);
4564 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4565 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4567 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4569 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4572 if((ud->access >= actor->access) && (ud != actor))
4574 reply("MSG_USER_OUTRANKED", victim->handle);
4577 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4578 override = CMD_LOG_OVERRIDE;
4582 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4583 return 1 | override;
4586 static CHANSERV_FUNC(cmd_resync)
4588 struct mod_chanmode *changes;
4589 struct chanData *cData = channel->channel_info;
4590 unsigned int ii, used;
4592 changes = mod_chanmode_alloc(channel->members.used * 2);
4593 for(ii = used = 0; ii < channel->members.used; ++ii)
4595 struct modeNode *mn = channel->members.list[ii];
4596 struct userData *uData;
4598 if(IsService(mn->user))
4601 uData = GetChannelAccess(cData, mn->user->handle_info);
4602 if(!cData->lvlOpts[lvlGiveOps]
4603 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4605 if(!(mn->modes & MODE_CHANOP))
4607 changes->args[used].mode = MODE_CHANOP;
4608 changes->args[used++].u.member = mn;
4611 else if(!cData->lvlOpts[lvlGiveVoice]
4612 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4614 if(mn->modes & MODE_CHANOP)
4616 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4617 changes->args[used++].u.member = mn;
4619 if(!(mn->modes & MODE_VOICE))
4621 changes->args[used].mode = MODE_VOICE;
4622 changes->args[used++].u.member = mn;
4629 changes->args[used].mode = MODE_REMOVE | mn->modes;
4630 changes->args[used++].u.member = mn;
4634 changes->argc = used;
4635 modcmd_chanmode_announce(changes);
4636 mod_chanmode_free(changes);
4637 reply("CSMSG_RESYNCED_USERS", channel->name);
4641 static CHANSERV_FUNC(cmd_seen)
4643 struct userData *uData;
4644 struct handle_info *handle;
4645 char seen[INTERVALLEN];
4649 if(!irccasecmp(argv[1], chanserv->nick))
4651 reply("CSMSG_IS_CHANSERV");
4655 if(!(handle = get_handle_info(argv[1])))
4657 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4661 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4663 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4668 reply("CSMSG_USER_PRESENT", handle->handle);
4669 else if(uData->seen)
4670 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4672 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4674 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4675 reply("CSMSG_USER_VACATION", handle->handle);
4680 static MODCMD_FUNC(cmd_names)
4682 struct userNode *targ;
4683 struct userData *targData;
4684 unsigned int ii, pos;
4687 for(ii=pos=0; ii<channel->members.used; ++ii)
4689 targ = channel->members.list[ii]->user;
4690 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4693 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4696 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4700 if(IsUserSuspended(targData))
4702 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4705 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4706 reply("CSMSG_END_NAMES", channel->name);
4711 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4713 switch(ntype->visible_type)
4715 case NOTE_VIS_ALL: return 1;
4716 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4717 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4722 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4724 struct userData *uData;
4726 switch(ntype->set_access_type)
4728 case NOTE_SET_CHANNEL_ACCESS:
4729 if(!user->handle_info)
4731 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4733 return uData->access >= ntype->set_access.min_ulevel;
4734 case NOTE_SET_CHANNEL_SETTER:
4735 return check_user_level(channel, user, lvlSetters, 1, 0);
4736 case NOTE_SET_PRIVILEGED: default:
4737 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4741 static CHANSERV_FUNC(cmd_note)
4743 struct chanData *cData;
4745 struct note_type *ntype;
4747 cData = channel->channel_info;
4750 reply("CSMSG_NOT_REGISTERED", channel->name);
4754 /* If no arguments, show all visible notes for the channel. */
4760 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4762 note = iter_data(it);
4763 if(!note_type_visible_to_user(cData, note->type, user))
4766 reply("CSMSG_NOTELIST_HEADER", channel->name);
4767 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4770 reply("CSMSG_NOTELIST_END", channel->name);
4772 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4774 /* If one argument, show the named note. */
4777 if((note = dict_find(cData->notes, argv[1], NULL))
4778 && note_type_visible_to_user(cData, note->type, user))
4780 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4782 else if((ntype = dict_find(note_types, argv[1], NULL))
4783 && note_type_visible_to_user(NULL, ntype, user))
4785 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4790 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4794 /* Assume they're trying to set a note. */
4798 ntype = dict_find(note_types, argv[1], NULL);
4801 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4804 else if(note_type_settable_by_user(channel, ntype, user))
4806 note_text = unsplit_string(argv+2, argc-2, NULL);
4807 if((note = dict_find(cData->notes, argv[1], NULL)))
4808 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4809 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4810 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4812 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4814 /* The note is viewable to staff only, so return 0
4815 to keep the invocation from getting logged (or
4816 regular users can see it in !events). */
4822 reply("CSMSG_NO_ACCESS");
4829 static CHANSERV_FUNC(cmd_delnote)
4834 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4835 || !note_type_settable_by_user(channel, note->type, user))
4837 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4840 dict_remove(channel->channel_info->notes, note->type->name);
4841 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4845 static CHANSERV_FUNC(cmd_events)
4847 struct logSearch discrim;
4848 struct logReport report;
4849 unsigned int matches, limit;
4851 limit = (argc > 1) ? atoi(argv[1]) : 10;
4852 if(limit < 1 || limit > 200)
4855 memset(&discrim, 0, sizeof(discrim));
4856 discrim.masks.bot = chanserv;
4857 discrim.masks.channel_name = channel->name;
4859 discrim.masks.command = argv[2];
4860 discrim.limit = limit;
4861 discrim.max_time = INT_MAX;
4862 discrim.severities = 1 << LOG_COMMAND;
4863 report.reporter = chanserv;
4865 reply("CSMSG_EVENT_SEARCH_RESULTS");
4866 matches = log_entry_search(&discrim, log_report_entry, &report);
4868 reply("MSG_MATCH_COUNT", matches);
4870 reply("MSG_NO_MATCHES");
4874 static CHANSERV_FUNC(cmd_say)
4880 msg = unsplit_string(argv + 1, argc - 1, NULL);
4881 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4883 else if(*argv[1] == '*' && argv[1][1] != '\0')
4885 struct handle_info *hi;
4886 struct userNode *authed;
4889 msg = unsplit_string(argv + 2, argc - 2, NULL);
4891 if (!(hi = get_handle_info(argv[1] + 1)))
4893 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4897 for (authed = hi->users; authed; authed = authed->next_authed)
4898 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4900 else if(GetUserH(argv[1]))
4903 msg = unsplit_string(argv + 2, argc - 2, NULL);
4904 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4908 reply("MSG_NOT_TARGET_NAME");
4914 static CHANSERV_FUNC(cmd_emote)
4920 /* CTCP is so annoying. */
4921 msg = unsplit_string(argv + 1, argc - 1, NULL);
4922 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4924 else if(*argv[1] == '*' && argv[1][1] != '\0')
4926 struct handle_info *hi;
4927 struct userNode *authed;
4930 msg = unsplit_string(argv + 2, argc - 2, NULL);
4932 if (!(hi = get_handle_info(argv[1] + 1)))
4934 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4938 for (authed = hi->users; authed; authed = authed->next_authed)
4939 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4941 else if(GetUserH(argv[1]))
4943 msg = unsplit_string(argv + 2, argc - 2, NULL);
4944 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4948 reply("MSG_NOT_TARGET_NAME");
4954 struct channelList *
4955 chanserv_support_channels(void)
4957 return &chanserv_conf.support_channels;
4960 static CHANSERV_FUNC(cmd_expire)
4962 int channel_count = registered_channels;
4963 expire_channels(NULL);
4964 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4969 chanserv_expire_suspension(void *data)
4971 struct suspended *suspended = data;
4972 struct chanNode *channel;
4975 /* Update the channel registration data structure. */
4976 if(!suspended->expires || (now < suspended->expires))
4977 suspended->revoked = now;
4978 channel = suspended->cData->channel;
4979 suspended->cData->channel = channel;
4980 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4982 /* If appropriate, re-join ChanServ to the channel. */
4983 if(!IsOffChannel(suspended->cData))
4985 struct mod_chanmode change;
4986 mod_chanmode_init(&change);
4988 change.args[0].mode = MODE_CHANOP;
4989 change.args[0].u.member = AddChannelUser(chanserv, channel);
4990 mod_chanmode_announce(chanserv, channel, &change);
4993 /* Mark everyone currently in the channel as present. */
4994 for(ii = 0; ii < channel->members.used; ++ii)
4996 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5005 static CHANSERV_FUNC(cmd_csuspend)
5007 struct suspended *suspended;
5008 char reason[MAXLEN];
5009 unsigned long expiry, duration;
5010 struct userData *uData;
5014 if(IsProtected(channel->channel_info))
5016 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5020 if(argv[1][0] == '!')
5022 else if(IsSuspended(channel->channel_info))
5024 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5025 show_suspension_info(cmd, user, channel->channel_info->suspended);
5029 if(!strcmp(argv[1], "0"))
5031 else if((duration = ParseInterval(argv[1])))
5032 expiry = now + duration;
5035 reply("MSG_INVALID_DURATION", argv[1]);
5039 unsplit_string(argv + 2, argc - 2, reason);
5041 suspended = calloc(1, sizeof(*suspended));
5042 suspended->revoked = 0;
5043 suspended->issued = now;
5044 suspended->suspender = strdup(user->handle_info->handle);
5045 suspended->expires = expiry;
5046 suspended->reason = strdup(reason);
5047 suspended->cData = channel->channel_info;
5048 suspended->previous = suspended->cData->suspended;
5049 suspended->cData->suspended = suspended;
5051 if(suspended->expires)
5052 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5054 if(IsSuspended(channel->channel_info))
5056 suspended->previous->revoked = now;
5057 if(suspended->previous->expires)
5058 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5059 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5060 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5064 /* Mark all users in channel as absent. */
5065 for(uData = channel->channel_info->users; uData; uData = uData->next)
5074 /* Mark the channel as suspended, then part. */
5075 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5076 DelChannelUser(chanserv, channel, suspended->reason, 0);
5077 reply("CSMSG_SUSPENDED", channel->name);
5078 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5079 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5084 static CHANSERV_FUNC(cmd_cunsuspend)
5086 struct suspended *suspended;
5087 char message[MAXLEN];
5089 if(!IsSuspended(channel->channel_info))
5091 reply("CSMSG_NOT_SUSPENDED", channel->name);
5095 suspended = channel->channel_info->suspended;
5097 /* Expire the suspension and join ChanServ to the channel. */
5098 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5099 chanserv_expire_suspension(suspended);
5100 reply("CSMSG_UNSUSPENDED", channel->name);
5101 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5102 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5106 typedef struct chanservSearch
5111 unsigned long unvisited;
5112 unsigned long registered;
5114 unsigned long flags;
5118 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5121 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5126 search = malloc(sizeof(struct chanservSearch));
5127 memset(search, 0, sizeof(*search));
5130 for(i = 0; i < argc; i++)
5132 /* Assume all criteria require arguments. */
5135 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5139 if(!irccasecmp(argv[i], "name"))
5140 search->name = argv[++i];
5141 else if(!irccasecmp(argv[i], "registrar"))
5142 search->registrar = argv[++i];
5143 else if(!irccasecmp(argv[i], "unvisited"))
5144 search->unvisited = ParseInterval(argv[++i]);
5145 else if(!irccasecmp(argv[i], "registered"))
5146 search->registered = ParseInterval(argv[++i]);
5147 else if(!irccasecmp(argv[i], "flags"))
5150 if(!irccasecmp(argv[i], "nodelete"))
5151 search->flags |= CHANNEL_NODELETE;
5152 else if(!irccasecmp(argv[i], "suspended"))
5153 search->flags |= CHANNEL_SUSPENDED;
5154 else if(!irccasecmp(argv[i], "unreviewed"))
5155 search->flags |= CHANNEL_UNREVIEWED;
5158 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5162 else if(!irccasecmp(argv[i], "limit"))
5163 search->limit = strtoul(argv[++i], NULL, 10);
5166 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5171 if(search->name && !strcmp(search->name, "*"))
5173 if(search->registrar && !strcmp(search->registrar, "*"))
5174 search->registrar = 0;
5183 chanserv_channel_match(struct chanData *channel, search_t search)
5185 const char *name = channel->channel->name;
5186 if((search->name && !match_ircglob(name, search->name)) ||
5187 (search->registrar && !channel->registrar) ||
5188 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5189 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5190 (search->registered && (now - channel->registered) > search->registered) ||
5191 (search->flags && ((search->flags & channel->flags) != search->flags)))
5198 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5200 struct chanData *channel;
5201 unsigned int matches = 0;
5203 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5205 if(!chanserv_channel_match(channel, search))
5215 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5220 search_print(struct chanData *channel, void *data)
5222 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5225 static CHANSERV_FUNC(cmd_search)
5228 unsigned int matches;
5229 channel_search_func action;
5233 if(!irccasecmp(argv[1], "count"))
5234 action = search_count;
5235 else if(!irccasecmp(argv[1], "print"))
5236 action = search_print;
5239 reply("CSMSG_ACTION_INVALID", argv[1]);
5243 search = chanserv_search_create(user, argc - 2, argv + 2);
5247 if(action == search_count)
5248 search->limit = INT_MAX;
5250 if(action == search_print)
5251 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5253 matches = chanserv_channel_search(search, action, user);
5256 reply("MSG_MATCH_COUNT", matches);
5258 reply("MSG_NO_MATCHES");
5264 static CHANSERV_FUNC(cmd_unvisited)
5266 struct chanData *cData;
5267 unsigned long interval = chanserv_conf.channel_expire_delay;
5268 char buffer[INTERVALLEN];
5269 unsigned int limit = 25, matches = 0;
5273 interval = ParseInterval(argv[1]);
5275 limit = atoi(argv[2]);
5278 intervalString(buffer, interval, user->handle_info);
5279 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5281 for(cData = channelList; cData && matches < limit; cData = cData->next)
5283 if((now - cData->visited) < interval)
5286 intervalString(buffer, now - cData->visited, user->handle_info);
5287 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5294 static MODCMD_FUNC(chan_opt_defaulttopic)
5300 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5302 reply("CSMSG_TOPIC_LOCKED", channel->name);
5306 topic = unsplit_string(argv+1, argc-1, NULL);
5308 free(channel->channel_info->topic);
5309 if(topic[0] == '*' && topic[1] == 0)
5311 topic = channel->channel_info->topic = NULL;
5315 topic = channel->channel_info->topic = strdup(topic);
5316 if(channel->channel_info->topic_mask
5317 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5318 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5320 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5323 if(channel->channel_info->topic)
5324 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5326 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5330 static MODCMD_FUNC(chan_opt_topicmask)
5334 struct chanData *cData = channel->channel_info;
5337 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5339 reply("CSMSG_TOPIC_LOCKED", channel->name);
5343 mask = unsplit_string(argv+1, argc-1, NULL);
5345 if(cData->topic_mask)
5346 free(cData->topic_mask);
5347 if(mask[0] == '*' && mask[1] == 0)
5349 cData->topic_mask = 0;
5353 cData->topic_mask = strdup(mask);
5355 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5356 else if(!match_ircglob(cData->topic, cData->topic_mask))
5357 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5361 if(channel->channel_info->topic_mask)
5362 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5364 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5368 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5372 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5376 if(greeting[0] == '*' && greeting[1] == 0)
5380 unsigned int length = strlen(greeting);
5381 if(length > chanserv_conf.greeting_length)
5383 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5386 *data = strdup(greeting);
5395 reply(name, user_find_message(user, "MSG_NONE"));
5399 static MODCMD_FUNC(chan_opt_greeting)
5401 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5404 static MODCMD_FUNC(chan_opt_usergreeting)
5406 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5409 static MODCMD_FUNC(chan_opt_modes)
5411 struct mod_chanmode *new_modes;
5416 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5418 reply("CSMSG_NO_ACCESS");
5421 if(argv[1][0] == '*' && argv[1][1] == 0)
5423 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5425 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5427 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5430 else if(new_modes->argc > 1)
5432 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5433 mod_chanmode_free(new_modes);
5438 channel->channel_info->modes = *new_modes;
5439 modcmd_chanmode_announce(new_modes);
5440 mod_chanmode_free(new_modes);
5444 mod_chanmode_format(&channel->channel_info->modes, modes);
5446 reply("CSMSG_SET_MODES", modes);
5448 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5452 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5454 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5456 struct chanData *cData = channel->channel_info;
5461 /* Set flag according to value. */
5462 if(enabled_string(argv[1]))
5464 cData->flags |= mask;
5467 else if(disabled_string(argv[1]))
5469 cData->flags &= ~mask;
5474 reply("MSG_INVALID_BINARY", argv[1]);
5480 /* Find current option value. */
5481 value = (cData->flags & mask) ? 1 : 0;
5485 reply(name, user_find_message(user, "MSG_ON"));
5487 reply(name, user_find_message(user, "MSG_OFF"));
5491 static MODCMD_FUNC(chan_opt_nodelete)
5493 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5495 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5499 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5502 static MODCMD_FUNC(chan_opt_dynlimit)
5504 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5507 static MODCMD_FUNC(chan_opt_offchannel)
5509 struct chanData *cData = channel->channel_info;
5514 /* Set flag according to value. */
5515 if(enabled_string(argv[1]))
5517 if(!IsOffChannel(cData))
5518 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5519 cData->flags |= CHANNEL_OFFCHANNEL;
5522 else if(disabled_string(argv[1]))
5524 if(IsOffChannel(cData))
5526 struct mod_chanmode change;
5527 mod_chanmode_init(&change);
5529 change.args[0].mode = MODE_CHANOP;
5530 change.args[0].u.member = AddChannelUser(chanserv, channel);
5531 mod_chanmode_announce(chanserv, channel, &change);
5533 cData->flags &= ~CHANNEL_OFFCHANNEL;
5538 reply("MSG_INVALID_BINARY", argv[1]);
5544 /* Find current option value. */
5545 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5549 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5551 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5555 static MODCMD_FUNC(chan_opt_unreviewed)
5557 struct chanData *cData = channel->channel_info;
5558 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5564 /* The two directions can have different ACLs. */
5565 if(enabled_string(argv[1]))
5567 else if(disabled_string(argv[1]))
5571 reply("MSG_INVALID_BINARY", argv[1]);
5575 if (new_value != value)
5577 struct svccmd *subcmd;
5578 char subcmd_name[32];
5580 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5581 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5584 reply("MSG_COMMAND_DISABLED", subcmd_name);
5587 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5591 cData->flags |= CHANNEL_UNREVIEWED;
5594 free(cData->registrar);
5595 cData->registrar = strdup(user->handle_info->handle);
5596 cData->flags &= ~CHANNEL_UNREVIEWED;
5603 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5605 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5609 static MODCMD_FUNC(chan_opt_defaults)
5611 struct userData *uData;
5612 struct chanData *cData;
5613 const char *confirm;
5614 enum levelOption lvlOpt;
5615 enum charOption chOpt;
5617 cData = channel->channel_info;
5618 uData = GetChannelUser(cData, user->handle_info);
5619 if(!uData || (uData->access < UL_OWNER))
5621 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5624 confirm = make_confirmation_string(uData);
5625 if((argc < 2) || strcmp(argv[1], confirm))
5627 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5630 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5631 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5632 cData->modes = chanserv_conf.default_modes;
5633 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5634 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5635 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5636 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5637 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5642 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5644 struct chanData *cData = channel->channel_info;
5645 struct userData *uData;
5646 unsigned short value;
5650 if(!check_user_level(channel, user, option, 1, 1))
5652 reply("CSMSG_CANNOT_SET");
5655 value = user_level_from_name(argv[1], UL_OWNER+1);
5656 if(!value && strcmp(argv[1], "0"))
5658 reply("CSMSG_INVALID_ACCESS", argv[1]);
5661 uData = GetChannelUser(cData, user->handle_info);
5662 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5664 reply("CSMSG_BAD_SETLEVEL");
5670 if(value > cData->lvlOpts[lvlGiveOps])
5672 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5677 if(value < cData->lvlOpts[lvlGiveVoice])
5679 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5684 /* This test only applies to owners, since non-owners
5685 * trying to set an option to above their level get caught
5686 * by the CSMSG_BAD_SETLEVEL test above.
5688 if(value > uData->access)
5690 reply("CSMSG_BAD_SETTERS");
5697 cData->lvlOpts[option] = value;
5699 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5703 static MODCMD_FUNC(chan_opt_enfops)
5705 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5708 static MODCMD_FUNC(chan_opt_giveops)
5710 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5713 static MODCMD_FUNC(chan_opt_enfmodes)
5715 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5718 static MODCMD_FUNC(chan_opt_enftopic)
5720 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5723 static MODCMD_FUNC(chan_opt_pubcmd)
5725 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5728 static MODCMD_FUNC(chan_opt_setters)
5730 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5733 static MODCMD_FUNC(chan_opt_ctcpusers)
5735 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5738 static MODCMD_FUNC(chan_opt_userinfo)
5740 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5743 static MODCMD_FUNC(chan_opt_givevoice)
5745 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5748 static MODCMD_FUNC(chan_opt_topicsnarf)
5750 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5753 static MODCMD_FUNC(chan_opt_inviteme)
5755 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5759 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5761 struct chanData *cData = channel->channel_info;
5762 int count = charOptions[option].count, idx;
5766 idx = atoi(argv[1]);
5768 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5770 reply("CSMSG_INVALID_NUMERIC", idx);
5771 /* Show possible values. */
5772 for(idx = 0; idx < count; idx++)
5773 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5777 cData->chOpts[option] = charOptions[option].values[idx].value;
5781 /* Find current option value. */
5784 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5788 /* Somehow, the option value is corrupt; reset it to the default. */
5789 cData->chOpts[option] = charOptions[option].default_value;
5794 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5798 static MODCMD_FUNC(chan_opt_protect)
5800 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5803 static MODCMD_FUNC(chan_opt_toys)
5805 return channel_multiple_option(chToys, CSFUNC_ARGS);
5808 static MODCMD_FUNC(chan_opt_ctcpreaction)
5810 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5813 static MODCMD_FUNC(chan_opt_topicrefresh)
5815 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5818 static struct svccmd_list set_shows_list;
5821 handle_svccmd_unbind(struct svccmd *target) {
5823 for(ii=0; ii<set_shows_list.used; ++ii)
5824 if(target == set_shows_list.list[ii])
5825 set_shows_list.used = 0;
5828 static CHANSERV_FUNC(cmd_set)
5830 struct svccmd *subcmd;
5834 /* Check if we need to (re-)initialize set_shows_list. */
5835 if(!set_shows_list.used)
5837 if(!set_shows_list.size)
5839 set_shows_list.size = chanserv_conf.set_shows->used;
5840 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5842 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5844 const char *name = chanserv_conf.set_shows->list[ii];
5845 sprintf(buf, "%s %s", argv[0], name);
5846 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5849 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5852 svccmd_list_append(&set_shows_list, subcmd);
5858 reply("CSMSG_CHANNEL_OPTIONS");
5859 for(ii = 0; ii < set_shows_list.used; ii++)
5861 subcmd = set_shows_list.list[ii];
5862 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5867 sprintf(buf, "%s %s", argv[0], argv[1]);
5868 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5871 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5874 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5876 reply("CSMSG_NO_ACCESS");
5882 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5886 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5888 struct userData *uData;
5890 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5893 reply("CSMSG_NOT_USER", channel->name);
5899 /* Just show current option value. */
5901 else if(enabled_string(argv[1]))
5903 uData->flags |= mask;
5905 else if(disabled_string(argv[1]))
5907 uData->flags &= ~mask;
5911 reply("MSG_INVALID_BINARY", argv[1]);
5915 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5919 static MODCMD_FUNC(user_opt_noautoop)
5921 struct userData *uData;
5923 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5926 reply("CSMSG_NOT_USER", channel->name);
5929 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5930 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5932 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5935 static MODCMD_FUNC(user_opt_autoinvite)
5937 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5939 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5941 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5944 static MODCMD_FUNC(user_opt_info)
5946 struct userData *uData;
5949 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5953 /* If they got past the command restrictions (which require access)
5954 * but fail this test, we have some fool with security override on.
5956 reply("CSMSG_NOT_USER", channel->name);
5963 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5964 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5966 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5969 bp = strcspn(infoline, "\001");
5972 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5977 if(infoline[0] == '*' && infoline[1] == 0)
5980 uData->info = strdup(infoline);
5983 reply("CSMSG_USET_INFO", uData->info);
5985 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5989 struct svccmd_list uset_shows_list;
5991 static CHANSERV_FUNC(cmd_uset)
5993 struct svccmd *subcmd;
5997 /* Check if we need to (re-)initialize uset_shows_list. */
5998 if(!uset_shows_list.used)
6002 "NoAutoOp", "AutoInvite", "Info"
6005 if(!uset_shows_list.size)
6007 uset_shows_list.size = ArrayLength(options);
6008 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6010 for(ii = 0; ii < ArrayLength(options); ii++)
6012 const char *name = options[ii];
6013 sprintf(buf, "%s %s", argv[0], name);
6014 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6017 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6020 svccmd_list_append(&uset_shows_list, subcmd);
6026 /* Do this so options are presented in a consistent order. */
6027 reply("CSMSG_USER_OPTIONS");
6028 for(ii = 0; ii < uset_shows_list.used; ii++)
6029 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6033 sprintf(buf, "%s %s", argv[0], argv[1]);
6034 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6037 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6041 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6044 static CHANSERV_FUNC(cmd_giveownership)
6046 struct handle_info *new_owner_hi;
6047 struct userData *new_owner;
6048 struct userData *curr_user;
6049 struct userData *invoker;
6050 struct chanData *cData = channel->channel_info;
6051 struct do_not_register *dnr;
6052 const char *confirm;
6054 unsigned short co_access;
6055 char reason[MAXLEN];
6058 curr_user = GetChannelAccess(cData, user->handle_info);
6059 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6060 if(!curr_user || (curr_user->access != UL_OWNER))
6062 struct userData *owner = NULL;
6063 for(curr_user = channel->channel_info->users;
6065 curr_user = curr_user->next)
6067 if(curr_user->access != UL_OWNER)
6071 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6078 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6080 char delay[INTERVALLEN];
6081 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6082 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6085 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6087 if(new_owner_hi == user->handle_info)
6089 reply("CSMSG_NO_TRANSFER_SELF");
6092 new_owner = GetChannelAccess(cData, new_owner_hi);
6097 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6101 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6105 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6107 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6110 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6111 if(!IsHelping(user))
6112 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6114 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6117 invoker = GetChannelUser(cData, user->handle_info);
6118 if(invoker->access <= UL_OWNER)
6120 confirm = make_confirmation_string(curr_user);
6121 if((argc < 3) || strcmp(argv[2], confirm))
6123 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6127 if(new_owner->access >= UL_COOWNER)
6128 co_access = new_owner->access;
6130 co_access = UL_COOWNER;
6131 new_owner->access = UL_OWNER;
6133 curr_user->access = co_access;
6134 cData->ownerTransfer = now;
6135 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6136 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6137 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6141 static CHANSERV_FUNC(cmd_suspend)
6143 struct handle_info *hi;
6144 struct userData *actor, *real_actor, *target;
6145 unsigned int override = 0;
6148 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6149 actor = GetChannelUser(channel->channel_info, user->handle_info);
6150 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6151 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6153 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6156 if(target->access >= actor->access)
6158 reply("MSG_USER_OUTRANKED", hi->handle);
6161 if(target->flags & USER_SUSPENDED)
6163 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6168 target->present = 0;
6171 if(!real_actor || target->access >= real_actor->access)
6172 override = CMD_LOG_OVERRIDE;
6173 target->flags |= USER_SUSPENDED;
6174 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6175 return 1 | override;
6178 static CHANSERV_FUNC(cmd_unsuspend)
6180 struct handle_info *hi;
6181 struct userData *actor, *real_actor, *target;
6182 unsigned int override = 0;
6185 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6186 actor = GetChannelUser(channel->channel_info, user->handle_info);
6187 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6188 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6190 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6193 if(target->access >= actor->access)
6195 reply("MSG_USER_OUTRANKED", hi->handle);
6198 if(!(target->flags & USER_SUSPENDED))
6200 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6203 if(!real_actor || target->access >= real_actor->access)
6204 override = CMD_LOG_OVERRIDE;
6205 target->flags &= ~USER_SUSPENDED;
6206 scan_user_presence(target, NULL);
6207 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6208 return 1 | override;
6211 static MODCMD_FUNC(cmd_deleteme)
6213 struct handle_info *hi;
6214 struct userData *target;
6215 const char *confirm_string;
6216 unsigned short access_level;
6219 hi = user->handle_info;
6220 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6222 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6225 if(target->access == UL_OWNER)
6227 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6230 confirm_string = make_confirmation_string(target);
6231 if((argc < 2) || strcmp(argv[1], confirm_string))
6233 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6236 access_level = target->access;
6237 channel_name = strdup(channel->name);
6238 del_channel_user(target, 1);
6239 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6245 chanserv_refresh_topics(UNUSED_ARG(void *data))
6247 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6248 struct chanData *cData;
6251 for(cData = channelList; cData; cData = cData->next)
6253 if(IsSuspended(cData))
6255 opt = cData->chOpts[chTopicRefresh];
6258 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6261 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6262 cData->last_refresh = refresh_num;
6264 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6267 static CHANSERV_FUNC(cmd_unf)
6271 char response[MAXLEN];
6272 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6273 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6274 irc_privmsg(cmd->parent->bot, channel->name, response);
6277 reply("CSMSG_UNF_RESPONSE");
6281 static CHANSERV_FUNC(cmd_ping)
6285 char response[MAXLEN];
6286 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6287 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6288 irc_privmsg(cmd->parent->bot, channel->name, response);
6291 reply("CSMSG_PING_RESPONSE");
6295 static CHANSERV_FUNC(cmd_wut)
6299 char response[MAXLEN];
6300 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6301 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6302 irc_privmsg(cmd->parent->bot, channel->name, response);
6305 reply("CSMSG_WUT_RESPONSE");
6309 static CHANSERV_FUNC(cmd_8ball)
6311 unsigned int i, j, accum;
6316 for(i=1; i<argc; i++)
6317 for(j=0; argv[i][j]; j++)
6318 accum = (accum << 5) - accum + toupper(argv[i][j]);
6319 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6322 char response[MAXLEN];
6323 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6324 irc_privmsg(cmd->parent->bot, channel->name, response);
6327 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6331 static CHANSERV_FUNC(cmd_d)
6333 unsigned long sides, count, modifier, ii, total;
6334 char response[MAXLEN], *sep;
6338 if((count = strtoul(argv[1], &sep, 10)) < 1)
6348 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6349 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6353 else if((sep[0] == '-') && isdigit(sep[1]))
6354 modifier = strtoul(sep, NULL, 10);
6355 else if((sep[0] == '+') && isdigit(sep[1]))
6356 modifier = strtoul(sep+1, NULL, 10);
6363 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6368 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6371 for(total = ii = 0; ii < count; ++ii)
6372 total += (rand() % sides) + 1;
6375 if((count > 1) || modifier)
6377 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6378 sprintf(response, fmt, total, count, sides, modifier);
6382 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6383 sprintf(response, fmt, total, sides);
6386 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6388 send_message_type(4, user, cmd->parent->bot, "%s", response);
6392 static CHANSERV_FUNC(cmd_huggle)
6394 /* CTCP must be via PRIVMSG, never notice */
6396 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6398 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6403 chanserv_adjust_limit(void *data)
6405 struct mod_chanmode change;
6406 struct chanData *cData = data;
6407 struct chanNode *channel = cData->channel;
6410 if(IsSuspended(cData))
6413 cData->limitAdjusted = now;
6414 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6415 if(cData->modes.modes_set & MODE_LIMIT)
6417 if(limit > cData->modes.new_limit)
6418 limit = cData->modes.new_limit;
6419 else if(limit == cData->modes.new_limit)
6423 mod_chanmode_init(&change);
6424 change.modes_set = MODE_LIMIT;
6425 change.new_limit = limit;
6426 mod_chanmode_announce(chanserv, channel, &change);
6430 handle_new_channel(struct chanNode *channel)
6432 struct chanData *cData;
6434 if(!(cData = channel->channel_info))
6437 if(cData->modes.modes_set || cData->modes.modes_clear)
6438 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6440 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6441 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6444 /* Welcome to my worst nightmare. Warning: Read (or modify)
6445 the code below at your own risk. */
6447 handle_join(struct modeNode *mNode)
6449 struct mod_chanmode change;
6450 struct userNode *user = mNode->user;
6451 struct chanNode *channel = mNode->channel;
6452 struct chanData *cData;
6453 struct userData *uData = NULL;
6454 struct banData *bData;
6455 struct handle_info *handle;
6456 unsigned int modes = 0, info = 0;
6459 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6462 cData = channel->channel_info;
6463 if(channel->members.used > cData->max)
6464 cData->max = channel->members.used;
6466 /* Check for bans. If they're joining through a ban, one of two
6468 * 1: Join during a netburst, by riding the break. Kick them
6469 * unless they have ops or voice in the channel.
6470 * 2: They're allowed to join through the ban (an invite in
6471 * ircu2.10, or a +e on Hybrid, or something).
6472 * If they're not joining through a ban, and the banlist is not
6473 * full, see if they're on the banlist for the channel. If so,
6476 if(user->uplink->burst && !mNode->modes)
6479 for(ii = 0; ii < channel->banlist.used; ii++)
6481 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6483 /* Riding a netburst. Naughty. */
6484 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6490 mod_chanmode_init(&change);
6492 if(channel->banlist.used < MAXBANS)
6494 /* Not joining through a ban. */
6495 for(bData = cData->bans;
6496 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6497 bData = bData->next);
6501 char kick_reason[MAXLEN];
6502 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6504 bData->triggered = now;
6505 if(bData != cData->bans)
6507 /* Shuffle the ban to the head of the list. */
6509 bData->next->prev = bData->prev;
6511 bData->prev->next = bData->next;
6514 bData->next = cData->bans;
6517 cData->bans->prev = bData;
6518 cData->bans = bData;
6521 change.args[0].mode = MODE_BAN;
6522 change.args[0].u.hostmask = bData->mask;
6523 mod_chanmode_announce(chanserv, channel, &change);
6524 KickChannelUser(user, channel, chanserv, kick_reason);
6529 /* ChanServ will not modify the limits in join-flooded channels,
6530 or when there are enough slots left below the limit. */
6531 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6532 && !channel->join_flooded
6533 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6535 /* The user count has begun "bumping" into the channel limit,
6536 so set a timer to raise the limit a bit. Any previous
6537 timers are removed so three incoming users within the delay
6538 results in one limit change, not three. */
6540 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6541 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6544 if(channel->join_flooded)
6546 /* don't automatically give ops or voice during a join flood */
6548 else if(cData->lvlOpts[lvlGiveOps] == 0)
6549 modes |= MODE_CHANOP;
6550 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6551 modes |= MODE_VOICE;
6553 greeting = cData->greeting;
6554 if(user->handle_info)
6556 handle = user->handle_info;
6558 if(IsHelper(user) && !IsHelping(user))
6561 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6563 if(channel == chanserv_conf.support_channels.list[ii])
6565 HANDLE_SET_FLAG(user->handle_info, HELPING);
6571 uData = GetTrueChannelAccess(cData, handle);
6572 if(uData && !IsUserSuspended(uData))
6574 /* Ops and above were handled by the above case. */
6575 if(IsUserAutoOp(uData))
6577 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6578 modes |= MODE_CHANOP;
6579 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6580 modes |= MODE_VOICE;
6582 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6583 cData->visited = now;
6584 if(cData->user_greeting)
6585 greeting = cData->user_greeting;
6587 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6588 && ((now - uData->seen) >= chanserv_conf.info_delay)
6596 /* If user joining normally (not during burst), apply op or voice,
6597 * and send greeting/userinfo as appropriate.
6599 if(!user->uplink->burst)
6603 if(modes & MODE_CHANOP)
6604 modes &= ~MODE_VOICE;
6605 change.args[0].mode = modes;
6606 change.args[0].u.member = mNode;
6607 mod_chanmode_announce(chanserv, channel, &change);
6610 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6611 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6612 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6618 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6620 struct mod_chanmode change;
6621 struct userData *channel;
6622 unsigned int ii, jj;
6624 if(!user->handle_info)
6627 mod_chanmode_init(&change);
6629 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6631 struct chanNode *cn;
6632 struct modeNode *mn;
6633 if(IsUserSuspended(channel)
6634 || IsSuspended(channel->channel)
6635 || !(cn = channel->channel->channel))
6638 mn = GetUserMode(cn, user);
6641 if(!IsUserSuspended(channel)
6642 && IsUserAutoInvite(channel)
6643 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6645 && !user->uplink->burst)
6646 irc_invite(chanserv, user, cn);
6650 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6651 channel->channel->visited = now;
6653 if(IsUserAutoOp(channel))
6655 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6656 change.args[0].mode = MODE_CHANOP;
6657 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6658 change.args[0].mode = MODE_VOICE;
6660 change.args[0].mode = 0;
6661 change.args[0].u.member = mn;
6662 if(change.args[0].mode)
6663 mod_chanmode_announce(chanserv, cn, &change);
6666 channel->seen = now;
6667 channel->present = 1;
6670 for(ii = 0; ii < user->channels.used; ++ii)
6672 struct chanNode *chan = user->channels.list[ii]->channel;
6673 struct banData *ban;
6675 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6676 || !chan->channel_info
6677 || IsSuspended(chan->channel_info))
6679 for(jj = 0; jj < chan->banlist.used; ++jj)
6680 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6682 if(jj < chan->banlist.used)
6684 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6686 char kick_reason[MAXLEN];
6687 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6689 change.args[0].mode = MODE_BAN;
6690 change.args[0].u.hostmask = ban->mask;
6691 mod_chanmode_announce(chanserv, chan, &change);
6692 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6693 KickChannelUser(user, chan, chanserv, kick_reason);
6694 ban->triggered = now;
6699 if(IsSupportHelper(user))
6701 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6703 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6705 HANDLE_SET_FLAG(user->handle_info, HELPING);
6713 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6715 struct chanData *cData;
6716 struct userData *uData;
6718 cData = mn->channel->channel_info;
6719 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6722 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6724 /* Allow for a bit of padding so that the limit doesn't
6725 track the user count exactly, which could get annoying. */
6726 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6728 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6729 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6733 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6735 scan_user_presence(uData, mn->user);
6737 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6738 cData->visited = now;
6741 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6744 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6745 struct chanNode *channel;
6746 struct userNode *exclude;
6747 /* When looking at the channel that is being /part'ed, we
6748 * have to skip over the client that is leaving. For
6749 * other channels, we must not do that.
6751 channel = chanserv_conf.support_channels.list[ii];
6752 exclude = (channel == mn->channel) ? mn->user : NULL;
6753 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6756 if(ii == chanserv_conf.support_channels.used)
6757 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6762 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6764 struct userData *uData;
6766 if(!channel->channel_info || !kicker || IsService(kicker)
6767 || (kicker == victim) || IsSuspended(channel->channel_info)
6768 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6771 if(protect_user(victim, kicker, channel->channel_info))
6773 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6774 KickChannelUser(kicker, channel, chanserv, reason);
6777 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6782 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6784 struct chanData *cData;
6786 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6789 cData = channel->channel_info;
6790 if(bad_topic(channel, user, channel->topic))
6792 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6793 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6794 SetChannelTopic(channel, chanserv, old_topic, 1);
6795 else if(cData->topic)
6796 SetChannelTopic(channel, chanserv, cData->topic, 1);
6799 /* With topicsnarf, grab the topic and save it as the default topic. */
6800 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6803 cData->topic = strdup(channel->topic);
6809 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6811 struct mod_chanmode *bounce = NULL;
6812 unsigned int bnc, ii;
6815 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6818 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6819 && mode_lock_violated(&channel->channel_info->modes, change))
6821 char correct[MAXLEN];
6822 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6823 mod_chanmode_format(&channel->channel_info->modes, correct);
6824 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6826 for(ii = bnc = 0; ii < change->argc; ++ii)
6828 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6830 const struct userNode *victim = change->args[ii].u.member->user;
6831 if(!protect_user(victim, user, channel->channel_info))
6834 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6837 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6838 bounce->args[bnc].u.member = GetUserMode(channel, user);
6839 if(bounce->args[bnc].u.member)
6843 bounce->args[bnc].mode = MODE_CHANOP;
6844 bounce->args[bnc].u.member = change->args[ii].u.member;
6846 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6848 else if(change->args[ii].mode & MODE_CHANOP)
6850 const struct userNode *victim = change->args[ii].u.member->user;
6851 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6854 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6855 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6856 bounce->args[bnc].u.member = change->args[ii].u.member;
6859 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6861 const char *ban = change->args[ii].u.hostmask;
6862 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6865 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6866 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6867 bounce->args[bnc].u.hostmask = strdup(ban);
6869 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6874 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6875 mod_chanmode_announce(chanserv, channel, bounce);
6876 for(ii = 0; ii < change->argc; ++ii)
6877 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6878 free((char*)bounce->args[ii].u.hostmask);
6879 mod_chanmode_free(bounce);
6884 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6886 struct chanNode *channel;
6887 struct banData *bData;
6888 struct mod_chanmode change;
6889 unsigned int ii, jj;
6890 char kick_reason[MAXLEN];
6892 mod_chanmode_init(&change);
6894 change.args[0].mode = MODE_BAN;
6895 for(ii = 0; ii < user->channels.used; ++ii)
6897 channel = user->channels.list[ii]->channel;
6898 /* Need not check for bans if they're opped or voiced. */
6899 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6901 /* Need not check for bans unless channel registration is active. */
6902 if(!channel->channel_info || IsSuspended(channel->channel_info))
6904 /* Look for a matching ban already on the channel. */
6905 for(jj = 0; jj < channel->banlist.used; ++jj)
6906 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6908 /* Need not act if we found one. */
6909 if(jj < channel->banlist.used)
6911 /* Look for a matching ban in this channel. */
6912 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6914 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6916 change.args[0].u.hostmask = bData->mask;
6917 mod_chanmode_announce(chanserv, channel, &change);
6918 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6919 KickChannelUser(user, channel, chanserv, kick_reason);
6920 bData->triggered = now;
6921 break; /* we don't need to check any more bans in the channel */
6926 static void handle_rename(struct handle_info *handle, const char *old_handle)
6928 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6932 dict_remove2(handle_dnrs, old_handle, 1);
6933 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6934 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6939 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6941 struct userNode *h_user;
6943 if(handle->channels)
6945 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6946 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6948 while(handle->channels)
6949 del_channel_user(handle->channels, 1);
6954 handle_server_link(UNUSED_ARG(struct server *server))
6956 struct chanData *cData;
6958 for(cData = channelList; cData; cData = cData->next)
6960 if(!IsSuspended(cData))
6961 cData->may_opchan = 1;
6962 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6963 && !cData->channel->join_flooded
6964 && ((cData->channel->limit - cData->channel->members.used)
6965 < chanserv_conf.adjust_threshold))
6967 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6968 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6974 chanserv_conf_read(void)
6978 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6979 struct mod_chanmode *change;
6980 struct string_list *strlist;
6981 struct chanNode *chan;
6984 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6986 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6989 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6990 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6991 chanserv_conf.support_channels.used = 0;
6992 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6994 for(ii = 0; ii < strlist->used; ++ii)
6996 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6999 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7001 channelList_append(&chanserv_conf.support_channels, chan);
7004 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7007 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7010 chan = AddChannel(str, now, str2, NULL);
7012 channelList_append(&chanserv_conf.support_channels, chan);
7014 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7015 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7016 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7017 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7018 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7019 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7020 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7021 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7022 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7023 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7024 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7025 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7026 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7027 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7028 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7029 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7030 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7031 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7032 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7033 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7034 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7035 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7036 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7037 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7038 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7040 NickChange(chanserv, str, 0);
7041 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7042 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7043 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7044 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7045 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7046 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7047 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7048 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7049 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7050 chanserv_conf.max_owned = str ? atoi(str) : 5;
7051 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7052 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7053 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7054 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7055 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7056 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7057 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7060 safestrncpy(mode_line, str, sizeof(mode_line));
7061 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7062 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7063 && (change->argc < 2))
7065 chanserv_conf.default_modes = *change;
7066 mod_chanmode_free(change);
7068 free_string_list(chanserv_conf.set_shows);
7069 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7071 strlist = string_list_copy(strlist);
7074 static const char *list[] = {
7075 /* free form text */
7076 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7077 /* options based on user level */
7078 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7079 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7080 /* multiple choice options */
7081 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7082 /* binary options */
7083 "DynLimit", "NoDelete",
7087 strlist = alloc_string_list(ArrayLength(list)-1);
7088 for(ii=0; list[ii]; ii++)
7089 string_list_append(strlist, strdup(list[ii]));
7091 chanserv_conf.set_shows = strlist;
7092 /* We don't look things up now, in case the list refers to options
7093 * defined by modules initialized after this point. Just mark the
7094 * function list as invalid, so it will be initialized.
7096 set_shows_list.used = 0;
7097 free_string_list(chanserv_conf.eightball);
7098 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7101 strlist = string_list_copy(strlist);
7105 strlist = alloc_string_list(4);
7106 string_list_append(strlist, strdup("Yes."));
7107 string_list_append(strlist, strdup("No."));
7108 string_list_append(strlist, strdup("Maybe so."));
7110 chanserv_conf.eightball = strlist;
7111 free_string_list(chanserv_conf.old_ban_names);
7112 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7114 strlist = string_list_copy(strlist);
7116 strlist = alloc_string_list(2);
7117 chanserv_conf.old_ban_names = strlist;
7118 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7119 off_channel = str ? atoi(str) : 0;
7123 chanserv_note_type_read(const char *key, struct record_data *rd)
7126 struct note_type *ntype;
7129 if(!(obj = GET_RECORD_OBJECT(rd)))
7131 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7134 if(!(ntype = chanserv_create_note_type(key)))
7136 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7140 /* Figure out set access */
7141 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7143 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7144 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7146 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7148 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7149 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7151 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7153 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7157 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7158 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7159 ntype->set_access.min_opserv = 0;
7162 /* Figure out visibility */
7163 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7164 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7165 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7166 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7167 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7168 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7169 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7170 ntype->visible_type = NOTE_VIS_ALL;
7172 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7174 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7175 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7179 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7181 struct handle_info *handle;
7182 struct userData *uData;
7183 char *seen, *inf, *flags;
7184 unsigned long last_seen;
7185 unsigned short access_level;
7187 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7189 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7193 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7194 if(access_level > UL_OWNER)
7196 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7200 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7201 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7202 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7203 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7204 handle = get_handle_info(key);
7207 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7211 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7212 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7216 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7218 struct banData *bData;
7219 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7220 unsigned long set_time, triggered_time, expires_time;
7222 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7224 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7228 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7229 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7230 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7231 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7232 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7233 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7234 if (!reason || !owner)
7237 set_time = set ? strtoul(set, NULL, 0) : now;
7238 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7240 expires_time = strtoul(s_expires, NULL, 0);
7242 expires_time = set_time + atoi(s_duration);
7246 if(!reason || (expires_time && (expires_time < now)))
7249 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7252 static struct suspended *
7253 chanserv_read_suspended(dict_t obj)
7255 struct suspended *suspended = calloc(1, sizeof(*suspended));
7259 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7260 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7261 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7262 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7263 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7264 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7265 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7266 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7267 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7268 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7273 chanserv_channel_read(const char *key, struct record_data *hir)
7275 struct suspended *suspended;
7276 struct mod_chanmode *modes;
7277 struct chanNode *cNode;
7278 struct chanData *cData;
7279 struct dict *channel, *obj;
7280 char *str, *argv[10];
7284 channel = hir->d.object;
7286 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7289 cNode = AddChannel(key, now, NULL, NULL);
7292 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7295 cData = register_channel(cNode, str);
7298 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7302 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7304 enum levelOption lvlOpt;
7305 enum charOption chOpt;
7307 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7308 cData->flags = atoi(str);
7310 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7312 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7314 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7315 else if(levelOptions[lvlOpt].old_flag)
7317 if(cData->flags & levelOptions[lvlOpt].old_flag)
7318 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7320 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7324 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7326 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7328 cData->chOpts[chOpt] = str[0];
7331 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7333 enum levelOption lvlOpt;
7334 enum charOption chOpt;
7337 cData->flags = base64toint(str, 5);
7338 count = strlen(str += 5);
7339 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7342 if(levelOptions[lvlOpt].old_flag)
7344 if(cData->flags & levelOptions[lvlOpt].old_flag)
7345 lvl = levelOptions[lvlOpt].flag_value;
7347 lvl = levelOptions[lvlOpt].default_value;
7349 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7351 case 'c': lvl = UL_COOWNER; break;
7352 case 'm': lvl = UL_MASTER; break;
7353 case 'n': lvl = UL_OWNER+1; break;
7354 case 'o': lvl = UL_OP; break;
7355 case 'p': lvl = UL_PEON; break;
7356 case 'w': lvl = UL_OWNER; break;
7357 default: lvl = 0; break;
7359 cData->lvlOpts[lvlOpt] = lvl;
7361 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7362 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7365 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7367 suspended = chanserv_read_suspended(obj);
7368 cData->suspended = suspended;
7369 suspended->cData = cData;
7370 /* We could use suspended->expires and suspended->revoked to
7371 * set the CHANNEL_SUSPENDED flag, but we don't. */
7373 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7375 suspended = calloc(1, sizeof(*suspended));
7376 suspended->issued = 0;
7377 suspended->revoked = 0;
7378 suspended->suspender = strdup(str);
7379 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7380 suspended->expires = str ? atoi(str) : 0;
7381 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7382 suspended->reason = strdup(str ? str : "No reason");
7383 suspended->previous = NULL;
7384 cData->suspended = suspended;
7385 suspended->cData = cData;
7389 cData->flags &= ~CHANNEL_SUSPENDED;
7390 suspended = NULL; /* to squelch a warning */
7393 if(IsSuspended(cData)) {
7394 if(suspended->expires > now)
7395 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7396 else if(suspended->expires)
7397 cData->flags &= ~CHANNEL_SUSPENDED;
7400 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7401 struct mod_chanmode change;
7402 mod_chanmode_init(&change);
7404 change.args[0].mode = MODE_CHANOP;
7405 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7406 mod_chanmode_announce(chanserv, cNode, &change);
7409 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7410 cData->registered = str ? strtoul(str, NULL, 0) : now;
7411 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7412 cData->visited = str ? strtoul(str, NULL, 0) : now;
7413 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7414 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7415 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7416 cData->max = str ? atoi(str) : 0;
7417 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7418 cData->greeting = str ? strdup(str) : NULL;
7419 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7420 cData->user_greeting = str ? strdup(str) : NULL;
7421 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7422 cData->topic_mask = str ? strdup(str) : NULL;
7423 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7424 cData->topic = str ? strdup(str) : NULL;
7426 if(!IsSuspended(cData)
7427 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7428 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7429 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7430 cData->modes = *modes;
7432 cData->modes.modes_set |= MODE_REGISTERED;
7433 if(cData->modes.argc > 1)
7434 cData->modes.argc = 1;
7435 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7436 mod_chanmode_free(modes);
7439 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7440 for(it = dict_first(obj); it; it = iter_next(it))
7441 user_read_helper(iter_key(it), iter_data(it), cData);
7443 if(!cData->users && !IsProtected(cData))
7445 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7446 unregister_channel(cData, "has empty user list.");
7450 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7451 for(it = dict_first(obj); it; it = iter_next(it))
7452 ban_read_helper(iter_key(it), iter_data(it), cData);
7454 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7455 for(it = dict_first(obj); it; it = iter_next(it))
7457 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7458 struct record_data *rd = iter_data(it);
7459 const char *note, *setter;
7461 if(rd->type != RECDB_OBJECT)
7463 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7467 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7469 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7471 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7475 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7476 if(!setter) setter = "<unknown>";
7477 chanserv_add_channel_note(cData, ntype, setter, note);
7485 chanserv_dnr_read(const char *key, struct record_data *hir)
7487 const char *setter, *reason, *str;
7488 struct do_not_register *dnr;
7489 unsigned long expiry;
7491 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7494 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7497 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7500 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7503 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7504 expiry = str ? strtoul(str, NULL, 0) : 0;
7505 if(expiry && expiry <= now)
7507 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7510 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7512 dnr->set = atoi(str);
7518 chanserv_saxdb_read(struct dict *database)
7520 struct dict *section;
7523 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7524 for(it = dict_first(section); it; it = iter_next(it))
7525 chanserv_note_type_read(iter_key(it), iter_data(it));
7527 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7528 for(it = dict_first(section); it; it = iter_next(it))
7529 chanserv_channel_read(iter_key(it), iter_data(it));
7531 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7532 for(it = dict_first(section); it; it = iter_next(it))
7533 chanserv_dnr_read(iter_key(it), iter_data(it));
7539 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7541 int high_present = 0;
7542 saxdb_start_record(ctx, KEY_USERS, 1);
7543 for(; uData; uData = uData->next)
7545 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7547 saxdb_start_record(ctx, uData->handle->handle, 0);
7548 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7549 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7551 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7553 saxdb_write_string(ctx, KEY_INFO, uData->info);
7554 saxdb_end_record(ctx);
7556 saxdb_end_record(ctx);
7557 return high_present;
7561 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7565 saxdb_start_record(ctx, KEY_BANS, 1);
7566 for(; bData; bData = bData->next)
7568 saxdb_start_record(ctx, bData->mask, 0);
7569 saxdb_write_int(ctx, KEY_SET, bData->set);
7570 if(bData->triggered)
7571 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7573 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7575 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7577 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7578 saxdb_end_record(ctx);
7580 saxdb_end_record(ctx);
7584 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7586 saxdb_start_record(ctx, name, 0);
7587 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7588 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7590 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7592 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7594 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7596 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7597 saxdb_end_record(ctx);
7601 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7605 enum levelOption lvlOpt;
7606 enum charOption chOpt;
7608 saxdb_start_record(ctx, channel->channel->name, 1);
7610 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7611 saxdb_write_int(ctx, KEY_MAX, channel->max);
7613 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7614 if(channel->registrar)
7615 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7616 if(channel->greeting)
7617 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7618 if(channel->user_greeting)
7619 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7620 if(channel->topic_mask)
7621 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7622 if(channel->suspended)
7623 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7625 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7626 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7627 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7628 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7629 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7631 buf[0] = channel->chOpts[chOpt];
7633 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7635 saxdb_end_record(ctx);
7637 if(channel->modes.modes_set || channel->modes.modes_clear)
7639 mod_chanmode_format(&channel->modes, buf);
7640 saxdb_write_string(ctx, KEY_MODES, buf);
7643 high_present = chanserv_write_users(ctx, channel->users);
7644 chanserv_write_bans(ctx, channel->bans);
7646 if(dict_size(channel->notes))
7650 saxdb_start_record(ctx, KEY_NOTES, 1);
7651 for(it = dict_first(channel->notes); it; it = iter_next(it))
7653 struct note *note = iter_data(it);
7654 saxdb_start_record(ctx, iter_key(it), 0);
7655 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7656 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7657 saxdb_end_record(ctx);
7659 saxdb_end_record(ctx);
7662 if(channel->ownerTransfer)
7663 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7664 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7665 saxdb_end_record(ctx);
7669 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7673 saxdb_start_record(ctx, ntype->name, 0);
7674 switch(ntype->set_access_type)
7676 case NOTE_SET_CHANNEL_ACCESS:
7677 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7679 case NOTE_SET_CHANNEL_SETTER:
7680 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7682 case NOTE_SET_PRIVILEGED: default:
7683 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7686 switch(ntype->visible_type)
7688 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7689 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7690 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7692 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7693 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7694 saxdb_end_record(ctx);
7698 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7700 struct do_not_register *dnr;
7701 dict_iterator_t it, next;
7703 for(it = dict_first(dnrs); it; it = next)
7705 next = iter_next(it);
7706 dnr = iter_data(it);
7707 if(dnr->expires && dnr->expires <= now)
7709 dict_remove(dnrs, iter_key(it));
7712 saxdb_start_record(ctx, dnr->chan_name, 0);
7714 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7716 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7717 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7718 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7719 saxdb_end_record(ctx);
7724 chanserv_saxdb_write(struct saxdb_context *ctx)
7727 struct chanData *channel;
7730 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7731 for(it = dict_first(note_types); it; it = iter_next(it))
7732 chanserv_write_note_type(ctx, iter_data(it));
7733 saxdb_end_record(ctx);
7736 saxdb_start_record(ctx, KEY_DNR, 1);
7737 write_dnrs_helper(ctx, handle_dnrs);
7738 write_dnrs_helper(ctx, plain_dnrs);
7739 write_dnrs_helper(ctx, mask_dnrs);
7740 saxdb_end_record(ctx);
7743 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7744 for(channel = channelList; channel; channel = channel->next)
7745 chanserv_write_channel(ctx, channel);
7746 saxdb_end_record(ctx);
7752 chanserv_db_cleanup(void) {
7754 unreg_part_func(handle_part);
7756 unregister_channel(channelList, "terminating.");
7757 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7758 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7759 free(chanserv_conf.support_channels.list);
7760 dict_delete(handle_dnrs);
7761 dict_delete(plain_dnrs);
7762 dict_delete(mask_dnrs);
7763 dict_delete(note_types);
7764 free_string_list(chanserv_conf.eightball);
7765 free_string_list(chanserv_conf.old_ban_names);
7766 free_string_list(chanserv_conf.set_shows);
7767 free(set_shows_list.list);
7768 free(uset_shows_list.list);
7771 struct userData *helper = helperList;
7772 helperList = helperList->next;
7777 #if defined(GCC_VARMACROS)
7778 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7779 #elif defined(C99_VARMACROS)
7780 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7782 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7783 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7786 init_chanserv(const char *nick)
7788 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7789 conf_register_reload(chanserv_conf_read);
7793 reg_server_link_func(handle_server_link);
7794 reg_new_channel_func(handle_new_channel);
7795 reg_join_func(handle_join);
7796 reg_part_func(handle_part);
7797 reg_kick_func(handle_kick);
7798 reg_topic_func(handle_topic);
7799 reg_mode_change_func(handle_mode);
7800 reg_nick_change_func(handle_nick_change);
7801 reg_auth_func(handle_auth);
7804 reg_handle_rename_func(handle_rename);
7805 reg_unreg_func(handle_unreg);
7807 handle_dnrs = dict_new();
7808 dict_set_free_data(handle_dnrs, free);
7809 plain_dnrs = dict_new();
7810 dict_set_free_data(plain_dnrs, free);
7811 mask_dnrs = dict_new();
7812 dict_set_free_data(mask_dnrs, free);
7814 reg_svccmd_unbind_func(handle_svccmd_unbind);
7815 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7816 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7817 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7818 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7819 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7820 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7821 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7822 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7823 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7824 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7825 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7826 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7827 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7829 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7830 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7832 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7833 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7834 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7835 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7836 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7838 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7839 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7840 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7841 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7842 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7844 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7845 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7846 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7847 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7849 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7850 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7851 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7852 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7853 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7854 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7855 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7856 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7858 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7859 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7860 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7861 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7862 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7863 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7864 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7865 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7866 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7867 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7868 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7869 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7870 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7871 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7873 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7874 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7875 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7876 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7877 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7879 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7880 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7882 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7883 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7884 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7885 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7886 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7887 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7888 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7889 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7890 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7891 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7892 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7894 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7895 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7897 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7898 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7899 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7900 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7902 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7903 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7904 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7905 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7906 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7908 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7909 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7910 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7911 DEFINE_COMMAND(8ball, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7912 DEFINE_COMMAND(d, 2, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7913 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7915 /* Channel options */
7916 DEFINE_CHANNEL_OPTION(defaulttopic);
7917 DEFINE_CHANNEL_OPTION(topicmask);
7918 DEFINE_CHANNEL_OPTION(greeting);
7919 DEFINE_CHANNEL_OPTION(usergreeting);
7920 DEFINE_CHANNEL_OPTION(modes);
7921 DEFINE_CHANNEL_OPTION(enfops);
7922 DEFINE_CHANNEL_OPTION(giveops);
7923 DEFINE_CHANNEL_OPTION(protect);
7924 DEFINE_CHANNEL_OPTION(enfmodes);
7925 DEFINE_CHANNEL_OPTION(enftopic);
7926 DEFINE_CHANNEL_OPTION(pubcmd);
7927 DEFINE_CHANNEL_OPTION(givevoice);
7928 DEFINE_CHANNEL_OPTION(userinfo);
7929 DEFINE_CHANNEL_OPTION(dynlimit);
7930 DEFINE_CHANNEL_OPTION(topicsnarf);
7931 DEFINE_CHANNEL_OPTION(nodelete);
7932 DEFINE_CHANNEL_OPTION(toys);
7933 DEFINE_CHANNEL_OPTION(setters);
7934 DEFINE_CHANNEL_OPTION(topicrefresh);
7935 DEFINE_CHANNEL_OPTION(ctcpusers);
7936 DEFINE_CHANNEL_OPTION(ctcpreaction);
7937 DEFINE_CHANNEL_OPTION(inviteme);
7938 DEFINE_CHANNEL_OPTION(unreviewed);
7939 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7940 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7942 DEFINE_CHANNEL_OPTION(offchannel);
7943 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7945 /* Alias set topic to set defaulttopic for compatibility. */
7946 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7949 DEFINE_USER_OPTION(noautoop);
7950 DEFINE_USER_OPTION(autoinvite);
7951 DEFINE_USER_OPTION(info);
7953 /* Alias uset autovoice to uset autoop. */
7954 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7956 note_types = dict_new();
7957 dict_set_free_data(note_types, chanserv_deref_note_type);
7960 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7961 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7962 service_register(chanserv)->trigger = '!';
7963 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7965 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7967 if(chanserv_conf.channel_expire_frequency)
7968 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7970 if(chanserv_conf.dnr_expire_frequency)
7971 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7973 if(chanserv_conf.refresh_period)
7975 unsigned long next_refresh;
7976 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7977 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7980 reg_exit_func(chanserv_db_cleanup);
7981 message_register_table(msgtab);