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() */
29 #define CHANSERV_CONF_NAME "services/chanserv"
31 /* ChanServ options */
32 #define KEY_SUPPORT_CHANNEL "support_channel"
33 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
34 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
35 #define KEY_INFO_DELAY "info_delay"
36 #define KEY_MAX_GREETLEN "max_greetlen"
37 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
38 #define KEY_ADJUST_DELAY "adjust_delay"
39 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
40 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
41 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
42 #define KEY_MAX_CHAN_USERS "max_chan_users"
43 #define KEY_MAX_CHAN_BANS "max_chan_bans"
44 #define KEY_NICK "nick"
45 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
46 #define KEY_8BALL_RESPONSES "8ball"
47 #define KEY_OLD_BAN_NAMES "old_ban_names"
48 #define KEY_REFRESH_PERIOD "refresh_period"
49 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
50 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
51 #define KEY_MAX_OWNED "max_owned"
52 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
53 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
54 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
55 #define KEY_NODELETE_LEVEL "nodelete_level"
56 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
57 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
59 /* ChanServ database */
60 #define KEY_CHANNELS "channels"
61 #define KEY_NOTE_TYPES "note_types"
63 /* Note type parameters */
64 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
65 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
66 #define KEY_NOTE_SETTER_ACCESS "setter_access"
67 #define KEY_NOTE_VISIBILITY "visibility"
68 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
69 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
70 #define KEY_NOTE_VIS_ALL "all"
71 #define KEY_NOTE_MAX_LENGTH "max_length"
72 #define KEY_NOTE_SETTER "setter"
73 #define KEY_NOTE_NOTE "note"
75 /* Do-not-register channels */
77 #define KEY_DNR_SET "set"
78 #define KEY_DNR_SETTER "setter"
79 #define KEY_DNR_REASON "reason"
82 #define KEY_REGISTERED "registered"
83 #define KEY_REGISTRAR "registrar"
84 #define KEY_SUSPENDED "suspended"
85 #define KEY_PREVIOUS "previous"
86 #define KEY_SUSPENDER "suspender"
87 #define KEY_ISSUED "issued"
88 #define KEY_REVOKED "revoked"
89 #define KEY_SUSPEND_EXPIRES "suspend_expires"
90 #define KEY_SUSPEND_REASON "suspend_reason"
91 #define KEY_VISITED "visited"
92 #define KEY_TOPIC "topic"
93 #define KEY_GREETING "greeting"
94 #define KEY_USER_GREETING "user_greeting"
95 #define KEY_MODES "modes"
96 #define KEY_FLAGS "flags"
97 #define KEY_OPTIONS "options"
98 #define KEY_USERS "users"
99 #define KEY_BANS "bans"
100 #define KEY_MAX "max"
101 #define KEY_NOTES "notes"
102 #define KEY_TOPIC_MASK "topic_mask"
103 #define KEY_OWNER_TRANSFER "owner_transfer"
106 #define KEY_LEVEL "level"
107 #define KEY_INFO "info"
108 #define KEY_SEEN "seen"
111 #define KEY_OWNER "owner"
112 #define KEY_REASON "reason"
113 #define KEY_SET "set"
114 #define KEY_DURATION "duration"
115 #define KEY_EXPIRES "expires"
116 #define KEY_TRIGGERED "triggered"
118 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
119 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
120 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
122 /* Administrative messages */
123 static const struct message_entry msgtab[] = {
124 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
126 /* Channel registration */
127 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
128 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
129 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
130 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
131 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
132 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
134 /* Do-not-register channels */
135 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
136 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
137 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
138 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
139 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
140 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
141 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
142 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
143 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
144 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
145 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
146 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
147 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
148 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
150 /* Channel unregistration */
151 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
152 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
153 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
154 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
157 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
158 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
160 /* Channel merging */
161 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
162 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
163 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
164 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
165 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
167 /* Handle unregistration */
168 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
171 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
172 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
173 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
174 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
175 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
176 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
177 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
178 { "CSMSG_GODMODE_UP", "You may not use $b%s$b to op yourself unless you are on the user list. Use the $bop$b command instead." },
179 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
180 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
181 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
182 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
183 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
184 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
186 /* Removing yourself from a channel. */
187 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
188 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
189 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
191 /* User management */
192 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
193 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
194 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
195 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
196 { "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." },
197 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
198 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
199 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
201 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
202 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
203 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
204 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
205 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
206 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
207 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
210 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
211 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
212 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
213 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
214 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
215 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
216 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
217 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
218 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
219 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
220 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
221 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
222 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
223 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
224 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
225 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
227 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
229 /* Channel management */
230 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
231 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
232 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
234 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
235 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
236 { "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" },
237 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
238 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
239 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
240 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
242 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
243 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
244 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
245 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
246 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
247 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
248 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
249 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
250 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
251 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
252 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
253 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
254 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
255 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
256 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
257 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
258 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
259 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
260 { "CSMSG_SET_MODES", "$bModes $b %s" },
261 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
262 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
263 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
264 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
265 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
266 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
267 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
268 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
269 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
270 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
271 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
272 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
273 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
274 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
275 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
276 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
277 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
278 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
279 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
280 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
281 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
282 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
283 { "CSMSG_USET_INFO", "$bInfo $b %s" },
285 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
286 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
287 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
288 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
289 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
290 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
291 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
292 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
293 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
294 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
295 { "CSMSG_PROTECT_NONE", "No users will be protected." },
296 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
297 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
298 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
299 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
300 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
301 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
302 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
303 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
304 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
305 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
306 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
307 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
309 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
310 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
311 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
312 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
313 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
314 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
315 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
316 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
318 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
319 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
320 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
322 /* Channel userlist */
323 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
324 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
325 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
326 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
328 /* Channel note list */
329 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
330 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
331 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
332 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
333 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
334 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
335 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
336 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
337 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
338 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
339 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
340 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
341 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
342 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
343 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
345 /* Channel [un]suspension */
346 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
347 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
348 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
349 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
350 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
351 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
352 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
354 /* Access information */
355 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
356 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
357 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
358 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
359 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
360 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
361 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
362 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
363 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
364 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
365 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
366 { "CSMSG_UC_H_TITLE", "network helper" },
367 { "CSMSG_LC_H_TITLE", "support helper" },
368 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
370 /* Seen information */
371 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
372 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
373 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
374 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
376 /* Names information */
377 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
378 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
380 /* Channel information */
381 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
382 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
383 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
384 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
385 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
386 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
387 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
388 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
389 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
390 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
391 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
392 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
393 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
394 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
395 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
396 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
397 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
400 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
401 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
403 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
404 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
405 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
406 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d" },
407 { "CSMSG_PEEK_OPS", "$bOps:$b" },
408 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
410 /* Network information */
411 { "CSMSG_NETWORK_INFO", "Network Information:" },
412 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
413 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
414 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
415 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
416 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
417 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
418 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
419 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
422 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
423 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
424 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
426 /* Channel searches */
427 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
428 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
429 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
430 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
432 /* Channel configuration */
433 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
434 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
435 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
436 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
439 { "CSMSG_USER_OPTIONS", "User Options:" },
440 { "CSMSG_USER_PROTECTED", "That user is protected." },
443 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
444 { "CSMSG_PING_RESPONSE", "Pong!" },
445 { "CSMSG_WUT_RESPONSE", "wut" },
446 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
447 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
448 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
449 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
450 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
451 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
452 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
455 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
459 /* eject_user and unban_user flags */
460 #define ACTION_KICK 0x0001
461 #define ACTION_BAN 0x0002
462 #define ACTION_ADD_BAN 0x0004
463 #define ACTION_ADD_TIMED_BAN 0x0008
464 #define ACTION_UNBAN 0x0010
465 #define ACTION_DEL_BAN 0x0020
467 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
468 #define MODELEN 40 + KEYLEN
472 #define CSFUNC_ARGS user, channel, argc, argv, cmd
474 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
475 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
476 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
477 reply("MSG_MISSING_PARAMS", argv[0]); \
481 DECLARE_LIST(dnrList, struct do_not_register *);
482 DEFINE_LIST(dnrList, struct do_not_register *)
484 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
486 struct userNode *chanserv;
489 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
490 static struct log_type *CS_LOG;
494 struct channelList support_channels;
495 struct mod_chanmode default_modes;
497 unsigned long db_backup_frequency;
498 unsigned long channel_expire_frequency;
499 unsigned long dnr_expire_frequency;
501 unsigned long info_delay;
502 unsigned long adjust_delay;
503 unsigned long channel_expire_delay;
504 unsigned int nodelete_level;
506 unsigned int adjust_threshold;
507 int join_flood_threshold;
509 unsigned int greeting_length;
510 unsigned int refresh_period;
511 unsigned int giveownership_period;
513 unsigned int max_owned;
514 unsigned int max_chan_users;
515 unsigned int max_chan_bans;
516 unsigned int max_userinfo_length;
518 struct string_list *set_shows;
519 struct string_list *eightball;
520 struct string_list *old_ban_names;
522 const char *ctcp_short_ban_duration;
523 const char *ctcp_long_ban_duration;
525 const char *irc_operator_epithet;
526 const char *network_helper_epithet;
527 const char *support_helper_epithet;
532 struct userNode *user;
533 struct userNode *bot;
534 struct chanNode *channel;
536 unsigned short lowest;
537 unsigned short highest;
538 struct userData **users;
539 struct helpfile_table table;
542 enum note_access_type
544 NOTE_SET_CHANNEL_ACCESS,
545 NOTE_SET_CHANNEL_SETTER,
549 enum note_visible_type
552 NOTE_VIS_CHANNEL_USERS,
558 enum note_access_type set_access_type;
560 unsigned int min_opserv;
561 unsigned short min_ulevel;
563 enum note_visible_type visible_type;
564 unsigned int max_length;
571 struct note_type *type;
572 char setter[NICKSERV_HANDLE_LEN+1];
576 static unsigned int registered_channels;
577 static unsigned int banCount;
579 static const struct {
582 unsigned short level;
585 { "peon", "Peon", UL_PEON, '+' },
586 { "op", "Op", UL_OP, '@' },
587 { "master", "Master", UL_MASTER, '%' },
588 { "coowner", "Coowner", UL_COOWNER, '*' },
589 { "owner", "Owner", UL_OWNER, '!' },
590 { "helper", "BUG:", UL_HELPER, 'X' }
593 static const struct {
596 unsigned short default_value;
597 unsigned int old_idx;
598 unsigned int old_flag;
599 unsigned short flag_value;
601 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
602 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
603 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
604 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
605 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
606 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
607 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
608 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
609 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
610 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
611 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
614 struct charOptionValues {
617 } protectValues[] = {
618 { 'a', "CSMSG_PROTECT_ALL" },
619 { 'e', "CSMSG_PROTECT_EQUAL" },
620 { 'l', "CSMSG_PROTECT_LOWER" },
621 { 'n', "CSMSG_PROTECT_NONE" }
623 { 'd', "CSMSG_TOYS_DISABLED" },
624 { 'n', "CSMSG_TOYS_PRIVATE" },
625 { 'p', "CSMSG_TOYS_PUBLIC" }
626 }, topicRefreshValues[] = {
627 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
628 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
629 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
630 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
631 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
632 }, ctcpReactionValues[] = {
633 { 'k', "CSMSG_CTCPREACTION_KICK" },
634 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
635 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
636 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
639 static const struct {
643 unsigned int old_idx;
645 struct charOptionValues *values;
647 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
648 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
649 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
650 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
653 struct userData *helperList;
654 struct chanData *channelList;
655 static struct module *chanserv_module;
656 static unsigned int userCount;
658 #define GetChannelUser(channel, handle) _GetChannelUser(channel, handle, 1, 0)
659 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
660 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
663 user_level_from_name(const char *name, unsigned short clamp_level)
665 unsigned int level = 0, ii;
667 level = strtoul(name, NULL, 10);
668 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
669 if(!irccasecmp(name, accessLevels[ii].name))
670 level = accessLevels[ii].level;
671 if(level > clamp_level)
677 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
680 *minl = strtoul(arg, &sep, 10);
688 *maxl = strtoul(sep+1, &sep, 10);
696 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
698 struct userData *uData, **head;
700 if(!channel || !handle)
703 if(override && HANDLE_FLAGGED(handle, HELPING)
704 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
706 for(uData = helperList;
707 uData && uData->handle != handle;
708 uData = uData->next);
712 uData = calloc(1, sizeof(struct userData));
713 uData->handle = handle;
715 uData->access = UL_HELPER;
721 uData->next = helperList;
723 helperList->prev = uData;
731 for(uData = channel->users; uData; uData = uData->next)
732 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
735 head = &(channel->users);
738 if(uData && (uData != *head))
740 /* Shuffle the user to the head of whatever list he was in. */
742 uData->next->prev = uData->prev;
744 uData->prev->next = uData->next;
750 (**head).prev = uData;
757 /* Returns non-zero if user has at least the minimum access.
758 * exempt_owner is set when handling !set, so the owner can set things
761 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
763 struct userData *uData;
764 struct chanData *cData = channel->channel_info;
765 unsigned short minimum = cData->lvlOpts[opt];
768 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
771 if(minimum <= uData->access)
773 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
778 /* Scan for other users authenticated to the same handle
779 still in the channel. If so, keep them listed as present.
781 user is optional, if not null, it skips checking that userNode
782 (for the handle_part function) */
784 scan_user_presence(struct userData *uData, struct userNode *user)
788 if(IsSuspended(uData->channel)
789 || IsUserSuspended(uData)
790 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
802 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
804 unsigned int eflags, argc;
806 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
808 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
809 if(!channel->channel_info
810 || IsSuspended(channel->channel_info)
812 || !ircncasecmp(text, "ACTION ", 7))
814 /* Figure out the minimum level needed to CTCP the channel */
815 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
817 /* We need to enforce against them; do so. */
819 argv[0] = (char*)text;
820 argv[1] = user->nick;
822 if(GetUserMode(channel, user))
823 eflags |= ACTION_KICK;
824 switch(channel->channel_info->chOpts[chCTCPReaction]) {
825 default: case 'k': /* just do the kick */ break;
827 eflags |= ACTION_BAN;
830 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
831 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
834 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
835 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
838 argv[argc++] = bad_ctcp_reason;
839 eject_user(chanserv, channel, argc, argv, NULL, eflags);
843 chanserv_create_note_type(const char *name)
845 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
846 strcpy(ntype->name, name);
848 dict_insert(note_types, ntype->name, ntype);
853 chanserv_deref_note_type(void *data)
855 struct note_type *ntype = data;
857 if(--ntype->refs > 0)
863 chanserv_flush_note_type(struct note_type *ntype)
865 struct chanData *cData;
866 for(cData = channelList; cData; cData = cData->next)
867 dict_remove(cData->notes, ntype->name);
871 chanserv_truncate_notes(struct note_type *ntype)
873 struct chanData *cData;
875 unsigned int size = sizeof(*note) + ntype->max_length;
877 for(cData = channelList; cData; cData = cData->next) {
878 note = dict_find(cData->notes, ntype->name, NULL);
881 if(strlen(note->note) <= ntype->max_length)
883 dict_remove2(cData->notes, ntype->name, 1);
884 note = realloc(note, size);
885 note->note[ntype->max_length] = 0;
886 dict_insert(cData->notes, ntype->name, note);
890 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
893 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
896 unsigned int len = strlen(text);
898 if(len > type->max_length) len = type->max_length;
899 note = calloc(1, sizeof(*note) + len);
901 strncpy(note->setter, setter, sizeof(note->setter)-1);
902 memcpy(note->note, text, len);
904 dict_insert(channel->notes, type->name, note);
910 chanserv_free_note(void *data)
912 struct note *note = data;
914 chanserv_deref_note_type(note->type);
915 assert(note->type->refs > 0); /* must use delnote to remove the type */
919 static MODCMD_FUNC(cmd_createnote) {
920 struct note_type *ntype;
921 unsigned int arg = 1, existed = 0, max_length;
923 if((ntype = dict_find(note_types, argv[1], NULL)))
926 ntype = chanserv_create_note_type(argv[arg]);
927 if(!irccasecmp(argv[++arg], "privileged"))
930 ntype->set_access_type = NOTE_SET_PRIVILEGED;
931 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
933 else if(!irccasecmp(argv[arg], "channel"))
935 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
938 reply("CSMSG_INVALID_ACCESS", argv[arg]);
941 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
942 ntype->set_access.min_ulevel = ulvl;
944 else if(!irccasecmp(argv[arg], "setter"))
946 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
950 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
954 if(!irccasecmp(argv[++arg], "privileged"))
955 ntype->visible_type = NOTE_VIS_PRIVILEGED;
956 else if(!irccasecmp(argv[arg], "channel_users"))
957 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
958 else if(!irccasecmp(argv[arg], "all"))
959 ntype->visible_type = NOTE_VIS_ALL;
961 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
965 if((arg+1) >= argc) {
966 reply("MSG_MISSING_PARAMS", argv[0]);
969 max_length = strtoul(argv[++arg], NULL, 0);
970 if(max_length < 20 || max_length > 450)
972 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
975 if(existed && (max_length < ntype->max_length))
977 ntype->max_length = max_length;
978 chanserv_truncate_notes(ntype);
980 ntype->max_length = max_length;
983 reply("CSMSG_NOTE_MODIFIED", ntype->name);
985 reply("CSMSG_NOTE_CREATED", ntype->name);
990 dict_remove(note_types, ntype->name);
994 static MODCMD_FUNC(cmd_removenote) {
995 struct note_type *ntype;
998 ntype = dict_find(note_types, argv[1], NULL);
999 force = (argc > 2) && !irccasecmp(argv[2], "force");
1002 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1009 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1012 chanserv_flush_note_type(ntype);
1014 dict_remove(note_types, argv[1]);
1015 reply("CSMSG_NOTE_DELETED", argv[1]);
1020 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1024 if(orig->modes_set & change->modes_clear)
1026 if(orig->modes_clear & change->modes_set)
1028 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1029 && strcmp(orig->new_key, change->new_key))
1031 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1032 && (orig->new_limit != change->new_limit))
1037 static char max_length_text[MAXLEN+1][16];
1039 static struct helpfile_expansion
1040 chanserv_expand_variable(const char *variable)
1042 struct helpfile_expansion exp;
1044 if(!irccasecmp(variable, "notes"))
1047 exp.type = HF_TABLE;
1048 exp.value.table.length = 1;
1049 exp.value.table.width = 3;
1050 exp.value.table.flags = 0;
1051 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1052 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1053 exp.value.table.contents[0][0] = "Note Type";
1054 exp.value.table.contents[0][1] = "Visibility";
1055 exp.value.table.contents[0][2] = "Max Length";
1056 for(it=dict_first(note_types); it; it=iter_next(it))
1058 struct note_type *ntype = iter_data(it);
1061 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1062 row = exp.value.table.length++;
1063 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1064 exp.value.table.contents[row][0] = ntype->name;
1065 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1066 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1068 if(!max_length_text[ntype->max_length][0])
1069 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1070 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1075 exp.type = HF_STRING;
1076 exp.value.str = NULL;
1080 static struct chanData*
1081 register_channel(struct chanNode *cNode, char *registrar)
1083 struct chanData *channel;
1084 enum levelOption lvlOpt;
1085 enum charOption chOpt;
1087 channel = calloc(1, sizeof(struct chanData));
1089 channel->notes = dict_new();
1090 dict_set_free_data(channel->notes, chanserv_free_note);
1092 channel->registrar = strdup(registrar);
1093 channel->registered = now;
1094 channel->visited = now;
1095 channel->limitAdjusted = now;
1096 channel->ownerTransfer = now;
1097 channel->flags = CHANNEL_DEFAULT_FLAGS;
1098 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1099 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1100 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1101 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1103 channel->prev = NULL;
1104 channel->next = channelList;
1107 channelList->prev = channel;
1108 channelList = channel;
1109 registered_channels++;
1111 channel->channel = cNode;
1113 cNode->channel_info = channel;
1118 static struct userData*
1119 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access, unsigned long seen, const char *info)
1121 struct userData *ud;
1123 if(access > UL_OWNER)
1126 ud = calloc(1, sizeof(*ud));
1127 ud->channel = channel;
1128 ud->handle = handle;
1130 ud->access = access;
1131 ud->info = info ? strdup(info) : NULL;
1134 ud->next = channel->users;
1136 channel->users->prev = ud;
1137 channel->users = ud;
1139 channel->userCount++;
1143 ud->u_next = ud->handle->channels;
1145 ud->u_next->u_prev = ud;
1146 ud->handle->channels = ud;
1151 static void unregister_channel(struct chanData *channel, const char *reason);
1154 del_channel_user(struct userData *user, int do_gc)
1156 struct chanData *channel = user->channel;
1158 channel->userCount--;
1162 user->prev->next = user->next;
1164 channel->users = user->next;
1166 user->next->prev = user->prev;
1169 user->u_prev->u_next = user->u_next;
1171 user->handle->channels = user->u_next;
1173 user->u_next->u_prev = user->u_prev;
1177 if(do_gc && !channel->users && !IsProtected(channel))
1178 unregister_channel(channel, "lost all users.");
1181 static void expire_ban(void *data);
1183 static struct banData*
1184 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1187 unsigned int ii, l1, l2;
1192 bd = malloc(sizeof(struct banData));
1194 bd->channel = channel;
1196 bd->triggered = triggered;
1197 bd->expires = expires;
1199 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1201 extern const char *hidden_host_suffix;
1202 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1206 l2 = strlen(old_name);
1209 if(irccasecmp(mask + l1 - l2, old_name))
1211 new_mask = alloca(MAXLEN);
1212 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1215 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1217 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1218 bd->reason = strdup(reason);
1221 timeq_add(expires, expire_ban, bd);
1224 bd->next = channel->bans;
1226 channel->bans->prev = bd;
1228 channel->banCount++;
1235 del_channel_ban(struct banData *ban)
1237 ban->channel->banCount--;
1241 ban->prev->next = ban->next;
1243 ban->channel->bans = ban->next;
1246 ban->next->prev = ban->prev;
1249 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1258 expire_ban(void *data)
1260 struct banData *bd = data;
1261 if(!IsSuspended(bd->channel))
1263 struct banList bans;
1264 struct mod_chanmode change;
1266 bans = bd->channel->channel->banlist;
1267 mod_chanmode_init(&change);
1268 for(ii=0; ii<bans.used; ii++)
1270 if(!strcmp(bans.list[ii]->ban, bd->mask))
1273 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1274 change.args[0].u.hostmask = bd->mask;
1275 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1281 del_channel_ban(bd);
1284 static void chanserv_expire_suspension(void *data);
1287 unregister_channel(struct chanData *channel, const char *reason)
1289 struct mod_chanmode change;
1290 char msgbuf[MAXLEN];
1292 /* After channel unregistration, the following must be cleaned
1294 - Channel information.
1297 - Channel suspension data.
1298 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1304 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1308 mod_chanmode_init(&change);
1309 change.modes_clear |= MODE_REGISTERED;
1310 mod_chanmode_announce(chanserv, channel->channel, &change);
1313 while(channel->users)
1314 del_channel_user(channel->users, 0);
1316 while(channel->bans)
1317 del_channel_ban(channel->bans);
1319 free(channel->topic);
1320 free(channel->registrar);
1321 free(channel->greeting);
1322 free(channel->user_greeting);
1323 free(channel->topic_mask);
1326 channel->prev->next = channel->next;
1328 channelList = channel->next;
1331 channel->next->prev = channel->prev;
1333 if(channel->suspended)
1335 struct chanNode *cNode = channel->channel;
1336 struct suspended *suspended, *next_suspended;
1338 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1340 next_suspended = suspended->previous;
1341 free(suspended->suspender);
1342 free(suspended->reason);
1343 if(suspended->expires)
1344 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1349 cNode->channel_info = NULL;
1351 channel->channel->channel_info = NULL;
1353 dict_delete(channel->notes);
1354 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1355 if(!IsSuspended(channel))
1356 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1357 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1358 UnlockChannel(channel->channel);
1360 registered_channels--;
1364 expire_channels(UNUSED_ARG(void *data))
1366 struct chanData *channel, *next;
1367 struct userData *user;
1368 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1370 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1371 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1373 for(channel = channelList; channel; channel = next)
1375 next = channel->next;
1377 /* See if the channel can be expired. */
1378 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1379 || IsProtected(channel))
1382 /* Make sure there are no high-ranking users still in the channel. */
1383 for(user=channel->users; user; user=user->next)
1384 if(user->present && (user->access >= UL_PRESENT))
1389 /* Unregister the channel */
1390 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1391 unregister_channel(channel, "registration expired.");
1394 if(chanserv_conf.channel_expire_frequency)
1395 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1399 expire_dnrs(UNUSED_ARG(void *data))
1401 dict_iterator_t it, next;
1402 struct do_not_register *dnr;
1404 for(it = dict_first(handle_dnrs); it; it = next)
1406 dnr = iter_data(it);
1407 next = iter_next(it);
1408 if(dnr->expires && dnr->expires <= now)
1409 dict_remove(handle_dnrs, dnr->chan_name + 1);
1411 for(it = dict_first(plain_dnrs); it; it = next)
1413 dnr = iter_data(it);
1414 next = iter_next(it);
1415 if(dnr->expires && dnr->expires <= now)
1416 dict_remove(plain_dnrs, dnr->chan_name + 1);
1418 for(it = dict_first(mask_dnrs); it; it = next)
1420 dnr = iter_data(it);
1421 next = iter_next(it);
1422 if(dnr->expires && dnr->expires <= now)
1423 dict_remove(mask_dnrs, dnr->chan_name + 1);
1426 if(chanserv_conf.dnr_expire_frequency)
1427 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1431 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1433 char protect = channel->chOpts[chProtect];
1434 struct userData *cs_victim, *cs_aggressor;
1436 /* Don't protect if no one is to be protected, someone is attacking
1437 himself, or if the aggressor is an IRC Operator. */
1438 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1441 /* Don't protect if the victim isn't authenticated (because they
1442 can't be a channel user), unless we are to protect non-users
1444 cs_victim = GetChannelAccess(channel, victim->handle_info);
1445 if(protect != 'a' && !cs_victim)
1448 /* Protect if the aggressor isn't a user because at this point,
1449 the aggressor can only be less than or equal to the victim. */
1450 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1454 /* If the aggressor was a user, then the victim can't be helped. */
1461 if(cs_victim->access > cs_aggressor->access)
1466 if(cs_victim->access >= cs_aggressor->access)
1475 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1477 struct chanData *cData = channel->channel_info;
1478 struct userData *cs_victim;
1480 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1481 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1482 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1484 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1492 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1494 if(IsService(victim))
1496 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1500 if(protect_user(victim, user, channel->channel_info))
1502 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1509 static struct do_not_register *
1510 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1512 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1513 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1514 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1515 strcpy(dnr->reason, reason);
1517 dnr->expires = expires;
1518 if(dnr->chan_name[0] == '*')
1519 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1520 else if(strpbrk(dnr->chan_name, "*?"))
1521 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1523 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1527 static struct dnrList
1528 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1530 struct dnrList list;
1531 dict_iterator_t it, next;
1532 struct do_not_register *dnr;
1534 dnrList_init(&list);
1536 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1538 if(dnr->expires && dnr->expires <= now)
1539 dict_remove(handle_dnrs, handle);
1540 else if(list.used < max)
1541 dnrList_append(&list, dnr);
1544 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1546 if(dnr->expires && dnr->expires <= now)
1547 dict_remove(plain_dnrs, chan_name);
1548 else if(list.used < max)
1549 dnrList_append(&list, dnr);
1554 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1556 next = iter_next(it);
1557 if(!match_ircglob(chan_name, iter_key(it)))
1559 dnr = iter_data(it);
1560 if(dnr->expires && dnr->expires <= now)
1561 dict_remove(mask_dnrs, iter_key(it));
1563 dnrList_append(&list, dnr);
1570 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1572 struct userNode *user;
1573 char buf1[INTERVALLEN];
1574 char buf2[INTERVALLEN];
1581 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1586 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1587 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1591 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1594 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1599 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1601 struct dnrList list;
1604 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1605 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1606 dnr_print_func(list.list[ii], user);
1608 reply("CSMSG_MORE_DNRS", list.used - ii);
1613 struct do_not_register *
1614 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1616 struct dnrList list;
1617 struct do_not_register *dnr;
1619 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1620 dnr = list.used ? list.list[0] : NULL;
1625 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1627 struct do_not_register *dnr;
1628 dict_iterator_t it, next;
1629 unsigned int matches = 0;
1631 for(it = dict_first(dict); it; it = next)
1633 dnr = iter_data(it);
1634 next = iter_next(it);
1635 if(dnr->expires && dnr->expires <= now)
1637 dict_remove(dict, iter_key(it));
1640 dnr_print_func(dnr, user);
1647 static CHANSERV_FUNC(cmd_noregister)
1651 unsigned long expiry, duration;
1652 unsigned int matches;
1656 reply("CSMSG_DNR_SEARCH_RESULTS");
1657 matches = send_dnrs(user, handle_dnrs);
1658 matches += send_dnrs(user, plain_dnrs);
1659 matches += send_dnrs(user, mask_dnrs);
1661 reply("MSG_MATCH_COUNT", matches);
1663 reply("MSG_NO_MATCHES");
1669 if(!IsChannelName(target) && (*target != '*'))
1671 reply("CSMSG_NOT_DNR", target);
1679 reply("MSG_INVALID_DURATION", argv[2]);
1683 if(!strcmp(argv[2], "0"))
1685 else if((duration = ParseInterval(argv[2])))
1686 expiry = now + duration;
1689 reply("MSG_INVALID_DURATION", argv[2]);
1693 reason = unsplit_string(argv + 3, argc - 3, NULL);
1694 if((*target == '*') && !get_handle_info(target + 1))
1696 reply("MSG_HANDLE_UNKNOWN", target + 1);
1699 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1700 reply("CSMSG_NOREGISTER_CHANNEL", target);
1704 reply("CSMSG_DNR_SEARCH_RESULTS");
1706 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1708 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1710 reply("MSG_NO_MATCHES");
1714 static CHANSERV_FUNC(cmd_allowregister)
1716 const char *chan_name = argv[1];
1718 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1719 || dict_remove(plain_dnrs, chan_name)
1720 || dict_remove(mask_dnrs, chan_name))
1722 reply("CSMSG_DNR_REMOVED", chan_name);
1725 reply("CSMSG_NO_SUCH_DNR", chan_name);
1730 struct userNode *source;
1734 unsigned long min_set, max_set;
1735 unsigned long min_expires, max_expires;
1740 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1742 return !((dnr->set < search->min_set)
1743 || (dnr->set > search->max_set)
1744 || (dnr->expires < search->min_expires)
1745 || (search->max_expires
1746 && ((dnr->expires == 0)
1747 || (dnr->expires > search->max_expires)))
1748 || (search->chan_mask
1749 && !match_ircglob(dnr->chan_name, search->chan_mask))
1750 || (search->setter_mask
1751 && !match_ircglob(dnr->setter, search->setter_mask))
1752 || (search->reason_mask
1753 && !match_ircglob(dnr->reason, search->reason_mask)));
1756 static struct dnr_search *
1757 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1759 struct dnr_search *discrim;
1762 discrim = calloc(1, sizeof(*discrim));
1763 discrim->source = user;
1764 discrim->chan_mask = NULL;
1765 discrim->setter_mask = NULL;
1766 discrim->reason_mask = NULL;
1767 discrim->max_set = INT_MAX;
1768 discrim->limit = 50;
1770 for(ii=0; ii<argc; ++ii)
1774 reply("MSG_MISSING_PARAMS", argv[ii]);
1777 else if(0 == irccasecmp(argv[ii], "channel"))
1779 discrim->chan_mask = argv[++ii];
1781 else if(0 == irccasecmp(argv[ii], "setter"))
1783 discrim->setter_mask = argv[++ii];
1785 else if(0 == irccasecmp(argv[ii], "reason"))
1787 discrim->reason_mask = argv[++ii];
1789 else if(0 == irccasecmp(argv[ii], "limit"))
1791 discrim->limit = strtoul(argv[++ii], NULL, 0);
1793 else if(0 == irccasecmp(argv[ii], "set"))
1795 const char *cmp = argv[++ii];
1798 discrim->min_set = now - ParseInterval(cmp + 2);
1800 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1801 } else if(cmp[0] == '=') {
1802 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1803 } else if(cmp[0] == '>') {
1805 discrim->max_set = now - ParseInterval(cmp + 2);
1807 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1809 discrim->max_set = now - (ParseInterval(cmp) - 1);
1812 else if(0 == irccasecmp(argv[ii], "expires"))
1814 const char *cmp = argv[++ii];
1817 discrim->max_expires = now + ParseInterval(cmp + 2);
1819 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1820 } else if(cmp[0] == '=') {
1821 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1822 } else if(cmp[0] == '>') {
1824 discrim->min_expires = now + ParseInterval(cmp + 2);
1826 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1828 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1833 reply("MSG_INVALID_CRITERIA", argv[ii]);
1844 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1847 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1849 struct do_not_register *dnr;
1850 dict_iterator_t next;
1855 /* Initialize local variables. */
1858 if(discrim->chan_mask)
1860 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1861 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1865 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1867 /* Check against account-based DNRs. */
1868 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1869 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1872 else if(target_fixed)
1874 /* Check against channel-based DNRs. */
1875 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1876 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1881 /* Exhaustively search account DNRs. */
1882 for(it = dict_first(handle_dnrs); it; it = next)
1884 next = iter_next(it);
1885 dnr = iter_data(it);
1886 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1890 /* Do the same for channel DNRs. */
1891 for(it = dict_first(plain_dnrs); it; it = next)
1893 next = iter_next(it);
1894 dnr = iter_data(it);
1895 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1899 /* Do the same for wildcarded channel DNRs. */
1900 for(it = dict_first(mask_dnrs); it; it = next)
1902 next = iter_next(it);
1903 dnr = iter_data(it);
1904 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1912 dnr_remove_func(struct do_not_register *match, void *extra)
1914 struct userNode *user;
1917 chan_name = alloca(strlen(match->chan_name) + 1);
1918 strcpy(chan_name, match->chan_name);
1920 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1921 || dict_remove(plain_dnrs, chan_name)
1922 || dict_remove(mask_dnrs, chan_name))
1924 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1930 dnr_count_func(struct do_not_register *match, void *extra)
1932 return 0; (void)match; (void)extra;
1935 static MODCMD_FUNC(cmd_dnrsearch)
1937 struct dnr_search *discrim;
1938 dnr_search_func action;
1939 struct svccmd *subcmd;
1940 unsigned int matches;
1943 sprintf(buf, "dnrsearch %s", argv[1]);
1944 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1947 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1950 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1952 if(!irccasecmp(argv[1], "print"))
1953 action = dnr_print_func;
1954 else if(!irccasecmp(argv[1], "remove"))
1955 action = dnr_remove_func;
1956 else if(!irccasecmp(argv[1], "count"))
1957 action = dnr_count_func;
1960 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1964 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1968 if(action == dnr_print_func)
1969 reply("CSMSG_DNR_SEARCH_RESULTS");
1970 matches = dnr_search(discrim, action, user);
1972 reply("MSG_MATCH_COUNT", matches);
1974 reply("MSG_NO_MATCHES");
1980 chanserv_get_owned_count(struct handle_info *hi)
1982 struct userData *cList;
1985 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
1986 if(cList->access == UL_OWNER)
1991 static CHANSERV_FUNC(cmd_register)
1993 struct handle_info *handle;
1994 struct chanData *cData;
1995 struct modeNode *mn;
1996 char reason[MAXLEN];
1998 unsigned int new_channel, force=0;
1999 struct do_not_register *dnr;
2003 if(channel->channel_info)
2005 reply("CSMSG_ALREADY_REGGED", channel->name);
2009 if(channel->bad_channel)
2011 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2016 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2018 reply("CSMSG_MUST_BE_OPPED", channel->name);
2023 chan_name = channel->name;
2027 if((argc < 2) || !IsChannelName(argv[1]))
2029 reply("MSG_NOT_CHANNEL_NAME");
2033 if(opserv_bad_channel(argv[1]))
2035 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2040 chan_name = argv[1];
2043 if(argc >= (new_channel+2))
2045 if(!IsHelping(user))
2047 reply("CSMSG_PROXY_FORBIDDEN");
2051 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2053 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2054 dnr = chanserv_is_dnr(chan_name, handle);
2058 handle = user->handle_info;
2059 dnr = chanserv_is_dnr(chan_name, handle);
2063 if(!IsHelping(user))
2064 reply("CSMSG_DNR_CHANNEL", chan_name);
2066 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2070 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2072 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2077 channel = AddChannel(argv[1], now, NULL, NULL);
2079 cData = register_channel(channel, user->handle_info->handle);
2080 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2081 cData->modes = chanserv_conf.default_modes;
2083 cData->modes.modes_set |= MODE_REGISTERED;
2084 if (IsOffChannel(cData))
2086 mod_chanmode_announce(chanserv, channel, &cData->modes);
2090 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2091 change->args[change->argc].mode = MODE_CHANOP;
2092 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2094 mod_chanmode_announce(chanserv, channel, change);
2095 mod_chanmode_free(change);
2098 /* Initialize the channel's max user record. */
2099 cData->max = channel->members.used;
2101 if(handle != user->handle_info)
2102 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2104 reply("CSMSG_REG_SUCCESS", channel->name);
2106 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2107 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2112 make_confirmation_string(struct userData *uData)
2114 static char strbuf[16];
2119 for(src = uData->handle->handle; *src; )
2120 accum = accum * 31 + toupper(*src++);
2122 for(src = uData->channel->channel->name; *src; )
2123 accum = accum * 31 + toupper(*src++);
2124 sprintf(strbuf, "%08x", accum);
2128 static CHANSERV_FUNC(cmd_unregister)
2131 char reason[MAXLEN];
2132 struct chanData *cData;
2133 struct userData *uData;
2135 cData = channel->channel_info;
2138 reply("CSMSG_NOT_REGISTERED", channel->name);
2142 uData = GetChannelUser(cData, user->handle_info);
2143 if(!uData || (uData->access < UL_OWNER))
2145 reply("CSMSG_NO_ACCESS");
2149 if(IsProtected(cData))
2151 reply("CSMSG_UNREG_NODELETE", channel->name);
2155 if(!IsHelping(user))
2157 const char *confirm_string;
2158 if(IsSuspended(cData))
2160 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2163 confirm_string = make_confirmation_string(uData);
2164 if((argc < 2) || strcmp(argv[1], confirm_string))
2166 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2171 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2172 name = strdup(channel->name);
2173 unregister_channel(cData, reason);
2174 reply("CSMSG_UNREG_SUCCESS", name);
2179 static CHANSERV_FUNC(cmd_move)
2181 struct mod_chanmode change;
2182 struct chanNode *target;
2183 struct modeNode *mn;
2184 struct userData *uData;
2185 char reason[MAXLEN];
2186 struct do_not_register *dnr;
2190 if(IsProtected(channel->channel_info))
2192 reply("CSMSG_MOVE_NODELETE", channel->name);
2196 if(!IsChannelName(argv[1]))
2198 reply("MSG_NOT_CHANNEL_NAME");
2202 if(opserv_bad_channel(argv[1]))
2204 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2208 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2210 for(uData = channel->channel_info->users; uData; uData = uData->next)
2212 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2214 if(!IsHelping(user))
2215 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2217 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2223 mod_chanmode_init(&change);
2224 if(!(target = GetChannel(argv[1])))
2226 target = AddChannel(argv[1], now, NULL, NULL);
2227 if(!IsSuspended(channel->channel_info))
2228 AddChannelUser(chanserv, target);
2230 else if(target->channel_info)
2232 reply("CSMSG_ALREADY_REGGED", target->name);
2235 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2236 && !IsHelping(user))
2238 reply("CSMSG_MUST_BE_OPPED", target->name);
2241 else if(!IsSuspended(channel->channel_info))
2244 change.args[0].mode = MODE_CHANOP;
2245 change.args[0].u.member = AddChannelUser(chanserv, target);
2246 mod_chanmode_announce(chanserv, target, &change);
2251 /* Clear MODE_REGISTERED from old channel, add it to new. */
2253 change.modes_clear = MODE_REGISTERED;
2254 mod_chanmode_announce(chanserv, channel, &change);
2255 change.modes_clear = 0;
2256 change.modes_set = MODE_REGISTERED;
2257 mod_chanmode_announce(chanserv, target, &change);
2260 /* Move the channel_info to the target channel; it
2261 shouldn't be necessary to clear timeq callbacks
2262 for the old channel. */
2263 target->channel_info = channel->channel_info;
2264 target->channel_info->channel = target;
2265 channel->channel_info = NULL;
2267 reply("CSMSG_MOVE_SUCCESS", target->name);
2269 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2270 if(!IsSuspended(target->channel_info))
2272 char reason2[MAXLEN];
2273 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2274 DelChannelUser(chanserv, channel, reason2, 0);
2276 UnlockChannel(channel);
2277 LockChannel(target);
2278 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2283 merge_users(struct chanData *source, struct chanData *target)
2285 struct userData *suData, *tuData, *next;
2291 /* Insert the source's users into the scratch area. */
2292 for(suData = source->users; suData; suData = suData->next)
2293 dict_insert(merge, suData->handle->handle, suData);
2295 /* Iterate through the target's users, looking for
2296 users common to both channels. The lower access is
2297 removed from either the scratch area or target user
2299 for(tuData = target->users; tuData; tuData = next)
2301 struct userData *choice;
2303 next = tuData->next;
2305 /* If a source user exists with the same handle as a target
2306 channel's user, resolve the conflict by removing one. */
2307 suData = dict_find(merge, tuData->handle->handle, NULL);
2311 /* Pick the data we want to keep. */
2312 /* If the access is the same, use the later seen time. */
2313 if(suData->access == tuData->access)
2314 choice = (suData->seen > tuData->seen) ? suData : tuData;
2315 else /* Otherwise, keep the higher access level. */
2316 choice = (suData->access > tuData->access) ? suData : tuData;
2318 /* Remove the user that wasn't picked. */
2319 if(choice == tuData)
2321 dict_remove(merge, suData->handle->handle);
2322 del_channel_user(suData, 0);
2325 del_channel_user(tuData, 0);
2328 /* Move the remaining users to the target channel. */
2329 for(it = dict_first(merge); it; it = iter_next(it))
2331 suData = iter_data(it);
2333 /* Insert the user into the target channel's linked list. */
2334 suData->prev = NULL;
2335 suData->next = target->users;
2336 suData->channel = target;
2339 target->users->prev = suData;
2340 target->users = suData;
2342 /* Update the user counts for the target channel; the
2343 source counts are left alone. */
2344 target->userCount++;
2347 /* Possible to assert (source->users == NULL) here. */
2348 source->users = NULL;
2353 merge_bans(struct chanData *source, struct chanData *target)
2355 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2357 /* Hold on to the original head of the target ban list
2358 to avoid comparing source bans with source bans. */
2359 tFront = target->bans;
2361 /* Perform a totally expensive O(n*m) merge, ick. */
2362 for(sbData = source->bans; sbData; sbData = sNext)
2364 /* Flag to track whether the ban's been moved
2365 to the destination yet. */
2368 /* Possible to assert (sbData->prev == NULL) here. */
2369 sNext = sbData->next;
2371 for(tbData = tFront; tbData; tbData = tNext)
2373 tNext = tbData->next;
2375 /* Perform two comparisons between each source
2376 and target ban, conflicts are resolved by
2377 keeping the broader ban and copying the later
2378 expiration and triggered time. */
2379 if(match_ircglobs(tbData->mask, sbData->mask))
2381 /* There is a broader ban in the target channel that
2382 overrides one in the source channel; remove the
2383 source ban and break. */
2384 if(sbData->expires > tbData->expires)
2385 tbData->expires = sbData->expires;
2386 if(sbData->triggered > tbData->triggered)
2387 tbData->triggered = sbData->triggered;
2388 del_channel_ban(sbData);
2391 else if(match_ircglobs(sbData->mask, tbData->mask))
2393 /* There is a broader ban in the source channel that
2394 overrides one in the target channel; remove the
2395 target ban, fall through and move the source over. */
2396 if(tbData->expires > sbData->expires)
2397 sbData->expires = tbData->expires;
2398 if(tbData->triggered > sbData->triggered)
2399 sbData->triggered = tbData->triggered;
2400 if(tbData == tFront)
2402 del_channel_ban(tbData);
2405 /* Source bans can override multiple target bans, so
2406 we allow a source to run through this loop multiple
2407 times, but we can only move it once. */
2412 /* Remove the source ban from the source ban list. */
2414 sbData->next->prev = sbData->prev;
2416 /* Modify the source ban's associated channel. */
2417 sbData->channel = target;
2419 /* Insert the ban into the target channel's linked list. */
2420 sbData->prev = NULL;
2421 sbData->next = target->bans;
2424 target->bans->prev = sbData;
2425 target->bans = sbData;
2427 /* Update the user counts for the target channel. */
2432 /* Possible to assert (source->bans == NULL) here. */
2433 source->bans = NULL;
2437 merge_data(struct chanData *source, struct chanData *target)
2439 /* Use more recent visited and owner-transfer time; use older
2440 * registered time. Bitwise or may_opchan. Use higher max.
2441 * Do not touch last_refresh, ban count or user counts.
2443 if(source->visited > target->visited)
2444 target->visited = source->visited;
2445 if(source->registered < target->registered)
2446 target->registered = source->registered;
2447 if(source->ownerTransfer > target->ownerTransfer)
2448 target->ownerTransfer = source->ownerTransfer;
2449 if(source->may_opchan)
2450 target->may_opchan = 1;
2451 if(source->max > target->max)
2452 target->max = source->max;
2456 merge_channel(struct chanData *source, struct chanData *target)
2458 merge_users(source, target);
2459 merge_bans(source, target);
2460 merge_data(source, target);
2463 static CHANSERV_FUNC(cmd_merge)
2465 struct userData *target_user;
2466 struct chanNode *target;
2467 char reason[MAXLEN];
2471 /* Make sure the target channel exists and is registered to the user
2472 performing the command. */
2473 if(!(target = GetChannel(argv[1])))
2475 reply("MSG_INVALID_CHANNEL");
2479 if(!target->channel_info)
2481 reply("CSMSG_NOT_REGISTERED", target->name);
2485 if(IsProtected(channel->channel_info))
2487 reply("CSMSG_MERGE_NODELETE");
2491 if(IsSuspended(target->channel_info))
2493 reply("CSMSG_MERGE_SUSPENDED");
2497 if(channel == target)
2499 reply("CSMSG_MERGE_SELF");
2503 target_user = GetChannelUser(target->channel_info, user->handle_info);
2504 if(!target_user || (target_user->access < UL_OWNER))
2506 reply("CSMSG_MERGE_NOT_OWNER");
2510 /* Merge the channel structures and associated data. */
2511 merge_channel(channel->channel_info, target->channel_info);
2512 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2513 unregister_channel(channel->channel_info, reason);
2514 reply("CSMSG_MERGE_SUCCESS", target->name);
2518 static CHANSERV_FUNC(cmd_opchan)
2520 struct mod_chanmode change;
2521 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2523 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2526 channel->channel_info->may_opchan = 0;
2527 mod_chanmode_init(&change);
2529 change.args[0].mode = MODE_CHANOP;
2530 change.args[0].u.member = GetUserMode(channel, chanserv);
2531 if(!change.args[0].u.member)
2533 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2536 mod_chanmode_announce(chanserv, channel, &change);
2537 reply("CSMSG_OPCHAN_DONE", channel->name);
2541 static CHANSERV_FUNC(cmd_adduser)
2543 struct userData *actee;
2544 struct userData *actor, *real_actor;
2545 struct handle_info *handle;
2546 unsigned short access, override = 0;
2550 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2552 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2556 access = user_level_from_name(argv[2], UL_OWNER);
2559 reply("CSMSG_INVALID_ACCESS", argv[2]);
2563 actor = GetChannelUser(channel->channel_info, user->handle_info);
2564 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2566 if(actor->access <= access)
2568 reply("CSMSG_NO_BUMP_ACCESS");
2572 /* Trying to add someone with equal/more access? */
2573 if (!real_actor || real_actor->access <= access)
2574 override = CMD_LOG_OVERRIDE;
2576 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2579 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2581 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2585 actee = add_channel_user(channel->channel_info, handle, access, 0, NULL);
2586 scan_user_presence(actee, NULL);
2587 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access);
2588 return 1 | override;
2591 static CHANSERV_FUNC(cmd_clvl)
2593 struct handle_info *handle;
2594 struct userData *victim;
2595 struct userData *actor, *real_actor;
2596 unsigned short new_access, override = 0;
2597 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2601 actor = GetChannelUser(channel->channel_info, user->handle_info);
2602 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2604 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2607 if(handle == user->handle_info && !privileged)
2609 reply("CSMSG_NO_SELF_CLVL");
2613 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2615 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2619 if(actor->access <= victim->access && !privileged)
2621 reply("MSG_USER_OUTRANKED", handle->handle);
2625 new_access = user_level_from_name(argv[2], UL_OWNER);
2629 reply("CSMSG_INVALID_ACCESS", argv[2]);
2633 if(new_access >= actor->access && !privileged)
2635 reply("CSMSG_NO_BUMP_ACCESS");
2639 /* Trying to clvl a equal/higher user? */
2640 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2641 override = CMD_LOG_OVERRIDE;
2642 /* Trying to clvl someone to equal/higher access? */
2643 if(!real_actor || new_access >= real_actor->access)
2644 override = CMD_LOG_OVERRIDE;
2645 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2646 * If they lower their own access it's not a big problem.
2649 victim->access = new_access;
2650 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2651 return 1 | override;
2654 static CHANSERV_FUNC(cmd_deluser)
2656 struct handle_info *handle;
2657 struct userData *victim;
2658 struct userData *actor, *real_actor;
2659 unsigned short access, override = 0;
2664 actor = GetChannelUser(channel->channel_info, user->handle_info);
2665 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2667 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2670 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2672 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2678 access = user_level_from_name(argv[1], UL_OWNER);
2681 reply("CSMSG_INVALID_ACCESS", argv[1]);
2684 if(access != victim->access)
2686 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2692 access = victim->access;
2695 if((actor->access <= victim->access) && !IsHelping(user))
2697 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2701 /* If people delete themselves it is an override, but they
2702 * could've used deleteme so we don't log it as an override
2704 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2705 override = CMD_LOG_OVERRIDE;
2707 chan_name = strdup(channel->name);
2708 del_channel_user(victim, 1);
2709 reply("CSMSG_DELETED_USER", handle->handle, access, chan_name);
2711 return 1 | override;
2715 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2717 struct userData *actor, *real_actor, *uData, *next;
2718 unsigned int override = 0;
2720 actor = GetChannelUser(channel->channel_info, user->handle_info);
2721 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2723 if(min_access > max_access)
2725 reply("CSMSG_BAD_RANGE", min_access, max_access);
2729 if((actor->access <= max_access) && !IsHelping(user))
2731 reply("CSMSG_NO_ACCESS");
2735 if(!real_actor || real_actor->access <= max_access)
2736 override = CMD_LOG_OVERRIDE;
2738 for(uData = channel->channel_info->users; uData; uData = next)
2742 if((uData->access >= min_access)
2743 && (uData->access <= max_access)
2744 && match_ircglob(uData->handle->handle, mask))
2745 del_channel_user(uData, 1);
2748 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2749 return 1 | override;
2752 static CHANSERV_FUNC(cmd_mdelowner)
2754 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2757 static CHANSERV_FUNC(cmd_mdelcoowner)
2759 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2762 static CHANSERV_FUNC(cmd_mdelmaster)
2764 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2767 static CHANSERV_FUNC(cmd_mdelop)
2769 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2772 static CHANSERV_FUNC(cmd_mdelpeon)
2774 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2778 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2780 struct banData *bData, *next;
2781 char interval[INTERVALLEN];
2783 unsigned long limit;
2786 limit = now - duration;
2787 for(bData = channel->channel_info->bans; bData; bData = next)
2791 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2794 del_channel_ban(bData);
2798 intervalString(interval, duration, user->handle_info);
2799 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2804 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2806 struct userData *actor, *uData, *next;
2807 char interval[INTERVALLEN];
2809 unsigned long limit;
2811 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2812 if(min_access > max_access)
2814 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2818 if(!actor || actor->access <= max_access)
2820 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2825 limit = now - duration;
2826 for(uData = channel->channel_info->users; uData; uData = next)
2830 if((uData->seen > limit)
2832 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2835 if(((uData->access >= min_access) && (uData->access <= max_access))
2836 || (!max_access && (uData->access < actor->access)))
2838 del_channel_user(uData, 1);
2846 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2848 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2852 static CHANSERV_FUNC(cmd_trim)
2854 unsigned long duration;
2855 unsigned short min_level, max_level;
2860 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2861 duration = ParseInterval(argv[2]);
2864 reply("CSMSG_CANNOT_TRIM");
2868 if(!irccasecmp(argv[1], "bans"))
2870 cmd_trim_bans(user, channel, duration);
2873 else if(!irccasecmp(argv[1], "users"))
2875 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2878 else if(parse_level_range(&min_level, &max_level, argv[1]))
2880 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2883 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2885 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2890 reply("CSMSG_INVALID_TRIM", argv[1]);
2895 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2896 to the user. cmd_all takes advantage of this. */
2897 static CHANSERV_FUNC(cmd_up)
2899 struct mod_chanmode change;
2900 struct userData *uData;
2903 mod_chanmode_init(&change);
2905 change.args[0].u.member = GetUserMode(channel, user);
2906 if(!change.args[0].u.member)
2909 reply("MSG_CHANNEL_ABSENT", channel->name);
2913 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2917 reply("CSMSG_GODMODE_UP", argv[0]);
2920 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2922 change.args[0].mode = MODE_CHANOP;
2923 errmsg = "CSMSG_ALREADY_OPPED";
2925 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2927 change.args[0].mode = MODE_VOICE;
2928 errmsg = "CSMSG_ALREADY_VOICED";
2933 reply("CSMSG_NO_ACCESS");
2936 change.args[0].mode &= ~change.args[0].u.member->modes;
2937 if(!change.args[0].mode)
2940 reply(errmsg, channel->name);
2943 modcmd_chanmode_announce(&change);
2947 static CHANSERV_FUNC(cmd_down)
2949 struct mod_chanmode change;
2951 mod_chanmode_init(&change);
2953 change.args[0].u.member = GetUserMode(channel, user);
2954 if(!change.args[0].u.member)
2957 reply("MSG_CHANNEL_ABSENT", channel->name);
2961 if(!change.args[0].u.member->modes)
2964 reply("CSMSG_ALREADY_DOWN", channel->name);
2968 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
2969 modcmd_chanmode_announce(&change);
2973 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)
2975 struct userData *cList;
2977 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
2979 if(IsSuspended(cList->channel)
2980 || IsUserSuspended(cList)
2981 || !GetUserMode(cList->channel->channel, user))
2984 mcmd(user, cList->channel->channel, 0, NULL, cmd);
2990 static CHANSERV_FUNC(cmd_upall)
2992 return cmd_all(CSFUNC_ARGS, cmd_up);
2995 static CHANSERV_FUNC(cmd_downall)
2997 return cmd_all(CSFUNC_ARGS, cmd_down);
3000 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3001 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3004 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)
3006 unsigned int ii, valid;
3007 struct userNode *victim;
3008 struct mod_chanmode *change;
3010 change = mod_chanmode_alloc(argc - 1);
3012 for(ii=valid=0; ++ii < argc; )
3014 if(!(victim = GetUserH(argv[ii])))
3016 change->args[valid].mode = mode;
3017 change->args[valid].u.member = GetUserMode(channel, victim);
3018 if(!change->args[valid].u.member)
3020 if(validate && !validate(user, channel, victim))
3025 change->argc = valid;
3026 if(valid < (argc-1))
3027 reply("CSMSG_PROCESS_FAILED");
3030 modcmd_chanmode_announce(change);
3031 reply(action, channel->name);
3033 mod_chanmode_free(change);
3037 static CHANSERV_FUNC(cmd_op)
3039 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3042 static CHANSERV_FUNC(cmd_deop)
3044 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3047 static CHANSERV_FUNC(cmd_voice)
3049 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3052 static CHANSERV_FUNC(cmd_devoice)
3054 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3058 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3064 for(ii=0; ii<channel->members.used; ii++)
3066 struct modeNode *mn = channel->members.list[ii];
3068 if(IsService(mn->user))
3071 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3074 if(protect_user(mn->user, user, channel->channel_info))
3078 victims[(*victimCount)++] = mn;
3084 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3086 struct userNode *victim;
3087 struct modeNode **victims;
3088 unsigned int offset, n, victimCount, duration = 0;
3089 char *reason = "Bye.", *ban, *name;
3090 char interval[INTERVALLEN];
3092 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3093 REQUIRE_PARAMS(offset);
3096 reason = unsplit_string(argv + offset, argc - offset, NULL);
3097 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3099 /* Truncate the reason to a length of TOPICLEN, as
3100 the ircd does; however, leave room for an ellipsis
3101 and the kicker's nick. */
3102 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3106 if((victim = GetUserH(argv[1])))
3108 victims = alloca(sizeof(victims[0]));
3109 victims[0] = GetUserMode(channel, victim);
3110 /* XXX: The comparison with ACTION_KICK is just because all
3111 * other actions can work on users outside the channel, and we
3112 * want to allow those (e.g. unbans) in that case. If we add
3113 * some other ejection action for in-channel users, change
3115 victimCount = victims[0] ? 1 : 0;
3117 if(IsService(victim))
3119 reply("MSG_SERVICE_IMMUNE", victim->nick);
3123 if((action == ACTION_KICK) && !victimCount)
3125 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3129 if(protect_user(victim, user, channel->channel_info))
3131 reply("CSMSG_USER_PROTECTED", victim->nick);
3135 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3136 name = victim->nick;
3138 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3140 struct handle_info *hi;
3141 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3142 const char *accountname = argv[1] + 1;
3144 if(!(hi = get_handle_info(accountname)))
3146 reply("MSG_HANDLE_UNKNOWN", accountname);
3150 snprintf(banmask, sizeof(banmask), "*!*@%s.*", hi->handle);
3151 victims = alloca(sizeof(victims[0]) * channel->members.used);
3153 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3155 reply("CSMSG_MASK_PROTECTED", banmask);
3159 if((action == ACTION_KICK) && (victimCount == 0))
3161 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3165 name = ban = strdup(banmask);
3169 if(!is_ircmask(argv[1]))
3171 reply("MSG_NICK_UNKNOWN", argv[1]);
3175 victims = alloca(sizeof(victims[0]) * channel->members.used);
3177 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3179 reply("CSMSG_MASK_PROTECTED", argv[1]);
3183 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3185 reply("CSMSG_LAME_MASK", argv[1]);
3189 if((action == ACTION_KICK) && (victimCount == 0))
3191 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3195 name = ban = strdup(argv[1]);
3198 /* Truncate the ban in place if necessary; we must ensure
3199 that 'ban' is a valid ban mask before sanitizing it. */
3200 sanitize_ircmask(ban);
3202 if(action & ACTION_ADD_BAN)
3204 struct banData *bData, *next;
3206 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3208 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3213 if(action & ACTION_ADD_TIMED_BAN)
3215 duration = ParseInterval(argv[2]);
3219 reply("CSMSG_DURATION_TOO_LOW");
3223 else if(duration > (86400 * 365 * 2))
3225 reply("CSMSG_DURATION_TOO_HIGH");
3231 for(bData = channel->channel_info->bans; bData; bData = next)
3233 if(match_ircglobs(bData->mask, ban))
3235 int exact = !irccasecmp(bData->mask, ban);
3237 /* The ban is redundant; there is already a ban
3238 with the same effect in place. */
3242 free(bData->reason);
3243 bData->reason = strdup(reason);
3244 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3246 reply("CSMSG_REASON_CHANGE", ban);
3250 if(exact && bData->expires)
3254 /* If the ban matches an existing one exactly,
3255 extend the expiration time if the provided
3256 duration is longer. */
3257 if(duration && (now + duration > bData->expires))
3259 bData->expires = now + duration;
3270 /* Delete the expiration timeq entry and
3271 requeue if necessary. */
3272 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3275 timeq_add(bData->expires, expire_ban, bData);
3279 /* automated kickban */
3282 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3284 reply("CSMSG_BAN_ADDED", name, channel->name);
3290 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3297 if(match_ircglobs(ban, bData->mask))
3299 /* The ban we are adding makes previously existing
3300 bans redundant; silently remove them. */
3301 del_channel_ban(bData);
3305 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);
3307 name = ban = strdup(bData->mask);
3311 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3313 extern const char *hidden_host_suffix;
3314 const char *old_name = chanserv_conf.old_ban_names->list[n];
3316 unsigned int l1, l2;
3319 l2 = strlen(old_name);
3322 if(irccasecmp(ban + l1 - l2, old_name))
3324 new_mask = malloc(MAXLEN);
3325 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3327 name = ban = new_mask;
3332 if(action & ACTION_BAN)
3334 unsigned int exists;
3335 struct mod_chanmode *change;
3337 if(channel->banlist.used >= MAXBANS)
3340 reply("CSMSG_BANLIST_FULL", channel->name);
3345 exists = ChannelBanExists(channel, ban);
3346 change = mod_chanmode_alloc(victimCount + 1);
3347 for(n = 0; n < victimCount; ++n)
3349 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3350 change->args[n].u.member = victims[n];
3354 change->args[n].mode = MODE_BAN;
3355 change->args[n++].u.hostmask = ban;
3359 modcmd_chanmode_announce(change);
3361 mod_chanmode_announce(chanserv, channel, change);
3362 mod_chanmode_free(change);
3364 if(exists && (action == ACTION_BAN))
3367 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3373 if(action & ACTION_KICK)
3375 char kick_reason[MAXLEN];
3376 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3378 for(n = 0; n < victimCount; n++)
3379 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3384 /* No response, since it was automated. */
3386 else if(action & ACTION_ADD_BAN)
3389 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3391 reply("CSMSG_BAN_ADDED", name, channel->name);
3393 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3394 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3395 else if(action & ACTION_BAN)
3396 reply("CSMSG_BAN_DONE", name, channel->name);
3397 else if(action & ACTION_KICK && victimCount)
3398 reply("CSMSG_KICK_DONE", name, channel->name);
3404 static CHANSERV_FUNC(cmd_kickban)
3406 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3409 static CHANSERV_FUNC(cmd_kick)
3411 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3414 static CHANSERV_FUNC(cmd_ban)
3416 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3419 static CHANSERV_FUNC(cmd_addban)
3421 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3424 static CHANSERV_FUNC(cmd_addtimedban)
3426 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3429 static struct mod_chanmode *
3430 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3432 struct mod_chanmode *change;
3433 unsigned char *match;
3434 unsigned int ii, count;
3436 match = alloca(bans->used);
3439 for(ii = count = 0; ii < bans->used; ++ii)
3441 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3442 MATCH_USENICK | MATCH_VISIBLE);
3449 for(ii = count = 0; ii < bans->used; ++ii)
3451 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3458 change = mod_chanmode_alloc(count);
3459 for(ii = count = 0; ii < bans->used; ++ii)
3463 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3464 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3466 assert(count == change->argc);
3471 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3473 struct userNode *actee;
3479 /* may want to allow a comma delimited list of users... */
3480 if(!(actee = GetUserH(argv[1])))
3482 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3484 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3485 const char *accountname = argv[1] + 1;
3487 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3488 mask = strdup(banmask);
3490 else if(!is_ircmask(argv[1]))
3492 reply("MSG_NICK_UNKNOWN", argv[1]);
3497 mask = strdup(argv[1]);
3501 /* We don't sanitize the mask here because ircu
3503 if(action & ACTION_UNBAN)
3505 struct mod_chanmode *change;
3506 change = find_matching_bans(&channel->banlist, actee, mask);
3511 modcmd_chanmode_announce(change);
3512 for(ii = 0; ii < change->argc; ++ii)
3513 free((char*)change->args[ii].u.hostmask);
3514 mod_chanmode_free(change);
3519 if(action & ACTION_DEL_BAN)
3521 struct banData *ban, *next;
3523 ban = channel->channel_info->bans;
3527 for( ; ban && !user_matches_glob(actee, ban->mask,
3528 MATCH_USENICK | MATCH_VISIBLE);
3531 for( ; ban && !match_ircglobs(mask, ban->mask);
3536 del_channel_ban(ban);
3543 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3545 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3551 static CHANSERV_FUNC(cmd_unban)
3553 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3556 static CHANSERV_FUNC(cmd_delban)
3558 /* it doesn't necessarily have to remove the channel ban - may want
3559 to make that an option. */
3560 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3563 static CHANSERV_FUNC(cmd_unbanme)
3565 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3566 long flags = ACTION_UNBAN;
3568 /* remove permanent bans if the user has the proper access. */
3569 if(uData->access >= UL_MASTER)
3570 flags |= ACTION_DEL_BAN;
3572 argv[1] = user->nick;
3573 return unban_user(user, channel, 2, argv, cmd, flags);
3576 static CHANSERV_FUNC(cmd_unbanall)
3578 struct mod_chanmode *change;
3581 if(!channel->banlist.used)
3583 reply("CSMSG_NO_BANS", channel->name);
3587 change = mod_chanmode_alloc(channel->banlist.used);
3588 for(ii=0; ii<channel->banlist.used; ii++)
3590 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3591 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3593 modcmd_chanmode_announce(change);
3594 for(ii = 0; ii < change->argc; ++ii)
3595 free((char*)change->args[ii].u.hostmask);
3596 mod_chanmode_free(change);
3597 reply("CSMSG_BANS_REMOVED", channel->name);
3601 static CHANSERV_FUNC(cmd_open)
3603 struct mod_chanmode *change;
3606 change = find_matching_bans(&channel->banlist, user, NULL);
3608 change = mod_chanmode_alloc(0);
3609 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3610 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3611 && channel->channel_info->modes.modes_set)
3612 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3613 modcmd_chanmode_announce(change);
3614 reply("CSMSG_CHANNEL_OPENED", channel->name);
3615 for(ii = 0; ii < change->argc; ++ii)
3616 free((char*)change->args[ii].u.hostmask);
3617 mod_chanmode_free(change);
3621 static CHANSERV_FUNC(cmd_myaccess)
3623 static struct string_buffer sbuf;
3624 struct handle_info *target_handle;
3625 struct userData *uData;
3628 target_handle = user->handle_info;
3629 else if(!IsHelping(user))
3631 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3634 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3637 if(!target_handle->channels)
3639 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3643 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3644 for(uData = target_handle->channels; uData; uData = uData->u_next)
3646 struct chanData *cData = uData->channel;
3648 if(uData->access > UL_OWNER)
3650 if(IsProtected(cData)
3651 && (target_handle != user->handle_info)
3652 && !GetTrueChannelAccess(cData, user->handle_info))
3655 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3656 if(uData->flags != USER_AUTO_OP)
3657 string_buffer_append(&sbuf, ',');
3658 if(IsUserSuspended(uData))
3659 string_buffer_append(&sbuf, 's');
3660 if(IsUserAutoOp(uData))
3662 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3663 string_buffer_append(&sbuf, 'o');
3664 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3665 string_buffer_append(&sbuf, 'v');
3667 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3668 string_buffer_append(&sbuf, 'i');
3670 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3672 string_buffer_append_string(&sbuf, ")]");
3673 string_buffer_append(&sbuf, '\0');
3674 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3680 static CHANSERV_FUNC(cmd_access)
3682 struct userNode *target;
3683 struct handle_info *target_handle;
3684 struct userData *uData;
3686 char prefix[MAXLEN];
3691 target_handle = target->handle_info;
3693 else if((target = GetUserH(argv[1])))
3695 target_handle = target->handle_info;
3697 else if(argv[1][0] == '*')
3699 if(!(target_handle = get_handle_info(argv[1]+1)))
3701 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3707 reply("MSG_NICK_UNKNOWN", argv[1]);
3711 assert(target || target_handle);
3713 if(target == chanserv)
3715 reply("CSMSG_IS_CHANSERV");
3723 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3728 reply("MSG_USER_AUTHENTICATE", target->nick);
3731 reply("MSG_AUTHENTICATE");
3737 const char *epithet = NULL, *type = NULL;
3740 epithet = chanserv_conf.irc_operator_epithet;
3741 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3743 else if(IsNetworkHelper(target))
3745 epithet = chanserv_conf.network_helper_epithet;
3746 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3748 else if(IsSupportHelper(target))
3750 epithet = chanserv_conf.support_helper_epithet;
3751 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3755 if(target_handle->epithet)
3756 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3758 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3760 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3764 sprintf(prefix, "%s", target_handle->handle);
3767 if(!channel->channel_info)
3769 reply("CSMSG_NOT_REGISTERED", channel->name);
3773 helping = HANDLE_FLAGGED(target_handle, HELPING)
3774 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3775 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3777 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3778 /* To prevent possible information leaks, only show infolines
3779 * if the requestor is in the channel or it's their own
3781 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3783 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3785 /* Likewise, only say it's suspended if the user has active
3786 * access in that channel or it's their own entry. */
3787 if(IsUserSuspended(uData)
3788 && (GetChannelUser(channel->channel_info, user->handle_info)
3789 || (user->handle_info == uData->handle)))
3791 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3796 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3803 zoot_list(struct listData *list)
3805 struct userData *uData;
3806 unsigned int start, curr, highest, lowest;
3807 struct helpfile_table tmp_table;
3808 const char **temp, *msg;
3810 if(list->table.length == 1)
3813 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3815 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3816 msg = user_find_message(list->user, "MSG_NONE");
3817 send_message_type(4, list->user, list->bot, " %s", msg);
3819 tmp_table.width = list->table.width;
3820 tmp_table.flags = list->table.flags;
3821 list->table.contents[0][0] = " ";
3822 highest = list->highest;
3823 if(list->lowest != 0)
3824 lowest = list->lowest;
3825 else if(highest < 100)
3828 lowest = highest - 100;
3829 for(start = curr = 1; curr < list->table.length; )
3831 uData = list->users[curr-1];
3832 list->table.contents[curr++][0] = " ";
3833 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3836 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3838 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3839 temp = list->table.contents[--start];
3840 list->table.contents[start] = list->table.contents[0];
3841 tmp_table.contents = list->table.contents + start;
3842 tmp_table.length = curr - start;
3843 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3844 list->table.contents[start] = temp;
3846 highest = lowest - 1;
3847 lowest = (highest < 100) ? 0 : (highest - 99);
3853 def_list(struct listData *list)
3857 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3859 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3860 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3861 if(list->table.length == 1)
3863 msg = user_find_message(list->user, "MSG_NONE");
3864 send_message_type(4, list->user, list->bot, " %s", msg);
3869 userData_access_comp(const void *arg_a, const void *arg_b)
3871 const struct userData *a = *(struct userData**)arg_a;
3872 const struct userData *b = *(struct userData**)arg_b;
3874 if(a->access != b->access)
3875 res = b->access - a->access;
3877 res = irccasecmp(a->handle->handle, b->handle->handle);
3882 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3884 void (*send_list)(struct listData *);
3885 struct userData *uData;
3886 struct listData lData;
3887 unsigned int matches;
3891 lData.bot = cmd->parent->bot;
3892 lData.channel = channel;
3893 lData.lowest = lowest;
3894 lData.highest = highest;
3895 lData.search = (argc > 1) ? argv[1] : NULL;
3896 send_list = def_list;
3897 (void)zoot_list; /* since it doesn't show user levels */
3899 if(user->handle_info)
3901 switch(user->handle_info->userlist_style)
3903 case HI_STYLE_DEF: send_list = def_list; break;
3904 case HI_STYLE_ZOOT: send_list = def_list; break;
3908 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3910 for(uData = channel->channel_info->users; uData; uData = uData->next)
3912 if((uData->access < lowest)
3913 || (uData->access > highest)
3914 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3916 lData.users[matches++] = uData;
3918 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3920 lData.table.length = matches+1;
3921 lData.table.width = 4;
3922 lData.table.flags = TABLE_NO_FREE;
3923 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3924 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3925 lData.table.contents[0] = ary;
3928 ary[2] = "Last Seen";
3930 for(matches = 1; matches < lData.table.length; ++matches)
3932 struct userData *uData = lData.users[matches-1];
3933 char seen[INTERVALLEN];
3935 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3936 lData.table.contents[matches] = ary;
3937 ary[0] = strtab(uData->access);
3938 ary[1] = uData->handle->handle;
3941 else if(!uData->seen)
3944 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
3945 ary[2] = strdup(ary[2]);
3946 if(IsUserSuspended(uData))
3947 ary[3] = "Suspended";
3948 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
3949 ary[3] = "Vacation";
3954 for(matches = 1; matches < lData.table.length; ++matches)
3956 free((char*)lData.table.contents[matches][2]);
3957 free(lData.table.contents[matches]);
3959 free(lData.table.contents[0]);
3960 free(lData.table.contents);
3964 static CHANSERV_FUNC(cmd_users)
3966 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
3969 static CHANSERV_FUNC(cmd_wlist)
3971 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
3974 static CHANSERV_FUNC(cmd_clist)
3976 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
3979 static CHANSERV_FUNC(cmd_mlist)
3981 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
3984 static CHANSERV_FUNC(cmd_olist)
3986 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
3989 static CHANSERV_FUNC(cmd_plist)
3991 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
3994 static CHANSERV_FUNC(cmd_bans)
3996 struct userNode *search_u = NULL;
3997 struct helpfile_table tbl;
3998 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
3999 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4000 const char *msg_never, *triggered, *expires;
4001 struct banData *ban, **bans;
4005 else if(strchr(search = argv[1], '!'))
4008 search_wilds = search[strcspn(search, "?*")];
4010 else if(!(search_u = GetUserH(search)))
4011 reply("MSG_NICK_UNKNOWN", search);
4013 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4015 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4019 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4024 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4027 bans[matches++] = ban;
4032 tbl.length = matches + 1;
4033 tbl.width = 4 + timed;
4035 tbl.flags = TABLE_NO_FREE;
4036 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4037 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4038 tbl.contents[0][0] = "Mask";
4039 tbl.contents[0][1] = "Set By";
4040 tbl.contents[0][2] = "Triggered";
4043 tbl.contents[0][3] = "Expires";
4044 tbl.contents[0][4] = "Reason";
4047 tbl.contents[0][3] = "Reason";
4050 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4052 free(tbl.contents[0]);
4057 msg_never = user_find_message(user, "MSG_NEVER");
4058 for(ii = 0; ii < matches; )
4064 else if(ban->expires)
4065 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4067 expires = msg_never;
4070 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4072 triggered = msg_never;
4074 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4075 tbl.contents[ii][0] = ban->mask;
4076 tbl.contents[ii][1] = ban->owner;
4077 tbl.contents[ii][2] = strdup(triggered);
4080 tbl.contents[ii][3] = strdup(expires);
4081 tbl.contents[ii][4] = ban->reason;
4084 tbl.contents[ii][3] = ban->reason;
4086 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4087 reply("MSG_MATCH_COUNT", matches);
4088 for(ii = 1; ii < tbl.length; ++ii)
4090 free((char*)tbl.contents[ii][2]);
4092 free((char*)tbl.contents[ii][3]);
4093 free(tbl.contents[ii]);
4095 free(tbl.contents[0]);
4101 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4103 struct chanData *cData = channel->channel_info;
4104 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4106 if(cData->topic_mask)
4107 return !match_ircglob(new_topic, cData->topic_mask);
4108 else if(cData->topic)
4109 return irccasecmp(new_topic, cData->topic);
4114 static CHANSERV_FUNC(cmd_topic)
4116 struct chanData *cData;
4119 cData = channel->channel_info;
4124 SetChannelTopic(channel, chanserv, cData->topic, 1);
4125 reply("CSMSG_TOPIC_SET", cData->topic);
4129 reply("CSMSG_NO_TOPIC", channel->name);
4133 topic = unsplit_string(argv + 1, argc - 1, NULL);
4134 /* If they say "!topic *", use an empty topic. */
4135 if((topic[0] == '*') && (topic[1] == 0))
4137 if(bad_topic(channel, user, topic))
4139 char *topic_mask = cData->topic_mask;
4142 char new_topic[TOPICLEN+1], tchar;
4143 int pos=0, starpos=-1, dpos=0, len;
4145 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4152 len = strlen(topic);
4153 if((dpos + len) > TOPICLEN)
4154 len = TOPICLEN + 1 - dpos;
4155 memcpy(new_topic+dpos, topic, len);
4159 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4160 default: new_topic[dpos++] = tchar; break;
4163 if((dpos > TOPICLEN) || tchar)
4166 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4167 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4170 new_topic[dpos] = 0;
4171 SetChannelTopic(channel, chanserv, new_topic, 1);
4173 reply("CSMSG_TOPIC_LOCKED", channel->name);
4178 SetChannelTopic(channel, chanserv, topic, 1);
4180 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4182 /* Grab the topic and save it as the default topic. */
4184 cData->topic = strdup(channel->topic);
4190 static CHANSERV_FUNC(cmd_mode)
4192 struct userData *uData;
4193 struct mod_chanmode *change;
4198 change = &channel->channel_info->modes;
4199 if(change->modes_set || change->modes_clear) {
4200 modcmd_chanmode_announce(change);
4201 reply("CSMSG_DEFAULTED_MODES", channel->name);
4203 reply("CSMSG_NO_MODES", channel->name);
4207 uData = GetChannelUser(channel->channel_info, user->handle_info);
4209 base_oplevel = MAXOPLEVEL;
4210 else if (uData->access >= UL_OWNER)
4213 base_oplevel = 1 + UL_OWNER - uData->access;
4214 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, base_oplevel);
4217 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4221 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4222 && mode_lock_violated(&channel->channel_info->modes, change))
4225 mod_chanmode_format(&channel->channel_info->modes, modes);
4226 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4230 modcmd_chanmode_announce(change);
4231 mod_chanmode_free(change);
4232 reply("CSMSG_MODES_SET", unsplit_string(argv+1, argc-1, NULL));
4236 static CHANSERV_FUNC(cmd_invite)
4238 struct userData *uData;
4239 struct userNode *invite;
4241 uData = GetChannelUser(channel->channel_info, user->handle_info);
4245 if(!(invite = GetUserH(argv[1])))
4247 reply("MSG_NICK_UNKNOWN", argv[1]);
4254 if(GetUserMode(channel, invite))
4256 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4264 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4265 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4268 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4270 irc_invite(chanserv, invite, channel);
4272 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4277 static CHANSERV_FUNC(cmd_inviteme)
4279 if(GetUserMode(channel, user))
4281 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4284 if(channel->channel_info
4285 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4287 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4290 irc_invite(cmd->parent->bot, user, channel);
4295 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4298 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4300 /* We display things based on two dimensions:
4301 * - Issue time: present or absent
4302 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4303 * (in order of precedence, so something both expired and revoked
4304 * only counts as revoked)
4306 combo = (suspended->issued ? 4 : 0)
4307 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4309 case 0: /* no issue time, indefinite expiration */
4310 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4312 case 1: /* no issue time, expires in future */
4313 intervalString(buf1, suspended->expires-now, user->handle_info);
4314 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4316 case 2: /* no issue time, expired */
4317 intervalString(buf1, now-suspended->expires, user->handle_info);
4318 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4320 case 3: /* no issue time, revoked */
4321 intervalString(buf1, now-suspended->revoked, user->handle_info);
4322 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4324 case 4: /* issue time set, indefinite expiration */
4325 intervalString(buf1, now-suspended->issued, user->handle_info);
4326 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4328 case 5: /* issue time set, expires in future */
4329 intervalString(buf1, now-suspended->issued, user->handle_info);
4330 intervalString(buf2, suspended->expires-now, user->handle_info);
4331 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4333 case 6: /* issue time set, expired */
4334 intervalString(buf1, now-suspended->issued, user->handle_info);
4335 intervalString(buf2, now-suspended->expires, user->handle_info);
4336 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4338 case 7: /* issue time set, revoked */
4339 intervalString(buf1, now-suspended->issued, user->handle_info);
4340 intervalString(buf2, now-suspended->revoked, user->handle_info);
4341 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4344 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4349 static CHANSERV_FUNC(cmd_info)
4351 char modes[MAXLEN], buffer[INTERVALLEN];
4352 struct userData *uData, *owner;
4353 struct chanData *cData;
4354 struct do_not_register *dnr;
4359 cData = channel->channel_info;
4360 reply("CSMSG_CHANNEL_INFO", channel->name);
4362 uData = GetChannelUser(cData, user->handle_info);
4363 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4365 mod_chanmode_format(&cData->modes, modes);
4366 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4367 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4370 for(it = dict_first(cData->notes); it; it = iter_next(it))
4374 note = iter_data(it);
4375 if(!note_type_visible_to_user(cData, note->type, user))
4378 padding = PADLEN - 1 - strlen(iter_key(it));
4379 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4382 reply("CSMSG_CHANNEL_MAX", cData->max);
4383 for(owner = cData->users; owner; owner = owner->next)
4384 if(owner->access == UL_OWNER)
4385 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4386 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4387 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4388 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4390 privileged = IsStaff(user);
4392 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4393 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4394 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4396 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4397 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4399 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4401 struct suspended *suspended;
4402 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4403 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4404 show_suspension_info(cmd, user, suspended);
4406 else if(IsSuspended(cData))
4408 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4409 show_suspension_info(cmd, user, cData->suspended);
4414 static CHANSERV_FUNC(cmd_netinfo)
4416 extern unsigned long boot_time;
4417 extern unsigned long burst_length;
4418 char interval[INTERVALLEN];
4420 reply("CSMSG_NETWORK_INFO");
4421 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4422 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4423 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4424 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4425 reply("CSMSG_NETWORK_BANS", banCount);
4426 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4427 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4428 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4433 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4435 struct helpfile_table table;
4437 struct userNode *user;
4442 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4443 table.contents = alloca(list->used*sizeof(*table.contents));
4444 for(nn=0; nn<list->used; nn++)
4446 user = list->list[nn];
4447 if(user->modes & skip_flags)
4451 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4454 nick = alloca(strlen(user->nick)+3);
4455 sprintf(nick, "(%s)", user->nick);
4459 table.contents[table.length][0] = nick;
4462 table_send(chanserv, to->nick, 0, NULL, table);
4465 static CHANSERV_FUNC(cmd_ircops)
4467 reply("CSMSG_STAFF_OPERS");
4468 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4472 static CHANSERV_FUNC(cmd_helpers)
4474 reply("CSMSG_STAFF_HELPERS");
4475 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4479 static CHANSERV_FUNC(cmd_staff)
4481 reply("CSMSG_NETWORK_STAFF");
4482 cmd_ircops(CSFUNC_ARGS);
4483 cmd_helpers(CSFUNC_ARGS);
4487 static CHANSERV_FUNC(cmd_peek)
4489 struct modeNode *mn;
4490 char modes[MODELEN];
4492 struct helpfile_table table;
4494 irc_make_chanmode(channel, modes);
4496 reply("CSMSG_PEEK_INFO", channel->name);
4497 reply("CSMSG_PEEK_TOPIC", channel->topic);
4498 reply("CSMSG_PEEK_MODES", modes);
4499 reply("CSMSG_PEEK_USERS", channel->members.used);
4503 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4504 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4505 for(n = 0; n < channel->members.used; n++)
4507 mn = channel->members.list[n];
4508 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4510 table.contents[table.length] = alloca(sizeof(**table.contents));
4511 table.contents[table.length][0] = mn->user->nick;
4516 reply("CSMSG_PEEK_OPS");
4517 table_send(chanserv, user->nick, 0, NULL, table);
4520 reply("CSMSG_PEEK_NO_OPS");
4524 static MODCMD_FUNC(cmd_wipeinfo)
4526 struct handle_info *victim;
4527 struct userData *ud, *actor, *real_actor;
4528 unsigned int override = 0;
4531 actor = GetChannelUser(channel->channel_info, user->handle_info);
4532 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4533 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4535 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4537 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4540 if((ud->access >= actor->access) && (ud != actor))
4542 reply("MSG_USER_OUTRANKED", victim->handle);
4545 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4546 override = CMD_LOG_OVERRIDE;
4550 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4551 return 1 | override;
4554 static CHANSERV_FUNC(cmd_resync)
4556 struct mod_chanmode *changes;
4557 struct chanData *cData = channel->channel_info;
4558 unsigned int ii, used;
4560 changes = mod_chanmode_alloc(channel->members.used * 2);
4561 for(ii = used = 0; ii < channel->members.used; ++ii)
4563 struct modeNode *mn = channel->members.list[ii];
4564 struct userData *uData;
4566 if(IsService(mn->user))
4569 uData = GetChannelAccess(cData, mn->user->handle_info);
4570 if(!cData->lvlOpts[lvlGiveOps]
4571 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4573 if(!(mn->modes & MODE_CHANOP))
4575 changes->args[used].mode = MODE_CHANOP;
4576 changes->args[used++].u.member = mn;
4579 else if(!cData->lvlOpts[lvlGiveVoice]
4580 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4582 if(mn->modes & MODE_CHANOP)
4584 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4585 changes->args[used++].u.member = mn;
4587 if(!(mn->modes & MODE_VOICE))
4589 changes->args[used].mode = MODE_VOICE;
4590 changes->args[used++].u.member = mn;
4597 changes->args[used].mode = MODE_REMOVE | mn->modes;
4598 changes->args[used++].u.member = mn;
4602 changes->argc = used;
4603 modcmd_chanmode_announce(changes);
4604 mod_chanmode_free(changes);
4605 reply("CSMSG_RESYNCED_USERS", channel->name);
4609 static CHANSERV_FUNC(cmd_seen)
4611 struct userData *uData;
4612 struct handle_info *handle;
4613 char seen[INTERVALLEN];
4617 if(!irccasecmp(argv[1], chanserv->nick))
4619 reply("CSMSG_IS_CHANSERV");
4623 if(!(handle = get_handle_info(argv[1])))
4625 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4629 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4631 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4636 reply("CSMSG_USER_PRESENT", handle->handle);
4637 else if(uData->seen)
4638 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4640 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4642 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4643 reply("CSMSG_USER_VACATION", handle->handle);
4648 static MODCMD_FUNC(cmd_names)
4650 struct userNode *targ;
4651 struct userData *targData;
4652 unsigned int ii, pos;
4655 for(ii=pos=0; ii<channel->members.used; ++ii)
4657 targ = channel->members.list[ii]->user;
4658 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4661 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4664 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4668 if(IsUserSuspended(targData))
4670 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4673 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4674 reply("CSMSG_END_NAMES", channel->name);
4679 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4681 switch(ntype->visible_type)
4683 case NOTE_VIS_ALL: return 1;
4684 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4685 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4690 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4692 struct userData *uData;
4694 switch(ntype->set_access_type)
4696 case NOTE_SET_CHANNEL_ACCESS:
4697 if(!user->handle_info)
4699 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4701 return uData->access >= ntype->set_access.min_ulevel;
4702 case NOTE_SET_CHANNEL_SETTER:
4703 return check_user_level(channel, user, lvlSetters, 1, 0);
4704 case NOTE_SET_PRIVILEGED: default:
4705 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4709 static CHANSERV_FUNC(cmd_note)
4711 struct chanData *cData;
4713 struct note_type *ntype;
4715 cData = channel->channel_info;
4718 reply("CSMSG_NOT_REGISTERED", channel->name);
4722 /* If no arguments, show all visible notes for the channel. */
4728 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4730 note = iter_data(it);
4731 if(!note_type_visible_to_user(cData, note->type, user))
4734 reply("CSMSG_NOTELIST_HEADER", channel->name);
4735 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4738 reply("CSMSG_NOTELIST_END", channel->name);
4740 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4742 /* If one argument, show the named note. */
4745 if((note = dict_find(cData->notes, argv[1], NULL))
4746 && note_type_visible_to_user(cData, note->type, user))
4748 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4750 else if((ntype = dict_find(note_types, argv[1], NULL))
4751 && note_type_visible_to_user(NULL, ntype, user))
4753 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4758 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4762 /* Assume they're trying to set a note. */
4766 ntype = dict_find(note_types, argv[1], NULL);
4769 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4772 else if(note_type_settable_by_user(channel, ntype, user))
4774 note_text = unsplit_string(argv+2, argc-2, NULL);
4775 if((note = dict_find(cData->notes, argv[1], NULL)))
4776 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4777 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4778 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4780 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4782 /* The note is viewable to staff only, so return 0
4783 to keep the invocation from getting logged (or
4784 regular users can see it in !events). */
4790 reply("CSMSG_NO_ACCESS");
4797 static CHANSERV_FUNC(cmd_delnote)
4802 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4803 || !note_type_settable_by_user(channel, note->type, user))
4805 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4808 dict_remove(channel->channel_info->notes, note->type->name);
4809 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4813 static CHANSERV_FUNC(cmd_events)
4815 struct logSearch discrim;
4816 struct logReport report;
4817 unsigned int matches, limit;
4819 limit = (argc > 1) ? atoi(argv[1]) : 10;
4820 if(limit < 1 || limit > 200)
4823 memset(&discrim, 0, sizeof(discrim));
4824 discrim.masks.bot = chanserv;
4825 discrim.masks.channel_name = channel->name;
4827 discrim.masks.command = argv[2];
4828 discrim.limit = limit;
4829 discrim.max_time = INT_MAX;
4830 discrim.severities = 1 << LOG_COMMAND;
4831 report.reporter = chanserv;
4833 reply("CSMSG_EVENT_SEARCH_RESULTS");
4834 matches = log_entry_search(&discrim, log_report_entry, &report);
4836 reply("MSG_MATCH_COUNT", matches);
4838 reply("MSG_NO_MATCHES");
4842 static CHANSERV_FUNC(cmd_say)
4848 msg = unsplit_string(argv + 1, argc - 1, NULL);
4849 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4851 else if(*argv[1] == '*' && argv[1][1] != '\0')
4853 struct handle_info *hi;
4854 struct userNode *authed;
4857 msg = unsplit_string(argv + 2, argc - 2, NULL);
4859 if (!(hi = get_handle_info(argv[1] + 1)))
4861 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4865 for (authed = hi->users; authed; authed = authed->next_authed)
4866 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4868 else if(GetUserH(argv[1]))
4871 msg = unsplit_string(argv + 2, argc - 2, NULL);
4872 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4876 reply("MSG_NOT_TARGET_NAME");
4882 static CHANSERV_FUNC(cmd_emote)
4888 /* CTCP is so annoying. */
4889 msg = unsplit_string(argv + 1, argc - 1, NULL);
4890 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
4892 else if(*argv[1] == '*' && argv[1][1] != '\0')
4894 struct handle_info *hi;
4895 struct userNode *authed;
4898 msg = unsplit_string(argv + 2, argc - 2, NULL);
4900 if (!(hi = get_handle_info(argv[1] + 1)))
4902 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4906 for (authed = hi->users; authed; authed = authed->next_authed)
4907 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
4909 else if(GetUserH(argv[1]))
4911 msg = unsplit_string(argv + 2, argc - 2, NULL);
4912 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
4916 reply("MSG_NOT_TARGET_NAME");
4922 struct channelList *
4923 chanserv_support_channels(void)
4925 return &chanserv_conf.support_channels;
4928 static CHANSERV_FUNC(cmd_expire)
4930 int channel_count = registered_channels;
4931 expire_channels(NULL);
4932 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
4937 chanserv_expire_suspension(void *data)
4939 struct suspended *suspended = data;
4940 struct chanNode *channel;
4942 if(!suspended->expires || (now < suspended->expires))
4943 suspended->revoked = now;
4944 channel = suspended->cData->channel;
4945 suspended->cData->channel = channel;
4946 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
4947 if(!IsOffChannel(suspended->cData))
4949 struct mod_chanmode change;
4950 mod_chanmode_init(&change);
4952 change.args[0].mode = MODE_CHANOP;
4953 change.args[0].u.member = AddChannelUser(chanserv, channel);
4954 mod_chanmode_announce(chanserv, channel, &change);
4958 static CHANSERV_FUNC(cmd_csuspend)
4960 struct suspended *suspended;
4961 char reason[MAXLEN];
4962 unsigned long expiry, duration;
4963 struct userData *uData;
4967 if(IsProtected(channel->channel_info))
4969 reply("CSMSG_SUSPEND_NODELETE", channel->name);
4973 if(argv[1][0] == '!')
4975 else if(IsSuspended(channel->channel_info))
4977 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
4978 show_suspension_info(cmd, user, channel->channel_info->suspended);
4982 if(!strcmp(argv[1], "0"))
4984 else if((duration = ParseInterval(argv[1])))
4985 expiry = now + duration;
4988 reply("MSG_INVALID_DURATION", argv[1]);
4992 unsplit_string(argv + 2, argc - 2, reason);
4994 suspended = calloc(1, sizeof(*suspended));
4995 suspended->revoked = 0;
4996 suspended->issued = now;
4997 suspended->suspender = strdup(user->handle_info->handle);
4998 suspended->expires = expiry;
4999 suspended->reason = strdup(reason);
5000 suspended->cData = channel->channel_info;
5001 suspended->previous = suspended->cData->suspended;
5002 suspended->cData->suspended = suspended;
5004 if(suspended->expires)
5005 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5007 if(IsSuspended(channel->channel_info))
5009 suspended->previous->revoked = now;
5010 if(suspended->previous->expires)
5011 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5012 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5013 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5017 /* Mark all users in channel as absent. */
5018 for(uData = channel->channel_info->users; uData; uData = uData->next)
5027 /* Mark the channel as suspended, then part. */
5028 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5029 DelChannelUser(chanserv, channel, suspended->reason, 0);
5030 reply("CSMSG_SUSPENDED", channel->name);
5031 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5032 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5037 static CHANSERV_FUNC(cmd_cunsuspend)
5039 struct suspended *suspended;
5040 char message[MAXLEN];
5042 if(!IsSuspended(channel->channel_info))
5044 reply("CSMSG_NOT_SUSPENDED", channel->name);
5048 suspended = channel->channel_info->suspended;
5050 /* Expire the suspension and join ChanServ to the channel. */
5051 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5052 chanserv_expire_suspension(suspended);
5053 reply("CSMSG_UNSUSPENDED", channel->name);
5054 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5055 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5059 typedef struct chanservSearch
5064 unsigned long unvisited;
5065 unsigned long registered;
5067 unsigned long flags;
5071 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5074 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5079 search = malloc(sizeof(struct chanservSearch));
5080 memset(search, 0, sizeof(*search));
5083 for(i = 0; i < argc; i++)
5085 /* Assume all criteria require arguments. */
5088 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5092 if(!irccasecmp(argv[i], "name"))
5093 search->name = argv[++i];
5094 else if(!irccasecmp(argv[i], "registrar"))
5095 search->registrar = argv[++i];
5096 else if(!irccasecmp(argv[i], "unvisited"))
5097 search->unvisited = ParseInterval(argv[++i]);
5098 else if(!irccasecmp(argv[i], "registered"))
5099 search->registered = ParseInterval(argv[++i]);
5100 else if(!irccasecmp(argv[i], "flags"))
5103 if(!irccasecmp(argv[i], "nodelete"))
5104 search->flags |= CHANNEL_NODELETE;
5105 else if(!irccasecmp(argv[i], "suspended"))
5106 search->flags |= CHANNEL_SUSPENDED;
5107 else if(!irccasecmp(argv[i], "unreviewed"))
5108 search->flags |= CHANNEL_UNREVIEWED;
5111 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5115 else if(!irccasecmp(argv[i], "limit"))
5116 search->limit = strtoul(argv[++i], NULL, 10);
5119 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5124 if(search->name && !strcmp(search->name, "*"))
5126 if(search->registrar && !strcmp(search->registrar, "*"))
5127 search->registrar = 0;
5136 chanserv_channel_match(struct chanData *channel, search_t search)
5138 const char *name = channel->channel->name;
5139 if((search->name && !match_ircglob(name, search->name)) ||
5140 (search->registrar && !channel->registrar) ||
5141 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5142 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5143 (search->registered && (now - channel->registered) > search->registered) ||
5144 (search->flags && ((search->flags & channel->flags) != search->flags)))
5151 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5153 struct chanData *channel;
5154 unsigned int matches = 0;
5156 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5158 if(!chanserv_channel_match(channel, search))
5168 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5173 search_print(struct chanData *channel, void *data)
5175 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5178 static CHANSERV_FUNC(cmd_search)
5181 unsigned int matches;
5182 channel_search_func action;
5186 if(!irccasecmp(argv[1], "count"))
5187 action = search_count;
5188 else if(!irccasecmp(argv[1], "print"))
5189 action = search_print;
5192 reply("CSMSG_ACTION_INVALID", argv[1]);
5196 search = chanserv_search_create(user, argc - 2, argv + 2);
5200 if(action == search_count)
5201 search->limit = INT_MAX;
5203 if(action == search_print)
5204 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5206 matches = chanserv_channel_search(search, action, user);
5209 reply("MSG_MATCH_COUNT", matches);
5211 reply("MSG_NO_MATCHES");
5217 static CHANSERV_FUNC(cmd_unvisited)
5219 struct chanData *cData;
5220 unsigned long interval = chanserv_conf.channel_expire_delay;
5221 char buffer[INTERVALLEN];
5222 unsigned int limit = 25, matches = 0;
5226 interval = ParseInterval(argv[1]);
5228 limit = atoi(argv[2]);
5231 intervalString(buffer, interval, user->handle_info);
5232 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5234 for(cData = channelList; cData && matches < limit; cData = cData->next)
5236 if((now - cData->visited) < interval)
5239 intervalString(buffer, now - cData->visited, user->handle_info);
5240 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5247 static MODCMD_FUNC(chan_opt_defaulttopic)
5253 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5255 reply("CSMSG_TOPIC_LOCKED", channel->name);
5259 topic = unsplit_string(argv+1, argc-1, NULL);
5261 free(channel->channel_info->topic);
5262 if(topic[0] == '*' && topic[1] == 0)
5264 topic = channel->channel_info->topic = NULL;
5268 topic = channel->channel_info->topic = strdup(topic);
5269 if(channel->channel_info->topic_mask
5270 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5271 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5273 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5276 if(channel->channel_info->topic)
5277 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5279 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5283 static MODCMD_FUNC(chan_opt_topicmask)
5287 struct chanData *cData = channel->channel_info;
5290 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5292 reply("CSMSG_TOPIC_LOCKED", channel->name);
5296 mask = unsplit_string(argv+1, argc-1, NULL);
5298 if(cData->topic_mask)
5299 free(cData->topic_mask);
5300 if(mask[0] == '*' && mask[1] == 0)
5302 cData->topic_mask = 0;
5306 cData->topic_mask = strdup(mask);
5308 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5309 else if(!match_ircglob(cData->topic, cData->topic_mask))
5310 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5314 if(channel->channel_info->topic_mask)
5315 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5317 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5321 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5325 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5329 if(greeting[0] == '*' && greeting[1] == 0)
5333 unsigned int length = strlen(greeting);
5334 if(length > chanserv_conf.greeting_length)
5336 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5339 *data = strdup(greeting);
5348 reply(name, user_find_message(user, "MSG_NONE"));
5352 static MODCMD_FUNC(chan_opt_greeting)
5354 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5357 static MODCMD_FUNC(chan_opt_usergreeting)
5359 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5362 static MODCMD_FUNC(chan_opt_modes)
5364 struct mod_chanmode *new_modes;
5365 char modes[MODELEN];
5369 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5371 reply("CSMSG_NO_ACCESS");
5374 if(argv[1][0] == '*' && argv[1][1] == 0)
5376 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5378 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED, 0)))
5380 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5383 else if(new_modes->argc > 1)
5385 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5386 mod_chanmode_free(new_modes);
5391 channel->channel_info->modes = *new_modes;
5392 modcmd_chanmode_announce(new_modes);
5393 mod_chanmode_free(new_modes);
5397 mod_chanmode_format(&channel->channel_info->modes, modes);
5399 reply("CSMSG_SET_MODES", modes);
5401 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5405 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5407 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5409 struct chanData *cData = channel->channel_info;
5414 /* Set flag according to value. */
5415 if(enabled_string(argv[1]))
5417 cData->flags |= mask;
5420 else if(disabled_string(argv[1]))
5422 cData->flags &= ~mask;
5427 reply("MSG_INVALID_BINARY", argv[1]);
5433 /* Find current option value. */
5434 value = (cData->flags & mask) ? 1 : 0;
5438 reply(name, user_find_message(user, "MSG_ON"));
5440 reply(name, user_find_message(user, "MSG_OFF"));
5444 static MODCMD_FUNC(chan_opt_nodelete)
5446 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5448 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5452 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5455 static MODCMD_FUNC(chan_opt_dynlimit)
5457 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5460 static MODCMD_FUNC(chan_opt_offchannel)
5462 struct chanData *cData = channel->channel_info;
5467 /* Set flag according to value. */
5468 if(enabled_string(argv[1]))
5470 if(!IsOffChannel(cData))
5471 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5472 cData->flags |= CHANNEL_OFFCHANNEL;
5475 else if(disabled_string(argv[1]))
5477 if(IsOffChannel(cData))
5479 struct mod_chanmode change;
5480 mod_chanmode_init(&change);
5482 change.args[0].mode = MODE_CHANOP;
5483 change.args[0].u.member = AddChannelUser(chanserv, channel);
5484 mod_chanmode_announce(chanserv, channel, &change);
5486 cData->flags &= ~CHANNEL_OFFCHANNEL;
5491 reply("MSG_INVALID_BINARY", argv[1]);
5497 /* Find current option value. */
5498 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5502 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5504 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5508 static MODCMD_FUNC(chan_opt_unreviewed)
5510 struct chanData *cData = channel->channel_info;
5511 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5517 /* The two directions can have different ACLs. */
5518 if(enabled_string(argv[1]))
5520 else if(disabled_string(argv[1]))
5524 reply("MSG_INVALID_BINARY", argv[1]);
5528 if (new_value != value)
5530 struct svccmd *subcmd;
5531 char subcmd_name[32];
5533 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5534 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5537 reply("MSG_COMMAND_DISABLED", subcmd_name);
5540 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5544 cData->flags |= CHANNEL_UNREVIEWED;
5547 free(cData->registrar);
5548 cData->registrar = strdup(user->handle_info->handle);
5549 cData->flags &= ~CHANNEL_UNREVIEWED;
5556 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5558 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5562 static MODCMD_FUNC(chan_opt_defaults)
5564 struct userData *uData;
5565 struct chanData *cData;
5566 const char *confirm;
5567 enum levelOption lvlOpt;
5568 enum charOption chOpt;
5570 cData = channel->channel_info;
5571 uData = GetChannelUser(cData, user->handle_info);
5572 if(!uData || (uData->access < UL_OWNER))
5574 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5577 confirm = make_confirmation_string(uData);
5578 if((argc < 2) || strcmp(argv[1], confirm))
5580 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5583 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5584 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5585 cData->modes = chanserv_conf.default_modes;
5586 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5587 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5588 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5589 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5590 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5595 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5597 struct chanData *cData = channel->channel_info;
5598 struct userData *uData;
5599 unsigned short value;
5603 if(!check_user_level(channel, user, option, 1, 1))
5605 reply("CSMSG_CANNOT_SET");
5608 value = user_level_from_name(argv[1], UL_OWNER+1);
5609 if(!value && strcmp(argv[1], "0"))
5611 reply("CSMSG_INVALID_ACCESS", argv[1]);
5614 uData = GetChannelUser(cData, user->handle_info);
5615 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5617 reply("CSMSG_BAD_SETLEVEL");
5623 if(value > cData->lvlOpts[lvlGiveOps])
5625 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5630 if(value < cData->lvlOpts[lvlGiveVoice])
5632 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5637 /* This test only applies to owners, since non-owners
5638 * trying to set an option to above their level get caught
5639 * by the CSMSG_BAD_SETLEVEL test above.
5641 if(value > uData->access)
5643 reply("CSMSG_BAD_SETTERS");
5650 cData->lvlOpts[option] = value;
5652 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5656 static MODCMD_FUNC(chan_opt_enfops)
5658 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5661 static MODCMD_FUNC(chan_opt_giveops)
5663 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5666 static MODCMD_FUNC(chan_opt_enfmodes)
5668 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5671 static MODCMD_FUNC(chan_opt_enftopic)
5673 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5676 static MODCMD_FUNC(chan_opt_pubcmd)
5678 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5681 static MODCMD_FUNC(chan_opt_setters)
5683 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5686 static MODCMD_FUNC(chan_opt_ctcpusers)
5688 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5691 static MODCMD_FUNC(chan_opt_userinfo)
5693 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5696 static MODCMD_FUNC(chan_opt_givevoice)
5698 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5701 static MODCMD_FUNC(chan_opt_topicsnarf)
5703 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5706 static MODCMD_FUNC(chan_opt_inviteme)
5708 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5712 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5714 struct chanData *cData = channel->channel_info;
5715 int count = charOptions[option].count, index;
5719 index = atoi(argv[1]);
5721 if(!isdigit(argv[1][0]) || (index < 0) || (index >= count))
5723 reply("CSMSG_INVALID_NUMERIC", index);
5724 /* Show possible values. */
5725 for(index = 0; index < count; index++)
5726 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5730 cData->chOpts[option] = charOptions[option].values[index].value;
5734 /* Find current option value. */
5737 (index < count) && (cData->chOpts[option] != charOptions[option].values[index].value);
5741 /* Somehow, the option value is corrupt; reset it to the default. */
5742 cData->chOpts[option] = charOptions[option].default_value;
5747 reply(charOptions[option].format_name, index, user_find_message(user, charOptions[option].values[index].format_name));
5751 static MODCMD_FUNC(chan_opt_protect)
5753 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5756 static MODCMD_FUNC(chan_opt_toys)
5758 return channel_multiple_option(chToys, CSFUNC_ARGS);
5761 static MODCMD_FUNC(chan_opt_ctcpreaction)
5763 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5766 static MODCMD_FUNC(chan_opt_topicrefresh)
5768 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5771 static struct svccmd_list set_shows_list;
5774 handle_svccmd_unbind(struct svccmd *target) {
5776 for(ii=0; ii<set_shows_list.used; ++ii)
5777 if(target == set_shows_list.list[ii])
5778 set_shows_list.used = 0;
5781 static CHANSERV_FUNC(cmd_set)
5783 struct svccmd *subcmd;
5787 /* Check if we need to (re-)initialize set_shows_list. */
5788 if(!set_shows_list.used)
5790 if(!set_shows_list.size)
5792 set_shows_list.size = chanserv_conf.set_shows->used;
5793 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5795 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5797 const char *name = chanserv_conf.set_shows->list[ii];
5798 sprintf(buf, "%s %s", argv[0], name);
5799 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5802 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5805 svccmd_list_append(&set_shows_list, subcmd);
5811 reply("CSMSG_CHANNEL_OPTIONS");
5812 for(ii = 0; ii < set_shows_list.used; ii++)
5814 subcmd = set_shows_list.list[ii];
5815 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5820 sprintf(buf, "%s %s", argv[0], argv[1]);
5821 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5824 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5827 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5829 reply("CSMSG_NO_ACCESS");
5835 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5839 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5841 struct userData *uData;
5843 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5846 reply("CSMSG_NOT_USER", channel->name);
5852 /* Just show current option value. */
5854 else if(enabled_string(argv[1]))
5856 uData->flags |= mask;
5858 else if(disabled_string(argv[1]))
5860 uData->flags &= ~mask;
5864 reply("MSG_INVALID_BINARY", argv[1]);
5868 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5872 static MODCMD_FUNC(user_opt_noautoop)
5874 struct userData *uData;
5876 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5879 reply("CSMSG_NOT_USER", channel->name);
5882 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
5883 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
5885 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
5888 static MODCMD_FUNC(user_opt_autoinvite)
5890 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
5892 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
5894 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
5897 static MODCMD_FUNC(user_opt_info)
5899 struct userData *uData;
5902 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5906 /* If they got past the command restrictions (which require access)
5907 * but fail this test, we have some fool with security override on.
5909 reply("CSMSG_NOT_USER", channel->name);
5916 infoline = unsplit_string(argv + 1, argc - 1, NULL);
5917 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
5919 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
5922 bp = strcspn(infoline, "\001");
5925 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
5930 if(infoline[0] == '*' && infoline[1] == 0)
5933 uData->info = strdup(infoline);
5936 reply("CSMSG_USET_INFO", uData->info);
5938 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
5942 struct svccmd_list uset_shows_list;
5944 static CHANSERV_FUNC(cmd_uset)
5946 struct svccmd *subcmd;
5950 /* Check if we need to (re-)initialize uset_shows_list. */
5951 if(!uset_shows_list.used)
5955 "NoAutoOp", "AutoInvite", "Info"
5958 if(!uset_shows_list.size)
5960 uset_shows_list.size = ArrayLength(options);
5961 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
5963 for(ii = 0; ii < ArrayLength(options); ii++)
5965 const char *name = options[ii];
5966 sprintf(buf, "%s %s", argv[0], name);
5967 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5970 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
5973 svccmd_list_append(&uset_shows_list, subcmd);
5979 /* Do this so options are presented in a consistent order. */
5980 reply("CSMSG_USER_OPTIONS");
5981 for(ii = 0; ii < uset_shows_list.used; ii++)
5982 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
5986 sprintf(buf, "%s %s", argv[0], argv[1]);
5987 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5990 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5994 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5997 static CHANSERV_FUNC(cmd_giveownership)
5999 struct handle_info *new_owner_hi;
6000 struct userData *new_owner;
6001 struct userData *curr_user;
6002 struct userData *invoker;
6003 struct chanData *cData = channel->channel_info;
6004 struct do_not_register *dnr;
6005 const char *confirm;
6007 unsigned short co_access;
6008 char reason[MAXLEN];
6011 curr_user = GetChannelAccess(cData, user->handle_info);
6012 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6013 if(!curr_user || (curr_user->access != UL_OWNER))
6015 struct userData *owner = NULL;
6016 for(curr_user = channel->channel_info->users;
6018 curr_user = curr_user->next)
6020 if(curr_user->access != UL_OWNER)
6024 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6031 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6033 char delay[INTERVALLEN];
6034 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6035 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6038 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6040 if(new_owner_hi == user->handle_info)
6042 reply("CSMSG_NO_TRANSFER_SELF");
6045 new_owner = GetChannelAccess(cData, new_owner_hi);
6050 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6054 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6058 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6060 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6063 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6064 if(!IsHelping(user))
6065 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6067 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6070 invoker = GetChannelUser(cData, user->handle_info);
6071 if(invoker->access <= UL_OWNER)
6073 confirm = make_confirmation_string(curr_user);
6074 if((argc < 3) || strcmp(argv[2], confirm))
6076 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6080 if(new_owner->access >= UL_COOWNER)
6081 co_access = new_owner->access;
6083 co_access = UL_COOWNER;
6084 new_owner->access = UL_OWNER;
6086 curr_user->access = co_access;
6087 cData->ownerTransfer = now;
6088 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6089 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6090 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6094 static CHANSERV_FUNC(cmd_suspend)
6096 struct handle_info *hi;
6097 struct userData *self, *real_self, *target;
6098 unsigned int override = 0;
6101 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6102 self = GetChannelUser(channel->channel_info, user->handle_info);
6103 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6104 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6106 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6109 if(target->access >= self->access)
6111 reply("MSG_USER_OUTRANKED", hi->handle);
6114 if(target->flags & USER_SUSPENDED)
6116 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6121 target->present = 0;
6124 if(!real_self || target->access >= real_self->access)
6125 override = CMD_LOG_OVERRIDE;
6126 target->flags |= USER_SUSPENDED;
6127 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6128 return 1 | override;
6131 static CHANSERV_FUNC(cmd_unsuspend)
6133 struct handle_info *hi;
6134 struct userData *self, *real_self, *target;
6135 unsigned int override = 0;
6138 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6139 self = GetChannelUser(channel->channel_info, user->handle_info);
6140 real_self = GetChannelAccess(channel->channel_info, user->handle_info);
6141 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6143 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6146 if(target->access >= self->access)
6148 reply("MSG_USER_OUTRANKED", hi->handle);
6151 if(!(target->flags & USER_SUSPENDED))
6153 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6156 if(!real_self || target->access >= real_self->access)
6157 override = CMD_LOG_OVERRIDE;
6158 target->flags &= ~USER_SUSPENDED;
6159 scan_user_presence(target, NULL);
6160 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6161 return 1 | override;
6164 static MODCMD_FUNC(cmd_deleteme)
6166 struct handle_info *hi;
6167 struct userData *target;
6168 const char *confirm_string;
6169 unsigned short access;
6172 hi = user->handle_info;
6173 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6175 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6178 if(target->access == UL_OWNER)
6180 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6183 confirm_string = make_confirmation_string(target);
6184 if((argc < 2) || strcmp(argv[1], confirm_string))
6186 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6189 access = target->access;
6190 channel_name = strdup(channel->name);
6191 del_channel_user(target, 1);
6192 reply("CSMSG_DELETED_YOU", access, channel_name);
6198 chanserv_refresh_topics(UNUSED_ARG(void *data))
6200 unsigned int refresh_num = (now - self->link) / chanserv_conf.refresh_period;
6201 struct chanData *cData;
6204 for(cData = channelList; cData; cData = cData->next)
6206 if(IsSuspended(cData))
6208 opt = cData->chOpts[chTopicRefresh];
6211 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6214 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6215 cData->last_refresh = refresh_num;
6217 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6220 static CHANSERV_FUNC(cmd_unf)
6224 char response[MAXLEN];
6225 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6226 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6227 irc_privmsg(cmd->parent->bot, channel->name, response);
6230 reply("CSMSG_UNF_RESPONSE");
6234 static CHANSERV_FUNC(cmd_ping)
6238 char response[MAXLEN];
6239 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6240 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6241 irc_privmsg(cmd->parent->bot, channel->name, response);
6244 reply("CSMSG_PING_RESPONSE");
6248 static CHANSERV_FUNC(cmd_wut)
6252 char response[MAXLEN];
6253 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6254 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6255 irc_privmsg(cmd->parent->bot, channel->name, response);
6258 reply("CSMSG_WUT_RESPONSE");
6262 static CHANSERV_FUNC(cmd_8ball)
6264 unsigned int i, j, accum;
6269 for(i=1; i<argc; i++)
6270 for(j=0; argv[i][j]; j++)
6271 accum = (accum << 5) - accum + toupper(argv[i][j]);
6272 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6275 char response[MAXLEN];
6276 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6277 irc_privmsg(cmd->parent->bot, channel->name, response);
6280 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6284 static CHANSERV_FUNC(cmd_d)
6286 unsigned long sides, count, modifier, ii, total;
6287 char response[MAXLEN], *sep;
6291 if((count = strtoul(argv[1], &sep, 10)) < 1)
6301 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6302 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6306 else if((sep[0] == '-') && isdigit(sep[1]))
6307 modifier = strtoul(sep, NULL, 10);
6308 else if((sep[0] == '+') && isdigit(sep[1]))
6309 modifier = strtoul(sep+1, NULL, 10);
6316 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6321 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6324 for(total = ii = 0; ii < count; ++ii)
6325 total += (rand() % sides) + 1;
6328 if((count > 1) || modifier)
6330 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6331 sprintf(response, fmt, total, count, sides, modifier);
6335 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6336 sprintf(response, fmt, total, sides);
6339 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6341 send_message_type(4, user, cmd->parent->bot, "%s", response);
6345 static CHANSERV_FUNC(cmd_huggle)
6347 /* CTCP must be via PRIVMSG, never notice */
6349 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6351 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6356 chanserv_adjust_limit(void *data)
6358 struct mod_chanmode change;
6359 struct chanData *cData = data;
6360 struct chanNode *channel = cData->channel;
6363 if(IsSuspended(cData))
6366 cData->limitAdjusted = now;
6367 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6368 if(cData->modes.modes_set & MODE_LIMIT)
6370 if(limit > cData->modes.new_limit)
6371 limit = cData->modes.new_limit;
6372 else if(limit == cData->modes.new_limit)
6376 mod_chanmode_init(&change);
6377 change.modes_set = MODE_LIMIT;
6378 change.new_limit = limit;
6379 mod_chanmode_announce(chanserv, channel, &change);
6383 handle_new_channel(struct chanNode *channel)
6385 struct chanData *cData;
6387 if(!(cData = channel->channel_info))
6390 if(cData->modes.modes_set || cData->modes.modes_clear)
6391 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6393 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6394 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6397 /* Welcome to my worst nightmare. Warning: Read (or modify)
6398 the code below at your own risk. */
6400 handle_join(struct modeNode *mNode)
6402 struct mod_chanmode change;
6403 struct userNode *user = mNode->user;
6404 struct chanNode *channel = mNode->channel;
6405 struct chanData *cData;
6406 struct userData *uData = NULL;
6407 struct banData *bData;
6408 struct handle_info *handle;
6409 unsigned int modes = 0, info = 0;
6412 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6415 cData = channel->channel_info;
6416 if(channel->members.used > cData->max)
6417 cData->max = channel->members.used;
6419 /* Check for bans. If they're joining through a ban, one of two
6421 * 1: Join during a netburst, by riding the break. Kick them
6422 * unless they have ops or voice in the channel.
6423 * 2: They're allowed to join through the ban (an invite in
6424 * ircu2.10, or a +e on Hybrid, or something).
6425 * If they're not joining through a ban, and the banlist is not
6426 * full, see if they're on the banlist for the channel. If so,
6429 if(user->uplink->burst && !mNode->modes)
6432 for(ii = 0; ii < channel->banlist.used; ii++)
6434 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6436 /* Riding a netburst. Naughty. */
6437 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6443 mod_chanmode_init(&change);
6445 if(channel->banlist.used < MAXBANS)
6447 /* Not joining through a ban. */
6448 for(bData = cData->bans;
6449 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6450 bData = bData->next);
6454 char kick_reason[MAXLEN];
6455 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6457 bData->triggered = now;
6458 if(bData != cData->bans)
6460 /* Shuffle the ban to the head of the list. */
6462 bData->next->prev = bData->prev;
6464 bData->prev->next = bData->next;
6467 bData->next = cData->bans;
6470 cData->bans->prev = bData;
6471 cData->bans = bData;
6474 change.args[0].mode = MODE_BAN;
6475 change.args[0].u.hostmask = bData->mask;
6476 mod_chanmode_announce(chanserv, channel, &change);
6477 KickChannelUser(user, channel, chanserv, kick_reason);
6482 /* ChanServ will not modify the limits in join-flooded channels.
6483 It will also skip DynLimit processing when the user (or srvx)
6484 is bursting in, because there are likely more incoming. */
6485 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6486 && !user->uplink->burst
6487 && !channel->join_flooded
6488 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6490 /* The user count has begun "bumping" into the channel limit,
6491 so set a timer to raise the limit a bit. Any previous
6492 timers are removed so three incoming users within the delay
6493 results in one limit change, not three. */
6495 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6496 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6499 if(channel->join_flooded)
6501 /* don't automatically give ops or voice during a join flood */
6503 else if(cData->lvlOpts[lvlGiveOps] == 0)
6504 modes |= MODE_CHANOP;
6505 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6506 modes |= MODE_VOICE;
6508 greeting = cData->greeting;
6509 if(user->handle_info)
6511 handle = user->handle_info;
6513 if(IsHelper(user) && !IsHelping(user))
6516 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6518 if(channel == chanserv_conf.support_channels.list[ii])
6520 HANDLE_SET_FLAG(user->handle_info, HELPING);
6526 uData = GetTrueChannelAccess(cData, handle);
6527 if(uData && !IsUserSuspended(uData))
6529 /* Ops and above were handled by the above case. */
6530 if(IsUserAutoOp(uData))
6532 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6533 modes |= MODE_CHANOP;
6534 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6535 modes |= MODE_VOICE;
6537 if(uData->access >= UL_PRESENT)
6538 cData->visited = now;
6539 if(cData->user_greeting)
6540 greeting = cData->user_greeting;
6542 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6543 && ((now - uData->seen) >= chanserv_conf.info_delay)
6551 /* If user joining normally (not during burst), apply op or voice,
6552 * and send greeting/userinfo as appropriate.
6554 if(!user->uplink->burst)
6558 if(modes & MODE_CHANOP)
6559 modes &= ~MODE_VOICE;
6560 change.args[0].mode = modes;
6561 change.args[0].u.member = mNode;
6562 mod_chanmode_announce(chanserv, channel, &change);
6565 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6567 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6573 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6575 struct mod_chanmode change;
6576 struct userData *channel;
6577 unsigned int ii, jj;
6579 if(!user->handle_info)
6582 mod_chanmode_init(&change);
6584 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6586 struct chanNode *cn;
6587 struct modeNode *mn;
6588 if(IsUserSuspended(channel)
6589 || IsSuspended(channel->channel)
6590 || !(cn = channel->channel->channel))
6593 mn = GetUserMode(cn, user);
6596 if(!IsUserSuspended(channel)
6597 && IsUserAutoInvite(channel)
6598 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6600 && !user->uplink->burst)
6601 irc_invite(chanserv, user, cn);
6605 if(channel->access >= UL_PRESENT)
6606 channel->channel->visited = now;
6608 if(IsUserAutoOp(channel))
6610 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6611 change.args[0].mode = MODE_CHANOP;
6612 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6613 change.args[0].mode = MODE_VOICE;
6615 change.args[0].mode = 0;
6616 change.args[0].u.member = mn;
6617 if(change.args[0].mode)
6618 mod_chanmode_announce(chanserv, cn, &change);
6621 channel->seen = now;
6622 channel->present = 1;
6625 for(ii = 0; ii < user->channels.used; ++ii)
6627 struct chanNode *channel = user->channels.list[ii]->channel;
6628 struct banData *ban;
6630 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6631 || !channel->channel_info
6632 || IsSuspended(channel->channel_info))
6634 for(jj = 0; jj < channel->banlist.used; ++jj)
6635 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6637 if(jj < channel->banlist.used)
6639 for(ban = channel->channel_info->bans; ban; ban = ban->next)
6641 char kick_reason[MAXLEN];
6642 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6644 change.args[0].mode = MODE_BAN;
6645 change.args[0].u.hostmask = ban->mask;
6646 mod_chanmode_announce(chanserv, channel, &change);
6647 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6648 KickChannelUser(user, channel, chanserv, kick_reason);
6649 ban->triggered = now;
6654 if(IsSupportHelper(user))
6656 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6658 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6660 HANDLE_SET_FLAG(user->handle_info, HELPING);
6668 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6670 struct chanData *cData;
6671 struct userData *uData;
6673 cData = mn->channel->channel_info;
6674 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6677 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6679 /* Allow for a bit of padding so that the limit doesn't
6680 track the user count exactly, which could get annoying. */
6681 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6683 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6684 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6688 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6690 scan_user_presence(uData, mn->user);
6692 if (uData->access >= UL_PRESENT)
6693 cData->visited = now;
6696 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6698 unsigned int ii, jj;
6699 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6701 for(jj = 0; jj < mn->user->channels.used; ++jj)
6702 if(mn->user->channels.list[jj]->channel == chanserv_conf.support_channels.list[ii])
6704 if(jj < mn->user->channels.used)
6707 if(ii == chanserv_conf.support_channels.used)
6708 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6713 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6715 struct userData *uData;
6717 if(!channel->channel_info || !kicker || IsService(kicker)
6718 || (kicker == victim) || IsSuspended(channel->channel_info)
6719 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6722 if(protect_user(victim, kicker, channel->channel_info))
6724 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED");
6725 KickChannelUser(kicker, channel, chanserv, reason);
6728 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6733 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6735 struct chanData *cData;
6737 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6740 cData = channel->channel_info;
6741 if(bad_topic(channel, user, channel->topic))
6743 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6744 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6745 SetChannelTopic(channel, chanserv, old_topic, 1);
6746 else if(cData->topic)
6747 SetChannelTopic(channel, chanserv, cData->topic, 1);
6750 /* With topicsnarf, grab the topic and save it as the default topic. */
6751 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6754 cData->topic = strdup(channel->topic);
6760 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6762 struct mod_chanmode *bounce = NULL;
6763 unsigned int bnc, ii;
6766 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6769 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6770 && mode_lock_violated(&channel->channel_info->modes, change))
6772 char correct[MAXLEN];
6773 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6774 mod_chanmode_format(&channel->channel_info->modes, correct);
6775 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6777 for(ii = bnc = 0; ii < change->argc; ++ii)
6779 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6781 const struct userNode *victim = change->args[ii].u.member->user;
6782 if(!protect_user(victim, user, channel->channel_info))
6785 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6788 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6789 bounce->args[bnc].u.member = GetUserMode(channel, user);
6790 if(bounce->args[bnc].u.member)
6794 bounce->args[bnc].mode = MODE_CHANOP;
6795 bounce->args[bnc].u.member = change->args[ii].u.member;
6797 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6799 else if(change->args[ii].mode & MODE_CHANOP)
6801 const struct userNode *victim = change->args[ii].u.member->user;
6802 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6805 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6806 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6807 bounce->args[bnc].u.member = change->args[ii].u.member;
6810 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6812 const char *ban = change->args[ii].u.hostmask;
6813 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6816 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6817 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6818 bounce->args[bnc].u.hostmask = strdup(ban);
6820 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6825 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6826 mod_chanmode_announce(chanserv, channel, bounce);
6827 for(ii = 0; ii < change->argc; ++ii)
6828 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6829 free((char*)bounce->args[ii].u.hostmask);
6830 mod_chanmode_free(bounce);
6835 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6837 struct chanNode *channel;
6838 struct banData *bData;
6839 struct mod_chanmode change;
6840 unsigned int ii, jj;
6841 char kick_reason[MAXLEN];
6843 mod_chanmode_init(&change);
6845 change.args[0].mode = MODE_BAN;
6846 for(ii = 0; ii < user->channels.used; ++ii)
6848 channel = user->channels.list[ii]->channel;
6849 /* Need not check for bans if they're opped or voiced. */
6850 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6852 /* Need not check for bans unless channel registration is active. */
6853 if(!channel->channel_info || IsSuspended(channel->channel_info))
6855 /* Look for a matching ban already on the channel. */
6856 for(jj = 0; jj < channel->banlist.used; ++jj)
6857 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6859 /* Need not act if we found one. */
6860 if(jj < channel->banlist.used)
6862 /* Look for a matching ban in this channel. */
6863 for(bData = channel->channel_info->bans; bData; bData = bData->next)
6865 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
6867 change.args[0].u.hostmask = bData->mask;
6868 mod_chanmode_announce(chanserv, channel, &change);
6869 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6870 KickChannelUser(user, channel, chanserv, kick_reason);
6871 bData->triggered = now;
6872 break; /* we don't need to check any more bans in the channel */
6877 static void handle_rename(struct handle_info *handle, const char *old_handle)
6879 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
6883 dict_remove2(handle_dnrs, old_handle, 1);
6884 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
6885 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
6890 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
6892 struct userNode *h_user;
6894 if(handle->channels)
6896 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
6897 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
6899 while(handle->channels)
6900 del_channel_user(handle->channels, 1);
6905 handle_server_link(UNUSED_ARG(struct server *server))
6907 struct chanData *cData;
6909 for(cData = channelList; cData; cData = cData->next)
6911 if(!IsSuspended(cData))
6912 cData->may_opchan = 1;
6913 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6914 && !cData->channel->join_flooded
6915 && ((cData->channel->limit - cData->channel->members.used)
6916 < chanserv_conf.adjust_threshold))
6918 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6919 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6925 chanserv_conf_read(void)
6929 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
6930 struct mod_chanmode *change;
6931 struct string_list *strlist;
6932 struct chanNode *chan;
6935 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
6937 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
6940 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6941 UnlockChannel(chanserv_conf.support_channels.list[ii]);
6942 chanserv_conf.support_channels.used = 0;
6943 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
6945 for(ii = 0; ii < strlist->used; ++ii)
6947 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6950 chan = AddChannel(strlist->list[ii], now, str2, NULL);
6952 channelList_append(&chanserv_conf.support_channels, chan);
6955 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
6958 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
6961 chan = AddChannel(str, now, str2, NULL);
6963 channelList_append(&chanserv_conf.support_channels, chan);
6965 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
6966 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
6967 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
6968 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
6969 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
6970 chanserv_conf.greeting_length = str ? atoi(str) : 200;
6971 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
6972 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
6973 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
6974 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
6975 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
6976 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
6977 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
6978 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
6979 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
6980 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
6981 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
6982 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
6983 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
6984 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
6985 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
6986 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
6987 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
6988 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
6989 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
6991 NickChange(chanserv, str, 0);
6992 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
6993 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
6994 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
6995 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
6996 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
6997 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
6998 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
6999 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7000 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7001 chanserv_conf.max_owned = str ? atoi(str) : 5;
7002 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7003 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7004 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7005 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7006 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7007 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7008 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7011 safestrncpy(mode_line, str, sizeof(mode_line));
7012 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7013 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE, 0))
7014 && (change->argc < 2))
7016 chanserv_conf.default_modes = *change;
7017 mod_chanmode_free(change);
7019 free_string_list(chanserv_conf.set_shows);
7020 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7022 strlist = string_list_copy(strlist);
7025 static const char *list[] = {
7026 /* free form text */
7027 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7028 /* options based on user level */
7029 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7030 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7031 /* multiple choice options */
7032 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7033 /* binary options */
7034 "DynLimit", "NoDelete",
7039 strlist = alloc_string_list(ArrayLength(list)-1);
7040 for(ii=0; list[ii]; ii++)
7041 string_list_append(strlist, strdup(list[ii]));
7043 chanserv_conf.set_shows = strlist;
7044 /* We don't look things up now, in case the list refers to options
7045 * defined by modules initialized after this point. Just mark the
7046 * function list as invalid, so it will be initialized.
7048 set_shows_list.used = 0;
7049 free_string_list(chanserv_conf.eightball);
7050 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7053 strlist = string_list_copy(strlist);
7057 strlist = alloc_string_list(4);
7058 string_list_append(strlist, strdup("Yes."));
7059 string_list_append(strlist, strdup("No."));
7060 string_list_append(strlist, strdup("Maybe so."));
7062 chanserv_conf.eightball = strlist;
7063 free_string_list(chanserv_conf.old_ban_names);
7064 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7066 strlist = string_list_copy(strlist);
7068 strlist = alloc_string_list(2);
7069 chanserv_conf.old_ban_names = strlist;
7070 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7071 off_channel = str ? atoi(str) : 0;
7075 chanserv_note_type_read(const char *key, struct record_data *rd)
7078 struct note_type *ntype;
7081 if(!(obj = GET_RECORD_OBJECT(rd)))
7083 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7086 if(!(ntype = chanserv_create_note_type(key)))
7088 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7092 /* Figure out set access */
7093 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7095 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7096 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7098 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7100 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7101 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7103 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7105 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7109 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7110 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7111 ntype->set_access.min_opserv = 0;
7114 /* Figure out visibility */
7115 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7116 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7117 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7118 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7119 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7120 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7121 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7122 ntype->visible_type = NOTE_VIS_ALL;
7124 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7126 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7127 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7131 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7133 struct handle_info *handle;
7134 struct userData *uData;
7135 char *seen, *inf, *flags;
7136 unsigned long last_seen;
7137 unsigned short access;
7139 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7141 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7145 access = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7146 if(access > UL_OWNER)
7148 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7152 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7153 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7154 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7155 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7156 handle = get_handle_info(key);
7159 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7163 uData = add_channel_user(chan, handle, access, last_seen, inf);
7164 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7168 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7170 struct banData *bData;
7171 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7172 unsigned long set_time, triggered_time, expires_time;
7174 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7176 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7180 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7181 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7182 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7183 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7184 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7185 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7186 if (!reason || !owner)
7189 set_time = set ? strtoul(set, NULL, 0) : now;
7190 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7192 expires_time = strtoul(s_expires, NULL, 0);
7194 expires_time = set_time + atoi(s_duration);
7198 if(!reason || (expires_time && (expires_time < now)))
7201 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7204 static struct suspended *
7205 chanserv_read_suspended(dict_t obj)
7207 struct suspended *suspended = calloc(1, sizeof(*suspended));
7211 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7212 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7213 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7214 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7215 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7216 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7217 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7218 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7219 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7220 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7225 chanserv_channel_read(const char *key, struct record_data *hir)
7227 struct suspended *suspended;
7228 struct mod_chanmode *modes;
7229 struct chanNode *cNode;
7230 struct chanData *cData;
7231 struct dict *channel, *obj;
7232 char *str, *argv[10];
7236 channel = hir->d.object;
7238 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7241 cNode = AddChannel(key, now, NULL, NULL);
7244 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7247 cData = register_channel(cNode, str);
7250 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7254 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7256 enum levelOption lvlOpt;
7257 enum charOption chOpt;
7259 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7260 cData->flags = atoi(str);
7262 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7264 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7266 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7267 else if(levelOptions[lvlOpt].old_flag)
7269 if(cData->flags & levelOptions[lvlOpt].old_flag)
7270 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7272 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7276 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7278 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7280 cData->chOpts[chOpt] = str[0];
7283 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7285 enum levelOption lvlOpt;
7286 enum charOption chOpt;
7289 cData->flags = base64toint(str, 5);
7290 count = strlen(str += 5);
7291 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7294 if(levelOptions[lvlOpt].old_flag)
7296 if(cData->flags & levelOptions[lvlOpt].old_flag)
7297 lvl = levelOptions[lvlOpt].flag_value;
7299 lvl = levelOptions[lvlOpt].default_value;
7301 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7303 case 'c': lvl = UL_COOWNER; break;
7304 case 'm': lvl = UL_MASTER; break;
7305 case 'n': lvl = UL_OWNER+1; break;
7306 case 'o': lvl = UL_OP; break;
7307 case 'p': lvl = UL_PEON; break;
7308 case 'w': lvl = UL_OWNER; break;
7309 default: lvl = 0; break;
7311 cData->lvlOpts[lvlOpt] = lvl;
7313 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7314 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7317 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7319 suspended = chanserv_read_suspended(obj);
7320 cData->suspended = suspended;
7321 suspended->cData = cData;
7322 /* We could use suspended->expires and suspended->revoked to
7323 * set the CHANNEL_SUSPENDED flag, but we don't. */
7325 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7327 suspended = calloc(1, sizeof(*suspended));
7328 suspended->issued = 0;
7329 suspended->revoked = 0;
7330 suspended->suspender = strdup(str);
7331 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7332 suspended->expires = str ? atoi(str) : 0;
7333 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7334 suspended->reason = strdup(str ? str : "No reason");
7335 suspended->previous = NULL;
7336 cData->suspended = suspended;
7337 suspended->cData = cData;
7341 cData->flags &= ~CHANNEL_SUSPENDED;
7342 suspended = NULL; /* to squelch a warning */
7345 if(IsSuspended(cData)) {
7346 if(suspended->expires > now)
7347 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7348 else if(suspended->expires)
7349 cData->flags &= ~CHANNEL_SUSPENDED;
7352 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7353 struct mod_chanmode change;
7354 mod_chanmode_init(&change);
7356 change.args[0].mode = MODE_CHANOP;
7357 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7358 mod_chanmode_announce(chanserv, cNode, &change);
7361 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7362 cData->registered = str ? strtoul(str, NULL, 0) : now;
7363 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7364 cData->visited = str ? strtoul(str, NULL, 0) : now;
7365 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7366 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7367 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7368 cData->max = str ? atoi(str) : 0;
7369 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7370 cData->greeting = str ? strdup(str) : NULL;
7371 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7372 cData->user_greeting = str ? strdup(str) : NULL;
7373 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7374 cData->topic_mask = str ? strdup(str) : NULL;
7375 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7376 cData->topic = str ? strdup(str) : NULL;
7378 if(!IsSuspended(cData)
7379 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7380 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7381 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE, 0))) {
7382 cData->modes = *modes;
7384 cData->modes.modes_set |= MODE_REGISTERED;
7385 if(cData->modes.argc > 1)
7386 cData->modes.argc = 1;
7387 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7388 mod_chanmode_free(modes);
7391 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7392 for(it = dict_first(obj); it; it = iter_next(it))
7393 user_read_helper(iter_key(it), iter_data(it), cData);
7395 if(!cData->users && !IsProtected(cData))
7397 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7398 unregister_channel(cData, "has empty user list.");
7402 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7403 for(it = dict_first(obj); it; it = iter_next(it))
7404 ban_read_helper(iter_key(it), iter_data(it), cData);
7406 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7407 for(it = dict_first(obj); it; it = iter_next(it))
7409 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7410 struct record_data *rd = iter_data(it);
7411 const char *note, *setter;
7413 if(rd->type != RECDB_OBJECT)
7415 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7419 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7421 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7423 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7427 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7428 if(!setter) setter = "<unknown>";
7429 chanserv_add_channel_note(cData, ntype, setter, note);
7437 chanserv_dnr_read(const char *key, struct record_data *hir)
7439 const char *setter, *reason, *str;
7440 struct do_not_register *dnr;
7441 unsigned long expiry;
7443 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7446 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7449 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7452 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7455 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7456 expiry = str ? strtoul(str, NULL, 0) : 0;
7457 if(expiry && expiry <= now)
7459 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7462 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7464 dnr->set = atoi(str);
7470 chanserv_saxdb_read(struct dict *database)
7472 struct dict *section;
7475 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7476 for(it = dict_first(section); it; it = iter_next(it))
7477 chanserv_note_type_read(iter_key(it), iter_data(it));
7479 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7480 for(it = dict_first(section); it; it = iter_next(it))
7481 chanserv_channel_read(iter_key(it), iter_data(it));
7483 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7484 for(it = dict_first(section); it; it = iter_next(it))
7485 chanserv_dnr_read(iter_key(it), iter_data(it));
7491 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7493 int high_present = 0;
7494 saxdb_start_record(ctx, KEY_USERS, 1);
7495 for(; uData; uData = uData->next)
7497 if((uData->access >= UL_PRESENT) && uData->present)
7499 saxdb_start_record(ctx, uData->handle->handle, 0);
7500 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7501 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7503 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7505 saxdb_write_string(ctx, KEY_INFO, uData->info);
7506 saxdb_end_record(ctx);
7508 saxdb_end_record(ctx);
7509 return high_present;
7513 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7517 saxdb_start_record(ctx, KEY_BANS, 1);
7518 for(; bData; bData = bData->next)
7520 saxdb_start_record(ctx, bData->mask, 0);
7521 saxdb_write_int(ctx, KEY_SET, bData->set);
7522 if(bData->triggered)
7523 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7525 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7527 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7529 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7530 saxdb_end_record(ctx);
7532 saxdb_end_record(ctx);
7536 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7538 saxdb_start_record(ctx, name, 0);
7539 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7540 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7542 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7544 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7546 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7548 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7549 saxdb_end_record(ctx);
7553 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7557 enum levelOption lvlOpt;
7558 enum charOption chOpt;
7560 saxdb_start_record(ctx, channel->channel->name, 1);
7562 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7563 saxdb_write_int(ctx, KEY_MAX, channel->max);
7565 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7566 if(channel->registrar)
7567 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7568 if(channel->greeting)
7569 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7570 if(channel->user_greeting)
7571 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7572 if(channel->topic_mask)
7573 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7574 if(channel->suspended)
7575 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7577 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7578 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7579 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7580 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7581 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7583 buf[0] = channel->chOpts[chOpt];
7585 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7587 saxdb_end_record(ctx);
7589 if(channel->modes.modes_set || channel->modes.modes_clear)
7591 mod_chanmode_format(&channel->modes, buf);
7592 saxdb_write_string(ctx, KEY_MODES, buf);
7595 high_present = chanserv_write_users(ctx, channel->users);
7596 chanserv_write_bans(ctx, channel->bans);
7598 if(dict_size(channel->notes))
7602 saxdb_start_record(ctx, KEY_NOTES, 1);
7603 for(it = dict_first(channel->notes); it; it = iter_next(it))
7605 struct note *note = iter_data(it);
7606 saxdb_start_record(ctx, iter_key(it), 0);
7607 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7608 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7609 saxdb_end_record(ctx);
7611 saxdb_end_record(ctx);
7614 if(channel->ownerTransfer)
7615 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7616 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7617 saxdb_end_record(ctx);
7621 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7625 saxdb_start_record(ctx, ntype->name, 0);
7626 switch(ntype->set_access_type)
7628 case NOTE_SET_CHANNEL_ACCESS:
7629 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7631 case NOTE_SET_CHANNEL_SETTER:
7632 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7634 case NOTE_SET_PRIVILEGED: default:
7635 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7638 switch(ntype->visible_type)
7640 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7641 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7642 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7644 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7645 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7646 saxdb_end_record(ctx);
7650 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7652 struct do_not_register *dnr;
7653 dict_iterator_t it, next;
7655 for(it = dict_first(dnrs); it; it = next)
7657 next = iter_next(it);
7658 dnr = iter_data(it);
7659 if(dnr->expires && dnr->expires <= now)
7661 dict_remove(dnrs, iter_key(it));
7664 saxdb_start_record(ctx, dnr->chan_name, 0);
7666 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7668 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7669 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7670 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7671 saxdb_end_record(ctx);
7676 chanserv_saxdb_write(struct saxdb_context *ctx)
7679 struct chanData *channel;
7682 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7683 for(it = dict_first(note_types); it; it = iter_next(it))
7684 chanserv_write_note_type(ctx, iter_data(it));
7685 saxdb_end_record(ctx);
7688 saxdb_start_record(ctx, KEY_DNR, 1);
7689 write_dnrs_helper(ctx, handle_dnrs);
7690 write_dnrs_helper(ctx, plain_dnrs);
7691 write_dnrs_helper(ctx, mask_dnrs);
7692 saxdb_end_record(ctx);
7695 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7696 for(channel = channelList; channel; channel = channel->next)
7697 chanserv_write_channel(ctx, channel);
7698 saxdb_end_record(ctx);
7704 chanserv_db_cleanup(void) {
7706 unreg_part_func(handle_part);
7708 unregister_channel(channelList, "terminating.");
7709 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7710 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7711 free(chanserv_conf.support_channels.list);
7712 dict_delete(handle_dnrs);
7713 dict_delete(plain_dnrs);
7714 dict_delete(mask_dnrs);
7715 dict_delete(note_types);
7716 free_string_list(chanserv_conf.eightball);
7717 free_string_list(chanserv_conf.old_ban_names);
7718 free_string_list(chanserv_conf.set_shows);
7719 free(set_shows_list.list);
7720 free(uset_shows_list.list);
7723 struct userData *helper = helperList;
7724 helperList = helperList->next;
7729 #if defined(GCC_VARMACROS)
7730 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7731 #elif defined(C99_VARMACROS)
7732 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7734 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7735 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7738 init_chanserv(const char *nick)
7740 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7741 conf_register_reload(chanserv_conf_read);
7745 reg_server_link_func(handle_server_link);
7746 reg_new_channel_func(handle_new_channel);
7747 reg_join_func(handle_join);
7748 reg_part_func(handle_part);
7749 reg_kick_func(handle_kick);
7750 reg_topic_func(handle_topic);
7751 reg_mode_change_func(handle_mode);
7752 reg_nick_change_func(handle_nick_change);
7753 reg_auth_func(handle_auth);
7756 reg_handle_rename_func(handle_rename);
7757 reg_unreg_func(handle_unreg);
7759 handle_dnrs = dict_new();
7760 dict_set_free_data(handle_dnrs, free);
7761 plain_dnrs = dict_new();
7762 dict_set_free_data(plain_dnrs, free);
7763 mask_dnrs = dict_new();
7764 dict_set_free_data(mask_dnrs, free);
7766 reg_svccmd_unbind_func(handle_svccmd_unbind);
7767 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7768 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7769 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7770 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7771 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7772 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7773 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7774 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7775 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7776 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7777 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7778 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7779 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7781 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7782 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7784 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7785 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7786 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7787 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7788 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7790 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7791 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7792 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7793 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7794 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7796 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7797 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7798 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7799 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7801 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7802 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7803 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7804 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7805 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7806 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7807 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7808 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7810 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7811 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7812 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7813 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7814 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7815 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7816 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7817 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7818 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7819 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7820 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7821 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7822 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7823 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7825 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7826 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7827 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7828 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7829 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7831 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7832 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7834 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7835 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7836 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7837 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7838 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7839 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7840 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7841 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7842 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7843 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7844 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7846 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7847 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7849 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7850 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7851 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7852 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7854 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7855 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7856 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7857 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7858 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
7860 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7861 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7862 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7863 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7864 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7865 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
7867 /* Channel options */
7868 DEFINE_CHANNEL_OPTION(defaulttopic);
7869 DEFINE_CHANNEL_OPTION(topicmask);
7870 DEFINE_CHANNEL_OPTION(greeting);
7871 DEFINE_CHANNEL_OPTION(usergreeting);
7872 DEFINE_CHANNEL_OPTION(modes);
7873 DEFINE_CHANNEL_OPTION(enfops);
7874 DEFINE_CHANNEL_OPTION(giveops);
7875 DEFINE_CHANNEL_OPTION(protect);
7876 DEFINE_CHANNEL_OPTION(enfmodes);
7877 DEFINE_CHANNEL_OPTION(enftopic);
7878 DEFINE_CHANNEL_OPTION(pubcmd);
7879 DEFINE_CHANNEL_OPTION(givevoice);
7880 DEFINE_CHANNEL_OPTION(userinfo);
7881 DEFINE_CHANNEL_OPTION(dynlimit);
7882 DEFINE_CHANNEL_OPTION(topicsnarf);
7883 DEFINE_CHANNEL_OPTION(nodelete);
7884 DEFINE_CHANNEL_OPTION(toys);
7885 DEFINE_CHANNEL_OPTION(setters);
7886 DEFINE_CHANNEL_OPTION(topicrefresh);
7887 DEFINE_CHANNEL_OPTION(ctcpusers);
7888 DEFINE_CHANNEL_OPTION(ctcpreaction);
7889 DEFINE_CHANNEL_OPTION(inviteme);
7890 DEFINE_CHANNEL_OPTION(unreviewed);
7891 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
7892 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
7894 DEFINE_CHANNEL_OPTION(offchannel);
7895 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
7897 /* Alias set topic to set defaulttopic for compatibility. */
7898 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
7901 DEFINE_USER_OPTION(noautoop);
7902 DEFINE_USER_OPTION(autoinvite);
7903 DEFINE_USER_OPTION(info);
7905 /* Alias uset autovoice to uset autoop. */
7906 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
7908 note_types = dict_new();
7909 dict_set_free_data(note_types, chanserv_deref_note_type);
7912 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
7913 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
7914 service_register(chanserv)->trigger = '!';
7915 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
7917 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
7919 if(chanserv_conf.channel_expire_frequency)
7920 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
7922 if(chanserv_conf.dnr_expire_frequency)
7923 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
7925 if(chanserv_conf.refresh_period)
7927 unsigned long next_refresh;
7928 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
7929 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
7932 reg_exit_func(chanserv_db_cleanup);
7933 message_register_table(msgtab);