1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
30 #define CHANSERV_CONF_NAME "services/chanserv"
32 /* ChanServ options */
33 #define KEY_SUPPORT_CHANNEL "support_channel"
34 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
35 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
36 #define KEY_INFO_DELAY "info_delay"
37 #define KEY_MAX_GREETLEN "max_greetlen"
38 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
39 #define KEY_ADJUST_DELAY "adjust_delay"
40 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
41 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
42 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
43 #define KEY_MAX_CHAN_USERS "max_chan_users"
44 #define KEY_MAX_CHAN_BANS "max_chan_bans"
45 #define KEY_NICK "nick"
46 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
47 #define KEY_8BALL_RESPONSES "8ball"
48 #define KEY_OLD_BAN_NAMES "old_ban_names"
49 #define KEY_REFRESH_PERIOD "refresh_period"
50 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
51 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
52 #define KEY_MAX_OWNED "max_owned"
53 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
54 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
55 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
56 #define KEY_NODELETE_LEVEL "nodelete_level"
57 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
58 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 /* ChanServ database */
61 #define KEY_CHANNELS "channels"
62 #define KEY_NOTE_TYPES "note_types"
64 /* Note type parameters */
65 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
66 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
67 #define KEY_NOTE_SETTER_ACCESS "setter_access"
68 #define KEY_NOTE_VISIBILITY "visibility"
69 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
70 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
71 #define KEY_NOTE_VIS_ALL "all"
72 #define KEY_NOTE_MAX_LENGTH "max_length"
73 #define KEY_NOTE_SETTER "setter"
74 #define KEY_NOTE_NOTE "note"
76 /* Do-not-register channels */
78 #define KEY_DNR_SET "set"
79 #define KEY_DNR_SETTER "setter"
80 #define KEY_DNR_REASON "reason"
83 #define KEY_REGISTERED "registered"
84 #define KEY_REGISTRAR "registrar"
85 #define KEY_SUSPENDED "suspended"
86 #define KEY_PREVIOUS "previous"
87 #define KEY_SUSPENDER "suspender"
88 #define KEY_ISSUED "issued"
89 #define KEY_REVOKED "revoked"
90 #define KEY_SUSPEND_EXPIRES "suspend_expires"
91 #define KEY_SUSPEND_REASON "suspend_reason"
92 #define KEY_VISITED "visited"
93 #define KEY_TOPIC "topic"
94 #define KEY_GREETING "greeting"
95 #define KEY_USER_GREETING "user_greeting"
96 #define KEY_MODES "modes"
97 #define KEY_FLAGS "flags"
98 #define KEY_OPTIONS "options"
99 #define KEY_USERS "users"
100 #define KEY_BANS "bans"
101 #define KEY_MAX "max"
102 #define KEY_NOTES "notes"
103 #define KEY_TOPIC_MASK "topic_mask"
104 #define KEY_OWNER_TRANSFER "owner_transfer"
107 #define KEY_LEVEL "level"
108 #define KEY_INFO "info"
109 #define KEY_SEEN "seen"
112 #define KEY_OWNER "owner"
113 #define KEY_REASON "reason"
114 #define KEY_SET "set"
115 #define KEY_DURATION "duration"
116 #define KEY_EXPIRES "expires"
117 #define KEY_TRIGGERED "triggered"
119 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
120 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
121 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
123 /* Administrative messages */
124 static const struct message_entry msgtab[] = {
125 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
127 /* Channel registration */
128 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
129 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
130 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
131 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
132 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
133 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
135 /* Do-not-register channels */
136 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
137 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
138 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
140 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
141 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
142 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
143 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
144 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
145 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
146 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
147 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
148 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
149 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
151 /* Channel unregistration */
152 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
153 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
154 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
155 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
158 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
159 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
161 /* Channel merging */
162 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
163 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
164 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
165 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
166 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
168 /* Handle unregistration */
169 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
172 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
173 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
174 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
175 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
176 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
177 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
178 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
179 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
180 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
181 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
182 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
183 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
184 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
185 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
187 /* Removing yourself from a channel. */
188 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
189 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
190 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
192 /* User management */
193 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
194 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
195 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
196 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
197 { "CSMSG_TRIMMED_USERS", "Trimmed $b%d users$b with access from %d to %d from the %s user list who were inactive for at least %s." },
198 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
199 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
200 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
202 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
203 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
204 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
205 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
206 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
207 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
208 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
211 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
212 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
213 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
214 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
215 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
216 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
217 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
218 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
219 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
220 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
221 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
222 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
223 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
224 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
225 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
226 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
228 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
230 /* Channel management */
231 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
232 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
233 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
235 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
236 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
237 { "CSMSG_TOPICMASK_CONFLICT1", "I do not know how to make that topic work with the current topic mask in $b%s$b, which is: %s" },
238 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
239 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
240 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
241 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
243 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
244 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
245 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
246 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
247 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
248 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
249 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
250 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
251 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
252 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
253 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
254 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
255 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
256 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
257 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
258 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
259 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
260 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
261 { "CSMSG_SET_MODES", "$bModes $b %s" },
262 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
263 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
264 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
265 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
266 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
267 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
268 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
269 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
270 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
271 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
272 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
273 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
274 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
275 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
276 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
277 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
278 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
279 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
280 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
281 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
282 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
283 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
284 { "CSMSG_USET_INFO", "$bInfo $b %s" },
286 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
287 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
288 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
289 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
290 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
291 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
292 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
293 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
294 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
295 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
296 { "CSMSG_PROTECT_NONE", "No users will be protected." },
297 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
298 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
299 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
300 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
301 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
302 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
303 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
304 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
305 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
307 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
308 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
310 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
311 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
312 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
313 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
314 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
315 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
316 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
317 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
319 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
320 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
321 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
323 /* Channel userlist */
324 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
325 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
326 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
327 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
329 /* Channel note list */
330 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
331 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
332 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
333 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
334 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
335 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
336 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
337 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
338 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
339 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
340 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
341 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
342 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
343 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
344 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
346 /* Channel [un]suspension */
347 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
348 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
349 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
350 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
351 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
352 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
353 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
355 /* Access information */
356 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
357 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
358 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
359 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
360 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
361 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
362 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
363 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
364 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
365 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
366 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
367 { "CSMSG_UC_H_TITLE", "network helper" },
368 { "CSMSG_LC_H_TITLE", "support helper" },
369 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
371 /* Seen information */
372 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
373 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
374 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
375 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
377 /* Names information */
378 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
379 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
381 /* Channel information */
382 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
383 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
384 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
385 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
386 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
387 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
388 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
389 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
390 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
391 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
392 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
393 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
401 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
402 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
404 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
405 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
406 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
407 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
408 { "CSMSG_PEEK_OPS", "$bOps:$b" },
409 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
411 /* Network information */
412 { "CSMSG_NETWORK_INFO", "Network Information:" },
413 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
414 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
415 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
416 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
417 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
418 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
419 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
420 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
423 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
424 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
425 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
427 /* Channel searches */
428 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
429 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
430 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
431 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
433 /* Channel configuration */
434 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
435 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
436 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
437 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
440 { "CSMSG_USER_OPTIONS", "User Options:" },
441 { "CSMSG_USER_PROTECTED", "That user is protected." },
444 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
445 { "CSMSG_PING_RESPONSE", "Pong!" },
446 { "CSMSG_WUT_RESPONSE", "wut" },
447 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
448 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
449 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
450 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
451 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
452 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
453 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
456 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
460 /* eject_user and unban_user flags */
461 #define ACTION_KICK 0x0001
462 #define ACTION_BAN 0x0002
463 #define ACTION_ADD_BAN 0x0004
464 #define ACTION_ADD_TIMED_BAN 0x0008
465 #define ACTION_UNBAN 0x0010
466 #define ACTION_DEL_BAN 0x0020
468 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
469 #define MODELEN 40 + KEYLEN
473 #define CSFUNC_ARGS user, channel, argc, argv, cmd
475 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
476 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
477 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
478 reply("MSG_MISSING_PARAMS", argv[0]); \
482 DECLARE_LIST(dnrList, struct do_not_register *);
483 DEFINE_LIST(dnrList, struct do_not_register *)
485 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
487 struct userNode *chanserv;
490 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
491 static struct log_type *CS_LOG;
495 struct channelList support_channels;
496 struct mod_chanmode default_modes;
498 unsigned long db_backup_frequency;
499 unsigned long channel_expire_frequency;
500 unsigned long dnr_expire_frequency;
502 unsigned long info_delay;
503 unsigned long adjust_delay;
504 unsigned long channel_expire_delay;
505 unsigned int nodelete_level;
507 unsigned int adjust_threshold;
508 int join_flood_threshold;
510 unsigned int greeting_length;
511 unsigned int refresh_period;
512 unsigned int giveownership_period;
514 unsigned int max_owned;
515 unsigned int max_chan_users;
516 unsigned int max_chan_bans;
517 unsigned int max_userinfo_length;
519 struct string_list *set_shows;
520 struct string_list *eightball;
521 struct string_list *old_ban_names;
523 const char *ctcp_short_ban_duration;
524 const char *ctcp_long_ban_duration;
526 const char *irc_operator_epithet;
527 const char *network_helper_epithet;
528 const char *support_helper_epithet;
533 struct userNode *user;
534 struct userNode *bot;
535 struct chanNode *channel;
537 unsigned short lowest;
538 unsigned short highest;
539 struct userData **users;
540 struct helpfile_table table;
543 enum note_access_type
545 NOTE_SET_CHANNEL_ACCESS,
546 NOTE_SET_CHANNEL_SETTER,
550 enum note_visible_type
553 NOTE_VIS_CHANNEL_USERS,
559 enum note_access_type set_access_type;
561 unsigned int min_opserv;
562 unsigned short min_ulevel;
564 enum note_visible_type visible_type;
565 unsigned int max_length;
572 struct note_type *type;
573 char setter[NICKSERV_HANDLE_LEN+1];
577 static unsigned int registered_channels;
578 static unsigned int banCount;
580 static const struct {
583 unsigned short level;
586 { "peon", "Peon", UL_PEON, '+' },
587 { "op", "Op", UL_OP, '@' },
588 { "master", "Master", UL_MASTER, '%' },
589 { "coowner", "Coowner", UL_COOWNER, '*' },
590 { "owner", "Owner", UL_OWNER, '!' },
591 { "helper", "BUG:", UL_HELPER, 'X' }
594 static const struct {
597 unsigned short default_value;
598 unsigned int old_idx;
599 unsigned int old_flag;
600 unsigned short flag_value;
602 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
603 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
604 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
605 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
606 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
607 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
608 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
609 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
610 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
611 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
612 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
615 struct charOptionValues {
618 } protectValues[] = {
619 { 'a', "CSMSG_PROTECT_ALL" },
620 { 'e', "CSMSG_PROTECT_EQUAL" },
621 { 'l', "CSMSG_PROTECT_LOWER" },
622 { 'n', "CSMSG_PROTECT_NONE" }
624 { 'd', "CSMSG_TOYS_DISABLED" },
625 { 'n', "CSMSG_TOYS_PRIVATE" },
626 { 'p', "CSMSG_TOYS_PUBLIC" }
627 }, topicRefreshValues[] = {
628 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
629 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
630 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
631 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
632 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
633 }, ctcpReactionValues[] = {
634 { 'k', "CSMSG_CTCPREACTION_KICK" },
635 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
636 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
637 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
640 static const struct {
644 unsigned int old_idx;
646 struct charOptionValues *values;
648 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
649 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
650 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
651 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
654 struct userData *helperList;
655 struct chanData *channelList;
656 static struct module *chanserv_module;
657 static unsigned int userCount;
659 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
660 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
661 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
664 user_level_from_name(const char *name, unsigned short clamp_level)
666 unsigned int level = 0, ii;
668 level = strtoul(name, NULL, 10);
669 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
670 if(!irccasecmp(name, accessLevels[ii].name))
671 level = accessLevels[ii].level;
672 if(level > clamp_level)
678 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
681 *minl = strtoul(arg, &sep, 10);
689 *maxl = strtoul(sep+1, &sep, 10);
697 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
699 struct userData *uData, **head;
701 if(!channel || !handle)
704 if(override && HANDLE_FLAGGED(handle, HELPING)
705 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
707 for(uData = helperList;
708 uData && uData->handle != handle;
709 uData = uData->next);
713 uData = calloc(1, sizeof(struct userData));
714 uData->handle = handle;
716 uData->access = UL_HELPER;
722 uData->next = helperList;
724 helperList->prev = uData;
732 for(uData = channel->users; uData; uData = uData->next)
733 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
736 head = &(channel->users);
739 if(uData && (uData != *head))
741 /* Shuffle the user to the head of whatever list he was in. */
743 uData->next->prev = uData->prev;
745 uData->prev->next = uData->next;
751 (**head).prev = uData;
758 /* Returns non-zero if user has at least the minimum access.
759 * exempt_owner is set when handling !set, so the owner can set things
762 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
764 struct userData *uData;
765 struct chanData *cData = channel->channel_info;
766 unsigned short minimum = cData->lvlOpts[opt];
769 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
772 if(minimum <= uData->access)
774 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
779 /* Scan for other users authenticated to the same handle
780 still in the channel. If so, keep them listed as present.
782 user is optional, if not null, it skips checking that userNode
783 (for the handle_part function) */
785 scan_user_presence(struct userData *uData, struct userNode *user)
789 if(IsSuspended(uData->channel)
790 || IsUserSuspended(uData)
791 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
803 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
805 unsigned int eflags, argc;
807 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
809 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
810 if(!channel->channel_info
811 || IsSuspended(channel->channel_info)
813 || !ircncasecmp(text, "ACTION ", 7))
815 /* Figure out the minimum level needed to CTCP the channel */
816 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
818 /* We need to enforce against them; do so. */
820 argv[0] = (char*)text;
821 argv[1] = user->nick;
823 if(GetUserMode(channel, user))
824 eflags |= ACTION_KICK;
825 switch(channel->channel_info->chOpts[chCTCPReaction]) {
826 default: case 'k': /* just do the kick */ break;
828 eflags |= ACTION_BAN;
831 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
832 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
835 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
836 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
839 argv[argc++] = bad_ctcp_reason;
840 eject_user(chanserv, channel, argc, argv, NULL, eflags);
844 chanserv_create_note_type(const char *name)
846 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
847 strcpy(ntype->name, name);
849 dict_insert(note_types, ntype->name, ntype);
854 chanserv_deref_note_type(void *data)
856 struct note_type *ntype = data;
858 if(--ntype->refs > 0)
864 chanserv_flush_note_type(struct note_type *ntype)
866 struct chanData *cData;
867 for(cData = channelList; cData; cData = cData->next)
868 dict_remove(cData->notes, ntype->name);
872 chanserv_truncate_notes(struct note_type *ntype)
874 struct chanData *cData;
876 unsigned int size = sizeof(*note) + ntype->max_length;
878 for(cData = channelList; cData; cData = cData->next) {
879 note = dict_find(cData->notes, ntype->name, NULL);
882 if(strlen(note->note) <= ntype->max_length)
884 dict_remove2(cData->notes, ntype->name, 1);
885 note = realloc(note, size);
886 note->note[ntype->max_length] = 0;
887 dict_insert(cData->notes, ntype->name, note);
891 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
894 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
897 unsigned int len = strlen(text);
899 if(len > type->max_length) len = type->max_length;
900 note = calloc(1, sizeof(*note) + len);
902 strncpy(note->setter, setter, sizeof(note->setter)-1);
903 memcpy(note->note, text, len);
905 dict_insert(channel->notes, type->name, note);
911 chanserv_free_note(void *data)
913 struct note *note = data;
915 chanserv_deref_note_type(note->type);
916 assert(note->type->refs > 0); /* must use delnote to remove the type */
920 static MODCMD_FUNC(cmd_createnote) {
921 struct note_type *ntype;
922 unsigned int arg = 1, existed = 0, max_length;
924 if((ntype = dict_find(note_types, argv[1], NULL)))
927 ntype = chanserv_create_note_type(argv[arg]);
928 if(!irccasecmp(argv[++arg], "privileged"))
931 ntype->set_access_type = NOTE_SET_PRIVILEGED;
932 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
934 else if(!irccasecmp(argv[arg], "channel"))
936 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
939 reply("CSMSG_INVALID_ACCESS", argv[arg]);
942 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
943 ntype->set_access.min_ulevel = ulvl;
945 else if(!irccasecmp(argv[arg], "setter"))
947 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
951 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
955 if(!irccasecmp(argv[++arg], "privileged"))
956 ntype->visible_type = NOTE_VIS_PRIVILEGED;
957 else if(!irccasecmp(argv[arg], "channel_users"))
958 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
959 else if(!irccasecmp(argv[arg], "all"))
960 ntype->visible_type = NOTE_VIS_ALL;
962 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
966 if((arg+1) >= argc) {
967 reply("MSG_MISSING_PARAMS", argv[0]);
970 max_length = strtoul(argv[++arg], NULL, 0);
971 if(max_length < 20 || max_length > 450)
973 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
976 if(existed && (max_length < ntype->max_length))
978 ntype->max_length = max_length;
979 chanserv_truncate_notes(ntype);
981 ntype->max_length = max_length;
984 reply("CSMSG_NOTE_MODIFIED", ntype->name);
986 reply("CSMSG_NOTE_CREATED", ntype->name);
991 dict_remove(note_types, ntype->name);
995 static MODCMD_FUNC(cmd_removenote) {
996 struct note_type *ntype;
999 ntype = dict_find(note_types, argv[1], NULL);
1000 force = (argc > 2) && !irccasecmp(argv[2], "force");
1003 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1010 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1013 chanserv_flush_note_type(ntype);
1015 dict_remove(note_types, argv[1]);
1016 reply("CSMSG_NOTE_DELETED", argv[1]);
1021 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1025 if(orig->modes_set & change->modes_clear)
1027 if(orig->modes_clear & change->modes_set)
1029 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1030 && strcmp(orig->new_key, change->new_key))
1032 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1033 && (orig->new_limit != change->new_limit))
1038 static char max_length_text[MAXLEN+1][16];
1040 static struct helpfile_expansion
1041 chanserv_expand_variable(const char *variable)
1043 struct helpfile_expansion exp;
1045 if(!irccasecmp(variable, "notes"))
1048 exp.type = HF_TABLE;
1049 exp.value.table.length = 1;
1050 exp.value.table.width = 3;
1051 exp.value.table.flags = 0;
1052 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1053 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1054 exp.value.table.contents[0][0] = "Note Type";
1055 exp.value.table.contents[0][1] = "Visibility";
1056 exp.value.table.contents[0][2] = "Max Length";
1057 for(it=dict_first(note_types); it; it=iter_next(it))
1059 struct note_type *ntype = iter_data(it);
1062 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1063 row = exp.value.table.length++;
1064 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1065 exp.value.table.contents[row][0] = ntype->name;
1066 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1067 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1069 if(!max_length_text[ntype->max_length][0])
1070 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1071 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1076 exp.type = HF_STRING;
1077 exp.value.str = NULL;
1081 static struct chanData*
1082 register_channel(struct chanNode *cNode, char *registrar)
1084 struct chanData *channel;
1085 enum levelOption lvlOpt;
1086 enum charOption chOpt;
1088 channel = calloc(1, sizeof(struct chanData));
1090 channel->notes = dict_new();
1091 dict_set_free_data(channel->notes, chanserv_free_note);
1093 channel->registrar = strdup(registrar);
1094 channel->registered = now;
1095 channel->visited = now;
1096 channel->limitAdjusted = now;
1097 channel->ownerTransfer = now;
1098 channel->flags = CHANNEL_DEFAULT_FLAGS;
1099 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1100 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1101 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1102 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1104 channel->prev = NULL;
1105 channel->next = channelList;
1108 channelList->prev = channel;
1109 channelList = channel;
1110 registered_channels++;
1112 channel->channel = cNode;
1114 cNode->channel_info = channel;
1119 static struct userData*
1120 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1122 struct userData *ud;
1124 if(access_level > UL_OWNER)
1127 ud = calloc(1, sizeof(*ud));
1128 ud->channel = channel;
1129 ud->handle = handle;
1131 ud->access = access_level;
1132 ud->info = info ? strdup(info) : NULL;
1135 ud->next = channel->users;
1137 channel->users->prev = ud;
1138 channel->users = ud;
1140 channel->userCount++;
1144 ud->u_next = ud->handle->channels;
1146 ud->u_next->u_prev = ud;
1147 ud->handle->channels = ud;
1152 static void unregister_channel(struct chanData *channel, const char *reason);
1155 del_channel_user(struct userData *user, int do_gc)
1157 struct chanData *channel = user->channel;
1159 channel->userCount--;
1163 user->prev->next = user->next;
1165 channel->users = user->next;
1167 user->next->prev = user->prev;
1170 user->u_prev->u_next = user->u_next;
1172 user->handle->channels = user->u_next;
1174 user->u_next->u_prev = user->u_prev;
1178 if(do_gc && !channel->users && !IsProtected(channel))
1179 unregister_channel(channel, "lost all users.");
1182 static void expire_ban(void *data);
1184 static struct banData*
1185 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1188 unsigned int ii, l1, l2;
1193 bd = malloc(sizeof(struct banData));
1195 bd->channel = channel;
1197 bd->triggered = triggered;
1198 bd->expires = expires;
1200 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1202 extern const char *hidden_host_suffix;
1203 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1207 l2 = strlen(old_name);
1210 if(irccasecmp(mask + l1 - l2, old_name))
1212 new_mask = alloca(MAXLEN);
1213 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1216 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1218 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1219 bd->reason = strdup(reason);
1222 timeq_add(expires, expire_ban, bd);
1225 bd->next = channel->bans;
1227 channel->bans->prev = bd;
1229 channel->banCount++;
1236 del_channel_ban(struct banData *ban)
1238 ban->channel->banCount--;
1242 ban->prev->next = ban->next;
1244 ban->channel->bans = ban->next;
1247 ban->next->prev = ban->prev;
1250 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1259 expire_ban(void *data)
1261 struct banData *bd = data;
1262 if(!IsSuspended(bd->channel))
1264 struct banList bans;
1265 struct mod_chanmode change;
1267 bans = bd->channel->channel->banlist;
1268 mod_chanmode_init(&change);
1269 for(ii=0; ii<bans.used; ii++)
1271 if(!strcmp(bans.list[ii]->ban, bd->mask))
1274 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1275 change.args[0].u.hostmask = bd->mask;
1276 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1282 del_channel_ban(bd);
1285 static void chanserv_expire_suspension(void *data);
1288 unregister_channel(struct chanData *channel, const char *reason)
1290 struct mod_chanmode change;
1291 char msgbuf[MAXLEN];
1293 /* After channel unregistration, the following must be cleaned
1295 - Channel information.
1298 - Channel suspension data.
1299 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1305 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1309 mod_chanmode_init(&change);
1310 change.modes_clear |= MODE_REGISTERED;
1311 mod_chanmode_announce(chanserv, channel->channel, &change);
1314 while(channel->users)
1315 del_channel_user(channel->users, 0);
1317 while(channel->bans)
1318 del_channel_ban(channel->bans);
1320 free(channel->topic);
1321 free(channel->registrar);
1322 free(channel->greeting);
1323 free(channel->user_greeting);
1324 free(channel->topic_mask);
1327 channel->prev->next = channel->next;
1329 channelList = channel->next;
1332 channel->next->prev = channel->prev;
1334 if(channel->suspended)
1336 struct chanNode *cNode = channel->channel;
1337 struct suspended *suspended, *next_suspended;
1339 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1341 next_suspended = suspended->previous;
1342 free(suspended->suspender);
1343 free(suspended->reason);
1344 if(suspended->expires)
1345 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1350 cNode->channel_info = NULL;
1352 channel->channel->channel_info = NULL;
1354 dict_delete(channel->notes);
1355 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1356 if(!IsSuspended(channel))
1357 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1358 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1359 UnlockChannel(channel->channel);
1361 registered_channels--;
1365 expire_channels(UNUSED_ARG(void *data))
1367 struct chanData *channel, *next;
1368 struct userData *user;
1369 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1371 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1372 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1374 for(channel = channelList; channel; channel = next)
1376 next = channel->next;
1378 /* See if the channel can be expired. */
1379 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1380 || IsProtected(channel))
1383 /* Make sure there are no high-ranking users still in the channel. */
1384 for(user=channel->users; user; user=user->next)
1385 if(user->present && (user->access >= UL_PRESENT))
1390 /* Unregister the channel */
1391 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1392 unregister_channel(channel, "registration expired.");
1395 if(chanserv_conf.channel_expire_frequency)
1396 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1400 expire_dnrs(UNUSED_ARG(void *data))
1402 dict_iterator_t it, next;
1403 struct do_not_register *dnr;
1405 for(it = dict_first(handle_dnrs); it; it = next)
1407 dnr = iter_data(it);
1408 next = iter_next(it);
1409 if(dnr->expires && dnr->expires <= now)
1410 dict_remove(handle_dnrs, dnr->chan_name + 1);
1412 for(it = dict_first(plain_dnrs); it; it = next)
1414 dnr = iter_data(it);
1415 next = iter_next(it);
1416 if(dnr->expires && dnr->expires <= now)
1417 dict_remove(plain_dnrs, dnr->chan_name + 1);
1419 for(it = dict_first(mask_dnrs); it; it = next)
1421 dnr = iter_data(it);
1422 next = iter_next(it);
1423 if(dnr->expires && dnr->expires <= now)
1424 dict_remove(mask_dnrs, dnr->chan_name + 1);
1427 if(chanserv_conf.dnr_expire_frequency)
1428 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1432 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1434 char protect = channel->chOpts[chProtect];
1435 struct userData *cs_victim, *cs_aggressor;
1437 /* Don't protect if no one is to be protected, someone is attacking
1438 himself, or if the aggressor is an IRC Operator. */
1439 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1442 /* Don't protect if the victim isn't authenticated (because they
1443 can't be a channel user), unless we are to protect non-users
1445 cs_victim = GetChannelAccess(channel, victim->handle_info);
1446 if(protect != 'a' && !cs_victim)
1449 /* Protect if the aggressor isn't a user because at this point,
1450 the aggressor can only be less than or equal to the victim. */
1451 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1455 /* If the aggressor was a user, then the victim can't be helped. */
1462 if(cs_victim->access > cs_aggressor->access)
1467 if(cs_victim->access >= cs_aggressor->access)
1476 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1478 struct chanData *cData = channel->channel_info;
1479 struct userData *cs_victim;
1481 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1482 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1483 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1485 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1493 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1495 if(IsService(victim))
1497 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1501 if(protect_user(victim, user, channel->channel_info))
1503 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1510 static struct do_not_register *
1511 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1513 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1514 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1515 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1516 strcpy(dnr->reason, reason);
1518 dnr->expires = expires;
1519 if(dnr->chan_name[0] == '*')
1520 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1521 else if(strpbrk(dnr->chan_name, "*?"))
1522 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1524 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1528 static struct dnrList
1529 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1531 struct dnrList list;
1532 dict_iterator_t it, next;
1533 struct do_not_register *dnr;
1535 dnrList_init(&list);
1537 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1539 if(dnr->expires && dnr->expires <= now)
1540 dict_remove(handle_dnrs, handle);
1541 else if(list.used < max)
1542 dnrList_append(&list, dnr);
1545 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1547 if(dnr->expires && dnr->expires <= now)
1548 dict_remove(plain_dnrs, chan_name);
1549 else if(list.used < max)
1550 dnrList_append(&list, dnr);
1555 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1557 next = iter_next(it);
1558 if(!match_ircglob(chan_name, iter_key(it)))
1560 dnr = iter_data(it);
1561 if(dnr->expires && dnr->expires <= now)
1562 dict_remove(mask_dnrs, iter_key(it));
1564 dnrList_append(&list, dnr);
1571 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1573 struct userNode *user;
1574 char buf1[INTERVALLEN];
1575 char buf2[INTERVALLEN];
1582 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1587 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1588 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1592 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1595 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1600 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1602 struct dnrList list;
1605 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1606 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1607 dnr_print_func(list.list[ii], user);
1609 reply("CSMSG_MORE_DNRS", list.used - ii);
1614 struct do_not_register *
1615 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1617 struct dnrList list;
1618 struct do_not_register *dnr;
1620 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1621 dnr = list.used ? list.list[0] : NULL;
1626 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1628 struct do_not_register *dnr;
1629 dict_iterator_t it, next;
1630 unsigned int matches = 0;
1632 for(it = dict_first(dict); it; it = next)
1634 dnr = iter_data(it);
1635 next = iter_next(it);
1636 if(dnr->expires && dnr->expires <= now)
1638 dict_remove(dict, iter_key(it));
1641 dnr_print_func(dnr, user);
1648 static CHANSERV_FUNC(cmd_noregister)
1652 unsigned long expiry, duration;
1653 unsigned int matches;
1657 reply("CSMSG_DNR_SEARCH_RESULTS");
1658 matches = send_dnrs(user, handle_dnrs);
1659 matches += send_dnrs(user, plain_dnrs);
1660 matches += send_dnrs(user, mask_dnrs);
1662 reply("MSG_MATCH_COUNT", matches);
1664 reply("MSG_NO_MATCHES");
1670 if(!IsChannelName(target) && (*target != '*'))
1672 reply("CSMSG_NOT_DNR", target);
1680 reply("MSG_INVALID_DURATION", argv[2]);
1684 if(!strcmp(argv[2], "0"))
1686 else if((duration = ParseInterval(argv[2])))
1687 expiry = now + duration;
1690 reply("MSG_INVALID_DURATION", argv[2]);
1694 reason = unsplit_string(argv + 3, argc - 3, NULL);
1695 if((*target == '*') && !get_handle_info(target + 1))
1697 reply("MSG_HANDLE_UNKNOWN", target + 1);
1700 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1701 reply("CSMSG_NOREGISTER_CHANNEL", target);
1705 reply("CSMSG_DNR_SEARCH_RESULTS");
1707 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1709 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1711 reply("MSG_NO_MATCHES");
1715 static CHANSERV_FUNC(cmd_allowregister)
1717 const char *chan_name = argv[1];
1719 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1720 || dict_remove(plain_dnrs, chan_name)
1721 || dict_remove(mask_dnrs, chan_name))
1723 reply("CSMSG_DNR_REMOVED", chan_name);
1726 reply("CSMSG_NO_SUCH_DNR", chan_name);
1731 struct userNode *source;
1735 unsigned long min_set, max_set;
1736 unsigned long min_expires, max_expires;
1741 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1743 return !((dnr->set < search->min_set)
1744 || (dnr->set > search->max_set)
1745 || (dnr->expires < search->min_expires)
1746 || (search->max_expires
1747 && ((dnr->expires == 0)
1748 || (dnr->expires > search->max_expires)))
1749 || (search->chan_mask
1750 && !match_ircglob(dnr->chan_name, search->chan_mask))
1751 || (search->setter_mask
1752 && !match_ircglob(dnr->setter, search->setter_mask))
1753 || (search->reason_mask
1754 && !match_ircglob(dnr->reason, search->reason_mask)));
1757 static struct dnr_search *
1758 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1760 struct dnr_search *discrim;
1763 discrim = calloc(1, sizeof(*discrim));
1764 discrim->source = user;
1765 discrim->chan_mask = NULL;
1766 discrim->setter_mask = NULL;
1767 discrim->reason_mask = NULL;
1768 discrim->max_set = INT_MAX;
1769 discrim->limit = 50;
1771 for(ii=0; ii<argc; ++ii)
1775 reply("MSG_MISSING_PARAMS", argv[ii]);
1778 else if(0 == irccasecmp(argv[ii], "channel"))
1780 discrim->chan_mask = argv[++ii];
1782 else if(0 == irccasecmp(argv[ii], "setter"))
1784 discrim->setter_mask = argv[++ii];
1786 else if(0 == irccasecmp(argv[ii], "reason"))
1788 discrim->reason_mask = argv[++ii];
1790 else if(0 == irccasecmp(argv[ii], "limit"))
1792 discrim->limit = strtoul(argv[++ii], NULL, 0);
1794 else if(0 == irccasecmp(argv[ii], "set"))
1796 const char *cmp = argv[++ii];
1799 discrim->min_set = now - ParseInterval(cmp + 2);
1801 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1802 } else if(cmp[0] == '=') {
1803 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1804 } else if(cmp[0] == '>') {
1806 discrim->max_set = now - ParseInterval(cmp + 2);
1808 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1810 discrim->max_set = now - (ParseInterval(cmp) - 1);
1813 else if(0 == irccasecmp(argv[ii], "expires"))
1815 const char *cmp = argv[++ii];
1818 discrim->max_expires = now + ParseInterval(cmp + 2);
1820 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1821 } else if(cmp[0] == '=') {
1822 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1823 } else if(cmp[0] == '>') {
1825 discrim->min_expires = now + ParseInterval(cmp + 2);
1827 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1829 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1834 reply("MSG_INVALID_CRITERIA", argv[ii]);
1845 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1848 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1850 struct do_not_register *dnr;
1851 dict_iterator_t next;
1856 /* Initialize local variables. */
1859 if(discrim->chan_mask)
1861 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1862 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1866 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1868 /* Check against account-based DNRs. */
1869 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1870 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1873 else if(target_fixed)
1875 /* Check against channel-based DNRs. */
1876 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1877 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1882 /* Exhaustively search account DNRs. */
1883 for(it = dict_first(handle_dnrs); it; it = next)
1885 next = iter_next(it);
1886 dnr = iter_data(it);
1887 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1891 /* Do the same for channel DNRs. */
1892 for(it = dict_first(plain_dnrs); it; it = next)
1894 next = iter_next(it);
1895 dnr = iter_data(it);
1896 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1900 /* Do the same for wildcarded channel DNRs. */
1901 for(it = dict_first(mask_dnrs); it; it = next)
1903 next = iter_next(it);
1904 dnr = iter_data(it);
1905 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1913 dnr_remove_func(struct do_not_register *match, void *extra)
1915 struct userNode *user;
1918 chan_name = alloca(strlen(match->chan_name) + 1);
1919 strcpy(chan_name, match->chan_name);
1921 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1922 || dict_remove(plain_dnrs, chan_name)
1923 || dict_remove(mask_dnrs, chan_name))
1925 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1931 dnr_count_func(struct do_not_register *match, void *extra)
1933 return 0; (void)match; (void)extra;
1936 static MODCMD_FUNC(cmd_dnrsearch)
1938 struct dnr_search *discrim;
1939 dnr_search_func action;
1940 struct svccmd *subcmd;
1941 unsigned int matches;
1944 sprintf(buf, "dnrsearch %s", argv[1]);
1945 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1948 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1951 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1953 if(!irccasecmp(argv[1], "print"))
1954 action = dnr_print_func;
1955 else if(!irccasecmp(argv[1], "remove"))
1956 action = dnr_remove_func;
1957 else if(!irccasecmp(argv[1], "count"))
1958 action = dnr_count_func;
1961 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1965 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1969 if(action == dnr_print_func)
1970 reply("CSMSG_DNR_SEARCH_RESULTS");
1971 matches = dnr_search(discrim, action, user);
1973 reply("MSG_MATCH_COUNT", matches);
1975 reply("MSG_NO_MATCHES");
1981 chanserv_get_owned_count(struct handle_info *hi)
1983 struct userData *cList;
1986 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1987 if(cList->access == UL_OWNER)
1992 static CHANSERV_FUNC(cmd_register)
1994 struct handle_info *handle;
1995 struct chanData *cData;
1996 struct modeNode *mn;
1997 char reason[MAXLEN];
1999 unsigned int new_channel, force=0;
2000 struct do_not_register *dnr;
2004 if(channel->channel_info)
2006 reply("CSMSG_ALREADY_REGGED", channel->name);
2010 if(channel->bad_channel)
2012 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2017 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2019 reply("CSMSG_MUST_BE_OPPED", channel->name);
2024 chan_name = channel->name;
2028 if((argc < 2) || !IsChannelName(argv[1]))
2030 reply("MSG_NOT_CHANNEL_NAME");
2034 if(opserv_bad_channel(argv[1]))
2036 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2041 chan_name = argv[1];
2044 if(argc >= (new_channel+2))
2046 if(!IsHelping(user))
2048 reply("CSMSG_PROXY_FORBIDDEN");
2052 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2054 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2055 dnr = chanserv_is_dnr(chan_name, handle);
2059 handle = user->handle_info;
2060 dnr = chanserv_is_dnr(chan_name, handle);
2064 if(!IsHelping(user))
2065 reply("CSMSG_DNR_CHANNEL", chan_name);
2067 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2071 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2073 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2078 channel = AddChannel(argv[1], now, NULL, NULL);
2080 cData = register_channel(channel, user->handle_info->handle);
2081 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2082 cData->modes = chanserv_conf.default_modes;
2084 cData->modes.modes_set |= MODE_REGISTERED;
2085 if (IsOffChannel(cData))
2087 mod_chanmode_announce(chanserv, channel, &cData->modes);
2091 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2092 change->args[change->argc].mode = MODE_CHANOP;
2093 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2095 mod_chanmode_announce(chanserv, channel, change);
2096 mod_chanmode_free(change);
2099 /* Initialize the channel's max user record. */
2100 cData->max = channel->members.used;
2102 if(handle != user->handle_info)
2103 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2105 reply("CSMSG_REG_SUCCESS", channel->name);
2107 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2108 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2113 make_confirmation_string(struct userData *uData)
2115 static char strbuf[16];
2120 for(src = uData->handle->handle; *src; )
2121 accum = accum * 31 + toupper(*src++);
2123 for(src = uData->channel->channel->name; *src; )
2124 accum = accum * 31 + toupper(*src++);
2125 sprintf(strbuf, "%08x", accum);
2129 static CHANSERV_FUNC(cmd_unregister)
2132 char reason[MAXLEN];
2133 struct chanData *cData;
2134 struct userData *uData;
2136 cData = channel->channel_info;
2139 reply("CSMSG_NOT_REGISTERED", channel->name);
2143 uData = GetChannelUser(cData, user->handle_info);
2144 if(!uData || (uData->access < UL_OWNER))
2146 reply("CSMSG_NO_ACCESS");
2150 if(IsProtected(cData))
2152 reply("CSMSG_UNREG_NODELETE", channel->name);
2156 if(!IsHelping(user))
2158 const char *confirm_string;
2159 if(IsSuspended(cData))
2161 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2164 confirm_string = make_confirmation_string(uData);
2165 if((argc < 2) || strcmp(argv[1], confirm_string))
2167 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2172 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2173 name = strdup(channel->name);
2174 unregister_channel(cData, reason);
2175 reply("CSMSG_UNREG_SUCCESS", name);
2180 static CHANSERV_FUNC(cmd_move)
2182 struct mod_chanmode change;
2183 struct chanNode *target;
2184 struct modeNode *mn;
2185 struct userData *uData;
2186 char reason[MAXLEN];
2187 struct do_not_register *dnr;
2191 if(IsProtected(channel->channel_info))
2193 reply("CSMSG_MOVE_NODELETE", channel->name);
2197 if(!IsChannelName(argv[1]))
2199 reply("MSG_NOT_CHANNEL_NAME");
2203 if(opserv_bad_channel(argv[1]))
2205 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2209 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2211 for(uData = channel->channel_info->users; uData; uData = uData->next)
2213 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2215 if(!IsHelping(user))
2216 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2218 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2224 mod_chanmode_init(&change);
2225 if(!(target = GetChannel(argv[1])))
2227 target = AddChannel(argv[1], now, NULL, NULL);
2228 if(!IsSuspended(channel->channel_info))
2229 AddChannelUser(chanserv, target);
2231 else if(target->channel_info)
2233 reply("CSMSG_ALREADY_REGGED", target->name);
2236 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2237 && !IsHelping(user))
2239 reply("CSMSG_MUST_BE_OPPED", target->name);
2242 else if(!IsSuspended(channel->channel_info))
2245 change.args[0].mode = MODE_CHANOP;
2246 change.args[0].u.member = AddChannelUser(chanserv, target);
2247 mod_chanmode_announce(chanserv, target, &change);
2252 /* Clear MODE_REGISTERED from old channel, add it to new. */
2254 change.modes_clear = MODE_REGISTERED;
2255 mod_chanmode_announce(chanserv, channel, &change);
2256 change.modes_clear = 0;
2257 change.modes_set = MODE_REGISTERED;
2258 mod_chanmode_announce(chanserv, target, &change);
2261 /* Move the channel_info to the target channel; it
2262 shouldn't be necessary to clear timeq callbacks
2263 for the old channel. */
2264 target->channel_info = channel->channel_info;
2265 target->channel_info->channel = target;
2266 channel->channel_info = NULL;
2268 reply("CSMSG_MOVE_SUCCESS", target->name);
2270 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2271 if(!IsSuspended(target->channel_info))
2273 char reason2[MAXLEN];
2274 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2275 DelChannelUser(chanserv, channel, reason2, 0);
2277 UnlockChannel(channel);
2278 LockChannel(target);
2279 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2284 merge_users(struct chanData *source, struct chanData *target)
2286 struct userData *suData, *tuData, *next;
2292 /* Insert the source's users into the scratch area. */
2293 for(suData = source->users; suData; suData = suData->next)
2294 dict_insert(merge, suData->handle->handle, suData);
2296 /* Iterate through the target's users, looking for
2297 users common to both channels. The lower access is
2298 removed from either the scratch area or target user
2300 for(tuData = target->users; tuData; tuData = next)
2302 struct userData *choice;
2304 next = tuData->next;
2306 /* If a source user exists with the same handle as a target
2307 channel's user, resolve the conflict by removing one. */
2308 suData = dict_find(merge, tuData->handle->handle, NULL);
2312 /* Pick the data we want to keep. */
2313 /* If the access is the same, use the later seen time. */
2314 if(suData->access == tuData->access)
2315 choice = (suData->seen > tuData->seen) ? suData : tuData;
2316 else /* Otherwise, keep the higher access level. */
2317 choice = (suData->access > tuData->access) ? suData : tuData;
2319 /* Remove the user that wasn't picked. */
2320 if(choice == tuData)
2322 dict_remove(merge, suData->handle->handle);
2323 del_channel_user(suData, 0);
2326 del_channel_user(tuData, 0);
2329 /* Move the remaining users to the target channel. */
2330 for(it = dict_first(merge); it; it = iter_next(it))
2332 suData = iter_data(it);
2334 /* Insert the user into the target channel's linked list. */
2335 suData->prev = NULL;
2336 suData->next = target->users;
2337 suData->channel = target;
2340 target->users->prev = suData;
2341 target->users = suData;
2343 /* Update the user counts for the target channel; the
2344 source counts are left alone. */
2345 target->userCount++;
2348 /* Possible to assert (source->users == NULL) here. */
2349 source->users = NULL;
2354 merge_bans(struct chanData *source, struct chanData *target)
2356 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2358 /* Hold on to the original head of the target ban list
2359 to avoid comparing source bans with source bans. */
2360 tFront = target->bans;
2362 /* Perform a totally expensive O(n*m) merge, ick. */
2363 for(sbData = source->bans; sbData; sbData = sNext)
2365 /* Flag to track whether the ban's been moved
2366 to the destination yet. */
2369 /* Possible to assert (sbData->prev == NULL) here. */
2370 sNext = sbData->next;
2372 for(tbData = tFront; tbData; tbData = tNext)
2374 tNext = tbData->next;
2376 /* Perform two comparisons between each source
2377 and target ban, conflicts are resolved by
2378 keeping the broader ban and copying the later
2379 expiration and triggered time. */
2380 if(match_ircglobs(tbData->mask, sbData->mask))
2382 /* There is a broader ban in the target channel that
2383 overrides one in the source channel; remove the
2384 source ban and break. */
2385 if(sbData->expires > tbData->expires)
2386 tbData->expires = sbData->expires;
2387 if(sbData->triggered > tbData->triggered)
2388 tbData->triggered = sbData->triggered;
2389 del_channel_ban(sbData);
2392 else if(match_ircglobs(sbData->mask, tbData->mask))
2394 /* There is a broader ban in the source channel that
2395 overrides one in the target channel; remove the
2396 target ban, fall through and move the source over. */
2397 if(tbData->expires > sbData->expires)
2398 sbData->expires = tbData->expires;
2399 if(tbData->triggered > sbData->triggered)
2400 sbData->triggered = tbData->triggered;
2401 if(tbData == tFront)
2403 del_channel_ban(tbData);
2406 /* Source bans can override multiple target bans, so
2407 we allow a source to run through this loop multiple
2408 times, but we can only move it once. */
2413 /* Remove the source ban from the source ban list. */
2415 sbData->next->prev = sbData->prev;
2417 /* Modify the source ban's associated channel. */
2418 sbData->channel = target;
2420 /* Insert the ban into the target channel's linked list. */
2421 sbData->prev = NULL;
2422 sbData->next = target->bans;
2425 target->bans->prev = sbData;
2426 target->bans = sbData;
2428 /* Update the user counts for the target channel. */
2433 /* Possible to assert (source->bans == NULL) here. */
2434 source->bans = NULL;
2438 merge_data(struct chanData *source, struct chanData *target)
2440 /* Use more recent visited and owner-transfer time; use older
2441 * registered time. Bitwise or may_opchan. Use higher max.
2442 * Do not touch last_refresh, ban count or user counts.
2444 if(source->visited > target->visited)
2445 target->visited = source->visited;
2446 if(source->registered < target->registered)
2447 target->registered = source->registered;
2448 if(source->ownerTransfer > target->ownerTransfer)
2449 target->ownerTransfer = source->ownerTransfer;
2450 if(source->may_opchan)
2451 target->may_opchan = 1;
2452 if(source->max > target->max)
2453 target->max = source->max;
2457 merge_channel(struct chanData *source, struct chanData *target)
2459 merge_users(source, target);
2460 merge_bans(source, target);
2461 merge_data(source, target);
2464 static CHANSERV_FUNC(cmd_merge)
2466 struct userData *target_user;
2467 struct chanNode *target;
2468 char reason[MAXLEN];
2472 /* Make sure the target channel exists and is registered to the user
2473 performing the command. */
2474 if(!(target = GetChannel(argv[1])))
2476 reply("MSG_INVALID_CHANNEL");
2480 if(!target->channel_info)
2482 reply("CSMSG_NOT_REGISTERED", target->name);
2486 if(IsProtected(channel->channel_info))
2488 reply("CSMSG_MERGE_NODELETE");
2492 if(IsSuspended(target->channel_info))
2494 reply("CSMSG_MERGE_SUSPENDED");
2498 if(channel == target)
2500 reply("CSMSG_MERGE_SELF");
2504 target_user = GetChannelUser(target->channel_info, user->handle_info);
2505 if(!target_user || (target_user->access < UL_OWNER))
2507 reply("CSMSG_MERGE_NOT_OWNER");
2511 /* Merge the channel structures and associated data. */
2512 merge_channel(channel->channel_info, target->channel_info);
2513 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2514 unregister_channel(channel->channel_info, reason);
2515 reply("CSMSG_MERGE_SUCCESS", target->name);
2519 static CHANSERV_FUNC(cmd_opchan)
2521 struct mod_chanmode change;
2522 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2524 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2527 channel->channel_info->may_opchan = 0;
2528 mod_chanmode_init(&change);
2530 change.args[0].mode = MODE_CHANOP;
2531 change.args[0].u.member = GetUserMode(channel, chanserv);
2532 if(!change.args[0].u.member)
2534 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2537 mod_chanmode_announce(chanserv, channel, &change);
2538 reply("CSMSG_OPCHAN_DONE", channel->name);
2542 static CHANSERV_FUNC(cmd_adduser)
2544 struct userData *actee;
2545 struct userData *actor, *real_actor;
2546 struct handle_info *handle;
2547 unsigned short access_level, override = 0;
2551 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2553 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2557 access_level = user_level_from_name(argv[2], UL_OWNER);
2560 reply("CSMSG_INVALID_ACCESS", argv[2]);
2564 actor = GetChannelUser(channel->channel_info, user->handle_info);
2565 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2567 if(actor->access <= access_level)
2569 reply("CSMSG_NO_BUMP_ACCESS");
2573 /* Trying to add someone with equal/more access? */
2574 if (!real_actor || real_actor->access <= access_level)
2575 override = CMD_LOG_OVERRIDE;
2577 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2580 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2582 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2586 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2587 scan_user_presence(actee, NULL);
2588 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2589 return 1 | override;
2592 static CHANSERV_FUNC(cmd_clvl)
2594 struct handle_info *handle;
2595 struct userData *victim;
2596 struct userData *actor, *real_actor;
2597 unsigned short new_access, override = 0;
2598 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2602 actor = GetChannelUser(channel->channel_info, user->handle_info);
2603 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2605 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2608 if(handle == user->handle_info && !privileged)
2610 reply("CSMSG_NO_SELF_CLVL");
2614 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2616 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2620 if(actor->access <= victim->access && !privileged)
2622 reply("MSG_USER_OUTRANKED", handle->handle);
2626 new_access = user_level_from_name(argv[2], UL_OWNER);
2630 reply("CSMSG_INVALID_ACCESS", argv[2]);
2634 if(new_access >= actor->access && !privileged)
2636 reply("CSMSG_NO_BUMP_ACCESS");
2640 /* Trying to clvl a equal/higher user? */
2641 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2642 override = CMD_LOG_OVERRIDE;
2643 /* Trying to clvl someone to equal/higher access? */
2644 if(!real_actor || new_access >= real_actor->access)
2645 override = CMD_LOG_OVERRIDE;
2646 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2647 * If they lower their own access it's not a big problem.
2650 victim->access = new_access;
2651 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2652 return 1 | override;
2655 static CHANSERV_FUNC(cmd_deluser)
2657 struct handle_info *handle;
2658 struct userData *victim;
2659 struct userData *actor, *real_actor;
2660 unsigned short access_level, override = 0;
2665 actor = GetChannelUser(channel->channel_info, user->handle_info);
2666 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2668 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2671 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2673 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2679 access_level = user_level_from_name(argv[1], UL_OWNER);
2682 reply("CSMSG_INVALID_ACCESS", argv[1]);
2685 if(access_level != victim->access)
2687 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2693 access_level = victim->access;
2696 if((actor->access <= victim->access) && !IsHelping(user))
2698 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2702 /* If people delete themselves it is an override, but they
2703 * could've used deleteme so we don't log it as an override
2705 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2706 override = CMD_LOG_OVERRIDE;
2708 chan_name = strdup(channel->name);
2709 del_channel_user(victim, 1);
2710 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2712 return 1 | override;
2716 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2718 struct userData *actor, *real_actor, *uData, *next;
2719 unsigned int override = 0;
2721 actor = GetChannelUser(channel->channel_info, user->handle_info);
2722 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2724 if(min_access > max_access)
2726 reply("CSMSG_BAD_RANGE", min_access, max_access);
2730 if((actor->access <= max_access) && !IsHelping(user))
2732 reply("CSMSG_NO_ACCESS");
2736 if(!real_actor || real_actor->access <= max_access)
2737 override = CMD_LOG_OVERRIDE;
2739 for(uData = channel->channel_info->users; uData; uData = next)
2743 if((uData->access >= min_access)
2744 && (uData->access <= max_access)
2745 && match_ircglob(uData->handle->handle, mask))
2746 del_channel_user(uData, 1);
2749 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2750 return 1 | override;
2753 static CHANSERV_FUNC(cmd_mdelowner)
2755 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2758 static CHANSERV_FUNC(cmd_mdelcoowner)
2760 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2763 static CHANSERV_FUNC(cmd_mdelmaster)
2765 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2768 static CHANSERV_FUNC(cmd_mdelop)
2770 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2773 static CHANSERV_FUNC(cmd_mdelpeon)
2775 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2779 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2781 struct banData *bData, *next;
2782 char interval[INTERVALLEN];
2784 unsigned long limit;
2787 limit = now - duration;
2788 for(bData = channel->channel_info->bans; bData; bData = next)
2792 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2795 del_channel_ban(bData);
2799 intervalString(interval, duration, user->handle_info);
2800 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2805 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2807 struct userData *actor, *uData, *next;
2808 char interval[INTERVALLEN];
2810 unsigned long limit;
2812 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2813 if(min_access > max_access)
2815 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2819 if(!actor || actor->access <= max_access)
2821 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2826 limit = now - duration;
2827 for(uData = channel->channel_info->users; uData; uData = next)
2831 if((uData->seen > limit)
2833 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2836 if(((uData->access >= min_access) && (uData->access <= max_access))
2837 || (!max_access && (uData->access < actor->access)))
2839 del_channel_user(uData, 1);
2847 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2849 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2853 static CHANSERV_FUNC(cmd_trim)
2855 unsigned long duration;
2856 unsigned short min_level, max_level;
2861 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2862 duration = ParseInterval(argv[2]);
2865 reply("CSMSG_CANNOT_TRIM");
2869 if(!irccasecmp(argv[1], "bans"))
2871 cmd_trim_bans(user, channel, duration);
2874 else if(!irccasecmp(argv[1], "users"))
2876 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2879 else if(parse_level_range(&min_level, &max_level, argv[1]))
2881 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2884 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2886 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2891 reply("CSMSG_INVALID_TRIM", argv[1]);
2896 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2897 to the user. cmd_all takes advantage of this. */
2898 static CHANSERV_FUNC(cmd_up)
2900 struct mod_chanmode change;
2901 struct userData *uData;
2904 mod_chanmode_init(&change);
2906 change.args[0].u.member = GetUserMode(channel, user);
2907 if(!change.args[0].u.member)
2910 reply("MSG_CHANNEL_ABSENT", channel->name);
2914 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2918 reply("CSMSG_GODMODE_UP", argv[0]);
2921 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2923 change.args[0].mode = MODE_CHANOP;
2924 errmsg = "CSMSG_ALREADY_OPPED";
2926 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2928 change.args[0].mode = MODE_VOICE;
2929 errmsg = "CSMSG_ALREADY_VOICED";
2934 reply("CSMSG_NO_ACCESS");
2937 change.args[0].mode &= ~change.args[0].u.member->modes;
2938 if(!change.args[0].mode)
2941 reply(errmsg, channel->name);
2944 modcmd_chanmode_announce(&change);
2948 static CHANSERV_FUNC(cmd_down)
2950 struct mod_chanmode change;
2952 mod_chanmode_init(&change);
2954 change.args[0].u.member = GetUserMode(channel, user);
2955 if(!change.args[0].u.member)
2958 reply("MSG_CHANNEL_ABSENT", channel->name);
2962 if(!change.args[0].u.member->modes)
2965 reply("CSMSG_ALREADY_DOWN", channel->name);
2969 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2970 modcmd_chanmode_announce(&change);
2974 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)
2976 struct userData *cList;
2978 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2980 if(IsSuspended(cList->channel)
2981 || IsUserSuspended(cList)
2982 || !GetUserMode(cList->channel->channel, user))
2985 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2991 static CHANSERV_FUNC(cmd_upall)
2993 return cmd_all(CSFUNC_ARGS, cmd_up);
2996 static CHANSERV_FUNC(cmd_downall)
2998 return cmd_all(CSFUNC_ARGS, cmd_down);
3001 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3002 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3005 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)
3007 unsigned int ii, valid;
3008 struct userNode *victim;
3009 struct mod_chanmode *change;
3011 change = mod_chanmode_alloc(argc - 1);
3013 for(ii=valid=0; ++ii < argc; )
3015 if(!(victim = GetUserH(argv[ii])))
3017 change->args[valid].mode = mode;
3018 change->args[valid].u.member = GetUserMode(channel, victim);
3019 if(!change->args[valid].u.member)
3021 if(validate && !validate(user, channel, victim))
3026 change->argc = valid;
3027 if(valid < (argc-1))
3028 reply("CSMSG_PROCESS_FAILED");
3031 modcmd_chanmode_announce(change);
3032 reply(action, channel->name);
3034 mod_chanmode_free(change);
3038 static CHANSERV_FUNC(cmd_op)
3040 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3043 static CHANSERV_FUNC(cmd_deop)
3045 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3048 static CHANSERV_FUNC(cmd_voice)
3050 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3053 static CHANSERV_FUNC(cmd_devoice)
3055 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3059 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3065 for(ii=0; ii<channel->members.used; ii++)
3067 struct modeNode *mn = channel->members.list[ii];
3069 if(IsService(mn->user))
3072 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3075 if(protect_user(mn->user, user, channel->channel_info))
3079 victims[(*victimCount)++] = mn;
3085 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3087 struct userNode *victim;
3088 struct modeNode **victims;
3089 unsigned int offset, n, victimCount, duration = 0;
3090 char *reason = "Bye.", *ban, *name;
3091 char interval[INTERVALLEN];
3093 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3094 REQUIRE_PARAMS(offset);
3097 reason = unsplit_string(argv + offset, argc - offset, NULL);
3098 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3100 /* Truncate the reason to a length of TOPICLEN, as
3101 the ircd does; however, leave room for an ellipsis
3102 and the kicker's nick. */
3103 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3107 if((victim = GetUserH(argv[1])))
3109 victims = alloca(sizeof(victims[0]));
3110 victims[0] = GetUserMode(channel, victim);
3111 /* XXX: The comparison with ACTION_KICK is just because all
3112 * other actions can work on users outside the channel, and we
3113 * want to allow those (e.g. unbans) in that case. If we add
3114 * some other ejection action for in-channel users, change
3116 victimCount = victims[0] ? 1 : 0;
3118 if(IsService(victim))
3120 reply("MSG_SERVICE_IMMUNE", victim->nick);
3124 if((action == ACTION_KICK) && !victimCount)
3126 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3130 if(protect_user(victim, user, channel->channel_info))
3132 reply("CSMSG_USER_PROTECTED", victim->nick);
3136 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3137 name = victim->nick;
3139 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3141 struct handle_info *hi;
3142 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3143 const char *accountname = argv[1] + 1;
3145 if(!(hi = get_handle_info(accountname)))
3147 reply("MSG_HANDLE_UNKNOWN", accountname);
3151 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3152 victims = alloca(sizeof(victims[0]) * channel->members.used);
3154 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3156 reply("CSMSG_MASK_PROTECTED", banmask);
3160 if((action == ACTION_KICK) && (victimCount == 0))
3162 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3166 name = ban = strdup(banmask);
3170 if(!is_ircmask(argv[1]))
3172 reply("MSG_NICK_UNKNOWN", argv[1]);
3176 victims = alloca(sizeof(victims[0]) * channel->members.used);
3178 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3180 reply("CSMSG_MASK_PROTECTED", argv[1]);
3184 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3186 reply("CSMSG_LAME_MASK", argv[1]);
3190 if((action == ACTION_KICK) && (victimCount == 0))
3192 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3196 name = ban = strdup(argv[1]);
3199 /* Truncate the ban in place if necessary; we must ensure
3200 that 'ban' is a valid ban mask before sanitizing it. */
3201 sanitize_ircmask(ban);
3203 if(action & ACTION_ADD_BAN)
3205 struct banData *bData, *next;
3207 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3209 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3214 if(action & ACTION_ADD_TIMED_BAN)
3216 duration = ParseInterval(argv[2]);
3220 reply("CSMSG_DURATION_TOO_LOW");
3224 else if(duration > (86400 * 365 * 2))
3226 reply("CSMSG_DURATION_TOO_HIGH");
3232 for(bData = channel->channel_info->bans; bData; bData = next)
3234 if(match_ircglobs(bData->mask, ban))
3236 int exact = !irccasecmp(bData->mask, ban);
3238 /* The ban is redundant; there is already a ban
3239 with the same effect in place. */
3243 free(bData->reason);
3244 bData->reason = strdup(reason);
3245 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3247 reply("CSMSG_REASON_CHANGE", ban);
3251 if(exact && bData->expires)
3255 /* If the ban matches an existing one exactly,
3256 extend the expiration time if the provided
3257 duration is longer. */
3258 if(duration && (now + duration > bData->expires))
3260 bData->expires = now + duration;
3271 /* Delete the expiration timeq entry and
3272 requeue if necessary. */
3273 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3276 timeq_add(bData->expires, expire_ban, bData);
3280 /* automated kickban */
3283 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3285 reply("CSMSG_BAN_ADDED", name, channel->name);
3291 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3298 if(match_ircglobs(ban, bData->mask))
3300 /* The ban we are adding makes previously existing
3301 bans redundant; silently remove them. */
3302 del_channel_ban(bData);
3306 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);
3308 name = ban = strdup(bData->mask);
3312 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3314 extern const char *hidden_host_suffix;
3315 const char *old_name = chanserv_conf.old_ban_names->list[n];
3317 unsigned int l1, l2;
3320 l2 = strlen(old_name);
3323 if(irccasecmp(ban + l1 - l2, old_name))
3325 new_mask = malloc(MAXLEN);
3326 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3328 name = ban = new_mask;
3333 if(action & ACTION_BAN)
3335 unsigned int exists;
3336 struct mod_chanmode *change;
3338 if(channel->banlist.used >= MAXBANS)
3341 reply("CSMSG_BANLIST_FULL", channel->name);
3346 exists = ChannelBanExists(channel, ban);
3347 change = mod_chanmode_alloc(victimCount + 1);
3348 for(n = 0; n < victimCount; ++n)
3350 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3351 change->args[n].u.member = victims[n];
3355 change->args[n].mode = MODE_BAN;
3356 change->args[n++].u.hostmask = ban;
3360 modcmd_chanmode_announce(change);
3362 mod_chanmode_announce(chanserv, channel, change);
3363 mod_chanmode_free(change);
3365 if(exists && (action == ACTION_BAN))
3368 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3374 if(action & ACTION_KICK)
3376 char kick_reason[MAXLEN];
3377 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3379 for(n = 0; n < victimCount; n++)
3380 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3385 /* No response, since it was automated. */
3387 else if(action & ACTION_ADD_BAN)
3390 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3392 reply("CSMSG_BAN_ADDED", name, channel->name);
3394 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3395 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3396 else if(action & ACTION_BAN)
3397 reply("CSMSG_BAN_DONE", name, channel->name);
3398 else if(action & ACTION_KICK && victimCount)
3399 reply("CSMSG_KICK_DONE", name, channel->name);
3405 static CHANSERV_FUNC(cmd_kickban)
3407 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3410 static CHANSERV_FUNC(cmd_kick)
3412 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3415 static CHANSERV_FUNC(cmd_ban)
3417 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3420 static CHANSERV_FUNC(cmd_addban)
3422 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3425 static CHANSERV_FUNC(cmd_addtimedban)
3427 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3430 static struct mod_chanmode *
3431 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3433 struct mod_chanmode *change;
3434 unsigned char *match;
3435 unsigned int ii, count;
3437 match = alloca(bans->used);
3440 for(ii = count = 0; ii < bans->used; ++ii)
3442 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3443 MATCH_USENICK | MATCH_VISIBLE);
3450 for(ii = count = 0; ii < bans->used; ++ii)
3452 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3459 change = mod_chanmode_alloc(count);
3460 for(ii = count = 0; ii < bans->used; ++ii)
3464 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3465 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3467 assert(count == change->argc);
3472 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3474 struct userNode *actee;
3480 /* may want to allow a comma delimited list of users... */
3481 if(!(actee = GetUserH(argv[1])))
3483 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3485 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3486 const char *accountname = argv[1] + 1;
3488 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3489 mask = strdup(banmask);
3491 else if(!is_ircmask(argv[1]))
3493 reply("MSG_NICK_UNKNOWN", argv[1]);
3498 mask = strdup(argv[1]);
3502 /* We don't sanitize the mask here because ircu
3504 if(action & ACTION_UNBAN)
3506 struct mod_chanmode *change;
3507 change = find_matching_bans(&channel->banlist, actee, mask);
3512 modcmd_chanmode_announce(change);
3513 for(ii = 0; ii < change->argc; ++ii)
3514 free((char*)change->args[ii].u.hostmask);
3515 mod_chanmode_free(change);
3520 if(action & ACTION_DEL_BAN)
3522 struct banData *ban, *next;
3524 ban = channel->channel_info->bans;
3528 for( ; ban && !user_matches_glob(actee, ban->mask,
3529 MATCH_USENICK | MATCH_VISIBLE);
3532 for( ; ban && !match_ircglobs(mask, ban->mask);
3537 del_channel_ban(ban);
3544 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3546 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3552 static CHANSERV_FUNC(cmd_unban)
3554 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3557 static CHANSERV_FUNC(cmd_delban)
3559 /* it doesn't necessarily have to remove the channel ban - may want
3560 to make that an option. */
3561 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3564 static CHANSERV_FUNC(cmd_unbanme)
3566 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3567 long flags = ACTION_UNBAN;
3569 /* remove permanent bans if the user has the proper access. */
3570 if(uData->access >= UL_MASTER)
3571 flags |= ACTION_DEL_BAN;
3573 argv[1] = user->nick;
3574 return unban_user(user, channel, 2, argv, cmd, flags);
3577 static CHANSERV_FUNC(cmd_unbanall)
3579 struct mod_chanmode *change;
3582 if(!channel->banlist.used)
3584 reply("CSMSG_NO_BANS", channel->name);
3588 change = mod_chanmode_alloc(channel->banlist.used);
3589 for(ii=0; ii<channel->banlist.used; ii++)
3591 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3592 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3594 modcmd_chanmode_announce(change);
3595 for(ii = 0; ii < change->argc; ++ii)
3596 free((char*)change->args[ii].u.hostmask);
3597 mod_chanmode_free(change);
3598 reply("CSMSG_BANS_REMOVED", channel->name);
3602 static CHANSERV_FUNC(cmd_open)
3604 struct mod_chanmode *change;
3607 change = find_matching_bans(&channel->banlist, user, NULL);
3609 change = mod_chanmode_alloc(0);
3610 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3611 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3612 && channel->channel_info->modes.modes_set)
3613 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3614 modcmd_chanmode_announce(change);
3615 reply("CSMSG_CHANNEL_OPENED", channel->name);
3616 for(ii = 0; ii < change->argc; ++ii)
3617 free((char*)change->args[ii].u.hostmask);
3618 mod_chanmode_free(change);
3622 static CHANSERV_FUNC(cmd_myaccess)
3624 static struct string_buffer sbuf;
3625 struct handle_info *target_handle;
3626 struct userData *uData;
3629 target_handle = user->handle_info;
3630 else if(!IsStaff(user))
3632 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3635 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3638 if(!oper_outranks(user, target_handle))
3641 if(!target_handle->channels)
3643 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3647 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3648 for(uData = target_handle->channels; uData; uData = uData->u_next)
3650 struct chanData *cData = uData->channel;
3652 if(uData->access > UL_OWNER)
3654 if(IsProtected(cData)
3655 && (target_handle != user->handle_info)
3656 && !GetTrueChannelAccess(cData, user->handle_info))
3659 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3660 if(uData->flags != USER_AUTO_OP)
3661 string_buffer_append(&sbuf, ',');
3662 if(IsUserSuspended(uData))
3663 string_buffer_append(&sbuf, 's');
3664 if(IsUserAutoOp(uData))
3666 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3667 string_buffer_append(&sbuf, 'o');
3668 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3669 string_buffer_append(&sbuf, 'v');
3671 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3672 string_buffer_append(&sbuf, 'i');
3674 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3676 string_buffer_append_string(&sbuf, ")]");
3677 string_buffer_append(&sbuf, '\0');
3678 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3684 static CHANSERV_FUNC(cmd_access)
3686 struct userNode *target;
3687 struct handle_info *target_handle;
3688 struct userData *uData;
3690 char prefix[MAXLEN];
3695 target_handle = target->handle_info;
3697 else if((target = GetUserH(argv[1])))
3699 target_handle = target->handle_info;
3701 else if(argv[1][0] == '*')
3703 if(!(target_handle = get_handle_info(argv[1]+1)))
3705 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3711 reply("MSG_NICK_UNKNOWN", argv[1]);
3715 assert(target || target_handle);
3717 if(target == chanserv)
3719 reply("CSMSG_IS_CHANSERV");
3727 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3732 reply("MSG_USER_AUTHENTICATE", target->nick);
3735 reply("MSG_AUTHENTICATE");
3741 const char *epithet = NULL, *type = NULL;
3744 epithet = chanserv_conf.irc_operator_epithet;
3745 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3747 else if(IsNetworkHelper(target))
3749 epithet = chanserv_conf.network_helper_epithet;
3750 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3752 else if(IsSupportHelper(target))
3754 epithet = chanserv_conf.support_helper_epithet;
3755 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3759 if(target_handle->epithet)
3760 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3762 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3764 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3768 sprintf(prefix, "%s", target_handle->handle);
3771 if(!channel->channel_info)
3773 reply("CSMSG_NOT_REGISTERED", channel->name);
3777 helping = HANDLE_FLAGGED(target_handle, HELPING)
3778 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3779 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3781 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3782 /* To prevent possible information leaks, only show infolines
3783 * if the requestor is in the channel or it's their own
3785 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3787 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3789 /* Likewise, only say it's suspended if the user has active
3790 * access in that channel or it's their own entry. */
3791 if(IsUserSuspended(uData)
3792 && (GetChannelUser(channel->channel_info, user->handle_info)
3793 || (user->handle_info == uData->handle)))
3795 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3800 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3807 zoot_list(struct listData *list)
3809 struct userData *uData;
3810 unsigned int start, curr, highest, lowest;
3811 struct helpfile_table tmp_table;
3812 const char **temp, *msg;
3814 if(list->table.length == 1)
3817 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3819 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3820 msg = user_find_message(list->user, "MSG_NONE");
3821 send_message_type(4, list->user, list->bot, " %s", msg);
3823 tmp_table.width = list->table.width;
3824 tmp_table.flags = list->table.flags;
3825 list->table.contents[0][0] = " ";
3826 highest = list->highest;
3827 if(list->lowest != 0)
3828 lowest = list->lowest;
3829 else if(highest < 100)
3832 lowest = highest - 100;
3833 for(start = curr = 1; curr < list->table.length; )
3835 uData = list->users[curr-1];
3836 list->table.contents[curr++][0] = " ";
3837 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3840 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3842 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3843 temp = list->table.contents[--start];
3844 list->table.contents[start] = list->table.contents[0];
3845 tmp_table.contents = list->table.contents + start;
3846 tmp_table.length = curr - start;
3847 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3848 list->table.contents[start] = temp;
3850 highest = lowest - 1;
3851 lowest = (highest < 100) ? 0 : (highest - 99);
3857 def_list(struct listData *list)
3861 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3863 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3864 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3865 if(list->table.length == 1)
3867 msg = user_find_message(list->user, "MSG_NONE");
3868 send_message_type(4, list->user, list->bot, " %s", msg);
3873 userData_access_comp(const void *arg_a, const void *arg_b)
3875 const struct userData *a = *(struct userData**)arg_a;
3876 const struct userData *b = *(struct userData**)arg_b;
3878 if(a->access != b->access)
3879 res = b->access - a->access;
3881 res = irccasecmp(a->handle->handle, b->handle->handle);
3886 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3888 void (*send_list)(struct listData *);
3889 struct userData *uData;
3890 struct listData lData;
3891 unsigned int matches;
3895 lData.bot = cmd->parent->bot;
3896 lData.channel = channel;
3897 lData.lowest = lowest;
3898 lData.highest = highest;
3899 lData.search = (argc > 1) ? argv[1] : NULL;
3900 send_list = def_list;
3901 (void)zoot_list; /* since it doesn't show user levels */
3903 if(user->handle_info)
3905 switch(user->handle_info->userlist_style)
3907 case HI_STYLE_DEF: send_list = def_list; break;
3908 case HI_STYLE_ZOOT: send_list = def_list; break;
3912 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3914 for(uData = channel->channel_info->users; uData; uData = uData->next)
3916 if((uData->access < lowest)
3917 || (uData->access > highest)
3918 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3920 lData.users[matches++] = uData;
3922 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3924 lData.table.length = matches+1;
3925 lData.table.width = 4;
3926 lData.table.flags = TABLE_NO_FREE;
3927 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3928 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3929 lData.table.contents[0] = ary;
3932 ary[2] = "Last Seen";
3934 for(matches = 1; matches < lData.table.length; ++matches)
3936 char seen[INTERVALLEN];
3938 uData = lData.users[matches-1];
3939 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3940 lData.table.contents[matches] = ary;
3941 ary[0] = strtab(uData->access);
3942 ary[1] = uData->handle->handle;
3945 else if(!uData->seen)
3948 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3949 ary[2] = strdup(ary[2]);
3950 if(IsUserSuspended(uData))
3951 ary[3] = "Suspended";
3952 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3953 ary[3] = "Vacation";
3958 for(matches = 1; matches < lData.table.length; ++matches)
3960 free((char*)lData.table.contents[matches][2]);
3961 free(lData.table.contents[matches]);
3963 free(lData.table.contents[0]);
3964 free(lData.table.contents);
3968 static CHANSERV_FUNC(cmd_users)
3970 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3973 static CHANSERV_FUNC(cmd_wlist)
3975 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3978 static CHANSERV_FUNC(cmd_clist)
3980 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3983 static CHANSERV_FUNC(cmd_mlist)
3985 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3988 static CHANSERV_FUNC(cmd_olist)
3990 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3993 static CHANSERV_FUNC(cmd_plist)
3995 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3998 static CHANSERV_FUNC(cmd_bans)
4000 struct userNode *search_u = NULL;
4001 struct helpfile_table tbl;
4002 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4003 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4004 const char *msg_never, *triggered, *expires;
4005 struct banData *ban, **bans;
4009 else if(strchr(search = argv[1], '!'))
4012 search_wilds = search[strcspn(search, "?*")];
4014 else if(!(search_u = GetUserH(search)))
4015 reply("MSG_NICK_UNKNOWN", search);
4017 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4019 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4023 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4028 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4031 bans[matches++] = ban;
4036 tbl.length = matches + 1;
4037 tbl.width = 4 + timed;
4039 tbl.flags = TABLE_NO_FREE;
4040 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4041 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4042 tbl.contents[0][0] = "Mask";
4043 tbl.contents[0][1] = "Set By";
4044 tbl.contents[0][2] = "Triggered";
4047 tbl.contents[0][3] = "Expires";
4048 tbl.contents[0][4] = "Reason";
4051 tbl.contents[0][3] = "Reason";
4054 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4056 free(tbl.contents[0]);
4061 msg_never = user_find_message(user, "MSG_NEVER");
4062 for(ii = 0; ii < matches; )
4068 else if(ban->expires)
4069 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4071 expires = msg_never;
4074 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4076 triggered = msg_never;
4078 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4079 tbl.contents[ii][0] = ban->mask;
4080 tbl.contents[ii][1] = ban->owner;
4081 tbl.contents[ii][2] = strdup(triggered);
4084 tbl.contents[ii][3] = strdup(expires);
4085 tbl.contents[ii][4] = ban->reason;
4088 tbl.contents[ii][3] = ban->reason;
4090 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4091 reply("MSG_MATCH_COUNT", matches);
4092 for(ii = 1; ii < tbl.length; ++ii)
4094 free((char*)tbl.contents[ii][2]);
4096 free((char*)tbl.contents[ii][3]);
4097 free(tbl.contents[ii]);
4099 free(tbl.contents[0]);
4105 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4107 struct chanData *cData = channel->channel_info;
4108 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4110 if(cData->topic_mask)
4111 return !match_ircglob(new_topic, cData->topic_mask);
4112 else if(cData->topic)
4113 return irccasecmp(new_topic, cData->topic);
4118 static CHANSERV_FUNC(cmd_topic)
4120 struct chanData *cData;
4123 cData = channel->channel_info;
4128 SetChannelTopic(channel, chanserv, cData->topic, 1);
4129 reply("CSMSG_TOPIC_SET", cData->topic);
4133 reply("CSMSG_NO_TOPIC", channel->name);
4137 topic = unsplit_string(argv + 1, argc - 1, NULL);
4138 /* If they say "!topic *", use an empty topic. */
4139 if((topic[0] == '*') && (topic[1] == 0))
4141 if(bad_topic(channel, user, topic))
4143 char *topic_mask = cData->topic_mask;
4146 char new_topic[TOPICLEN+1], tchar;
4147 int pos=0, starpos=-1, dpos=0, len;
4149 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4156 len = strlen(topic);
4157 if((dpos + len) > TOPICLEN)
4158 len = TOPICLEN + 1 - dpos;
4159 memcpy(new_topic+dpos, topic, len);
4163 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4164 default: new_topic[dpos++] = tchar; break;
4167 if((dpos > TOPICLEN) || tchar)
4170 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4171 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4174 new_topic[dpos] = 0;
4175 SetChannelTopic(channel, chanserv, new_topic, 1);
4177 reply("CSMSG_TOPIC_LOCKED", channel->name);
4182 SetChannelTopic(channel, chanserv, topic, 1);
4184 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4186 /* Grab the topic and save it as the default topic. */
4188 cData->topic = strdup(channel->topic);
4194 static CHANSERV_FUNC(cmd_mode)
4196 struct userData *uData;
4197 struct mod_chanmode *change;
4202 change = &channel->channel_info->modes;
4203 if(change->modes_set || change->modes_clear) {
4204 modcmd_chanmode_announce(change);
4205 reply("CSMSG_DEFAULTED_MODES", channel->name);
4207 reply("CSMSG_NO_MODES", channel->name);
4211 uData = GetChannelUser(channel->channel_info, user->handle_info);
4213 base_oplevel = MAXOPLEVEL;
4214 else if (uData->access >= UL_OWNER)
4217 base_oplevel = 1 + UL_OWNER - uData->access;
4218 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4221 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4225 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4226 && mode_lock_violated(&channel->channel_info->modes, change))
4229 mod_chanmode_format(&channel->channel_info->modes, modes);
4230 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4234 modcmd_chanmode_announce(change);
4235 mod_chanmode_free(change);
4236 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4240 static CHANSERV_FUNC(cmd_invite)
4242 struct userData *uData;
4243 struct userNode *invite;
4245 uData = GetChannelUser(channel->channel_info, user->handle_info);
4249 if(!(invite = GetUserH(argv[1])))
4251 reply("MSG_NICK_UNKNOWN", argv[1]);
4258 if(GetUserMode(channel, invite))
4260 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4268 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4269 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4272 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4274 irc_invite(chanserv, invite, channel);
4276 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4281 static CHANSERV_FUNC(cmd_inviteme)
4283 if(GetUserMode(channel, user))
4285 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4288 if(channel->channel_info
4289 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4291 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4294 irc_invite(cmd->parent->bot, user, channel);
4299 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4302 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4304 /* We display things based on two dimensions:
4305 * - Issue time: present or absent
4306 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4307 * (in order of precedence, so something both expired and revoked
4308 * only counts as revoked)
4310 combo = (suspended->issued ? 4 : 0)
4311 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4313 case 0: /* no issue time, indefinite expiration */
4314 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4316 case 1: /* no issue time, expires in future */
4317 intervalString(buf1, suspended->expires-now, user->handle_info);
4318 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4320 case 2: /* no issue time, expired */
4321 intervalString(buf1, now-suspended->expires, user->handle_info);
4322 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4324 case 3: /* no issue time, revoked */
4325 intervalString(buf1, now-suspended->revoked, user->handle_info);
4326 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4328 case 4: /* issue time set, indefinite expiration */
4329 intervalString(buf1, now-suspended->issued, user->handle_info);
4330 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4332 case 5: /* issue time set, expires in future */
4333 intervalString(buf1, now-suspended->issued, user->handle_info);
4334 intervalString(buf2, suspended->expires-now, user->handle_info);
4335 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4337 case 6: /* issue time set, expired */
4338 intervalString(buf1, now-suspended->issued, user->handle_info);
4339 intervalString(buf2, now-suspended->expires, user->handle_info);
4340 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4342 case 7: /* issue time set, revoked */
4343 intervalString(buf1, now-suspended->issued, user->handle_info);
4344 intervalString(buf2, now-suspended->revoked, user->handle_info);
4345 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4348 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4353 static CHANSERV_FUNC(cmd_info)
4355 char modes[MAXLEN], buffer[INTERVALLEN];
4356 struct userData *uData, *owner;
4357 struct chanData *cData;
4358 struct do_not_register *dnr;
4363 cData = channel->channel_info;
4364 reply("CSMSG_CHANNEL_INFO", channel->name);
4366 uData = GetChannelUser(cData, user->handle_info);
4367 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4369 mod_chanmode_format(&cData->modes, modes);
4370 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4371 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4374 for(it = dict_first(cData->notes); it; it = iter_next(it))
4378 note = iter_data(it);
4379 if(!note_type_visible_to_user(cData, note->type, user))
4382 padding = PADLEN - 1 - strlen(iter_key(it));
4383 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4386 reply("CSMSG_CHANNEL_MAX", cData->max);
4387 for(owner = cData->users; owner; owner = owner->next)
4388 if(owner->access == UL_OWNER)
4389 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4390 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4391 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4392 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4394 privileged = IsStaff(user);
4396 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4397 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4398 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4400 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4401 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4403 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4405 struct suspended *suspended;
4406 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4407 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4408 show_suspension_info(cmd, user, suspended);
4410 else if(IsSuspended(cData))
4412 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4413 show_suspension_info(cmd, user, cData->suspended);
4418 static CHANSERV_FUNC(cmd_netinfo)
4420 extern unsigned long boot_time;
4421 extern unsigned long burst_length;
4422 char interval[INTERVALLEN];
4424 reply("CSMSG_NETWORK_INFO");
4425 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4426 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4427 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4428 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4429 reply("CSMSG_NETWORK_BANS", banCount);
4430 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4431 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4432 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4437 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4439 struct helpfile_table table;
4441 struct userNode *user;
4446 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4447 table.contents = alloca(list->used*sizeof(*table.contents));
4448 for(nn=0; nn<list->used; nn++)
4450 user = list->list[nn];
4451 if(user->modes & skip_flags)
4455 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4458 nick = alloca(strlen(user->nick)+3);
4459 sprintf(nick, "(%s)", user->nick);
4463 table.contents[table.length][0] = nick;
4466 table_send(chanserv, to->nick, 0, NULL, table);
4469 static CHANSERV_FUNC(cmd_ircops)
4471 reply("CSMSG_STAFF_OPERS");
4472 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4476 static CHANSERV_FUNC(cmd_helpers)
4478 reply("CSMSG_STAFF_HELPERS");
4479 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4483 static CHANSERV_FUNC(cmd_staff)
4485 reply("CSMSG_NETWORK_STAFF");
4486 cmd_ircops(CSFUNC_ARGS);
4487 cmd_helpers(CSFUNC_ARGS);
4491 static CHANSERV_FUNC(cmd_peek)
4493 struct modeNode *mn;
4494 char modes[MODELEN];
4496 struct helpfile_table table;
4498 irc_make_chanmode(channel, modes);
4500 reply("CSMSG_PEEK_INFO", channel->name);
4501 reply("CSMSG_PEEK_TOPIC", channel->topic);
4502 reply("CSMSG_PEEK_MODES", modes);
4503 reply("CSMSG_PEEK_USERS", channel->members.used);
4507 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4508 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4509 for(n = 0; n < channel->members.used; n++)
4511 mn = channel->members.list[n];
4512 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4514 table.contents[table.length] = alloca(sizeof(**table.contents));
4515 table.contents[table.length][0] = mn->user->nick;
4520 reply("CSMSG_PEEK_OPS");
4521 table_send(chanserv, user->nick, 0, NULL, table);
4524 reply("CSMSG_PEEK_NO_OPS");
4528 static MODCMD_FUNC(cmd_wipeinfo)
4530 struct handle_info *victim;
4531 struct userData *ud, *actor, *real_actor;
4532 unsigned int override = 0;
4535 actor = GetChannelUser(channel->channel_info, user->handle_info);
4536 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4537 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4539 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4541 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4544 if((ud->access >= actor->access) && (ud != actor))
4546 reply("MSG_USER_OUTRANKED", victim->handle);
4549 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4550 override = CMD_LOG_OVERRIDE;
4554 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4555 return 1 | override;
4558 static CHANSERV_FUNC(cmd_resync)
4560 struct mod_chanmode *changes;
4561 struct chanData *cData = channel->channel_info;
4562 unsigned int ii, used;
4564 changes = mod_chanmode_alloc(channel->members.used * 2);
4565 for(ii = used = 0; ii < channel->members.used; ++ii)
4567 struct modeNode *mn = channel->members.list[ii];
4568 struct userData *uData;
4570 if(IsService(mn->user))
4573 uData = GetChannelAccess(cData, mn->user->handle_info);
4574 if(!cData->lvlOpts[lvlGiveOps]
4575 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4577 if(!(mn->modes & MODE_CHANOP))
4579 changes->args[used].mode = MODE_CHANOP;
4580 changes->args[used++].u.member = mn;
4583 else if(!cData->lvlOpts[lvlGiveVoice]
4584 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4586 if(mn->modes & MODE_CHANOP)
4588 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4589 changes->args[used++].u.member = mn;
4591 if(!(mn->modes & MODE_VOICE))
4593 changes->args[used].mode = MODE_VOICE;
4594 changes->args[used++].u.member = mn;
4601 changes->args[used].mode = MODE_REMOVE | mn->modes;
4602 changes->args[used++].u.member = mn;
4606 changes->argc = used;
4607 modcmd_chanmode_announce(changes);
4608 mod_chanmode_free(changes);
4609 reply("CSMSG_RESYNCED_USERS", channel->name);
4613 static CHANSERV_FUNC(cmd_seen)
4615 struct userData *uData;
4616 struct handle_info *handle;
4617 char seen[INTERVALLEN];
4621 if(!irccasecmp(argv[1], chanserv->nick))
4623 reply("CSMSG_IS_CHANSERV");
4627 if(!(handle = get_handle_info(argv[1])))
4629 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4633 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4635 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4640 reply("CSMSG_USER_PRESENT", handle->handle);
4641 else if(uData->seen)
4642 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4644 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4646 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4647 reply("CSMSG_USER_VACATION", handle->handle);
4652 static MODCMD_FUNC(cmd_names)
4654 struct userNode *targ;
4655 struct userData *targData;
4656 unsigned int ii, pos;
4659 for(ii=pos=0; ii<channel->members.used; ++ii)
4661 targ = channel->members.list[ii]->user;
4662 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4665 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4668 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4672 if(IsUserSuspended(targData))
4674 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4677 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4678 reply("CSMSG_END_NAMES", channel->name);
4683 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4685 switch(ntype->visible_type)
4687 case NOTE_VIS_ALL: return 1;
4688 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4689 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4694 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4696 struct userData *uData;
4698 switch(ntype->set_access_type)
4700 case NOTE_SET_CHANNEL_ACCESS:
4701 if(!user->handle_info)
4703 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4705 return uData->access >= ntype->set_access.min_ulevel;
4706 case NOTE_SET_CHANNEL_SETTER:
4707 return check_user_level(channel, user, lvlSetters, 1, 0);
4708 case NOTE_SET_PRIVILEGED: default:
4709 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4713 static CHANSERV_FUNC(cmd_note)
4715 struct chanData *cData;
4717 struct note_type *ntype;
4719 cData = channel->channel_info;
4722 reply("CSMSG_NOT_REGISTERED", channel->name);
4726 /* If no arguments, show all visible notes for the channel. */
4732 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4734 note = iter_data(it);
4735 if(!note_type_visible_to_user(cData, note->type, user))
4738 reply("CSMSG_NOTELIST_HEADER", channel->name);
4739 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4742 reply("CSMSG_NOTELIST_END", channel->name);
4744 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4746 /* If one argument, show the named note. */
4749 if((note = dict_find(cData->notes, argv[1], NULL))
4750 && note_type_visible_to_user(cData, note->type, user))
4752 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4754 else if((ntype = dict_find(note_types, argv[1], NULL))
4755 && note_type_visible_to_user(NULL, ntype, user))
4757 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4762 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4766 /* Assume they're trying to set a note. */
4770 ntype = dict_find(note_types, argv[1], NULL);
4773 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4776 else if(note_type_settable_by_user(channel, ntype, user))
4778 note_text = unsplit_string(argv+2, argc-2, NULL);
4779 if((note = dict_find(cData->notes, argv[1], NULL)))
4780 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4781 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4782 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4784 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4786 /* The note is viewable to staff only, so return 0
4787 to keep the invocation from getting logged (or
4788 regular users can see it in !events). */
4794 reply("CSMSG_NO_ACCESS");
4801 static CHANSERV_FUNC(cmd_delnote)
4806 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4807 || !note_type_settable_by_user(channel, note->type, user))
4809 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4812 dict_remove(channel->channel_info->notes, note->type->name);
4813 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4817 static CHANSERV_FUNC(cmd_events)
4819 struct logSearch discrim;
4820 struct logReport report;
4821 unsigned int matches, limit;
4823 limit = (argc > 1) ? atoi(argv[1]) : 10;
4824 if(limit < 1 || limit > 200)
4827 memset(&discrim, 0, sizeof(discrim));
4828 discrim.masks.bot = chanserv;
4829 discrim.masks.channel_name = channel->name;
4831 discrim.masks.command = argv[2];
4832 discrim.limit = limit;
4833 discrim.max_time = INT_MAX;
4834 discrim.severities = 1 << LOG_COMMAND;
4835 report.reporter = chanserv;
4837 reply("CSMSG_EVENT_SEARCH_RESULTS");
4838 matches = log_entry_search(&discrim, log_report_entry, &report);
4840 reply("MSG_MATCH_COUNT", matches);
4842 reply("MSG_NO_MATCHES");
4846 static CHANSERV_FUNC(cmd_say)
4852 msg = unsplit_string(argv + 1, argc - 1, NULL);
4853 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4855 else if(*argv[1] == '*' && argv[1][1] != '\0')
4857 struct handle_info *hi;
4858 struct userNode *authed;
4861 msg = unsplit_string(argv + 2, argc - 2, NULL);
4863 if (!(hi = get_handle_info(argv[1] + 1)))
4865 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4869 for (authed = hi->users; authed; authed = authed->next_authed)
4870 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4872 else if(GetUserH(argv[1]))
4875 msg = unsplit_string(argv + 2, argc - 2, NULL);
4876 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4880 reply("MSG_NOT_TARGET_NAME");
4886 static CHANSERV_FUNC(cmd_emote)
4892 /* CTCP is so annoying. */
4893 msg = unsplit_string(argv + 1, argc - 1, NULL);
4894 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4896 else if(*argv[1] == '*' && argv[1][1] != '\0')
4898 struct handle_info *hi;
4899 struct userNode *authed;
4902 msg = unsplit_string(argv + 2, argc - 2, NULL);
4904 if (!(hi = get_handle_info(argv[1] + 1)))
4906 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4910 for (authed = hi->users; authed; authed = authed->next_authed)
4911 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4913 else if(GetUserH(argv[1]))
4915 msg = unsplit_string(argv + 2, argc - 2, NULL);
4916 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4920 reply("MSG_NOT_TARGET_NAME");
4926 struct channelList *
4927 chanserv_support_channels(void)
4929 return &chanserv_conf.support_channels;
4932 static CHANSERV_FUNC(cmd_expire)
4934 int channel_count = registered_channels;
4935 expire_channels(NULL);
4936 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4941 chanserv_expire_suspension(void *data)
4943 struct suspended *suspended = data;
4944 struct chanNode *channel;
4946 if(!suspended->expires || (now < suspended->expires))
4947 suspended->revoked = now;
4948 channel = suspended->cData->channel;
4949 suspended->cData->channel = channel;
4950 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4951 if(!IsOffChannel(suspended->cData))
4953 struct mod_chanmode change;
4954 mod_chanmode_init(&change);
4956 change.args[0].mode = MODE_CHANOP;
4957 change.args[0].u.member = AddChannelUser(chanserv, channel);
4958 mod_chanmode_announce(chanserv, channel, &change);
4962 static CHANSERV_FUNC(cmd_csuspend)
4964 struct suspended *suspended;
4965 char reason[MAXLEN];
4966 unsigned long expiry, duration;
4967 struct userData *uData;
4971 if(IsProtected(channel->channel_info))
4973 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4977 if(argv[1][0] == '!')
4979 else if(IsSuspended(channel->channel_info))
4981 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4982 show_suspension_info(cmd, user, channel->channel_info->suspended);
4986 if(!strcmp(argv[1], "0"))
4988 else if((duration = ParseInterval(argv[1])))
4989 expiry = now + duration;
4992 reply("MSG_INVALID_DURATION", argv[1]);
4996 unsplit_string(argv + 2, argc - 2, reason);
4998 suspended = calloc(1, sizeof(*suspended));
4999 suspended->revoked = 0;
5000 suspended->issued = now;
5001 suspended->suspender = strdup(user->handle_info->handle);
5002 suspended->expires = expiry;
5003 suspended->reason = strdup(reason);
5004 suspended->cData = channel->channel_info;
5005 suspended->previous = suspended->cData->suspended;
5006 suspended->cData->suspended = suspended;
5008 if(suspended->expires)
5009 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5011 if(IsSuspended(channel->channel_info))
5013 suspended->previous->revoked = now;
5014 if(suspended->previous->expires)
5015 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5016 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5017 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5021 /* Mark all users in channel as absent. */
5022 for(uData = channel->channel_info->users; uData; uData = uData->next)
5031 /* Mark the channel as suspended, then part. */
5032 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5033 DelChannelUser(chanserv, channel, suspended->reason, 0);
5034 reply("CSMSG_SUSPENDED", channel->name);
5035 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5036 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5041 static CHANSERV_FUNC(cmd_cunsuspend)
5043 struct suspended *suspended;
5044 char message[MAXLEN];
5046 if(!IsSuspended(channel->channel_info))
5048 reply("CSMSG_NOT_SUSPENDED", channel->name);
5052 suspended = channel->channel_info->suspended;
5054 /* Expire the suspension and join ChanServ to the channel. */
5055 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5056 chanserv_expire_suspension(suspended);
5057 reply("CSMSG_UNSUSPENDED", channel->name);
5058 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5059 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5063 typedef struct chanservSearch
5068 unsigned long unvisited;
5069 unsigned long registered;
5071 unsigned long flags;
5075 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5078 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5083 search = malloc(sizeof(struct chanservSearch));
5084 memset(search, 0, sizeof(*search));
5087 for(i = 0; i < argc; i++)
5089 /* Assume all criteria require arguments. */
5092 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5096 if(!irccasecmp(argv[i], "name"))
5097 search->name = argv[++i];
5098 else if(!irccasecmp(argv[i], "registrar"))
5099 search->registrar = argv[++i];
5100 else if(!irccasecmp(argv[i], "unvisited"))
5101 search->unvisited = ParseInterval(argv[++i]);
5102 else if(!irccasecmp(argv[i], "registered"))
5103 search->registered = ParseInterval(argv[++i]);
5104 else if(!irccasecmp(argv[i], "flags"))
5107 if(!irccasecmp(argv[i], "nodelete"))
5108 search->flags |= CHANNEL_NODELETE;
5109 else if(!irccasecmp(argv[i], "suspended"))
5110 search->flags |= CHANNEL_SUSPENDED;
5111 else if(!irccasecmp(argv[i], "unreviewed"))
5112 search->flags |= CHANNEL_UNREVIEWED;
5115 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5119 else if(!irccasecmp(argv[i], "limit"))
5120 search->limit = strtoul(argv[++i], NULL, 10);
5123 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5128 if(search->name && !strcmp(search->name, "*"))
5130 if(search->registrar && !strcmp(search->registrar, "*"))
5131 search->registrar = 0;
5140 chanserv_channel_match(struct chanData *channel, search_t search)
5142 const char *name = channel->channel->name;
5143 if((search->name && !match_ircglob(name, search->name)) ||
5144 (search->registrar && !channel->registrar) ||
5145 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5146 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5147 (search->registered && (now - channel->registered) > search->registered) ||
5148 (search->flags && ((search->flags & channel->flags) != search->flags)))
5155 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5157 struct chanData *channel;
5158 unsigned int matches = 0;
5160 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5162 if(!chanserv_channel_match(channel, search))
5172 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5177 search_print(struct chanData *channel, void *data)
5179 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5182 static CHANSERV_FUNC(cmd_search)
5185 unsigned int matches;
5186 channel_search_func action;
5190 if(!irccasecmp(argv[1], "count"))
5191 action = search_count;
5192 else if(!irccasecmp(argv[1], "print"))
5193 action = search_print;
5196 reply("CSMSG_ACTION_INVALID", argv[1]);
5200 search = chanserv_search_create(user, argc - 2, argv + 2);
5204 if(action == search_count)
5205 search->limit = INT_MAX;
5207 if(action == search_print)
5208 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5210 matches = chanserv_channel_search(search, action, user);
5213 reply("MSG_MATCH_COUNT", matches);
5215 reply("MSG_NO_MATCHES");
5221 static CHANSERV_FUNC(cmd_unvisited)
5223 struct chanData *cData;
5224 unsigned long interval = chanserv_conf.channel_expire_delay;
5225 char buffer[INTERVALLEN];
5226 unsigned int limit = 25, matches = 0;
5230 interval = ParseInterval(argv[1]);
5232 limit = atoi(argv[2]);
5235 intervalString(buffer, interval, user->handle_info);
5236 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5238 for(cData = channelList; cData && matches < limit; cData = cData->next)
5240 if((now - cData->visited) < interval)
5243 intervalString(buffer, now - cData->visited, user->handle_info);
5244 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5251 static MODCMD_FUNC(chan_opt_defaulttopic)
5257 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5259 reply("CSMSG_TOPIC_LOCKED", channel->name);
5263 topic = unsplit_string(argv+1, argc-1, NULL);
5265 free(channel->channel_info->topic);
5266 if(topic[0] == '*' && topic[1] == 0)
5268 topic = channel->channel_info->topic = NULL;
5272 topic = channel->channel_info->topic = strdup(topic);
5273 if(channel->channel_info->topic_mask
5274 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5275 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5277 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5280 if(channel->channel_info->topic)
5281 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5283 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5287 static MODCMD_FUNC(chan_opt_topicmask)
5291 struct chanData *cData = channel->channel_info;
5294 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5296 reply("CSMSG_TOPIC_LOCKED", channel->name);
5300 mask = unsplit_string(argv+1, argc-1, NULL);
5302 if(cData->topic_mask)
5303 free(cData->topic_mask);
5304 if(mask[0] == '*' && mask[1] == 0)
5306 cData->topic_mask = 0;
5310 cData->topic_mask = strdup(mask);
5312 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5313 else if(!match_ircglob(cData->topic, cData->topic_mask))
5314 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5318 if(channel->channel_info->topic_mask)
5319 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5321 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5325 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5329 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5333 if(greeting[0] == '*' && greeting[1] == 0)
5337 unsigned int length = strlen(greeting);
5338 if(length > chanserv_conf.greeting_length)
5340 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5343 *data = strdup(greeting);
5352 reply(name, user_find_message(user, "MSG_NONE"));
5356 static MODCMD_FUNC(chan_opt_greeting)
5358 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5361 static MODCMD_FUNC(chan_opt_usergreeting)
5363 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5366 static MODCMD_FUNC(chan_opt_modes)
5368 struct mod_chanmode *new_modes;
5369 char modes[MODELEN];
5373 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5375 reply("CSMSG_NO_ACCESS");
5378 if(argv[1][0] == '*' && argv[1][1] == 0)
5380 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5382 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5384 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5387 else if(new_modes->argc > 1)
5389 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5390 mod_chanmode_free(new_modes);
5395 channel->channel_info->modes = *new_modes;
5396 modcmd_chanmode_announce(new_modes);
5397 mod_chanmode_free(new_modes);
5401 mod_chanmode_format(&channel->channel_info->modes, modes);
5403 reply("CSMSG_SET_MODES", modes);
5405 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5409 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5411 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5413 struct chanData *cData = channel->channel_info;
5418 /* Set flag according to value. */
5419 if(enabled_string(argv[1]))
5421 cData->flags |= mask;
5424 else if(disabled_string(argv[1]))
5426 cData->flags &= ~mask;
5431 reply("MSG_INVALID_BINARY", argv[1]);
5437 /* Find current option value. */
5438 value = (cData->flags & mask) ? 1 : 0;
5442 reply(name, user_find_message(user, "MSG_ON"));
5444 reply(name, user_find_message(user, "MSG_OFF"));
5448 static MODCMD_FUNC(chan_opt_nodelete)
5450 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5452 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5456 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5459 static MODCMD_FUNC(chan_opt_dynlimit)
5461 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5464 static MODCMD_FUNC(chan_opt_offchannel)
5466 struct chanData *cData = channel->channel_info;
5471 /* Set flag according to value. */
5472 if(enabled_string(argv[1]))
5474 if(!IsOffChannel(cData))
5475 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5476 cData->flags |= CHANNEL_OFFCHANNEL;
5479 else if(disabled_string(argv[1]))
5481 if(IsOffChannel(cData))
5483 struct mod_chanmode change;
5484 mod_chanmode_init(&change);
5486 change.args[0].mode = MODE_CHANOP;
5487 change.args[0].u.member = AddChannelUser(chanserv, channel);
5488 mod_chanmode_announce(chanserv, channel, &change);
5490 cData->flags &= ~CHANNEL_OFFCHANNEL;
5495 reply("MSG_INVALID_BINARY", argv[1]);
5501 /* Find current option value. */
5502 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5506 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5508 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5512 static MODCMD_FUNC(chan_opt_unreviewed)
5514 struct chanData *cData = channel->channel_info;
5515 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5521 /* The two directions can have different ACLs. */
5522 if(enabled_string(argv[1]))
5524 else if(disabled_string(argv[1]))
5528 reply("MSG_INVALID_BINARY", argv[1]);
5532 if (new_value != value)
5534 struct svccmd *subcmd;
5535 char subcmd_name[32];
5537 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5538 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5541 reply("MSG_COMMAND_DISABLED", subcmd_name);
5544 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5548 cData->flags |= CHANNEL_UNREVIEWED;
5551 free(cData->registrar);
5552 cData->registrar = strdup(user->handle_info->handle);
5553 cData->flags &= ~CHANNEL_UNREVIEWED;
5560 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5562 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5566 static MODCMD_FUNC(chan_opt_defaults)
5568 struct userData *uData;
5569 struct chanData *cData;
5570 const char *confirm;
5571 enum levelOption lvlOpt;
5572 enum charOption chOpt;
5574 cData = channel->channel_info;
5575 uData = GetChannelUser(cData, user->handle_info);
5576 if(!uData || (uData->access < UL_OWNER))
5578 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5581 confirm = make_confirmation_string(uData);
5582 if((argc < 2) || strcmp(argv[1], confirm))
5584 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5587 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5588 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5589 cData->modes = chanserv_conf.default_modes;
5590 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5591 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5592 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5593 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5594 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5599 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5601 struct chanData *cData = channel->channel_info;
5602 struct userData *uData;
5603 unsigned short value;
5607 if(!check_user_level(channel, user, option, 1, 1))
5609 reply("CSMSG_CANNOT_SET");
5612 value = user_level_from_name(argv[1], UL_OWNER+1);
5613 if(!value && strcmp(argv[1], "0"))
5615 reply("CSMSG_INVALID_ACCESS", argv[1]);
5618 uData = GetChannelUser(cData, user->handle_info);
5619 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5621 reply("CSMSG_BAD_SETLEVEL");
5627 if(value > cData->lvlOpts[lvlGiveOps])
5629 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5634 if(value < cData->lvlOpts[lvlGiveVoice])
5636 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5641 /* This test only applies to owners, since non-owners
5642 * trying to set an option to above their level get caught
5643 * by the CSMSG_BAD_SETLEVEL test above.
5645 if(value > uData->access)
5647 reply("CSMSG_BAD_SETTERS");
5654 cData->lvlOpts[option] = value;
5656 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5660 static MODCMD_FUNC(chan_opt_enfops)
5662 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5665 static MODCMD_FUNC(chan_opt_giveops)
5667 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5670 static MODCMD_FUNC(chan_opt_enfmodes)
5672 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5675 static MODCMD_FUNC(chan_opt_enftopic)
5677 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5680 static MODCMD_FUNC(chan_opt_pubcmd)
5682 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5685 static MODCMD_FUNC(chan_opt_setters)
5687 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5690 static MODCMD_FUNC(chan_opt_ctcpusers)
5692 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5695 static MODCMD_FUNC(chan_opt_userinfo)
5697 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5700 static MODCMD_FUNC(chan_opt_givevoice)
5702 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5705 static MODCMD_FUNC(chan_opt_topicsnarf)
5707 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5710 static MODCMD_FUNC(chan_opt_inviteme)
5712 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5716 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5718 struct chanData *cData = channel->channel_info;
5719 int count = charOptions[option].count, idx;
5723 idx = atoi(argv[1]);
5725 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5727 reply("CSMSG_INVALID_NUMERIC", idx);
5728 /* Show possible values. */
5729 for(idx = 0; idx < count; idx++)
5730 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5734 cData->chOpts[option] = charOptions[option].values[idx].value;
5738 /* Find current option value. */
5741 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5745 /* Somehow, the option value is corrupt; reset it to the default. */
5746 cData->chOpts[option] = charOptions[option].default_value;
5751 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5755 static MODCMD_FUNC(chan_opt_protect)
5757 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5760 static MODCMD_FUNC(chan_opt_toys)
5762 return channel_multiple_option(chToys, CSFUNC_ARGS);
5765 static MODCMD_FUNC(chan_opt_ctcpreaction)
5767 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5770 static MODCMD_FUNC(chan_opt_topicrefresh)
5772 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5775 static struct svccmd_list set_shows_list;
5778 handle_svccmd_unbind(struct svccmd *target) {
5780 for(ii=0; ii<set_shows_list.used; ++ii)
5781 if(target == set_shows_list.list[ii])
5782 set_shows_list.used = 0;
5785 static CHANSERV_FUNC(cmd_set)
5787 struct svccmd *subcmd;
5791 /* Check if we need to (re-)initialize set_shows_list. */
5792 if(!set_shows_list.used)
5794 if(!set_shows_list.size)
5796 set_shows_list.size = chanserv_conf.set_shows->used;
5797 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5799 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5801 const char *name = chanserv_conf.set_shows->list[ii];
5802 sprintf(buf, "%s %s", argv[0], name);
5803 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5806 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5809 svccmd_list_append(&set_shows_list, subcmd);
5815 reply("CSMSG_CHANNEL_OPTIONS");
5816 for(ii = 0; ii < set_shows_list.used; ii++)
5818 subcmd = set_shows_list.list[ii];
5819 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5824 sprintf(buf, "%s %s", argv[0], argv[1]);
5825 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5828 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5831 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5833 reply("CSMSG_NO_ACCESS");
5839 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5843 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5845 struct userData *uData;
5847 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5850 reply("CSMSG_NOT_USER", channel->name);
5856 /* Just show current option value. */
5858 else if(enabled_string(argv[1]))
5860 uData->flags |= mask;
5862 else if(disabled_string(argv[1]))
5864 uData->flags &= ~mask;
5868 reply("MSG_INVALID_BINARY", argv[1]);
5872 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5876 static MODCMD_FUNC(user_opt_noautoop)
5878 struct userData *uData;
5880 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5883 reply("CSMSG_NOT_USER", channel->name);
5886 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5887 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5889 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5892 static MODCMD_FUNC(user_opt_autoinvite)
5894 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5896 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5898 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5901 static MODCMD_FUNC(user_opt_info)
5903 struct userData *uData;
5906 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5910 /* If they got past the command restrictions (which require access)
5911 * but fail this test, we have some fool with security override on.
5913 reply("CSMSG_NOT_USER", channel->name);
5920 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5921 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5923 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5926 bp = strcspn(infoline, "\001");
5929 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5934 if(infoline[0] == '*' && infoline[1] == 0)
5937 uData->info = strdup(infoline);
5940 reply("CSMSG_USET_INFO", uData->info);
5942 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5946 struct svccmd_list uset_shows_list;
5948 static CHANSERV_FUNC(cmd_uset)
5950 struct svccmd *subcmd;
5954 /* Check if we need to (re-)initialize uset_shows_list. */
5955 if(!uset_shows_list.used)
5959 "NoAutoOp", "AutoInvite", "Info"
5962 if(!uset_shows_list.size)
5964 uset_shows_list.size = ArrayLength(options);
5965 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5967 for(ii = 0; ii < ArrayLength(options); ii++)
5969 const char *name = options[ii];
5970 sprintf(buf, "%s %s", argv[0], name);
5971 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5974 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5977 svccmd_list_append(&uset_shows_list, subcmd);
5983 /* Do this so options are presented in a consistent order. */
5984 reply("CSMSG_USER_OPTIONS");
5985 for(ii = 0; ii < uset_shows_list.used; ii++)
5986 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5990 sprintf(buf, "%s %s", argv[0], argv[1]);
5991 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5994 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5998 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6001 static CHANSERV_FUNC(cmd_giveownership)
6003 struct handle_info *new_owner_hi;
6004 struct userData *new_owner;
6005 struct userData *curr_user;
6006 struct userData *invoker;
6007 struct chanData *cData = channel->channel_info;
6008 struct do_not_register *dnr;
6009 const char *confirm;
6011 unsigned short co_access;
6012 char reason[MAXLEN];
6015 curr_user = GetChannelAccess(cData, user->handle_info);
6016 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6017 if(!curr_user || (curr_user->access != UL_OWNER))
6019 struct userData *owner = NULL;
6020 for(curr_user = channel->channel_info->users;
6022 curr_user = curr_user->next)
6024 if(curr_user->access != UL_OWNER)
6028 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6035 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6037 char delay[INTERVALLEN];
6038 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6039 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6042 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6044 if(new_owner_hi == user->handle_info)
6046 reply("CSMSG_NO_TRANSFER_SELF");
6049 new_owner = GetChannelAccess(cData, new_owner_hi);
6054 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6058 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6062 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6064 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6067 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6068 if(!IsHelping(user))
6069 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6071 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6074 invoker = GetChannelUser(cData, user->handle_info);
6075 if(invoker->access <= UL_OWNER)
6077 confirm = make_confirmation_string(curr_user);
6078 if((argc < 3) || strcmp(argv[2], confirm))
6080 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6084 if(new_owner->access >= UL_COOWNER)
6085 co_access = new_owner->access;
6087 co_access = UL_COOWNER;
6088 new_owner->access = UL_OWNER;
6090 curr_user->access = co_access;
6091 cData->ownerTransfer = now;
6092 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6093 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6094 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6098 static CHANSERV_FUNC(cmd_suspend)
6100 struct handle_info *hi;
6101 struct userData *actor, *real_actor, *target;
6102 unsigned int override = 0;
6105 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6106 actor = GetChannelUser(channel->channel_info, user->handle_info);
6107 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6108 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6110 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6113 if(target->access >= actor->access)
6115 reply("MSG_USER_OUTRANKED", hi->handle);
6118 if(target->flags & USER_SUSPENDED)
6120 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6125 target->present = 0;
6128 if(!real_actor || target->access >= real_actor->access)
6129 override = CMD_LOG_OVERRIDE;
6130 target->flags |= USER_SUSPENDED;
6131 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6132 return 1 | override;
6135 static CHANSERV_FUNC(cmd_unsuspend)
6137 struct handle_info *hi;
6138 struct userData *actor, *real_actor, *target;
6139 unsigned int override = 0;
6142 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6143 actor = GetChannelUser(channel->channel_info, user->handle_info);
6144 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6145 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6147 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6150 if(target->access >= actor->access)
6152 reply("MSG_USER_OUTRANKED", hi->handle);
6155 if(!(target->flags & USER_SUSPENDED))
6157 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6160 if(!real_actor || target->access >= real_actor->access)
6161 override = CMD_LOG_OVERRIDE;
6162 target->flags &= ~USER_SUSPENDED;
6163 scan_user_presence(target, NULL);
6164 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6165 return 1 | override;
6168 static MODCMD_FUNC(cmd_deleteme)
6170 struct handle_info *hi;
6171 struct userData *target;
6172 const char *confirm_string;
6173 unsigned short access_level;
6176 hi = user->handle_info;
6177 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6179 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6182 if(target->access == UL_OWNER)
6184 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6187 confirm_string = make_confirmation_string(target);
6188 if((argc < 2) || strcmp(argv[1], confirm_string))
6190 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6193 access_level = target->access;
6194 channel_name = strdup(channel->name);
6195 del_channel_user(target, 1);
6196 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6202 chanserv_refresh_topics(UNUSED_ARG(void *data))
6204 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6205 struct chanData *cData;
6208 for(cData = channelList; cData; cData = cData->next)
6210 if(IsSuspended(cData))
6212 opt = cData->chOpts[chTopicRefresh];
6215 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6218 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6219 cData->last_refresh = refresh_num;
6221 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6224 static CHANSERV_FUNC(cmd_unf)
6228 char response[MAXLEN];
6229 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6230 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6231 irc_privmsg(cmd->parent->bot, channel->name, response);
6234 reply("CSMSG_UNF_RESPONSE");
6238 static CHANSERV_FUNC(cmd_ping)
6242 char response[MAXLEN];
6243 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6244 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6245 irc_privmsg(cmd->parent->bot, channel->name, response);
6248 reply("CSMSG_PING_RESPONSE");
6252 static CHANSERV_FUNC(cmd_wut)
6256 char response[MAXLEN];
6257 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6258 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6259 irc_privmsg(cmd->parent->bot, channel->name, response);
6262 reply("CSMSG_WUT_RESPONSE");
6266 static CHANSERV_FUNC(cmd_8ball)
6268 unsigned int i, j, accum;
6273 for(i=1; i<argc; i++)
6274 for(j=0; argv[i][j]; j++)
6275 accum = (accum << 5) - accum + toupper(argv[i][j]);
6276 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6279 char response[MAXLEN];
6280 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6281 irc_privmsg(cmd->parent->bot, channel->name, response);
6284 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6288 static CHANSERV_FUNC(cmd_d)
6290 unsigned long sides, count, modifier, ii, total;
6291 char response[MAXLEN], *sep;
6295 if((count = strtoul(argv[1], &sep, 10)) < 1)
6305 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6306 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6310 else if((sep[0] == '-') && isdigit(sep[1]))
6311 modifier = strtoul(sep, NULL, 10);
6312 else if((sep[0] == '+') && isdigit(sep[1]))
6313 modifier = strtoul(sep+1, NULL, 10);
6320 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6325 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6328 for(total = ii = 0; ii < count; ++ii)
6329 total += (rand() % sides) + 1;
6332 if((count > 1) || modifier)
6334 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6335 sprintf(response, fmt, total, count, sides, modifier);
6339 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6340 sprintf(response, fmt, total, sides);
6343 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6345 send_message_type(4, user, cmd->parent->bot, "%s", response);
6349 static CHANSERV_FUNC(cmd_huggle)
6351 /* CTCP must be via PRIVMSG, never notice */
6353 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6355 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6360 chanserv_adjust_limit(void *data)
6362 struct mod_chanmode change;
6363 struct chanData *cData = data;
6364 struct chanNode *channel = cData->channel;
6367 if(IsSuspended(cData))
6370 cData->limitAdjusted = now;
6371 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6372 if(cData->modes.modes_set & MODE_LIMIT)
6374 if(limit > cData->modes.new_limit)
6375 limit = cData->modes.new_limit;
6376 else if(limit == cData->modes.new_limit)
6380 mod_chanmode_init(&change);
6381 change.modes_set = MODE_LIMIT;
6382 change.new_limit = limit;
6383 mod_chanmode_announce(chanserv, channel, &change);
6387 handle_new_channel(struct chanNode *channel)
6389 struct chanData *cData;
6391 if(!(cData = channel->channel_info))
6394 if(cData->modes.modes_set || cData->modes.modes_clear)
6395 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6397 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6398 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6401 /* Welcome to my worst nightmare. Warning: Read (or modify)
6402 the code below at your own risk. */
6404 handle_join(struct modeNode *mNode)
6406 struct mod_chanmode change;
6407 struct userNode *user = mNode->user;
6408 struct chanNode *channel = mNode->channel;
6409 struct chanData *cData;
6410 struct userData *uData = NULL;
6411 struct banData *bData;
6412 struct handle_info *handle;
6413 unsigned int modes = 0, info = 0;
6416 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6419 cData = channel->channel_info;
6420 if(channel->members.used > cData->max)
6421 cData->max = channel->members.used;
6423 /* Check for bans. If they're joining through a ban, one of two
6425 * 1: Join during a netburst, by riding the break. Kick them
6426 * unless they have ops or voice in the channel.
6427 * 2: They're allowed to join through the ban (an invite in
6428 * ircu2.10, or a +e on Hybrid, or something).
6429 * If they're not joining through a ban, and the banlist is not
6430 * full, see if they're on the banlist for the channel. If so,
6433 if(user->uplink->burst && !mNode->modes)
6436 for(ii = 0; ii < channel->banlist.used; ii++)
6438 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6440 /* Riding a netburst. Naughty. */
6441 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6447 mod_chanmode_init(&change);
6449 if(channel->banlist.used < MAXBANS)
6451 /* Not joining through a ban. */
6452 for(bData = cData->bans;
6453 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6454 bData = bData->next);
6458 char kick_reason[MAXLEN];
6459 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6461 bData->triggered = now;
6462 if(bData != cData->bans)
6464 /* Shuffle the ban to the head of the list. */
6466 bData->next->prev = bData->prev;
6468 bData->prev->next = bData->next;
6471 bData->next = cData->bans;
6474 cData->bans->prev = bData;
6475 cData->bans = bData;
6478 change.args[0].mode = MODE_BAN;
6479 change.args[0].u.hostmask = bData->mask;
6480 mod_chanmode_announce(chanserv, channel, &change);
6481 KickChannelUser(user, channel, chanserv, kick_reason);
6486 /* ChanServ will not modify the limits in join-flooded channels.
6487 It will also skip DynLimit processing when the user (or srvx)
6488 is bursting in, because there are likely more incoming. */
6489 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6490 && !user->uplink->burst
6491 && !channel->join_flooded
6492 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6494 /* The user count has begun "bumping" into the channel limit,
6495 so set a timer to raise the limit a bit. Any previous
6496 timers are removed so three incoming users within the delay
6497 results in one limit change, not three. */
6499 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6500 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6503 if(channel->join_flooded)
6505 /* don't automatically give ops or voice during a join flood */
6507 else if(cData->lvlOpts[lvlGiveOps] == 0)
6508 modes |= MODE_CHANOP;
6509 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6510 modes |= MODE_VOICE;
6512 greeting = cData->greeting;
6513 if(user->handle_info)
6515 handle = user->handle_info;
6517 if(IsHelper(user) && !IsHelping(user))
6520 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6522 if(channel == chanserv_conf.support_channels.list[ii])
6524 HANDLE_SET_FLAG(user->handle_info, HELPING);
6530 uData = GetTrueChannelAccess(cData, handle);
6531 if(uData && !IsUserSuspended(uData))
6533 /* Ops and above were handled by the above case. */
6534 if(IsUserAutoOp(uData))
6536 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6537 modes |= MODE_CHANOP;
6538 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6539 modes |= MODE_VOICE;
6541 if(uData->access >= UL_PRESENT)
6542 cData->visited = now;
6543 if(cData->user_greeting)
6544 greeting = cData->user_greeting;
6546 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6547 && ((now - uData->seen) >= chanserv_conf.info_delay)
6555 /* If user joining normally (not during burst), apply op or voice,
6556 * and send greeting/userinfo as appropriate.
6558 if(!user->uplink->burst)
6562 if(modes & MODE_CHANOP)
6563 modes &= ~MODE_VOICE;
6564 change.args[0].mode = modes;
6565 change.args[0].u.member = mNode;
6566 mod_chanmode_announce(chanserv, channel, &change);
6569 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6571 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6577 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6579 struct mod_chanmode change;
6580 struct userData *channel;
6581 unsigned int ii, jj;
6583 if(!user->handle_info)
6586 mod_chanmode_init(&change);
6588 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6590 struct chanNode *cn;
6591 struct modeNode *mn;
6592 if(IsUserSuspended(channel)
6593 || IsSuspended(channel->channel)
6594 || !(cn = channel->channel->channel))
6597 mn = GetUserMode(cn, user);
6600 if(!IsUserSuspended(channel)
6601 && IsUserAutoInvite(channel)
6602 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6604 && !user->uplink->burst)
6605 irc_invite(chanserv, user, cn);
6609 if(channel->access >= UL_PRESENT)
6610 channel->channel->visited = now;
6612 if(IsUserAutoOp(channel))
6614 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6615 change.args[0].mode = MODE_CHANOP;
6616 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6617 change.args[0].mode = MODE_VOICE;
6619 change.args[0].mode = 0;
6620 change.args[0].u.member = mn;
6621 if(change.args[0].mode)
6622 mod_chanmode_announce(chanserv, cn, &change);
6625 channel->seen = now;
6626 channel->present = 1;
6629 for(ii = 0; ii < user->channels.used; ++ii)
6631 struct chanNode *chan = user->channels.list[ii]->channel;
6632 struct banData *ban;
6634 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6635 || !chan->channel_info
6636 || IsSuspended(chan->channel_info))
6638 for(jj = 0; jj < chan->banlist.used; ++jj)
6639 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6641 if(jj < chan->banlist.used)
6643 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6645 char kick_reason[MAXLEN];
6646 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6648 change.args[0].mode = MODE_BAN;
6649 change.args[0].u.hostmask = ban->mask;
6650 mod_chanmode_announce(chanserv, chan, &change);
6651 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6652 KickChannelUser(user, chan, chanserv, kick_reason);
6653 ban->triggered = now;
6658 if(IsSupportHelper(user))
6660 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6662 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6664 HANDLE_SET_FLAG(user->handle_info, HELPING);
6672 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6674 struct chanData *cData;
6675 struct userData *uData;
6677 cData = mn->channel->channel_info;
6678 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6681 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6683 /* Allow for a bit of padding so that the limit doesn't
6684 track the user count exactly, which could get annoying. */
6685 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6687 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6688 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6692 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6694 scan_user_presence(uData, mn->user);
6696 if (uData->access >= UL_PRESENT)
6697 cData->visited = now;
6700 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6703 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6704 if(find_handle_in_channel(chanserv_conf.support_channels.list[ii], mn->user->handle_info, mn->user))
6706 if(ii == chanserv_conf.support_channels.used)
6707 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6712 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6714 struct userData *uData;
6716 if(!channel->channel_info || !kicker || IsService(kicker)
6717 || (kicker == victim) || IsSuspended(channel->channel_info)
6718 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6721 if(protect_user(victim, kicker, channel->channel_info))
6723 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6724 KickChannelUser(kicker, channel, chanserv, reason);
6727 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6732 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6734 struct chanData *cData;
6736 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6739 cData = channel->channel_info;
6740 if(bad_topic(channel, user, channel->topic))
6742 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6743 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6744 SetChannelTopic(channel, chanserv, old_topic, 1);
6745 else if(cData->topic)
6746 SetChannelTopic(channel, chanserv, cData->topic, 1);
6749 /* With topicsnarf, grab the topic and save it as the default topic. */
6750 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6753 cData->topic = strdup(channel->topic);
6759 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6761 struct mod_chanmode *bounce = NULL;
6762 unsigned int bnc, ii;
6765 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6768 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6769 && mode_lock_violated(&channel->channel_info->modes, change))
6771 char correct[MAXLEN];
6772 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6773 mod_chanmode_format(&channel->channel_info->modes, correct);
6774 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6776 for(ii = bnc = 0; ii < change->argc; ++ii)
6778 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6780 const struct userNode *victim = change->args[ii].u.member->user;
6781 if(!protect_user(victim, user, channel->channel_info))
6784 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6787 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6788 bounce->args[bnc].u.member = GetUserMode(channel, user);
6789 if(bounce->args[bnc].u.member)
6793 bounce->args[bnc].mode = MODE_CHANOP;
6794 bounce->args[bnc].u.member = change->args[ii].u.member;
6796 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6798 else if(change->args[ii].mode & MODE_CHANOP)
6800 const struct userNode *victim = change->args[ii].u.member->user;
6801 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6804 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6805 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6806 bounce->args[bnc].u.member = change->args[ii].u.member;
6809 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6811 const char *ban = change->args[ii].u.hostmask;
6812 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6815 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6816 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6817 bounce->args[bnc].u.hostmask = strdup(ban);
6819 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6824 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6825 mod_chanmode_announce(chanserv, channel, bounce);
6826 for(ii = 0; ii < change->argc; ++ii)
6827 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6828 free((char*)bounce->args[ii].u.hostmask);
6829 mod_chanmode_free(bounce);
6834 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6836 struct chanNode *channel;
6837 struct banData *bData;
6838 struct mod_chanmode change;
6839 unsigned int ii, jj;
6840 char kick_reason[MAXLEN];
6842 mod_chanmode_init(&change);
6844 change.args[0].mode = MODE_BAN;
6845 for(ii = 0; ii < user->channels.used; ++ii)
6847 channel = user->channels.list[ii]->channel;
6848 /* Need not check for bans if they're opped or voiced. */
6849 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6851 /* Need not check for bans unless channel registration is active. */
6852 if(!channel->channel_info || IsSuspended(channel->channel_info))
6854 /* Look for a matching ban already on the channel. */
6855 for(jj = 0; jj < channel->banlist.used; ++jj)
6856 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6858 /* Need not act if we found one. */
6859 if(jj < channel->banlist.used)
6861 /* Look for a matching ban in this channel. */
6862 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6864 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6866 change.args[0].u.hostmask = bData->mask;
6867 mod_chanmode_announce(chanserv, channel, &change);
6868 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6869 KickChannelUser(user, channel, chanserv, kick_reason);
6870 bData->triggered = now;
6871 break; /* we don't need to check any more bans in the channel */
6876 static void handle_rename(struct handle_info *handle, const char *old_handle)
6878 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6882 dict_remove2(handle_dnrs, old_handle, 1);
6883 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6884 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6889 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6891 struct userNode *h_user;
6893 if(handle->channels)
6895 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6896 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6898 while(handle->channels)
6899 del_channel_user(handle->channels, 1);
6904 handle_server_link(UNUSED_ARG(struct server *server))
6906 struct chanData *cData;
6908 for(cData = channelList; cData; cData = cData->next)
6910 if(!IsSuspended(cData))
6911 cData->may_opchan = 1;
6912 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6913 && !cData->channel->join_flooded
6914 && ((cData->channel->limit - cData->channel->members.used)
6915 < chanserv_conf.adjust_threshold))
6917 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6918 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6924 chanserv_conf_read(void)
6928 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6929 struct mod_chanmode *change;
6930 struct string_list *strlist;
6931 struct chanNode *chan;
6934 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6936 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6939 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6940 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6941 chanserv_conf.support_channels.used = 0;
6942 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6944 for(ii = 0; ii < strlist->used; ++ii)
6946 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6949 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6951 channelList_append(&chanserv_conf.support_channels, chan);
6954 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6957 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6960 chan = AddChannel(str, now, str2, NULL);
6962 channelList_append(&chanserv_conf.support_channels, chan);
6964 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6965 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6966 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6967 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6968 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6969 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6970 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6971 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6972 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6973 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6974 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6975 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6976 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6977 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6978 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6979 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6980 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6981 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6982 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6983 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6984 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6985 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6986 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6987 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6988 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6990 NickChange(chanserv, str, 0);
6991 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6992 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6993 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6994 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6995 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6996 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6997 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6998 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
6999 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7000 chanserv_conf.max_owned = str ? atoi(str) : 5;
7001 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7002 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7003 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7004 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7005 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7006 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7007 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7010 safestrncpy(mode_line, str, sizeof(mode_line));
7011 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7012 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7013 && (change->argc < 2))
7015 chanserv_conf.default_modes = *change;
7016 mod_chanmode_free(change);
7018 free_string_list(chanserv_conf.set_shows);
7019 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7021 strlist = string_list_copy(strlist);
7024 static const char *list[] = {
7025 /* free form text */
7026 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7027 /* options based on user level */
7028 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7029 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7030 /* multiple choice options */
7031 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7032 /* binary options */
7033 "DynLimit", "NoDelete",
7037 strlist = alloc_string_list(ArrayLength(list)-1);
7038 for(ii=0; list[ii]; ii++)
7039 string_list_append(strlist, strdup(list[ii]));
7041 chanserv_conf.set_shows = strlist;
7042 /* We don't look things up now, in case the list refers to options
7043 * defined by modules initialized after this point. Just mark the
7044 * function list as invalid, so it will be initialized.
7046 set_shows_list.used = 0;
7047 free_string_list(chanserv_conf.eightball);
7048 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7051 strlist = string_list_copy(strlist);
7055 strlist = alloc_string_list(4);
7056 string_list_append(strlist, strdup("Yes."));
7057 string_list_append(strlist, strdup("No."));
7058 string_list_append(strlist, strdup("Maybe so."));
7060 chanserv_conf.eightball = strlist;
7061 free_string_list(chanserv_conf.old_ban_names);
7062 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7064 strlist = string_list_copy(strlist);
7066 strlist = alloc_string_list(2);
7067 chanserv_conf.old_ban_names = strlist;
7068 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7069 off_channel = str ? atoi(str) : 0;
7073 chanserv_note_type_read(const char *key, struct record_data *rd)
7076 struct note_type *ntype;
7079 if(!(obj = GET_RECORD_OBJECT(rd)))
7081 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7084 if(!(ntype = chanserv_create_note_type(key)))
7086 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7090 /* Figure out set access */
7091 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7093 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7094 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7096 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7098 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7099 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7101 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7103 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7107 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7108 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7109 ntype->set_access.min_opserv = 0;
7112 /* Figure out visibility */
7113 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7114 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7115 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7116 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7117 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7118 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7119 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7120 ntype->visible_type = NOTE_VIS_ALL;
7122 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7124 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7125 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7129 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7131 struct handle_info *handle;
7132 struct userData *uData;
7133 char *seen, *inf, *flags;
7134 unsigned long last_seen;
7135 unsigned short access_level;
7137 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7139 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7143 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7144 if(access_level > UL_OWNER)
7146 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7150 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7151 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7152 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7153 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7154 handle = get_handle_info(key);
7157 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7161 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7162 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7166 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7168 struct banData *bData;
7169 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7170 unsigned long set_time, triggered_time, expires_time;
7172 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7174 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7178 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7179 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7180 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7181 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7182 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7183 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7184 if (!reason || !owner)
7187 set_time = set ? strtoul(set, NULL, 0) : now;
7188 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7190 expires_time = strtoul(s_expires, NULL, 0);
7192 expires_time = set_time + atoi(s_duration);
7196 if(!reason || (expires_time && (expires_time < now)))
7199 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7202 static struct suspended *
7203 chanserv_read_suspended(dict_t obj)
7205 struct suspended *suspended = calloc(1, sizeof(*suspended));
7209 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7210 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7211 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7212 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7213 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7214 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7215 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7216 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7217 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7218 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7223 chanserv_channel_read(const char *key, struct record_data *hir)
7225 struct suspended *suspended;
7226 struct mod_chanmode *modes;
7227 struct chanNode *cNode;
7228 struct chanData *cData;
7229 struct dict *channel, *obj;
7230 char *str, *argv[10];
7234 channel = hir->d.object;
7236 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7239 cNode = AddChannel(key, now, NULL, NULL);
7242 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7245 cData = register_channel(cNode, str);
7248 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7252 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7254 enum levelOption lvlOpt;
7255 enum charOption chOpt;
7257 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7258 cData->flags = atoi(str);
7260 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7262 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7264 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7265 else if(levelOptions[lvlOpt].old_flag)
7267 if(cData->flags & levelOptions[lvlOpt].old_flag)
7268 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7270 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7274 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7276 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7278 cData->chOpts[chOpt] = str[0];
7281 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7283 enum levelOption lvlOpt;
7284 enum charOption chOpt;
7287 cData->flags = base64toint(str, 5);
7288 count = strlen(str += 5);
7289 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7292 if(levelOptions[lvlOpt].old_flag)
7294 if(cData->flags & levelOptions[lvlOpt].old_flag)
7295 lvl = levelOptions[lvlOpt].flag_value;
7297 lvl = levelOptions[lvlOpt].default_value;
7299 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7301 case 'c': lvl = UL_COOWNER; break;
7302 case 'm': lvl = UL_MASTER; break;
7303 case 'n': lvl = UL_OWNER+1; break;
7304 case 'o': lvl = UL_OP; break;
7305 case 'p': lvl = UL_PEON; break;
7306 case 'w': lvl = UL_OWNER; break;
7307 default: lvl = 0; break;
7309 cData->lvlOpts[lvlOpt] = lvl;
7311 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7312 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7315 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7317 suspended = chanserv_read_suspended(obj);
7318 cData->suspended = suspended;
7319 suspended->cData = cData;
7320 /* We could use suspended->expires and suspended->revoked to
7321 * set the CHANNEL_SUSPENDED flag, but we don't. */
7323 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7325 suspended = calloc(1, sizeof(*suspended));
7326 suspended->issued = 0;
7327 suspended->revoked = 0;
7328 suspended->suspender = strdup(str);
7329 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7330 suspended->expires = str ? atoi(str) : 0;
7331 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7332 suspended->reason = strdup(str ? str : "No reason");
7333 suspended->previous = NULL;
7334 cData->suspended = suspended;
7335 suspended->cData = cData;
7339 cData->flags &= ~CHANNEL_SUSPENDED;
7340 suspended = NULL; /* to squelch a warning */
7343 if(IsSuspended(cData)) {
7344 if(suspended->expires > now)
7345 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7346 else if(suspended->expires)
7347 cData->flags &= ~CHANNEL_SUSPENDED;
7350 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7351 struct mod_chanmode change;
7352 mod_chanmode_init(&change);
7354 change.args[0].mode = MODE_CHANOP;
7355 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7356 mod_chanmode_announce(chanserv, cNode, &change);
7359 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7360 cData->registered = str ? strtoul(str, NULL, 0) : now;
7361 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7362 cData->visited = str ? strtoul(str, NULL, 0) : now;
7363 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7364 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7365 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7366 cData->max = str ? atoi(str) : 0;
7367 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7368 cData->greeting = str ? strdup(str) : NULL;
7369 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7370 cData->user_greeting = str ? strdup(str) : NULL;
7371 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7372 cData->topic_mask = str ? strdup(str) : NULL;
7373 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7374 cData->topic = str ? strdup(str) : NULL;
7376 if(!IsSuspended(cData)
7377 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7378 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7379 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7380 cData->modes = *modes;
7382 cData->modes.modes_set |= MODE_REGISTERED;
7383 if(cData->modes.argc > 1)
7384 cData->modes.argc = 1;
7385 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7386 mod_chanmode_free(modes);
7389 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7390 for(it = dict_first(obj); it; it = iter_next(it))
7391 user_read_helper(iter_key(it), iter_data(it), cData);
7393 if(!cData->users && !IsProtected(cData))
7395 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7396 unregister_channel(cData, "has empty user list.");
7400 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7401 for(it = dict_first(obj); it; it = iter_next(it))
7402 ban_read_helper(iter_key(it), iter_data(it), cData);
7404 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7405 for(it = dict_first(obj); it; it = iter_next(it))
7407 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7408 struct record_data *rd = iter_data(it);
7409 const char *note, *setter;
7411 if(rd->type != RECDB_OBJECT)
7413 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7417 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7419 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7421 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7425 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7426 if(!setter) setter = "<unknown>";
7427 chanserv_add_channel_note(cData, ntype, setter, note);
7435 chanserv_dnr_read(const char *key, struct record_data *hir)
7437 const char *setter, *reason, *str;
7438 struct do_not_register *dnr;
7439 unsigned long expiry;
7441 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7444 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7447 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7450 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7453 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7454 expiry = str ? strtoul(str, NULL, 0) : 0;
7455 if(expiry && expiry <= now)
7457 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7460 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7462 dnr->set = atoi(str);
7468 chanserv_saxdb_read(struct dict *database)
7470 struct dict *section;
7473 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7474 for(it = dict_first(section); it; it = iter_next(it))
7475 chanserv_note_type_read(iter_key(it), iter_data(it));
7477 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7478 for(it = dict_first(section); it; it = iter_next(it))
7479 chanserv_channel_read(iter_key(it), iter_data(it));
7481 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7482 for(it = dict_first(section); it; it = iter_next(it))
7483 chanserv_dnr_read(iter_key(it), iter_data(it));
7489 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7491 int high_present = 0;
7492 saxdb_start_record(ctx, KEY_USERS, 1);
7493 for(; uData; uData = uData->next)
7495 if((uData->access >= UL_PRESENT) && uData->present)
7497 saxdb_start_record(ctx, uData->handle->handle, 0);
7498 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7499 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7501 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7503 saxdb_write_string(ctx, KEY_INFO, uData->info);
7504 saxdb_end_record(ctx);
7506 saxdb_end_record(ctx);
7507 return high_present;
7511 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7515 saxdb_start_record(ctx, KEY_BANS, 1);
7516 for(; bData; bData = bData->next)
7518 saxdb_start_record(ctx, bData->mask, 0);
7519 saxdb_write_int(ctx, KEY_SET, bData->set);
7520 if(bData->triggered)
7521 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7523 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7525 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7527 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7528 saxdb_end_record(ctx);
7530 saxdb_end_record(ctx);
7534 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7536 saxdb_start_record(ctx, name, 0);
7537 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7538 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7540 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7542 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7544 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7546 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7547 saxdb_end_record(ctx);
7551 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7555 enum levelOption lvlOpt;
7556 enum charOption chOpt;
7558 saxdb_start_record(ctx, channel->channel->name, 1);
7560 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7561 saxdb_write_int(ctx, KEY_MAX, channel->max);
7563 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7564 if(channel->registrar)
7565 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7566 if(channel->greeting)
7567 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7568 if(channel->user_greeting)
7569 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7570 if(channel->topic_mask)
7571 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7572 if(channel->suspended)
7573 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7575 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7576 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7577 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7578 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7579 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7581 buf[0] = channel->chOpts[chOpt];
7583 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7585 saxdb_end_record(ctx);
7587 if(channel->modes.modes_set || channel->modes.modes_clear)
7589 mod_chanmode_format(&channel->modes, buf);
7590 saxdb_write_string(ctx, KEY_MODES, buf);
7593 high_present = chanserv_write_users(ctx, channel->users);
7594 chanserv_write_bans(ctx, channel->bans);
7596 if(dict_size(channel->notes))
7600 saxdb_start_record(ctx, KEY_NOTES, 1);
7601 for(it = dict_first(channel->notes); it; it = iter_next(it))
7603 struct note *note = iter_data(it);
7604 saxdb_start_record(ctx, iter_key(it), 0);
7605 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7606 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7607 saxdb_end_record(ctx);
7609 saxdb_end_record(ctx);
7612 if(channel->ownerTransfer)
7613 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7614 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7615 saxdb_end_record(ctx);
7619 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7623 saxdb_start_record(ctx, ntype->name, 0);
7624 switch(ntype->set_access_type)
7626 case NOTE_SET_CHANNEL_ACCESS:
7627 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7629 case NOTE_SET_CHANNEL_SETTER:
7630 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7632 case NOTE_SET_PRIVILEGED: default:
7633 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7636 switch(ntype->visible_type)
7638 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7639 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7640 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7642 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7643 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7644 saxdb_end_record(ctx);
7648 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7650 struct do_not_register *dnr;
7651 dict_iterator_t it, next;
7653 for(it = dict_first(dnrs); it; it = next)
7655 next = iter_next(it);
7656 dnr = iter_data(it);
7657 if(dnr->expires && dnr->expires <= now)
7659 dict_remove(dnrs, iter_key(it));
7662 saxdb_start_record(ctx, dnr->chan_name, 0);
7664 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7666 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7667 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7668 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7669 saxdb_end_record(ctx);
7674 chanserv_saxdb_write(struct saxdb_context *ctx)
7677 struct chanData *channel;
7680 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7681 for(it = dict_first(note_types); it; it = iter_next(it))
7682 chanserv_write_note_type(ctx, iter_data(it));
7683 saxdb_end_record(ctx);
7686 saxdb_start_record(ctx, KEY_DNR, 1);
7687 write_dnrs_helper(ctx, handle_dnrs);
7688 write_dnrs_helper(ctx, plain_dnrs);
7689 write_dnrs_helper(ctx, mask_dnrs);
7690 saxdb_end_record(ctx);
7693 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7694 for(channel = channelList; channel; channel = channel->next)
7695 chanserv_write_channel(ctx, channel);
7696 saxdb_end_record(ctx);
7702 chanserv_db_cleanup(void) {
7704 unreg_part_func(handle_part);
7706 unregister_channel(channelList, "terminating.");
7707 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7708 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7709 free(chanserv_conf.support_channels.list);
7710 dict_delete(handle_dnrs);
7711 dict_delete(plain_dnrs);
7712 dict_delete(mask_dnrs);
7713 dict_delete(note_types);
7714 free_string_list(chanserv_conf.eightball);
7715 free_string_list(chanserv_conf.old_ban_names);
7716 free_string_list(chanserv_conf.set_shows);
7717 free(set_shows_list.list);
7718 free(uset_shows_list.list);
7721 struct userData *helper = helperList;
7722 helperList = helperList->next;
7727 #if defined(GCC_VARMACROS)
7728 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7729 #elif defined(C99_VARMACROS)
7730 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7732 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7733 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7736 init_chanserv(const char *nick)
7738 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7739 conf_register_reload(chanserv_conf_read);
7743 reg_server_link_func(handle_server_link);
7744 reg_new_channel_func(handle_new_channel);
7745 reg_join_func(handle_join);
7746 reg_part_func(handle_part);
7747 reg_kick_func(handle_kick);
7748 reg_topic_func(handle_topic);
7749 reg_mode_change_func(handle_mode);
7750 reg_nick_change_func(handle_nick_change);
7751 reg_auth_func(handle_auth);
7754 reg_handle_rename_func(handle_rename);
7755 reg_unreg_func(handle_unreg);
7757 handle_dnrs = dict_new();
7758 dict_set_free_data(handle_dnrs, free);
7759 plain_dnrs = dict_new();
7760 dict_set_free_data(plain_dnrs, free);
7761 mask_dnrs = dict_new();
7762 dict_set_free_data(mask_dnrs, free);
7764 reg_svccmd_unbind_func(handle_svccmd_unbind);
7765 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7766 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7767 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7768 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7769 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7770 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7771 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7772 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7773 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7774 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7775 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7776 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7777 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7779 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7780 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7782 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7783 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7784 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7785 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7786 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7788 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7789 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7790 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7791 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7792 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7794 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7795 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7796 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7797 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7799 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7800 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7801 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7802 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7803 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7804 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7805 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7806 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7808 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7809 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7810 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7811 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7812 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7813 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7814 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7815 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7816 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7817 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7818 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7819 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7820 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7821 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7823 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7824 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7825 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7826 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7827 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7829 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7830 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7832 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7833 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7834 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7835 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7836 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7837 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7838 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7839 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7840 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7841 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7842 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7844 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7845 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7847 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7848 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7849 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7850 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7852 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7853 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7854 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7855 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7856 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7858 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7859 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7860 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7861 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7862 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7863 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7865 /* Channel options */
7866 DEFINE_CHANNEL_OPTION(defaulttopic);
7867 DEFINE_CHANNEL_OPTION(topicmask);
7868 DEFINE_CHANNEL_OPTION(greeting);
7869 DEFINE_CHANNEL_OPTION(usergreeting);
7870 DEFINE_CHANNEL_OPTION(modes);
7871 DEFINE_CHANNEL_OPTION(enfops);
7872 DEFINE_CHANNEL_OPTION(giveops);
7873 DEFINE_CHANNEL_OPTION(protect);
7874 DEFINE_CHANNEL_OPTION(enfmodes);
7875 DEFINE_CHANNEL_OPTION(enftopic);
7876 DEFINE_CHANNEL_OPTION(pubcmd);
7877 DEFINE_CHANNEL_OPTION(givevoice);
7878 DEFINE_CHANNEL_OPTION(userinfo);
7879 DEFINE_CHANNEL_OPTION(dynlimit);
7880 DEFINE_CHANNEL_OPTION(topicsnarf);
7881 DEFINE_CHANNEL_OPTION(nodelete);
7882 DEFINE_CHANNEL_OPTION(toys);
7883 DEFINE_CHANNEL_OPTION(setters);
7884 DEFINE_CHANNEL_OPTION(topicrefresh);
7885 DEFINE_CHANNEL_OPTION(ctcpusers);
7886 DEFINE_CHANNEL_OPTION(ctcpreaction);
7887 DEFINE_CHANNEL_OPTION(inviteme);
7888 DEFINE_CHANNEL_OPTION(unreviewed);
7889 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7890 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7892 DEFINE_CHANNEL_OPTION(offchannel);
7893 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7895 /* Alias set topic to set defaulttopic for compatibility. */
7896 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7899 DEFINE_USER_OPTION(noautoop);
7900 DEFINE_USER_OPTION(autoinvite);
7901 DEFINE_USER_OPTION(info);
7903 /* Alias uset autovoice to uset autoop. */
7904 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7906 note_types = dict_new();
7907 dict_set_free_data(note_types, chanserv_deref_note_type);
7910 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7911 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7912 service_register(chanserv)->trigger = '!';
7913 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7915 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7917 if(chanserv_conf.channel_expire_frequency)
7918 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7920 if(chanserv_conf.dnr_expire_frequency)
7921 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7923 if(chanserv_conf.refresh_period)
7925 unsigned long next_refresh;
7926 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7927 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7930 reg_exit_func(chanserv_db_cleanup);
7931 message_register_table(msgtab);