1 /* chanserv.c - Channel service bot
2 * Copyright 2000-2007 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
25 #include "opserv.h" /* for opserv_bad_channel() */
26 #include "nickserv.h" /* for oper_outranks() */
31 #define CHANSERV_CONF_NAME "services/chanserv"
33 /* ChanServ options */
34 #define KEY_SUPPORT_CHANNEL "support_channel"
35 #define KEY_SUPPORT_CHANNEL_MODES "support_channel_modes"
36 #define KEY_DB_BACKUP_FREQ "db_backup_freq"
37 #define KEY_INFO_DELAY "info_delay"
38 #define KEY_MAX_GREETLEN "max_greetlen"
39 #define KEY_ADJUST_THRESHOLD "adjust_threshold"
40 #define KEY_ADJUST_DELAY "adjust_delay"
41 #define KEY_CHAN_EXPIRE_FREQ "chan_expire_freq"
42 #define KEY_CHAN_EXPIRE_DELAY "chan_expire_delay"
43 #define KEY_DNR_EXPIRE_FREQ "dnr_expire_freq"
44 #define KEY_MAX_CHAN_USERS "max_chan_users"
45 #define KEY_MAX_CHAN_BANS "max_chan_bans"
46 #define KEY_NICK "nick"
47 #define KEY_OLD_CHANSERV_NAME "old_chanserv_name"
48 #define KEY_8BALL_RESPONSES "8ball"
49 #define KEY_OLD_BAN_NAMES "old_ban_names"
50 #define KEY_REFRESH_PERIOD "refresh_period"
51 #define KEY_CTCP_SHORT_BAN_DURATION "ctcp_short_ban_duration"
52 #define KEY_CTCP_LONG_BAN_DURATION "ctcp_long_ban_duration"
53 #define KEY_MAX_OWNED "max_owned"
54 #define KEY_IRC_OPERATOR_EPITHET "irc_operator_epithet"
55 #define KEY_NETWORK_HELPER_EPITHET "network_helper_epithet"
56 #define KEY_SUPPORT_HELPER_EPITHET "support_helper_epithet"
57 #define KEY_NODELETE_LEVEL "nodelete_level"
58 #define KEY_MAX_USERINFO_LENGTH "max_userinfo_length"
59 #define KEY_GIVEOWNERSHIP_PERIOD "giveownership_timeout"
60 #define KEY_INVITED_INTERVAL "invite_timeout"
62 /* ChanServ database */
63 #define KEY_CHANNELS "channels"
64 #define KEY_NOTE_TYPES "note_types"
66 /* Note type parameters */
67 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
68 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
69 #define KEY_NOTE_SETTER_ACCESS "setter_access"
70 #define KEY_NOTE_VISIBILITY "visibility"
71 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
72 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
73 #define KEY_NOTE_VIS_ALL "all"
74 #define KEY_NOTE_MAX_LENGTH "max_length"
75 #define KEY_NOTE_SETTER "setter"
76 #define KEY_NOTE_NOTE "note"
78 /* Do-not-register channels */
80 #define KEY_DNR_SET "set"
81 #define KEY_DNR_SETTER "setter"
82 #define KEY_DNR_REASON "reason"
85 #define KEY_REGISTERED "registered"
86 #define KEY_REGISTRAR "registrar"
87 #define KEY_SUSPENDED "suspended"
88 #define KEY_PREVIOUS "previous"
89 #define KEY_SUSPENDER "suspender"
90 #define KEY_ISSUED "issued"
91 #define KEY_REVOKED "revoked"
92 #define KEY_SUSPEND_EXPIRES "suspend_expires"
93 #define KEY_SUSPEND_REASON "suspend_reason"
94 #define KEY_VISITED "visited"
95 #define KEY_TOPIC "topic"
96 #define KEY_GREETING "greeting"
97 #define KEY_USER_GREETING "user_greeting"
98 #define KEY_MODES "modes"
99 #define KEY_FLAGS "flags"
100 #define KEY_OPTIONS "options"
101 #define KEY_USERS "users"
102 #define KEY_BANS "bans"
103 #define KEY_MAX "max"
104 #define KEY_MAX_TIME "max_time"
105 #define KEY_NOTES "notes"
106 #define KEY_TOPIC_MASK "topic_mask"
107 #define KEY_OWNER_TRANSFER "owner_transfer"
110 #define KEY_LEVEL "level"
111 #define KEY_INFO "info"
112 #define KEY_SEEN "seen"
115 #define KEY_OWNER "owner"
116 #define KEY_REASON "reason"
117 #define KEY_SET "set"
118 #define KEY_DURATION "duration"
119 #define KEY_EXPIRES "expires"
120 #define KEY_TRIGGERED "triggered"
122 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
123 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
124 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
126 /* Administrative messages */
127 static const struct message_entry msgtab[] = {
128 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
130 /* Channel registration */
131 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
132 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
133 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
134 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
135 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
136 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
138 /* Do-not-register channels */
139 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
140 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
141 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
142 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
143 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
144 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
145 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
146 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
147 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
148 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
149 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
150 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
151 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
152 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
154 /* Channel unregistration */
155 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
156 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
157 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
158 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
161 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
162 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
164 /* Channel merging */
165 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
166 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
167 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
168 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
169 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
171 /* Handle unregistration */
172 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
175 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
176 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
177 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
178 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
179 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
180 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
181 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
182 { "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." },
183 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
184 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
185 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
186 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
187 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
188 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
190 /* Removing yourself from a channel. */
191 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
192 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
193 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
195 /* User management */
196 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
197 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
198 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
199 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
200 { "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." },
201 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
202 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
203 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
205 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
206 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
207 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
208 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
209 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
210 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
211 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
214 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
215 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
216 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
217 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
218 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
219 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
220 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
221 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
222 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
223 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
224 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
225 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
226 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
227 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
228 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
229 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
231 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
233 /* Channel management */
234 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
235 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
236 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
238 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
239 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
240 { "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" },
241 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
242 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
243 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
244 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
246 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
247 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
248 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
249 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
250 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
251 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
252 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
253 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
254 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
255 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
256 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
257 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
258 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
259 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
260 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
261 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
262 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
263 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
264 { "CSMSG_SET_MODES", "$bModes $b %s" },
265 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
266 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
267 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
268 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
269 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
270 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
271 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
272 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
273 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
274 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
275 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
276 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
277 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
278 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
279 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
280 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
281 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
282 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
283 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
284 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
285 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
286 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
287 { "CSMSG_USET_INFO", "$bInfo $b %s" },
289 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
290 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
291 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
292 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
293 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
294 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
295 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
296 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
297 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
298 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
299 { "CSMSG_PROTECT_NONE", "No users will be protected." },
300 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
301 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
302 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
303 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
304 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
305 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
306 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
307 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
308 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
309 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
310 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
311 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
313 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
314 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
315 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
316 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
317 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
318 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
319 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
320 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
322 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
323 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
324 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
326 /* Channel userlist */
327 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
328 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
329 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
330 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
332 /* Channel note list */
333 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
334 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
335 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
336 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
337 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
338 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
339 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
340 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
341 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
342 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
343 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
344 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
345 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
346 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
347 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
349 /* Channel [un]suspension */
350 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
351 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
352 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
353 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
354 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
355 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
356 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
358 /* Access information */
359 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
360 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
361 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
362 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
363 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
364 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
365 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
366 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
367 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
368 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
369 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
370 { "CSMSG_UC_H_TITLE", "network helper" },
371 { "CSMSG_LC_H_TITLE", "support helper" },
372 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
374 /* Seen information */
375 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
376 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
377 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
378 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
380 /* Names information */
381 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
382 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
384 /* Channel information */
385 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
386 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
387 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
388 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
389 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
390 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
391 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
392 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
393 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
394 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
395 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
396 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
397 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
398 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
399 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
400 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
401 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
402 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
403 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
404 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
405 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
406 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
408 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
409 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
410 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
411 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
412 { "CSMSG_PEEK_OPS", "$bOps:$b" },
413 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
415 /* Network information */
416 { "CSMSG_NETWORK_INFO", "Network Information:" },
417 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
418 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
419 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
420 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
421 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
422 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
423 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
424 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
427 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
428 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
429 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
431 /* Channel searches */
432 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
433 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
434 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
435 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
437 /* Channel configuration */
438 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
439 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
440 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
441 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
444 { "CSMSG_USER_OPTIONS", "User Options:" },
445 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
448 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
449 { "CSMSG_PING_RESPONSE", "Pong!" },
450 { "CSMSG_WUT_RESPONSE", "wut" },
451 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
452 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
453 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
454 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
455 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
456 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
457 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
460 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
464 /* eject_user and unban_user flags */
465 #define ACTION_KICK 0x0001
466 #define ACTION_BAN 0x0002
467 #define ACTION_ADD_BAN 0x0004
468 #define ACTION_ADD_TIMED_BAN 0x0008
469 #define ACTION_UNBAN 0x0010
470 #define ACTION_DEL_BAN 0x0020
472 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
473 #define MODELEN 40 + KEYLEN
477 #define CSFUNC_ARGS user, channel, argc, argv, cmd
479 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
480 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
481 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
482 reply("MSG_MISSING_PARAMS", argv[0]); \
486 DECLARE_LIST(dnrList, struct do_not_register *);
487 DEFINE_LIST(dnrList, struct do_not_register *)
489 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
491 struct userNode *chanserv;
494 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
495 static struct log_type *CS_LOG;
499 struct channelList support_channels;
500 struct mod_chanmode default_modes;
502 unsigned long db_backup_frequency;
503 unsigned long channel_expire_frequency;
504 unsigned long dnr_expire_frequency;
506 unsigned long invited_timeout;
508 unsigned long info_delay;
509 unsigned long adjust_delay;
510 unsigned long channel_expire_delay;
511 unsigned int nodelete_level;
513 unsigned int adjust_threshold;
514 int join_flood_threshold;
516 unsigned int greeting_length;
517 unsigned int refresh_period;
518 unsigned int giveownership_period;
520 unsigned int max_owned;
521 unsigned int max_chan_users;
522 unsigned int max_chan_bans;
523 unsigned int max_userinfo_length;
525 struct string_list *set_shows;
526 struct string_list *eightball;
527 struct string_list *old_ban_names;
529 const char *ctcp_short_ban_duration;
530 const char *ctcp_long_ban_duration;
532 const char *irc_operator_epithet;
533 const char *network_helper_epithet;
534 const char *support_helper_epithet;
539 struct userNode *user;
540 struct userNode *bot;
541 struct chanNode *channel;
543 unsigned short lowest;
544 unsigned short highest;
545 struct userData **users;
546 struct helpfile_table table;
551 struct userNode *user;
552 struct chanNode *chan;
555 enum note_access_type
557 NOTE_SET_CHANNEL_ACCESS,
558 NOTE_SET_CHANNEL_SETTER,
562 enum note_visible_type
565 NOTE_VIS_CHANNEL_USERS,
571 enum note_access_type set_access_type;
573 unsigned int min_opserv;
574 unsigned short min_ulevel;
576 enum note_visible_type visible_type;
577 unsigned int max_length;
584 struct note_type *type;
585 char setter[NICKSERV_HANDLE_LEN+1];
589 static unsigned int registered_channels;
590 static unsigned int banCount;
592 static const struct {
595 unsigned short level;
598 { "peon", "Peon", UL_PEON, '+' },
599 { "op", "Op", UL_OP, '@' },
600 { "master", "Master", UL_MASTER, '%' },
601 { "coowner", "Coowner", UL_COOWNER, '*' },
602 { "owner", "Owner", UL_OWNER, '!' },
603 { "helper", "BUG:", UL_HELPER, 'X' }
606 static const struct {
609 unsigned short default_value;
610 unsigned int old_idx;
611 unsigned int old_flag;
612 unsigned short flag_value;
614 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
615 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
616 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
617 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
618 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
619 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
620 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
621 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
622 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
623 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
624 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
627 struct charOptionValues {
630 } protectValues[] = {
631 { 'a', "CSMSG_PROTECT_ALL" },
632 { 'e', "CSMSG_PROTECT_EQUAL" },
633 { 'l', "CSMSG_PROTECT_LOWER" },
634 { 'n', "CSMSG_PROTECT_NONE" }
636 { 'd', "CSMSG_TOYS_DISABLED" },
637 { 'n', "CSMSG_TOYS_PRIVATE" },
638 { 'p', "CSMSG_TOYS_PUBLIC" }
639 }, topicRefreshValues[] = {
640 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
641 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
642 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
643 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
644 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
645 }, ctcpReactionValues[] = {
646 { 'k', "CSMSG_CTCPREACTION_KICK" },
647 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
648 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
649 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
652 static const struct {
656 unsigned int old_idx;
658 struct charOptionValues *values;
660 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
661 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
662 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
663 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
666 struct userData *helperList;
667 struct chanData *channelList;
668 static struct module *chanserv_module;
669 static unsigned int userCount;
671 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
672 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
675 user_level_from_name(const char *name, unsigned short clamp_level)
677 unsigned int level = 0, ii;
679 level = strtoul(name, NULL, 10);
680 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
681 if(!irccasecmp(name, accessLevels[ii].name))
682 level = accessLevels[ii].level;
683 if(level > clamp_level)
689 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
692 *minl = strtoul(arg, &sep, 10);
700 *maxl = strtoul(sep+1, &sep, 10);
708 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
710 struct userData *uData, **head;
712 if(!channel || !handle)
715 if(override && HANDLE_FLAGGED(handle, HELPING)
716 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
718 for(uData = helperList;
719 uData && uData->handle != handle;
720 uData = uData->next);
724 uData = calloc(1, sizeof(struct userData));
725 uData->handle = handle;
727 uData->access = UL_HELPER;
733 uData->next = helperList;
735 helperList->prev = uData;
743 for(uData = channel->users; uData; uData = uData->next)
744 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
747 head = &(channel->users);
750 if(uData && (uData != *head))
752 /* Shuffle the user to the head of whatever list he was in. */
754 uData->next->prev = uData->prev;
756 uData->prev->next = uData->next;
762 (**head).prev = uData;
769 /* Returns non-zero if user has at least the minimum access.
770 * exempt_owner is set when handling !set, so the owner can set things
773 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
775 struct userData *uData;
776 struct chanData *cData = channel->channel_info;
777 unsigned short minimum = cData->lvlOpts[opt];
780 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
783 if(minimum <= uData->access)
785 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
790 /* Scan for other users authenticated to the same handle
791 still in the channel. If so, keep them listed as present.
793 user is optional, if not null, it skips checking that userNode
794 (for the handle_part function) */
796 scan_user_presence(struct userData *uData, struct userNode *user)
800 if(IsSuspended(uData->channel)
801 || IsUserSuspended(uData)
802 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
814 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
816 unsigned int eflags, argc;
818 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
820 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
821 if(!channel->channel_info
822 || IsSuspended(channel->channel_info)
824 || !ircncasecmp(text, "ACTION ", 7))
826 /* Figure out the minimum level needed to CTCP the channel */
827 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
829 /* We need to enforce against them; do so. */
831 argv[0] = (char*)text;
832 argv[1] = user->nick;
834 if(GetUserMode(channel, user))
835 eflags |= ACTION_KICK;
836 switch(channel->channel_info->chOpts[chCTCPReaction]) {
837 default: case 'k': /* just do the kick */ break;
839 eflags |= ACTION_BAN;
842 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
843 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
846 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
847 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
850 argv[argc++] = bad_ctcp_reason;
851 eject_user(chanserv, channel, argc, argv, NULL, eflags);
855 chanserv_create_note_type(const char *name)
857 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
858 strcpy(ntype->name, name);
860 dict_insert(note_types, ntype->name, ntype);
865 chanserv_deref_note_type(void *data)
867 struct note_type *ntype = data;
869 if(--ntype->refs > 0)
875 chanserv_flush_note_type(struct note_type *ntype)
877 struct chanData *cData;
878 for(cData = channelList; cData; cData = cData->next)
879 dict_remove(cData->notes, ntype->name);
883 chanserv_truncate_notes(struct note_type *ntype)
885 struct chanData *cData;
887 unsigned int size = sizeof(*note) + ntype->max_length;
889 for(cData = channelList; cData; cData = cData->next) {
890 note = dict_find(cData->notes, ntype->name, NULL);
893 if(strlen(note->note) <= ntype->max_length)
895 dict_remove2(cData->notes, ntype->name, 1);
896 note = realloc(note, size);
897 note->note[ntype->max_length] = 0;
898 dict_insert(cData->notes, ntype->name, note);
902 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
905 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
908 unsigned int len = strlen(text);
910 if(len > type->max_length) len = type->max_length;
911 note = calloc(1, sizeof(*note) + len);
913 strncpy(note->setter, setter, sizeof(note->setter)-1);
914 memcpy(note->note, text, len);
916 dict_insert(channel->notes, type->name, note);
922 chanserv_free_note(void *data)
924 struct note *note = data;
926 chanserv_deref_note_type(note->type);
927 assert(note->type->refs > 0); /* must use delnote to remove the type */
931 static MODCMD_FUNC(cmd_createnote) {
932 struct note_type *ntype;
933 unsigned int arg = 1, existed = 0, max_length;
935 if((ntype = dict_find(note_types, argv[1], NULL)))
938 ntype = chanserv_create_note_type(argv[arg]);
939 if(!irccasecmp(argv[++arg], "privileged"))
942 ntype->set_access_type = NOTE_SET_PRIVILEGED;
943 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
945 else if(!irccasecmp(argv[arg], "channel"))
947 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
950 reply("CSMSG_INVALID_ACCESS", argv[arg]);
953 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
954 ntype->set_access.min_ulevel = ulvl;
956 else if(!irccasecmp(argv[arg], "setter"))
958 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
962 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
966 if(!irccasecmp(argv[++arg], "privileged"))
967 ntype->visible_type = NOTE_VIS_PRIVILEGED;
968 else if(!irccasecmp(argv[arg], "channel_users"))
969 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
970 else if(!irccasecmp(argv[arg], "all"))
971 ntype->visible_type = NOTE_VIS_ALL;
973 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
977 if((arg+1) >= argc) {
978 reply("MSG_MISSING_PARAMS", argv[0]);
981 max_length = strtoul(argv[++arg], NULL, 0);
982 if(max_length < 20 || max_length > 450)
984 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
987 if(existed && (max_length < ntype->max_length))
989 ntype->max_length = max_length;
990 chanserv_truncate_notes(ntype);
992 ntype->max_length = max_length;
995 reply("CSMSG_NOTE_MODIFIED", ntype->name);
997 reply("CSMSG_NOTE_CREATED", ntype->name);
1002 dict_remove(note_types, ntype->name);
1006 static MODCMD_FUNC(cmd_removenote) {
1007 struct note_type *ntype;
1010 ntype = dict_find(note_types, argv[1], NULL);
1011 force = (argc > 2) && !irccasecmp(argv[2], "force");
1014 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1021 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1024 chanserv_flush_note_type(ntype);
1026 dict_remove(note_types, argv[1]);
1027 reply("CSMSG_NOTE_DELETED", argv[1]);
1032 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1036 if(orig->modes_set & change->modes_clear)
1038 if(orig->modes_clear & change->modes_set)
1040 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1041 && strcmp(orig->new_key, change->new_key))
1043 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1044 && (orig->new_limit != change->new_limit))
1049 static char max_length_text[MAXLEN+1][16];
1051 static struct helpfile_expansion
1052 chanserv_expand_variable(const char *variable)
1054 struct helpfile_expansion exp;
1056 if(!irccasecmp(variable, "notes"))
1059 exp.type = HF_TABLE;
1060 exp.value.table.length = 1;
1061 exp.value.table.width = 3;
1062 exp.value.table.flags = 0;
1063 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1064 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1065 exp.value.table.contents[0][0] = "Note Type";
1066 exp.value.table.contents[0][1] = "Visibility";
1067 exp.value.table.contents[0][2] = "Max Length";
1068 for(it=dict_first(note_types); it; it=iter_next(it))
1070 struct note_type *ntype = iter_data(it);
1073 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1074 row = exp.value.table.length++;
1075 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1076 exp.value.table.contents[row][0] = ntype->name;
1077 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1078 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1080 if(!max_length_text[ntype->max_length][0])
1081 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1082 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1087 exp.type = HF_STRING;
1088 exp.value.str = NULL;
1092 static struct chanData*
1093 register_channel(struct chanNode *cNode, char *registrar)
1095 struct chanData *channel;
1096 enum levelOption lvlOpt;
1097 enum charOption chOpt;
1099 channel = calloc(1, sizeof(struct chanData));
1101 channel->notes = dict_new();
1102 dict_set_free_data(channel->notes, chanserv_free_note);
1104 channel->registrar = strdup(registrar);
1105 channel->registered = now;
1106 channel->visited = now;
1107 channel->limitAdjusted = now;
1108 channel->ownerTransfer = now;
1109 channel->flags = CHANNEL_DEFAULT_FLAGS;
1110 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1111 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1112 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1113 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1115 channel->prev = NULL;
1116 channel->next = channelList;
1119 channelList->prev = channel;
1120 channelList = channel;
1121 registered_channels++;
1123 channel->channel = cNode;
1125 cNode->channel_info = channel;
1130 static struct userData*
1131 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1133 struct userData *ud;
1135 if(access_level > UL_OWNER)
1138 ud = calloc(1, sizeof(*ud));
1139 ud->channel = channel;
1140 ud->handle = handle;
1142 ud->access = access_level;
1143 ud->info = info ? strdup(info) : NULL;
1146 ud->next = channel->users;
1148 channel->users->prev = ud;
1149 channel->users = ud;
1151 channel->userCount++;
1155 ud->u_next = ud->handle->channels;
1157 ud->u_next->u_prev = ud;
1158 ud->handle->channels = ud;
1163 static void unregister_channel(struct chanData *channel, const char *reason);
1166 del_channel_user(struct userData *user, int do_gc)
1168 struct chanData *channel = user->channel;
1170 channel->userCount--;
1174 user->prev->next = user->next;
1176 channel->users = user->next;
1178 user->next->prev = user->prev;
1181 user->u_prev->u_next = user->u_next;
1183 user->handle->channels = user->u_next;
1185 user->u_next->u_prev = user->u_prev;
1189 if(do_gc && !channel->users && !IsProtected(channel)) {
1190 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1191 unregister_channel(channel, "lost all users.");
1195 static void expire_ban(void *data);
1198 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1201 unsigned int ii, l1, l2;
1206 bd = malloc(sizeof(struct banData));
1208 bd->channel = channel;
1210 bd->triggered = triggered;
1211 bd->expires = expires;
1213 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1215 extern const char *hidden_host_suffix;
1216 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1220 l2 = strlen(old_name);
1223 if(irccasecmp(mask + l1 - l2, old_name))
1225 new_mask = alloca(MAXLEN);
1226 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1229 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1231 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1232 bd->reason = strdup(reason);
1235 timeq_add(expires, expire_ban, bd);
1238 bd->next = channel->bans;
1240 channel->bans->prev = bd;
1242 channel->banCount++;
1249 del_channel_ban(struct banData *ban)
1251 ban->channel->banCount--;
1255 ban->prev->next = ban->next;
1257 ban->channel->bans = ban->next;
1260 ban->next->prev = ban->prev;
1263 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1272 expire_ban(void *data)
1274 struct banData *bd = data;
1275 if(!IsSuspended(bd->channel))
1277 struct banList bans;
1278 struct mod_chanmode change;
1280 bans = bd->channel->channel->banlist;
1281 mod_chanmode_init(&change);
1282 for(ii=0; ii<bans.used; ii++)
1284 if(!strcmp(bans.list[ii]->ban, bd->mask))
1287 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1288 change.args[0].u.hostmask = bd->mask;
1289 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1295 del_channel_ban(bd);
1298 static void chanserv_expire_suspension(void *data);
1301 unregister_channel(struct chanData *channel, const char *reason)
1303 struct mod_chanmode change;
1304 char msgbuf[MAXLEN];
1306 /* After channel unregistration, the following must be cleaned
1308 - Channel information.
1311 - Channel suspension data.
1312 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1318 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1322 mod_chanmode_init(&change);
1323 change.modes_clear |= MODE_REGISTERED;
1324 mod_chanmode_announce(chanserv, channel->channel, &change);
1327 while(channel->users)
1328 del_channel_user(channel->users, 0);
1330 while(channel->bans)
1331 del_channel_ban(channel->bans);
1333 free(channel->topic);
1334 free(channel->registrar);
1335 free(channel->greeting);
1336 free(channel->user_greeting);
1337 free(channel->topic_mask);
1340 channel->prev->next = channel->next;
1342 channelList = channel->next;
1345 channel->next->prev = channel->prev;
1347 if(channel->suspended)
1349 struct chanNode *cNode = channel->channel;
1350 struct suspended *suspended, *next_suspended;
1352 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1354 next_suspended = suspended->previous;
1355 free(suspended->suspender);
1356 free(suspended->reason);
1357 if(suspended->expires)
1358 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1363 cNode->channel_info = NULL;
1365 channel->channel->channel_info = NULL;
1367 dict_delete(channel->notes);
1368 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1369 if(!IsSuspended(channel))
1370 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1371 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1372 UnlockChannel(channel->channel);
1374 registered_channels--;
1378 expire_channels(UNUSED_ARG(void *data))
1380 struct chanData *channel, *next;
1381 struct userData *user;
1382 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1384 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1385 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1387 for(channel = channelList; channel; channel = next)
1389 next = channel->next;
1391 /* See if the channel can be expired. */
1392 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1393 || IsProtected(channel))
1396 /* Make sure there are no high-ranking users still in the channel. */
1397 for(user=channel->users; user; user=user->next)
1398 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1403 /* Unregister the channel */
1404 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1405 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1406 unregister_channel(channel, "registration expired.");
1409 if(chanserv_conf.channel_expire_frequency)
1410 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1414 expire_dnrs(UNUSED_ARG(void *data))
1416 dict_iterator_t it, next;
1417 struct do_not_register *dnr;
1419 for(it = dict_first(handle_dnrs); it; it = next)
1421 dnr = iter_data(it);
1422 next = iter_next(it);
1423 if(dnr->expires && dnr->expires <= now)
1424 dict_remove(handle_dnrs, dnr->chan_name + 1);
1426 for(it = dict_first(plain_dnrs); it; it = next)
1428 dnr = iter_data(it);
1429 next = iter_next(it);
1430 if(dnr->expires && dnr->expires <= now)
1431 dict_remove(plain_dnrs, dnr->chan_name + 1);
1433 for(it = dict_first(mask_dnrs); it; it = next)
1435 dnr = iter_data(it);
1436 next = iter_next(it);
1437 if(dnr->expires && dnr->expires <= now)
1438 dict_remove(mask_dnrs, dnr->chan_name + 1);
1441 if(chanserv_conf.dnr_expire_frequency)
1442 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1446 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1448 char protect = channel->chOpts[chProtect];
1449 struct userData *cs_victim, *cs_aggressor;
1451 /* Don't protect if no one is to be protected, someone is attacking
1452 himself, or if the aggressor is an IRC Operator. */
1453 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1456 /* Don't protect if the victim isn't authenticated (because they
1457 can't be a channel user), unless we are to protect non-users
1459 cs_victim = GetChannelAccess(channel, victim->handle_info);
1460 if(protect != 'a' && !cs_victim)
1463 /* Protect if the aggressor isn't a user because at this point,
1464 the aggressor can only be less than or equal to the victim. */
1465 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1469 /* If the aggressor was a user, then the victim can't be helped. */
1476 if(cs_victim->access > cs_aggressor->access)
1481 if(cs_victim->access >= cs_aggressor->access)
1490 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1492 struct chanData *cData = channel->channel_info;
1493 struct userData *cs_victim;
1495 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1496 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1497 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1499 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1507 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1509 if(IsService(victim))
1511 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1515 if(protect_user(victim, user, channel->channel_info))
1517 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1524 static struct do_not_register *
1525 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1527 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1528 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1529 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1530 strcpy(dnr->reason, reason);
1532 dnr->expires = expires;
1533 if(dnr->chan_name[0] == '*')
1534 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1535 else if(strpbrk(dnr->chan_name, "*?"))
1536 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1538 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1542 static struct dnrList
1543 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1545 struct dnrList list;
1546 dict_iterator_t it, next;
1547 struct do_not_register *dnr;
1549 dnrList_init(&list);
1551 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1553 if(dnr->expires && dnr->expires <= now)
1554 dict_remove(handle_dnrs, handle);
1555 else if(list.used < max)
1556 dnrList_append(&list, dnr);
1559 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1561 if(dnr->expires && dnr->expires <= now)
1562 dict_remove(plain_dnrs, chan_name);
1563 else if(list.used < max)
1564 dnrList_append(&list, dnr);
1569 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1571 next = iter_next(it);
1572 if(!match_ircglob(chan_name, iter_key(it)))
1574 dnr = iter_data(it);
1575 if(dnr->expires && dnr->expires <= now)
1576 dict_remove(mask_dnrs, iter_key(it));
1578 dnrList_append(&list, dnr);
1585 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1587 struct userNode *user;
1588 char buf1[INTERVALLEN];
1589 char buf2[INTERVALLEN];
1596 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1601 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1602 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1606 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1609 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1614 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1616 struct dnrList list;
1619 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1620 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1621 dnr_print_func(list.list[ii], user);
1623 reply("CSMSG_MORE_DNRS", list.used - ii);
1628 struct do_not_register *
1629 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1631 struct dnrList list;
1632 struct do_not_register *dnr;
1634 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1635 dnr = list.used ? list.list[0] : NULL;
1640 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1642 struct do_not_register *dnr;
1643 dict_iterator_t it, next;
1644 unsigned int matches = 0;
1646 for(it = dict_first(dict); it; it = next)
1648 dnr = iter_data(it);
1649 next = iter_next(it);
1650 if(dnr->expires && dnr->expires <= now)
1652 dict_remove(dict, iter_key(it));
1655 dnr_print_func(dnr, user);
1662 static CHANSERV_FUNC(cmd_noregister)
1666 unsigned long expiry, duration;
1667 unsigned int matches;
1671 reply("CSMSG_DNR_SEARCH_RESULTS");
1672 matches = send_dnrs(user, handle_dnrs);
1673 matches += send_dnrs(user, plain_dnrs);
1674 matches += send_dnrs(user, mask_dnrs);
1676 reply("MSG_MATCH_COUNT", matches);
1678 reply("MSG_NO_MATCHES");
1684 if(!IsChannelName(target) && (*target != '*'))
1686 reply("CSMSG_NOT_DNR", target);
1694 reply("MSG_INVALID_DURATION", argv[2]);
1698 if(!strcmp(argv[2], "0"))
1700 else if((duration = ParseInterval(argv[2])))
1701 expiry = now + duration;
1704 reply("MSG_INVALID_DURATION", argv[2]);
1708 reason = unsplit_string(argv + 3, argc - 3, NULL);
1709 if((*target == '*') && !get_handle_info(target + 1))
1711 reply("MSG_HANDLE_UNKNOWN", target + 1);
1714 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1715 reply("CSMSG_NOREGISTER_CHANNEL", target);
1719 reply("CSMSG_DNR_SEARCH_RESULTS");
1721 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1723 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1725 reply("MSG_NO_MATCHES");
1729 static CHANSERV_FUNC(cmd_allowregister)
1731 const char *chan_name = argv[1];
1733 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1734 || dict_remove(plain_dnrs, chan_name)
1735 || dict_remove(mask_dnrs, chan_name))
1737 reply("CSMSG_DNR_REMOVED", chan_name);
1740 reply("CSMSG_NO_SUCH_DNR", chan_name);
1745 struct userNode *source;
1749 unsigned long min_set, max_set;
1750 unsigned long min_expires, max_expires;
1755 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1757 return !((dnr->set < search->min_set)
1758 || (dnr->set > search->max_set)
1759 || (dnr->expires < search->min_expires)
1760 || (search->max_expires
1761 && ((dnr->expires == 0)
1762 || (dnr->expires > search->max_expires)))
1763 || (search->chan_mask
1764 && !match_ircglob(dnr->chan_name, search->chan_mask))
1765 || (search->setter_mask
1766 && !match_ircglob(dnr->setter, search->setter_mask))
1767 || (search->reason_mask
1768 && !match_ircglob(dnr->reason, search->reason_mask)));
1771 static struct dnr_search *
1772 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1774 struct dnr_search *discrim;
1777 discrim = calloc(1, sizeof(*discrim));
1778 discrim->source = user;
1779 discrim->chan_mask = NULL;
1780 discrim->setter_mask = NULL;
1781 discrim->reason_mask = NULL;
1782 discrim->max_set = INT_MAX;
1783 discrim->limit = 50;
1785 for(ii=0; ii<argc; ++ii)
1789 reply("MSG_MISSING_PARAMS", argv[ii]);
1792 else if(0 == irccasecmp(argv[ii], "channel"))
1794 discrim->chan_mask = argv[++ii];
1796 else if(0 == irccasecmp(argv[ii], "setter"))
1798 discrim->setter_mask = argv[++ii];
1800 else if(0 == irccasecmp(argv[ii], "reason"))
1802 discrim->reason_mask = argv[++ii];
1804 else if(0 == irccasecmp(argv[ii], "limit"))
1806 discrim->limit = strtoul(argv[++ii], NULL, 0);
1808 else if(0 == irccasecmp(argv[ii], "set"))
1810 const char *cmp = argv[++ii];
1813 discrim->min_set = now - ParseInterval(cmp + 2);
1815 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1816 } else if(cmp[0] == '=') {
1817 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1818 } else if(cmp[0] == '>') {
1820 discrim->max_set = now - ParseInterval(cmp + 2);
1822 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1824 discrim->max_set = now - (ParseInterval(cmp) - 1);
1827 else if(0 == irccasecmp(argv[ii], "expires"))
1829 const char *cmp = argv[++ii];
1832 discrim->max_expires = now + ParseInterval(cmp + 2);
1834 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1835 } else if(cmp[0] == '=') {
1836 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1837 } else if(cmp[0] == '>') {
1839 discrim->min_expires = now + ParseInterval(cmp + 2);
1841 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1843 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1848 reply("MSG_INVALID_CRITERIA", argv[ii]);
1859 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1862 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1864 struct do_not_register *dnr;
1865 dict_iterator_t next;
1870 /* Initialize local variables. */
1873 if(discrim->chan_mask)
1875 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1876 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1880 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1882 /* Check against account-based DNRs. */
1883 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1884 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1887 else if(target_fixed)
1889 /* Check against channel-based DNRs. */
1890 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1891 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1896 /* Exhaustively search account DNRs. */
1897 for(it = dict_first(handle_dnrs); it; it = next)
1899 next = iter_next(it);
1900 dnr = iter_data(it);
1901 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1905 /* Do the same for channel DNRs. */
1906 for(it = dict_first(plain_dnrs); it; it = next)
1908 next = iter_next(it);
1909 dnr = iter_data(it);
1910 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1914 /* Do the same for wildcarded channel DNRs. */
1915 for(it = dict_first(mask_dnrs); it; it = next)
1917 next = iter_next(it);
1918 dnr = iter_data(it);
1919 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1927 dnr_remove_func(struct do_not_register *match, void *extra)
1929 struct userNode *user;
1932 chan_name = alloca(strlen(match->chan_name) + 1);
1933 strcpy(chan_name, match->chan_name);
1935 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1936 || dict_remove(plain_dnrs, chan_name)
1937 || dict_remove(mask_dnrs, chan_name))
1939 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
1945 dnr_count_func(struct do_not_register *match, void *extra)
1947 return 0; (void)match; (void)extra;
1950 static MODCMD_FUNC(cmd_dnrsearch)
1952 struct dnr_search *discrim;
1953 dnr_search_func action;
1954 struct svccmd *subcmd;
1955 unsigned int matches;
1958 sprintf(buf, "dnrsearch %s", argv[1]);
1959 subcmd = dict_find(cmd->parent->commands, buf, NULL);
1962 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1965 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
1967 if(!irccasecmp(argv[1], "print"))
1968 action = dnr_print_func;
1969 else if(!irccasecmp(argv[1], "remove"))
1970 action = dnr_remove_func;
1971 else if(!irccasecmp(argv[1], "count"))
1972 action = dnr_count_func;
1975 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
1979 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
1983 if(action == dnr_print_func)
1984 reply("CSMSG_DNR_SEARCH_RESULTS");
1985 matches = dnr_search(discrim, action, user);
1987 reply("MSG_MATCH_COUNT", matches);
1989 reply("MSG_NO_MATCHES");
1995 chanserv_get_owned_count(struct handle_info *hi)
1997 struct userData *cList;
2000 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2001 if(cList->access == UL_OWNER)
2006 static CHANSERV_FUNC(cmd_register)
2008 struct handle_info *handle;
2009 struct chanData *cData;
2010 struct modeNode *mn;
2011 char reason[MAXLEN];
2013 unsigned int new_channel, force=0;
2014 struct do_not_register *dnr;
2018 if(channel->channel_info)
2020 reply("CSMSG_ALREADY_REGGED", channel->name);
2024 if(channel->bad_channel)
2026 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2031 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2033 reply("CSMSG_MUST_BE_OPPED", channel->name);
2038 chan_name = channel->name;
2042 if((argc < 2) || !IsChannelName(argv[1]))
2044 reply("MSG_NOT_CHANNEL_NAME");
2048 if(opserv_bad_channel(argv[1]))
2050 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2055 chan_name = argv[1];
2058 if(argc >= (new_channel+2))
2060 if(!IsHelping(user))
2062 reply("CSMSG_PROXY_FORBIDDEN");
2066 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2068 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2069 dnr = chanserv_is_dnr(chan_name, handle);
2073 handle = user->handle_info;
2074 dnr = chanserv_is_dnr(chan_name, handle);
2078 if(!IsHelping(user))
2079 reply("CSMSG_DNR_CHANNEL", chan_name);
2081 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2085 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2087 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2092 channel = AddChannel(argv[1], now, NULL, NULL);
2094 cData = register_channel(channel, user->handle_info->handle);
2095 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2096 cData->modes = chanserv_conf.default_modes;
2098 cData->modes.modes_set |= MODE_REGISTERED;
2099 if (IsOffChannel(cData))
2101 mod_chanmode_announce(chanserv, channel, &cData->modes);
2105 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2106 change->args[change->argc].mode = MODE_CHANOP;
2107 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2109 mod_chanmode_announce(chanserv, channel, change);
2110 mod_chanmode_free(change);
2113 /* Initialize the channel's max user record. */
2114 cData->max = channel->members.used;
2115 cData->max_time = 0;
2117 if(handle != user->handle_info)
2118 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2120 reply("CSMSG_REG_SUCCESS", channel->name);
2122 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2123 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2128 make_confirmation_string(struct userData *uData)
2130 static char strbuf[16];
2135 for(src = uData->handle->handle; *src; )
2136 accum = accum * 31 + toupper(*src++);
2138 for(src = uData->channel->channel->name; *src; )
2139 accum = accum * 31 + toupper(*src++);
2140 sprintf(strbuf, "%08x", accum);
2144 static CHANSERV_FUNC(cmd_unregister)
2147 char reason[MAXLEN];
2148 struct chanData *cData;
2149 struct userData *uData;
2151 cData = channel->channel_info;
2154 reply("CSMSG_NOT_REGISTERED", channel->name);
2158 uData = GetChannelUser(cData, user->handle_info);
2159 if(!uData || (uData->access < UL_OWNER))
2161 reply("CSMSG_NO_ACCESS");
2165 if(IsProtected(cData))
2167 reply("CSMSG_UNREG_NODELETE", channel->name);
2171 if(!IsHelping(user))
2173 const char *confirm_string;
2174 if(IsSuspended(cData))
2176 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2179 confirm_string = make_confirmation_string(uData);
2180 if((argc < 2) || strcmp(argv[1], confirm_string))
2182 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2187 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2188 name = strdup(channel->name);
2189 unregister_channel(cData, reason);
2190 spamserv_cs_unregister(user, channel, manually, "unregistered");
2191 reply("CSMSG_UNREG_SUCCESS", name);
2197 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2199 extern struct userNode *spamserv;
2200 struct mod_chanmode *change;
2202 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2204 change = mod_chanmode_alloc(2);
2206 change->args[0].mode = MODE_CHANOP;
2207 change->args[0].u.member = AddChannelUser(chanserv, channel);
2208 change->args[1].mode = MODE_CHANOP;
2209 change->args[1].u.member = AddChannelUser(spamserv, channel);
2213 change = mod_chanmode_alloc(1);
2215 change->args[0].mode = MODE_CHANOP;
2216 change->args[0].u.member = AddChannelUser(chanserv, channel);
2219 mod_chanmode_announce(chanserv, channel, change);
2220 mod_chanmode_free(change);
2223 static CHANSERV_FUNC(cmd_move)
2225 struct mod_chanmode change;
2226 struct chanNode *target;
2227 struct modeNode *mn;
2228 struct userData *uData;
2229 char reason[MAXLEN];
2230 struct do_not_register *dnr;
2231 int chanserv_join = 0, spamserv_join;
2235 if(IsProtected(channel->channel_info))
2237 reply("CSMSG_MOVE_NODELETE", channel->name);
2241 if(!IsChannelName(argv[1]))
2243 reply("MSG_NOT_CHANNEL_NAME");
2247 if(opserv_bad_channel(argv[1]))
2249 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2253 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2255 for(uData = channel->channel_info->users; uData; uData = uData->next)
2257 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2259 if(!IsHelping(user))
2260 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2262 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2268 mod_chanmode_init(&change);
2269 if(!(target = GetChannel(argv[1])))
2271 target = AddChannel(argv[1], now, NULL, NULL);
2272 if(!IsSuspended(channel->channel_info))
2275 else if(target->channel_info)
2277 reply("CSMSG_ALREADY_REGGED", target->name);
2280 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2281 && !IsHelping(user))
2283 reply("CSMSG_MUST_BE_OPPED", target->name);
2286 else if(!IsSuspended(channel->channel_info))
2291 /* Clear MODE_REGISTERED from old channel, add it to new. */
2293 change.modes_clear = MODE_REGISTERED;
2294 mod_chanmode_announce(chanserv, channel, &change);
2295 change.modes_clear = 0;
2296 change.modes_set = MODE_REGISTERED;
2297 mod_chanmode_announce(chanserv, target, &change);
2300 /* Move the channel_info to the target channel; it
2301 shouldn't be necessary to clear timeq callbacks
2302 for the old channel. */
2303 target->channel_info = channel->channel_info;
2304 target->channel_info->channel = target;
2305 channel->channel_info = NULL;
2307 /* Check whether users are present in the new channel. */
2308 for(uData = target->channel_info->users; uData; uData = uData->next)
2309 scan_user_presence(uData, NULL);
2311 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2314 ss_cs_join_channel(target, spamserv_join);
2316 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2317 if(!IsSuspended(target->channel_info))
2319 char reason2[MAXLEN];
2320 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2321 DelChannelUser(chanserv, channel, reason2, 0);
2323 UnlockChannel(channel);
2324 LockChannel(target);
2325 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2326 reply("CSMSG_MOVE_SUCCESS", target->name);
2331 merge_users(struct chanData *source, struct chanData *target)
2333 struct userData *suData, *tuData, *next;
2339 /* Insert the source's users into the scratch area. */
2340 for(suData = source->users; suData; suData = suData->next)
2341 dict_insert(merge, suData->handle->handle, suData);
2343 /* Iterate through the target's users, looking for
2344 users common to both channels. The lower access is
2345 removed from either the scratch area or target user
2347 for(tuData = target->users; tuData; tuData = next)
2349 struct userData *choice;
2351 next = tuData->next;
2353 /* If a source user exists with the same handle as a target
2354 channel's user, resolve the conflict by removing one. */
2355 suData = dict_find(merge, tuData->handle->handle, NULL);
2359 /* Pick the data we want to keep. */
2360 /* If the access is the same, use the later seen time. */
2361 if(suData->access == tuData->access)
2362 choice = (suData->seen > tuData->seen) ? suData : tuData;
2363 else /* Otherwise, keep the higher access level. */
2364 choice = (suData->access > tuData->access) ? suData : tuData;
2365 /* Use the later seen time. */
2366 if(suData->seen < tuData->seen)
2367 suData->seen = tuData->seen;
2369 tuData->seen = suData->seen;
2371 /* Remove the user that wasn't picked. */
2372 if(choice == tuData)
2374 dict_remove(merge, suData->handle->handle);
2375 del_channel_user(suData, 0);
2378 del_channel_user(tuData, 0);
2381 /* Move the remaining users to the target channel. */
2382 for(it = dict_first(merge); it; it = iter_next(it))
2384 suData = iter_data(it);
2386 /* Insert the user into the target channel's linked list. */
2387 suData->prev = NULL;
2388 suData->next = target->users;
2389 suData->channel = target;
2392 target->users->prev = suData;
2393 target->users = suData;
2395 /* Update the user counts for the target channel; the
2396 source counts are left alone. */
2397 target->userCount++;
2399 /* Check whether the user is in the target channel. */
2400 scan_user_presence(suData, NULL);
2403 /* Possible to assert (source->users == NULL) here. */
2404 source->users = NULL;
2409 merge_bans(struct chanData *source, struct chanData *target)
2411 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2413 /* Hold on to the original head of the target ban list
2414 to avoid comparing source bans with source bans. */
2415 tFront = target->bans;
2417 /* Perform a totally expensive O(n*m) merge, ick. */
2418 for(sbData = source->bans; sbData; sbData = sNext)
2420 /* Flag to track whether the ban's been moved
2421 to the destination yet. */
2424 /* Possible to assert (sbData->prev == NULL) here. */
2425 sNext = sbData->next;
2427 for(tbData = tFront; tbData; tbData = tNext)
2429 tNext = tbData->next;
2431 /* Perform two comparisons between each source
2432 and target ban, conflicts are resolved by
2433 keeping the broader ban and copying the later
2434 expiration and triggered time. */
2435 if(match_ircglobs(tbData->mask, sbData->mask))
2437 /* There is a broader ban in the target channel that
2438 overrides one in the source channel; remove the
2439 source ban and break. */
2440 if(sbData->expires > tbData->expires)
2441 tbData->expires = sbData->expires;
2442 if(sbData->triggered > tbData->triggered)
2443 tbData->triggered = sbData->triggered;
2444 del_channel_ban(sbData);
2447 else if(match_ircglobs(sbData->mask, tbData->mask))
2449 /* There is a broader ban in the source channel that
2450 overrides one in the target channel; remove the
2451 target ban, fall through and move the source over. */
2452 if(tbData->expires > sbData->expires)
2453 sbData->expires = tbData->expires;
2454 if(tbData->triggered > sbData->triggered)
2455 sbData->triggered = tbData->triggered;
2456 if(tbData == tFront)
2458 del_channel_ban(tbData);
2461 /* Source bans can override multiple target bans, so
2462 we allow a source to run through this loop multiple
2463 times, but we can only move it once. */
2468 /* Remove the source ban from the source ban list. */
2470 sbData->next->prev = sbData->prev;
2472 /* Modify the source ban's associated channel. */
2473 sbData->channel = target;
2475 /* Insert the ban into the target channel's linked list. */
2476 sbData->prev = NULL;
2477 sbData->next = target->bans;
2480 target->bans->prev = sbData;
2481 target->bans = sbData;
2483 /* Update the user counts for the target channel. */
2488 /* Possible to assert (source->bans == NULL) here. */
2489 source->bans = NULL;
2493 merge_data(struct chanData *source, struct chanData *target)
2495 /* Use more recent visited and owner-transfer time; use older
2496 * registered time. Bitwise or may_opchan. Use higher max.
2497 * Do not touch last_refresh, ban count or user counts.
2499 if(source->visited > target->visited)
2500 target->visited = source->visited;
2501 if(source->registered < target->registered)
2502 target->registered = source->registered;
2503 if(source->ownerTransfer > target->ownerTransfer)
2504 target->ownerTransfer = source->ownerTransfer;
2505 if(source->may_opchan)
2506 target->may_opchan = 1;
2507 if(source->max > target->max) {
2508 target->max = source->max;
2509 target->max_time = source->max_time;
2514 merge_channel(struct chanData *source, struct chanData *target)
2516 merge_users(source, target);
2517 merge_bans(source, target);
2518 merge_data(source, target);
2521 static CHANSERV_FUNC(cmd_merge)
2523 struct userData *target_user;
2524 struct chanNode *target;
2525 char reason[MAXLEN];
2529 /* Make sure the target channel exists and is registered to the user
2530 performing the command. */
2531 if(!(target = GetChannel(argv[1])))
2533 reply("MSG_INVALID_CHANNEL");
2537 if(!target->channel_info)
2539 reply("CSMSG_NOT_REGISTERED", target->name);
2543 if(IsProtected(channel->channel_info))
2545 reply("CSMSG_MERGE_NODELETE");
2549 if(IsSuspended(target->channel_info))
2551 reply("CSMSG_MERGE_SUSPENDED");
2555 if(channel == target)
2557 reply("CSMSG_MERGE_SELF");
2561 target_user = GetChannelUser(target->channel_info, user->handle_info);
2562 if(!target_user || (target_user->access < UL_OWNER))
2564 reply("CSMSG_MERGE_NOT_OWNER");
2568 /* Merge the channel structures and associated data. */
2569 merge_channel(channel->channel_info, target->channel_info);
2570 spamserv_cs_move_merge(user, channel, target, 0);
2571 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2572 unregister_channel(channel->channel_info, reason);
2573 reply("CSMSG_MERGE_SUCCESS", target->name);
2577 static CHANSERV_FUNC(cmd_opchan)
2579 struct mod_chanmode change;
2580 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2582 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2585 channel->channel_info->may_opchan = 0;
2586 mod_chanmode_init(&change);
2588 change.args[0].mode = MODE_CHANOP;
2589 change.args[0].u.member = GetUserMode(channel, chanserv);
2590 if(!change.args[0].u.member)
2592 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2595 mod_chanmode_announce(chanserv, channel, &change);
2596 reply("CSMSG_OPCHAN_DONE", channel->name);
2600 static CHANSERV_FUNC(cmd_adduser)
2602 struct userData *actee;
2603 struct userData *actor, *real_actor;
2604 struct handle_info *handle;
2605 unsigned short access_level, override = 0;
2609 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2611 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2615 access_level = user_level_from_name(argv[2], UL_OWNER);
2618 reply("CSMSG_INVALID_ACCESS", argv[2]);
2622 actor = GetChannelUser(channel->channel_info, user->handle_info);
2623 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2625 if(actor->access <= access_level)
2627 reply("CSMSG_NO_BUMP_ACCESS");
2631 /* Trying to add someone with equal/more access? */
2632 if (!real_actor || real_actor->access <= access_level)
2633 override = CMD_LOG_OVERRIDE;
2635 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2638 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2640 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2644 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2645 scan_user_presence(actee, NULL);
2646 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2647 return 1 | override;
2650 static CHANSERV_FUNC(cmd_clvl)
2652 struct handle_info *handle;
2653 struct userData *victim;
2654 struct userData *actor, *real_actor;
2655 unsigned short new_access, override = 0;
2656 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2660 actor = GetChannelUser(channel->channel_info, user->handle_info);
2661 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2663 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2666 if(handle == user->handle_info && !privileged)
2668 reply("CSMSG_NO_SELF_CLVL");
2672 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2674 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2678 if(actor->access <= victim->access && !privileged)
2680 reply("MSG_USER_OUTRANKED", handle->handle);
2684 new_access = user_level_from_name(argv[2], UL_OWNER);
2688 reply("CSMSG_INVALID_ACCESS", argv[2]);
2692 if(new_access >= actor->access && !privileged)
2694 reply("CSMSG_NO_BUMP_ACCESS");
2698 /* Trying to clvl a equal/higher user? */
2699 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2700 override = CMD_LOG_OVERRIDE;
2701 /* Trying to clvl someone to equal/higher access? */
2702 if(!real_actor || new_access >= real_actor->access)
2703 override = CMD_LOG_OVERRIDE;
2704 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2705 * If they lower their own access it's not a big problem.
2708 victim->access = new_access;
2709 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2710 return 1 | override;
2713 static CHANSERV_FUNC(cmd_deluser)
2715 struct handle_info *handle;
2716 struct userData *victim;
2717 struct userData *actor, *real_actor;
2718 unsigned short access_level, override = 0;
2723 actor = GetChannelUser(channel->channel_info, user->handle_info);
2724 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2726 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2729 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2731 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2737 access_level = user_level_from_name(argv[1], UL_OWNER);
2740 reply("CSMSG_INVALID_ACCESS", argv[1]);
2743 if(access_level != victim->access)
2745 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2751 access_level = victim->access;
2754 if((actor->access <= victim->access) && !IsHelping(user))
2756 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2760 /* If people delete themselves it is an override, but they
2761 * could've used deleteme so we don't log it as an override
2763 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2764 override = CMD_LOG_OVERRIDE;
2766 chan_name = strdup(channel->name);
2767 del_channel_user(victim, 1);
2768 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2770 return 1 | override;
2774 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2776 struct userData *actor, *real_actor, *uData, *next;
2777 unsigned int override = 0;
2779 actor = GetChannelUser(channel->channel_info, user->handle_info);
2780 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2782 if(min_access > max_access)
2784 reply("CSMSG_BAD_RANGE", min_access, max_access);
2788 if(actor->access <= max_access)
2790 reply("CSMSG_NO_ACCESS");
2794 if(!real_actor || real_actor->access <= max_access)
2795 override = CMD_LOG_OVERRIDE;
2797 for(uData = channel->channel_info->users; uData; uData = next)
2801 if((uData->access >= min_access)
2802 && (uData->access <= max_access)
2803 && match_ircglob(uData->handle->handle, mask))
2804 del_channel_user(uData, 1);
2807 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2808 return 1 | override;
2811 static CHANSERV_FUNC(cmd_mdelowner)
2813 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2816 static CHANSERV_FUNC(cmd_mdelcoowner)
2818 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2821 static CHANSERV_FUNC(cmd_mdelmaster)
2823 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2826 static CHANSERV_FUNC(cmd_mdelop)
2828 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2831 static CHANSERV_FUNC(cmd_mdelpeon)
2833 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2837 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2839 struct banData *bData, *next;
2840 char interval[INTERVALLEN];
2842 unsigned long limit;
2845 limit = now - duration;
2846 for(bData = channel->channel_info->bans; bData; bData = next)
2850 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2853 del_channel_ban(bData);
2857 intervalString(interval, duration, user->handle_info);
2858 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2863 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2865 struct userData *actor, *uData, *next;
2866 char interval[INTERVALLEN];
2868 unsigned long limit;
2870 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2871 if(min_access > max_access)
2873 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2877 if(!actor || actor->access <= max_access)
2879 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2884 limit = now - duration;
2885 for(uData = channel->channel_info->users; uData; uData = next)
2889 if((uData->seen > limit)
2891 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2894 if(((uData->access >= min_access) && (uData->access <= max_access))
2895 || (!max_access && (uData->access < actor->access)))
2897 del_channel_user(uData, 1);
2905 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2907 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2911 static CHANSERV_FUNC(cmd_trim)
2913 unsigned long duration;
2914 unsigned short min_level, max_level;
2919 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2920 duration = ParseInterval(argv[2]);
2923 reply("CSMSG_CANNOT_TRIM");
2927 if(!irccasecmp(argv[1], "bans"))
2929 cmd_trim_bans(user, channel, duration);
2932 else if(!irccasecmp(argv[1], "users"))
2934 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2937 else if(parse_level_range(&min_level, &max_level, argv[1]))
2939 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
2942 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
2944 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
2949 reply("CSMSG_INVALID_TRIM", argv[1]);
2954 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
2955 to the user. cmd_all takes advantage of this. */
2956 static CHANSERV_FUNC(cmd_up)
2958 struct mod_chanmode change;
2959 struct userData *uData;
2962 mod_chanmode_init(&change);
2964 change.args[0].u.member = GetUserMode(channel, user);
2965 if(!change.args[0].u.member)
2968 reply("MSG_CHANNEL_ABSENT", channel->name);
2972 uData = GetChannelAccess(channel->channel_info, user->handle_info);
2976 reply("CSMSG_GODMODE_UP", argv[0]);
2979 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
2981 change.args[0].mode = MODE_CHANOP;
2982 errmsg = "CSMSG_ALREADY_OPPED";
2984 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
2986 change.args[0].mode = MODE_VOICE;
2987 errmsg = "CSMSG_ALREADY_VOICED";
2992 reply("CSMSG_NO_ACCESS");
2995 change.args[0].mode &= ~change.args[0].u.member->modes;
2996 if(!change.args[0].mode)
2999 reply(errmsg, channel->name);
3002 modcmd_chanmode_announce(&change);
3006 static CHANSERV_FUNC(cmd_down)
3008 struct mod_chanmode change;
3010 mod_chanmode_init(&change);
3012 change.args[0].u.member = GetUserMode(channel, user);
3013 if(!change.args[0].u.member)
3016 reply("MSG_CHANNEL_ABSENT", channel->name);
3020 if(!change.args[0].u.member->modes)
3023 reply("CSMSG_ALREADY_DOWN", channel->name);
3027 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3028 modcmd_chanmode_announce(&change);
3032 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)
3034 struct userData *cList;
3036 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3038 if(IsSuspended(cList->channel)
3039 || IsUserSuspended(cList)
3040 || !GetUserMode(cList->channel->channel, user))
3043 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3049 static CHANSERV_FUNC(cmd_upall)
3051 return cmd_all(CSFUNC_ARGS, cmd_up);
3054 static CHANSERV_FUNC(cmd_downall)
3056 return cmd_all(CSFUNC_ARGS, cmd_down);
3059 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3060 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3063 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)
3065 unsigned int ii, valid;
3066 struct userNode *victim;
3067 struct mod_chanmode *change;
3069 change = mod_chanmode_alloc(argc - 1);
3071 for(ii=valid=0; ++ii < argc; )
3073 if(!(victim = GetUserH(argv[ii])))
3075 change->args[valid].mode = mode;
3076 change->args[valid].u.member = GetUserMode(channel, victim);
3077 if(!change->args[valid].u.member)
3079 if(validate && !validate(user, channel, victim))
3084 change->argc = valid;
3085 if(valid < (argc-1))
3086 reply("CSMSG_PROCESS_FAILED");
3089 modcmd_chanmode_announce(change);
3090 reply(action, channel->name);
3092 mod_chanmode_free(change);
3096 static CHANSERV_FUNC(cmd_op)
3098 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3101 static CHANSERV_FUNC(cmd_deop)
3103 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3106 static CHANSERV_FUNC(cmd_voice)
3108 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3111 static CHANSERV_FUNC(cmd_devoice)
3113 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3117 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3123 for(ii=0; ii<channel->members.used; ii++)
3125 struct modeNode *mn = channel->members.list[ii];
3127 if(IsService(mn->user))
3130 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3133 if(protect_user(mn->user, user, channel->channel_info))
3137 victims[(*victimCount)++] = mn;
3143 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3145 struct userNode *victim;
3146 struct modeNode **victims;
3147 unsigned int offset, n, victimCount, duration = 0;
3148 char *reason = "Bye.", *ban, *name;
3149 char interval[INTERVALLEN];
3151 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3152 REQUIRE_PARAMS(offset);
3155 reason = unsplit_string(argv + offset, argc - offset, NULL);
3156 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3158 /* Truncate the reason to a length of TOPICLEN, as
3159 the ircd does; however, leave room for an ellipsis
3160 and the kicker's nick. */
3161 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3165 if((victim = GetUserH(argv[1])))
3167 victims = alloca(sizeof(victims[0]));
3168 victims[0] = GetUserMode(channel, victim);
3169 /* XXX: The comparison with ACTION_KICK is just because all
3170 * other actions can work on users outside the channel, and we
3171 * want to allow those (e.g. unbans) in that case. If we add
3172 * some other ejection action for in-channel users, change
3174 victimCount = victims[0] ? 1 : 0;
3176 if(IsService(victim))
3178 reply("MSG_SERVICE_IMMUNE", victim->nick);
3182 if((action == ACTION_KICK) && !victimCount)
3184 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3188 if(protect_user(victim, user, channel->channel_info))
3190 reply("CSMSG_USER_PROTECTED", victim->nick);
3194 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3195 name = victim->nick;
3197 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3199 struct handle_info *hi;
3200 extern const char *titlehost_suffix;
3201 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3202 const char *accountname = argv[1] + 1;
3204 if(!(hi = get_handle_info(accountname)))
3206 reply("MSG_HANDLE_UNKNOWN", accountname);
3210 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3211 victims = alloca(sizeof(victims[0]) * channel->members.used);
3213 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3215 reply("CSMSG_MASK_PROTECTED", banmask);
3219 if((action == ACTION_KICK) && (victimCount == 0))
3221 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3225 name = ban = strdup(banmask);
3229 if(!is_ircmask(argv[1]))
3231 reply("MSG_NICK_UNKNOWN", argv[1]);
3235 victims = alloca(sizeof(victims[0]) * channel->members.used);
3237 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3239 reply("CSMSG_MASK_PROTECTED", argv[1]);
3243 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3245 reply("CSMSG_LAME_MASK", argv[1]);
3249 if((action == ACTION_KICK) && (victimCount == 0))
3251 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3255 name = ban = strdup(argv[1]);
3258 /* Truncate the ban in place if necessary; we must ensure
3259 that 'ban' is a valid ban mask before sanitizing it. */
3260 sanitize_ircmask(ban);
3262 if(action & ACTION_ADD_BAN)
3264 struct banData *bData, *next;
3266 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3268 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3273 if(action & ACTION_ADD_TIMED_BAN)
3275 duration = ParseInterval(argv[2]);
3279 reply("CSMSG_DURATION_TOO_LOW");
3283 else if(duration > (86400 * 365 * 2))
3285 reply("CSMSG_DURATION_TOO_HIGH");
3291 for(bData = channel->channel_info->bans; bData; bData = next)
3293 if(match_ircglobs(bData->mask, ban))
3295 int exact = !irccasecmp(bData->mask, ban);
3297 /* The ban is redundant; there is already a ban
3298 with the same effect in place. */
3302 free(bData->reason);
3303 bData->reason = strdup(reason);
3304 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3306 reply("CSMSG_REASON_CHANGE", ban);
3310 if(exact && bData->expires)
3314 /* If the ban matches an existing one exactly,
3315 extend the expiration time if the provided
3316 duration is longer. */
3317 if(duration && (now + duration > bData->expires))
3319 bData->expires = now + duration;
3330 /* Delete the expiration timeq entry and
3331 requeue if necessary. */
3332 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3335 timeq_add(bData->expires, expire_ban, bData);
3339 /* automated kickban */
3342 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3344 reply("CSMSG_BAN_ADDED", name, channel->name);
3350 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3357 if(match_ircglobs(ban, bData->mask))
3359 /* The ban we are adding makes previously existing
3360 bans redundant; silently remove them. */
3361 del_channel_ban(bData);
3365 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);
3367 name = ban = strdup(bData->mask);
3371 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3373 extern const char *hidden_host_suffix;
3374 const char *old_name = chanserv_conf.old_ban_names->list[n];
3376 unsigned int l1, l2;
3379 l2 = strlen(old_name);
3382 if(irccasecmp(ban + l1 - l2, old_name))
3384 new_mask = malloc(MAXLEN);
3385 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3387 name = ban = new_mask;
3392 if(action & ACTION_BAN)
3394 unsigned int exists;
3395 struct mod_chanmode *change;
3397 if(channel->banlist.used >= MAXBANS)
3400 reply("CSMSG_BANLIST_FULL", channel->name);
3405 exists = ChannelBanExists(channel, ban);
3406 change = mod_chanmode_alloc(victimCount + 1);
3407 for(n = 0; n < victimCount; ++n)
3409 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3410 change->args[n].u.member = victims[n];
3414 change->args[n].mode = MODE_BAN;
3415 change->args[n++].u.hostmask = ban;
3419 modcmd_chanmode_announce(change);
3421 mod_chanmode_announce(chanserv, channel, change);
3422 mod_chanmode_free(change);
3424 if(exists && (action == ACTION_BAN))
3427 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3433 if(action & ACTION_KICK)
3435 char kick_reason[MAXLEN];
3436 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3438 for(n = 0; n < victimCount; n++)
3439 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3444 /* No response, since it was automated. */
3446 else if(action & ACTION_ADD_BAN)
3449 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3451 reply("CSMSG_BAN_ADDED", name, channel->name);
3453 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3454 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3455 else if(action & ACTION_BAN)
3456 reply("CSMSG_BAN_DONE", name, channel->name);
3457 else if(action & ACTION_KICK && victimCount)
3458 reply("CSMSG_KICK_DONE", name, channel->name);
3464 static CHANSERV_FUNC(cmd_kickban)
3466 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3469 static CHANSERV_FUNC(cmd_kick)
3471 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3474 static CHANSERV_FUNC(cmd_ban)
3476 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3479 static CHANSERV_FUNC(cmd_addban)
3481 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3484 static CHANSERV_FUNC(cmd_addtimedban)
3486 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3489 static struct mod_chanmode *
3490 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3492 struct mod_chanmode *change;
3493 unsigned char *match;
3494 unsigned int ii, count;
3496 match = alloca(bans->used);
3499 for(ii = count = 0; ii < bans->used; ++ii)
3501 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3502 MATCH_USENICK | MATCH_VISIBLE);
3509 for(ii = count = 0; ii < bans->used; ++ii)
3511 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3518 change = mod_chanmode_alloc(count);
3519 for(ii = count = 0; ii < bans->used; ++ii)
3523 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3524 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3526 assert(count == change->argc);
3531 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3533 struct userNode *actee;
3539 /* may want to allow a comma delimited list of users... */
3540 if(!(actee = GetUserH(argv[1])))
3542 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3544 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3545 const char *accountname = argv[1] + 1;
3547 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3548 mask = strdup(banmask);
3550 else if(!is_ircmask(argv[1]))
3552 reply("MSG_NICK_UNKNOWN", argv[1]);
3557 mask = strdup(argv[1]);
3561 /* We don't sanitize the mask here because ircu
3563 if(action & ACTION_UNBAN)
3565 struct mod_chanmode *change;
3566 change = find_matching_bans(&channel->banlist, actee, mask);
3571 modcmd_chanmode_announce(change);
3572 for(ii = 0; ii < change->argc; ++ii)
3573 free((char*)change->args[ii].u.hostmask);
3574 mod_chanmode_free(change);
3579 if(action & ACTION_DEL_BAN)
3581 struct banData *ban, *next;
3583 ban = channel->channel_info->bans;
3587 for( ; ban && !user_matches_glob(actee, ban->mask,
3588 MATCH_USENICK | MATCH_VISIBLE);
3591 for( ; ban && !match_ircglobs(mask, ban->mask);
3596 del_channel_ban(ban);
3603 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3605 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3611 static CHANSERV_FUNC(cmd_unban)
3613 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3616 static CHANSERV_FUNC(cmd_delban)
3618 /* it doesn't necessarily have to remove the channel ban - may want
3619 to make that an option. */
3620 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3623 static CHANSERV_FUNC(cmd_unbanme)
3625 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3626 long flags = ACTION_UNBAN;
3628 /* remove permanent bans if the user has the proper access. */
3629 if(uData->access >= UL_MASTER)
3630 flags |= ACTION_DEL_BAN;
3632 argv[1] = user->nick;
3633 return unban_user(user, channel, 2, argv, cmd, flags);
3636 static CHANSERV_FUNC(cmd_unbanall)
3638 struct mod_chanmode *change;
3641 if(!channel->banlist.used)
3643 reply("CSMSG_NO_BANS", channel->name);
3647 change = mod_chanmode_alloc(channel->banlist.used);
3648 for(ii=0; ii<channel->banlist.used; ii++)
3650 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3651 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3653 modcmd_chanmode_announce(change);
3654 for(ii = 0; ii < change->argc; ++ii)
3655 free((char*)change->args[ii].u.hostmask);
3656 mod_chanmode_free(change);
3657 reply("CSMSG_BANS_REMOVED", channel->name);
3661 static CHANSERV_FUNC(cmd_open)
3663 struct mod_chanmode *change;
3666 change = find_matching_bans(&channel->banlist, user, NULL);
3668 change = mod_chanmode_alloc(0);
3669 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3670 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3671 && channel->channel_info->modes.modes_set)
3672 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3673 modcmd_chanmode_announce(change);
3674 reply("CSMSG_CHANNEL_OPENED", channel->name);
3675 for(ii = 0; ii < change->argc; ++ii)
3676 free((char*)change->args[ii].u.hostmask);
3677 mod_chanmode_free(change);
3681 static CHANSERV_FUNC(cmd_myaccess)
3683 static struct string_buffer sbuf;
3684 struct handle_info *target_handle;
3685 struct userData *uData;
3688 target_handle = user->handle_info;
3689 else if(!IsStaff(user))
3691 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3694 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3697 if(!oper_outranks(user, target_handle))
3700 if(!target_handle->channels)
3702 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3706 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3707 for(uData = target_handle->channels; uData; uData = uData->u_next)
3709 struct chanData *cData = uData->channel;
3711 if(uData->access > UL_OWNER)
3713 if(IsProtected(cData)
3714 && (target_handle != user->handle_info)
3715 && !GetTrueChannelAccess(cData, user->handle_info))
3718 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3719 if(uData->flags != USER_AUTO_OP)
3720 string_buffer_append(&sbuf, ',');
3721 if(IsUserSuspended(uData))
3722 string_buffer_append(&sbuf, 's');
3723 if(IsUserAutoOp(uData))
3725 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3726 string_buffer_append(&sbuf, 'o');
3727 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3728 string_buffer_append(&sbuf, 'v');
3730 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3731 string_buffer_append(&sbuf, 'i');
3733 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3735 string_buffer_append_string(&sbuf, ")]");
3736 string_buffer_append(&sbuf, '\0');
3737 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3743 static CHANSERV_FUNC(cmd_access)
3745 struct userNode *target;
3746 struct handle_info *target_handle;
3747 struct userData *uData;
3749 char prefix[MAXLEN];
3754 target_handle = target->handle_info;
3756 else if((target = GetUserH(argv[1])))
3758 target_handle = target->handle_info;
3760 else if(argv[1][0] == '*')
3762 if(!(target_handle = get_handle_info(argv[1]+1)))
3764 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3770 reply("MSG_NICK_UNKNOWN", argv[1]);
3774 assert(target || target_handle);
3776 if(target == chanserv)
3778 reply("CSMSG_IS_CHANSERV");
3786 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3791 reply("MSG_USER_AUTHENTICATE", target->nick);
3794 reply("MSG_AUTHENTICATE");
3800 const char *epithet = NULL, *type = NULL;
3803 epithet = chanserv_conf.irc_operator_epithet;
3804 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3806 else if(IsNetworkHelper(target))
3808 epithet = chanserv_conf.network_helper_epithet;
3809 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3811 else if(IsSupportHelper(target))
3813 epithet = chanserv_conf.support_helper_epithet;
3814 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3818 if(target_handle->epithet)
3819 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3821 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3823 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3827 sprintf(prefix, "%s", target_handle->handle);
3830 if(!channel->channel_info)
3832 reply("CSMSG_NOT_REGISTERED", channel->name);
3836 helping = HANDLE_FLAGGED(target_handle, HELPING)
3837 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3838 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3840 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3841 /* To prevent possible information leaks, only show infolines
3842 * if the requestor is in the channel or it's their own
3844 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3846 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3848 /* Likewise, only say it's suspended if the user has active
3849 * access in that channel or it's their own entry. */
3850 if(IsUserSuspended(uData)
3851 && (GetChannelUser(channel->channel_info, user->handle_info)
3852 || (user->handle_info == uData->handle)))
3854 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3859 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3866 zoot_list(struct listData *list)
3868 struct userData *uData;
3869 unsigned int start, curr, highest, lowest;
3870 struct helpfile_table tmp_table;
3871 const char **temp, *msg;
3873 if(list->table.length == 1)
3876 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3878 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3879 msg = user_find_message(list->user, "MSG_NONE");
3880 send_message_type(4, list->user, list->bot, " %s", msg);
3882 tmp_table.width = list->table.width;
3883 tmp_table.flags = list->table.flags;
3884 list->table.contents[0][0] = " ";
3885 highest = list->highest;
3886 if(list->lowest != 0)
3887 lowest = list->lowest;
3888 else if(highest < 100)
3891 lowest = highest - 100;
3892 for(start = curr = 1; curr < list->table.length; )
3894 uData = list->users[curr-1];
3895 list->table.contents[curr++][0] = " ";
3896 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3899 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3901 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3902 temp = list->table.contents[--start];
3903 list->table.contents[start] = list->table.contents[0];
3904 tmp_table.contents = list->table.contents + start;
3905 tmp_table.length = curr - start;
3906 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3907 list->table.contents[start] = temp;
3909 highest = lowest - 1;
3910 lowest = (highest < 100) ? 0 : (highest - 99);
3916 def_list(struct listData *list)
3920 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3922 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3923 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3924 if(list->table.length == 1)
3926 msg = user_find_message(list->user, "MSG_NONE");
3927 send_message_type(4, list->user, list->bot, " %s", msg);
3932 userData_access_comp(const void *arg_a, const void *arg_b)
3934 const struct userData *a = *(struct userData**)arg_a;
3935 const struct userData *b = *(struct userData**)arg_b;
3937 if(a->access != b->access)
3938 res = b->access - a->access;
3940 res = irccasecmp(a->handle->handle, b->handle->handle);
3945 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
3947 void (*send_list)(struct listData *);
3948 struct userData *uData;
3949 struct listData lData;
3950 unsigned int matches;
3954 lData.bot = cmd->parent->bot;
3955 lData.channel = channel;
3956 lData.lowest = lowest;
3957 lData.highest = highest;
3958 lData.search = (argc > 1) ? argv[1] : NULL;
3959 send_list = def_list;
3960 (void)zoot_list; /* since it doesn't show user levels */
3962 if(user->handle_info)
3964 switch(user->handle_info->userlist_style)
3966 case HI_STYLE_DEF: send_list = def_list; break;
3967 case HI_STYLE_ZOOT: send_list = def_list; break;
3971 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
3973 for(uData = channel->channel_info->users; uData; uData = uData->next)
3975 if((uData->access < lowest)
3976 || (uData->access > highest)
3977 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
3979 lData.users[matches++] = uData;
3981 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
3983 lData.table.length = matches+1;
3984 lData.table.width = 4;
3985 lData.table.flags = TABLE_NO_FREE;
3986 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
3987 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3988 lData.table.contents[0] = ary;
3991 ary[2] = "Last Seen";
3993 for(matches = 1; matches < lData.table.length; ++matches)
3995 char seen[INTERVALLEN];
3997 uData = lData.users[matches-1];
3998 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
3999 lData.table.contents[matches] = ary;
4000 ary[0] = strtab(uData->access);
4001 ary[1] = uData->handle->handle;
4004 else if(!uData->seen)
4007 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4008 ary[2] = strdup(ary[2]);
4009 if(IsUserSuspended(uData))
4010 ary[3] = "Suspended";
4011 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4012 ary[3] = "Vacation";
4013 else if(HANDLE_FLAGGED(uData->handle, BOT))
4019 for(matches = 1; matches < lData.table.length; ++matches)
4021 free((char*)lData.table.contents[matches][2]);
4022 free(lData.table.contents[matches]);
4024 free(lData.table.contents[0]);
4025 free(lData.table.contents);
4029 static CHANSERV_FUNC(cmd_users)
4031 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4034 static CHANSERV_FUNC(cmd_wlist)
4036 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4039 static CHANSERV_FUNC(cmd_clist)
4041 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4044 static CHANSERV_FUNC(cmd_mlist)
4046 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4049 static CHANSERV_FUNC(cmd_olist)
4051 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4054 static CHANSERV_FUNC(cmd_plist)
4056 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4059 static CHANSERV_FUNC(cmd_bans)
4061 struct userNode *search_u = NULL;
4062 struct helpfile_table tbl;
4063 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4064 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4065 const char *msg_never, *triggered, *expires;
4066 struct banData *ban, **bans;
4070 else if(strchr(search = argv[1], '!'))
4073 search_wilds = search[strcspn(search, "?*")];
4075 else if(!(search_u = GetUserH(search)))
4076 reply("MSG_NICK_UNKNOWN", search);
4078 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4080 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4084 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4089 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4092 bans[matches++] = ban;
4097 tbl.length = matches + 1;
4098 tbl.width = 4 + timed;
4100 tbl.flags = TABLE_NO_FREE;
4101 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4102 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4103 tbl.contents[0][0] = "Mask";
4104 tbl.contents[0][1] = "Set By";
4105 tbl.contents[0][2] = "Triggered";
4108 tbl.contents[0][3] = "Expires";
4109 tbl.contents[0][4] = "Reason";
4112 tbl.contents[0][3] = "Reason";
4115 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4117 free(tbl.contents[0]);
4122 msg_never = user_find_message(user, "MSG_NEVER");
4123 for(ii = 0; ii < matches; )
4129 else if(ban->expires)
4130 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4132 expires = msg_never;
4135 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4137 triggered = msg_never;
4139 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4140 tbl.contents[ii][0] = ban->mask;
4141 tbl.contents[ii][1] = ban->owner;
4142 tbl.contents[ii][2] = strdup(triggered);
4145 tbl.contents[ii][3] = strdup(expires);
4146 tbl.contents[ii][4] = ban->reason;
4149 tbl.contents[ii][3] = ban->reason;
4151 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4152 reply("MSG_MATCH_COUNT", matches);
4153 for(ii = 1; ii < tbl.length; ++ii)
4155 free((char*)tbl.contents[ii][2]);
4157 free((char*)tbl.contents[ii][3]);
4158 free(tbl.contents[ii]);
4160 free(tbl.contents[0]);
4166 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4168 struct chanData *cData = channel->channel_info;
4169 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4171 if(cData->topic_mask)
4172 return !match_ircglob(new_topic, cData->topic_mask);
4173 else if(cData->topic)
4174 return irccasecmp(new_topic, cData->topic);
4179 static CHANSERV_FUNC(cmd_topic)
4181 struct chanData *cData;
4184 cData = channel->channel_info;
4189 SetChannelTopic(channel, chanserv, cData->topic, 1);
4190 reply("CSMSG_TOPIC_SET", cData->topic);
4194 reply("CSMSG_NO_TOPIC", channel->name);
4198 topic = unsplit_string(argv + 1, argc - 1, NULL);
4199 /* If they say "!topic *", use an empty topic. */
4200 if((topic[0] == '*') && (topic[1] == 0))
4202 if(bad_topic(channel, user, topic))
4204 char *topic_mask = cData->topic_mask;
4207 char new_topic[TOPICLEN+1], tchar;
4208 int pos=0, starpos=-1, dpos=0, len;
4210 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4217 len = strlen(topic);
4218 if((dpos + len) > TOPICLEN)
4219 len = TOPICLEN + 1 - dpos;
4220 memcpy(new_topic+dpos, topic, len);
4224 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4225 default: new_topic[dpos++] = tchar; break;
4228 if((dpos > TOPICLEN) || tchar)
4231 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4232 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4235 new_topic[dpos] = 0;
4236 SetChannelTopic(channel, chanserv, new_topic, 1);
4238 reply("CSMSG_TOPIC_LOCKED", channel->name);
4243 SetChannelTopic(channel, chanserv, topic, 1);
4245 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4247 /* Grab the topic and save it as the default topic. */
4249 cData->topic = strdup(channel->topic);
4255 static CHANSERV_FUNC(cmd_mode)
4257 struct userData *uData;
4258 struct mod_chanmode *change;
4264 change = &channel->channel_info->modes;
4265 if(change->modes_set || change->modes_clear) {
4266 modcmd_chanmode_announce(change);
4267 reply("CSMSG_DEFAULTED_MODES", channel->name);
4269 reply("CSMSG_NO_MODES", channel->name);
4273 uData = GetChannelUser(channel->channel_info, user->handle_info);
4275 base_oplevel = MAXOPLEVEL;
4276 else if (uData->access >= UL_OWNER)
4279 base_oplevel = 1 + UL_OWNER - uData->access;
4280 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4283 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4287 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4288 && mode_lock_violated(&channel->channel_info->modes, change))
4291 mod_chanmode_format(&channel->channel_info->modes, modes);
4292 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4296 modcmd_chanmode_announce(change);
4297 mod_chanmode_format(change, fmt);
4298 mod_chanmode_free(change);
4299 reply("CSMSG_MODES_SET", fmt);
4304 chanserv_del_invite_mark(void *data)
4306 struct ChanUser *chanuser = data;
4307 struct chanNode *channel = chanuser->chan;
4309 if(!channel) return;
4310 for(i = 0; i < channel->invited.used; i++)
4312 if(channel->invited.list[i] == chanuser->user) {
4313 userList_remove(&channel->invited, chanuser->user);
4319 static CHANSERV_FUNC(cmd_invite)
4321 struct userData *uData;
4322 struct userNode *invite;
4323 struct ChanUser *chanuser;
4326 uData = GetChannelUser(channel->channel_info, user->handle_info);
4330 if(!(invite = GetUserH(argv[1])))
4332 reply("MSG_NICK_UNKNOWN", argv[1]);
4339 if(GetUserMode(channel, invite))
4341 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4345 for(i = 0; i < channel->invited.used; i++)
4347 if(channel->invited.list[i] == invite) {
4348 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4357 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4358 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4361 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4363 irc_invite(chanserv, invite, channel);
4365 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4367 userList_append(&channel->invited, invite);
4368 chanuser = calloc(1, sizeof(*chanuser));
4369 chanuser->user=invite;
4370 chanuser->chan=channel;
4371 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4376 static CHANSERV_FUNC(cmd_inviteme)
4378 if(GetUserMode(channel, user))
4380 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4383 if(channel->channel_info
4384 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4386 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4389 irc_invite(cmd->parent->bot, user, channel);
4394 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4397 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4399 /* We display things based on two dimensions:
4400 * - Issue time: present or absent
4401 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4402 * (in order of precedence, so something both expired and revoked
4403 * only counts as revoked)
4405 combo = (suspended->issued ? 4 : 0)
4406 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4408 case 0: /* no issue time, indefinite expiration */
4409 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4411 case 1: /* no issue time, expires in future */
4412 intervalString(buf1, suspended->expires-now, user->handle_info);
4413 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4415 case 2: /* no issue time, expired */
4416 intervalString(buf1, now-suspended->expires, user->handle_info);
4417 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4419 case 3: /* no issue time, revoked */
4420 intervalString(buf1, now-suspended->revoked, user->handle_info);
4421 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4423 case 4: /* issue time set, indefinite expiration */
4424 intervalString(buf1, now-suspended->issued, user->handle_info);
4425 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4427 case 5: /* issue time set, expires in future */
4428 intervalString(buf1, now-suspended->issued, user->handle_info);
4429 intervalString(buf2, suspended->expires-now, user->handle_info);
4430 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4432 case 6: /* issue time set, expired */
4433 intervalString(buf1, now-suspended->issued, user->handle_info);
4434 intervalString(buf2, now-suspended->expires, user->handle_info);
4435 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4437 case 7: /* issue time set, revoked */
4438 intervalString(buf1, now-suspended->issued, user->handle_info);
4439 intervalString(buf2, now-suspended->revoked, user->handle_info);
4440 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4443 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4448 static CHANSERV_FUNC(cmd_info)
4450 char modes[MAXLEN], buffer[INTERVALLEN];
4451 struct userData *uData, *owner;
4452 struct chanData *cData;
4453 struct do_not_register *dnr;
4458 cData = channel->channel_info;
4459 reply("CSMSG_CHANNEL_INFO", channel->name);
4461 uData = GetChannelUser(cData, user->handle_info);
4462 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4464 mod_chanmode_format(&cData->modes, modes);
4465 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4466 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4469 for(it = dict_first(cData->notes); it; it = iter_next(it))
4473 note = iter_data(it);
4474 if(!note_type_visible_to_user(cData, note->type, user))
4477 padding = PADLEN - 1 - strlen(iter_key(it));
4478 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4481 if(cData->max_time) {
4482 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4484 reply("CSMSG_CHANNEL_MAX", cData->max);
4486 for(owner = cData->users; owner; owner = owner->next)
4487 if(owner->access == UL_OWNER)
4488 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4489 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4490 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4491 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4493 privileged = IsStaff(user);
4495 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4496 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4497 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4499 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4500 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4502 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4504 struct suspended *suspended;
4505 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4506 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4507 show_suspension_info(cmd, user, suspended);
4509 else if(IsSuspended(cData))
4511 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4512 show_suspension_info(cmd, user, cData->suspended);
4517 static CHANSERV_FUNC(cmd_netinfo)
4519 extern unsigned long boot_time;
4520 extern unsigned long burst_length;
4521 char interval[INTERVALLEN];
4523 reply("CSMSG_NETWORK_INFO");
4524 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4525 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4526 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4527 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4528 reply("CSMSG_NETWORK_BANS", banCount);
4529 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4530 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4531 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4536 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4538 struct helpfile_table table;
4540 struct userNode *user;
4545 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4546 table.contents = alloca(list->used*sizeof(*table.contents));
4547 for(nn=0; nn<list->used; nn++)
4549 user = list->list[nn];
4550 if(user->modes & skip_flags)
4554 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4557 nick = alloca(strlen(user->nick)+3);
4558 sprintf(nick, "(%s)", user->nick);
4562 table.contents[table.length][0] = nick;
4565 table_send(chanserv, to->nick, 0, NULL, table);
4568 static CHANSERV_FUNC(cmd_ircops)
4570 reply("CSMSG_STAFF_OPERS");
4571 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4575 static CHANSERV_FUNC(cmd_helpers)
4577 reply("CSMSG_STAFF_HELPERS");
4578 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4582 static CHANSERV_FUNC(cmd_staff)
4584 reply("CSMSG_NETWORK_STAFF");
4585 cmd_ircops(CSFUNC_ARGS);
4586 cmd_helpers(CSFUNC_ARGS);
4590 static CHANSERV_FUNC(cmd_peek)
4592 struct modeNode *mn;
4593 char modes[MODELEN];
4595 struct helpfile_table table;
4596 int opcount = 0, voicecount = 0, srvcount = 0;
4598 irc_make_chanmode(channel, modes);
4600 reply("CSMSG_PEEK_INFO", channel->name);
4601 reply("CSMSG_PEEK_TOPIC", channel->topic);
4602 reply("CSMSG_PEEK_MODES", modes);
4606 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4607 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4608 for(n = 0; n < channel->members.used; n++)
4610 mn = channel->members.list[n];
4611 if(IsLocal(mn->user))
4613 else if(mn->modes & MODE_CHANOP)
4615 else if(mn->modes & MODE_VOICE)
4618 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4620 table.contents[table.length] = alloca(sizeof(**table.contents));
4621 table.contents[table.length][0] = mn->user->nick;
4625 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4626 (channel->members.used - opcount - voicecount - srvcount));
4630 reply("CSMSG_PEEK_OPS");
4631 table_send(chanserv, user->nick, 0, NULL, table);
4634 reply("CSMSG_PEEK_NO_OPS");
4638 static MODCMD_FUNC(cmd_wipeinfo)
4640 struct handle_info *victim;
4641 struct userData *ud, *actor, *real_actor;
4642 unsigned int override = 0;
4645 actor = GetChannelUser(channel->channel_info, user->handle_info);
4646 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4647 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4649 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4651 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4654 if((ud->access >= actor->access) && (ud != actor))
4656 reply("MSG_USER_OUTRANKED", victim->handle);
4659 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4660 override = CMD_LOG_OVERRIDE;
4664 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4665 return 1 | override;
4668 static CHANSERV_FUNC(cmd_resync)
4670 struct mod_chanmode *changes;
4671 struct chanData *cData = channel->channel_info;
4672 unsigned int ii, used;
4674 changes = mod_chanmode_alloc(channel->members.used * 2);
4675 for(ii = used = 0; ii < channel->members.used; ++ii)
4677 struct modeNode *mn = channel->members.list[ii];
4678 struct userData *uData;
4680 if(IsService(mn->user))
4683 uData = GetChannelAccess(cData, mn->user->handle_info);
4684 if(!cData->lvlOpts[lvlGiveOps]
4685 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4687 if(!(mn->modes & MODE_CHANOP))
4689 changes->args[used].mode = MODE_CHANOP;
4690 changes->args[used++].u.member = mn;
4693 else if(!cData->lvlOpts[lvlGiveVoice]
4694 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4696 if(mn->modes & MODE_CHANOP)
4698 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4699 changes->args[used++].u.member = mn;
4701 if(!(mn->modes & MODE_VOICE))
4703 changes->args[used].mode = MODE_VOICE;
4704 changes->args[used++].u.member = mn;
4711 changes->args[used].mode = MODE_REMOVE | mn->modes;
4712 changes->args[used++].u.member = mn;
4716 changes->argc = used;
4717 modcmd_chanmode_announce(changes);
4718 mod_chanmode_free(changes);
4719 reply("CSMSG_RESYNCED_USERS", channel->name);
4723 static CHANSERV_FUNC(cmd_seen)
4725 struct userData *uData;
4726 struct handle_info *handle;
4727 char seen[INTERVALLEN];
4731 if(!irccasecmp(argv[1], chanserv->nick))
4733 reply("CSMSG_IS_CHANSERV");
4737 if(!(handle = get_handle_info(argv[1])))
4739 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4743 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4745 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4750 reply("CSMSG_USER_PRESENT", handle->handle);
4751 else if(uData->seen)
4752 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4754 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4756 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4757 reply("CSMSG_USER_VACATION", handle->handle);
4762 static MODCMD_FUNC(cmd_names)
4764 struct userNode *targ;
4765 struct userData *targData;
4766 unsigned int ii, pos;
4769 for(ii=pos=0; ii<channel->members.used; ++ii)
4771 targ = channel->members.list[ii]->user;
4772 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4775 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4778 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4782 if(IsUserSuspended(targData))
4784 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4787 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4788 reply("CSMSG_END_NAMES", channel->name);
4793 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4795 switch(ntype->visible_type)
4797 case NOTE_VIS_ALL: return 1;
4798 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4799 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4804 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4806 struct userData *uData;
4808 switch(ntype->set_access_type)
4810 case NOTE_SET_CHANNEL_ACCESS:
4811 if(!user->handle_info)
4813 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4815 return uData->access >= ntype->set_access.min_ulevel;
4816 case NOTE_SET_CHANNEL_SETTER:
4817 return check_user_level(channel, user, lvlSetters, 1, 0);
4818 case NOTE_SET_PRIVILEGED: default:
4819 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4823 static CHANSERV_FUNC(cmd_note)
4825 struct chanData *cData;
4827 struct note_type *ntype;
4829 cData = channel->channel_info;
4832 reply("CSMSG_NOT_REGISTERED", channel->name);
4836 /* If no arguments, show all visible notes for the channel. */
4842 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4844 note = iter_data(it);
4845 if(!note_type_visible_to_user(cData, note->type, user))
4848 reply("CSMSG_NOTELIST_HEADER", channel->name);
4849 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4852 reply("CSMSG_NOTELIST_END", channel->name);
4854 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4856 /* If one argument, show the named note. */
4859 if((note = dict_find(cData->notes, argv[1], NULL))
4860 && note_type_visible_to_user(cData, note->type, user))
4862 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4864 else if((ntype = dict_find(note_types, argv[1], NULL))
4865 && note_type_visible_to_user(NULL, ntype, user))
4867 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4872 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4876 /* Assume they're trying to set a note. */
4880 ntype = dict_find(note_types, argv[1], NULL);
4883 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4886 else if(note_type_settable_by_user(channel, ntype, user))
4888 note_text = unsplit_string(argv+2, argc-2, NULL);
4889 if((note = dict_find(cData->notes, argv[1], NULL)))
4890 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4891 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4892 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4894 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4896 /* The note is viewable to staff only, so return 0
4897 to keep the invocation from getting logged (or
4898 regular users can see it in !events). */
4904 reply("CSMSG_NO_ACCESS");
4911 static CHANSERV_FUNC(cmd_delnote)
4916 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4917 || !note_type_settable_by_user(channel, note->type, user))
4919 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4922 dict_remove(channel->channel_info->notes, note->type->name);
4923 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4927 static CHANSERV_FUNC(cmd_events)
4929 struct logSearch discrim;
4930 struct logReport report;
4931 unsigned int matches, limit;
4933 limit = (argc > 1) ? atoi(argv[1]) : 10;
4934 if(limit < 1 || limit > 200)
4937 memset(&discrim, 0, sizeof(discrim));
4938 discrim.masks.bot = chanserv;
4939 discrim.masks.channel_name = channel->name;
4941 discrim.masks.command = argv[2];
4942 discrim.limit = limit;
4943 discrim.max_time = INT_MAX;
4944 discrim.severities = 1 << LOG_COMMAND;
4945 report.reporter = chanserv;
4947 reply("CSMSG_EVENT_SEARCH_RESULTS");
4948 matches = log_entry_search(&discrim, log_report_entry, &report);
4950 reply("MSG_MATCH_COUNT", matches);
4952 reply("MSG_NO_MATCHES");
4956 static CHANSERV_FUNC(cmd_say)
4962 msg = unsplit_string(argv + 1, argc - 1, NULL);
4963 send_channel_message(channel, cmd->parent->bot, "%s", msg);
4965 else if(*argv[1] == '*' && argv[1][1] != '\0')
4967 struct handle_info *hi;
4968 struct userNode *authed;
4971 msg = unsplit_string(argv + 2, argc - 2, NULL);
4973 if (!(hi = get_handle_info(argv[1] + 1)))
4975 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
4979 for (authed = hi->users; authed; authed = authed->next_authed)
4980 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
4982 else if(GetUserH(argv[1]))
4985 msg = unsplit_string(argv + 2, argc - 2, NULL);
4986 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
4990 reply("MSG_NOT_TARGET_NAME");
4996 static CHANSERV_FUNC(cmd_emote)
5002 /* CTCP is so annoying. */
5003 msg = unsplit_string(argv + 1, argc - 1, NULL);
5004 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5006 else if(*argv[1] == '*' && argv[1][1] != '\0')
5008 struct handle_info *hi;
5009 struct userNode *authed;
5012 msg = unsplit_string(argv + 2, argc - 2, NULL);
5014 if (!(hi = get_handle_info(argv[1] + 1)))
5016 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5020 for (authed = hi->users; authed; authed = authed->next_authed)
5021 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5023 else if(GetUserH(argv[1]))
5025 msg = unsplit_string(argv + 2, argc - 2, NULL);
5026 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5030 reply("MSG_NOT_TARGET_NAME");
5036 struct channelList *
5037 chanserv_support_channels(void)
5039 return &chanserv_conf.support_channels;
5042 static CHANSERV_FUNC(cmd_expire)
5044 int channel_count = registered_channels;
5045 expire_channels(NULL);
5046 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5051 chanserv_expire_suspension(void *data)
5053 struct suspended *suspended = data;
5054 struct chanNode *channel;
5057 /* Update the channel registration data structure. */
5058 if(!suspended->expires || (now < suspended->expires))
5059 suspended->revoked = now;
5060 channel = suspended->cData->channel;
5061 suspended->cData->channel = channel;
5062 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5064 /* If appropriate, re-join ChanServ to the channel. */
5065 if(!IsOffChannel(suspended->cData))
5067 spamserv_cs_suspend(channel, 0, 0, NULL);
5068 ss_cs_join_channel(channel, 1);
5071 /* Mark everyone currently in the channel as present. */
5072 for(ii = 0; ii < channel->members.used; ++ii)
5074 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5083 static CHANSERV_FUNC(cmd_csuspend)
5085 struct suspended *suspended;
5086 char reason[MAXLEN];
5087 unsigned long expiry, duration;
5088 struct userData *uData;
5092 if(IsProtected(channel->channel_info))
5094 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5098 if(argv[1][0] == '!')
5100 else if(IsSuspended(channel->channel_info))
5102 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5103 show_suspension_info(cmd, user, channel->channel_info->suspended);
5107 if(!strcmp(argv[1], "0"))
5109 else if((duration = ParseInterval(argv[1])))
5110 expiry = now + duration;
5113 reply("MSG_INVALID_DURATION", argv[1]);
5117 unsplit_string(argv + 2, argc - 2, reason);
5119 suspended = calloc(1, sizeof(*suspended));
5120 suspended->revoked = 0;
5121 suspended->issued = now;
5122 suspended->suspender = strdup(user->handle_info->handle);
5123 suspended->expires = expiry;
5124 suspended->reason = strdup(reason);
5125 suspended->cData = channel->channel_info;
5126 suspended->previous = suspended->cData->suspended;
5127 suspended->cData->suspended = suspended;
5129 if(suspended->expires)
5130 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5132 if(IsSuspended(channel->channel_info))
5134 suspended->previous->revoked = now;
5135 if(suspended->previous->expires)
5136 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5137 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5138 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5142 /* Mark all users in channel as absent. */
5143 for(uData = channel->channel_info->users; uData; uData = uData->next)
5152 /* Mark the channel as suspended, then part. */
5153 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5154 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5155 DelChannelUser(chanserv, channel, suspended->reason, 0);
5156 reply("CSMSG_SUSPENDED", channel->name);
5157 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5158 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5163 static CHANSERV_FUNC(cmd_cunsuspend)
5165 struct suspended *suspended;
5166 char message[MAXLEN];
5168 if(!IsSuspended(channel->channel_info))
5170 reply("CSMSG_NOT_SUSPENDED", channel->name);
5174 suspended = channel->channel_info->suspended;
5176 /* Expire the suspension and join ChanServ to the channel. */
5177 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5178 chanserv_expire_suspension(suspended);
5179 reply("CSMSG_UNSUSPENDED", channel->name);
5180 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5181 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5185 typedef struct chanservSearch
5190 unsigned long unvisited;
5191 unsigned long registered;
5193 unsigned long flags;
5197 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5200 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5205 search = malloc(sizeof(struct chanservSearch));
5206 memset(search, 0, sizeof(*search));
5209 for(i = 0; i < argc; i++)
5211 /* Assume all criteria require arguments. */
5214 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5218 if(!irccasecmp(argv[i], "name"))
5219 search->name = argv[++i];
5220 else if(!irccasecmp(argv[i], "registrar"))
5221 search->registrar = argv[++i];
5222 else if(!irccasecmp(argv[i], "unvisited"))
5223 search->unvisited = ParseInterval(argv[++i]);
5224 else if(!irccasecmp(argv[i], "registered"))
5225 search->registered = ParseInterval(argv[++i]);
5226 else if(!irccasecmp(argv[i], "flags"))
5229 if(!irccasecmp(argv[i], "nodelete"))
5230 search->flags |= CHANNEL_NODELETE;
5231 else if(!irccasecmp(argv[i], "suspended"))
5232 search->flags |= CHANNEL_SUSPENDED;
5233 else if(!irccasecmp(argv[i], "unreviewed"))
5234 search->flags |= CHANNEL_UNREVIEWED;
5237 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5241 else if(!irccasecmp(argv[i], "limit"))
5242 search->limit = strtoul(argv[++i], NULL, 10);
5245 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5250 if(search->name && !strcmp(search->name, "*"))
5252 if(search->registrar && !strcmp(search->registrar, "*"))
5253 search->registrar = 0;
5262 chanserv_channel_match(struct chanData *channel, search_t search)
5264 const char *name = channel->channel->name;
5265 if((search->name && !match_ircglob(name, search->name)) ||
5266 (search->registrar && !channel->registrar) ||
5267 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5268 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5269 (search->registered && (now - channel->registered) > search->registered) ||
5270 (search->flags && ((search->flags & channel->flags) != search->flags)))
5277 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5279 struct chanData *channel;
5280 unsigned int matches = 0;
5282 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5284 if(!chanserv_channel_match(channel, search))
5294 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5299 search_print(struct chanData *channel, void *data)
5301 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5304 static CHANSERV_FUNC(cmd_search)
5307 unsigned int matches;
5308 channel_search_func action;
5312 if(!irccasecmp(argv[1], "count"))
5313 action = search_count;
5314 else if(!irccasecmp(argv[1], "print"))
5315 action = search_print;
5318 reply("CSMSG_ACTION_INVALID", argv[1]);
5322 search = chanserv_search_create(user, argc - 2, argv + 2);
5326 if(action == search_count)
5327 search->limit = INT_MAX;
5329 if(action == search_print)
5330 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5332 matches = chanserv_channel_search(search, action, user);
5335 reply("MSG_MATCH_COUNT", matches);
5337 reply("MSG_NO_MATCHES");
5343 static CHANSERV_FUNC(cmd_unvisited)
5345 struct chanData *cData;
5346 unsigned long interval = chanserv_conf.channel_expire_delay;
5347 char buffer[INTERVALLEN];
5348 unsigned int limit = 25, matches = 0;
5352 interval = ParseInterval(argv[1]);
5354 limit = atoi(argv[2]);
5357 intervalString(buffer, interval, user->handle_info);
5358 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5360 for(cData = channelList; cData && matches < limit; cData = cData->next)
5362 if((now - cData->visited) < interval)
5365 intervalString(buffer, now - cData->visited, user->handle_info);
5366 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5373 static MODCMD_FUNC(chan_opt_defaulttopic)
5379 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5381 reply("CSMSG_TOPIC_LOCKED", channel->name);
5385 topic = unsplit_string(argv+1, argc-1, NULL);
5387 free(channel->channel_info->topic);
5388 if(topic[0] == '*' && topic[1] == 0)
5390 topic = channel->channel_info->topic = NULL;
5394 topic = channel->channel_info->topic = strdup(topic);
5395 if(channel->channel_info->topic_mask
5396 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5397 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5399 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5402 if(channel->channel_info->topic)
5403 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5405 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5409 static MODCMD_FUNC(chan_opt_topicmask)
5413 struct chanData *cData = channel->channel_info;
5416 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5418 reply("CSMSG_TOPIC_LOCKED", channel->name);
5422 mask = unsplit_string(argv+1, argc-1, NULL);
5424 if(cData->topic_mask)
5425 free(cData->topic_mask);
5426 if(mask[0] == '*' && mask[1] == 0)
5428 cData->topic_mask = 0;
5432 cData->topic_mask = strdup(mask);
5434 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5435 else if(!match_ircglob(cData->topic, cData->topic_mask))
5436 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5440 if(channel->channel_info->topic_mask)
5441 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5443 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5447 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5451 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5455 if(greeting[0] == '*' && greeting[1] == 0)
5459 unsigned int length = strlen(greeting);
5460 if(length > chanserv_conf.greeting_length)
5462 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5465 *data = strdup(greeting);
5474 reply(name, user_find_message(user, "MSG_NONE"));
5478 static MODCMD_FUNC(chan_opt_greeting)
5480 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5483 static MODCMD_FUNC(chan_opt_usergreeting)
5485 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5488 static MODCMD_FUNC(chan_opt_modes)
5490 struct mod_chanmode *new_modes;
5495 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5497 reply("CSMSG_NO_ACCESS");
5500 if(argv[1][0] == '*' && argv[1][1] == 0)
5502 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5504 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5506 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5509 else if(new_modes->argc > 1)
5511 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5512 mod_chanmode_free(new_modes);
5517 channel->channel_info->modes = *new_modes;
5518 modcmd_chanmode_announce(new_modes);
5519 mod_chanmode_free(new_modes);
5523 mod_chanmode_format(&channel->channel_info->modes, modes);
5525 reply("CSMSG_SET_MODES", modes);
5527 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5531 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5533 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5535 struct chanData *cData = channel->channel_info;
5540 /* Set flag according to value. */
5541 if(enabled_string(argv[1]))
5543 cData->flags |= mask;
5546 else if(disabled_string(argv[1]))
5548 cData->flags &= ~mask;
5553 reply("MSG_INVALID_BINARY", argv[1]);
5559 /* Find current option value. */
5560 value = (cData->flags & mask) ? 1 : 0;
5564 reply(name, user_find_message(user, "MSG_ON"));
5566 reply(name, user_find_message(user, "MSG_OFF"));
5570 static MODCMD_FUNC(chan_opt_nodelete)
5572 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5574 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5578 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5581 static MODCMD_FUNC(chan_opt_dynlimit)
5583 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5586 static MODCMD_FUNC(chan_opt_offchannel)
5588 struct chanData *cData = channel->channel_info;
5593 /* Set flag according to value. */
5594 if(enabled_string(argv[1]))
5596 if(!IsOffChannel(cData))
5597 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5598 cData->flags |= CHANNEL_OFFCHANNEL;
5601 else if(disabled_string(argv[1]))
5603 if(IsOffChannel(cData))
5605 struct mod_chanmode change;
5606 mod_chanmode_init(&change);
5608 change.args[0].mode = MODE_CHANOP;
5609 change.args[0].u.member = AddChannelUser(chanserv, channel);
5610 mod_chanmode_announce(chanserv, channel, &change);
5612 cData->flags &= ~CHANNEL_OFFCHANNEL;
5617 reply("MSG_INVALID_BINARY", argv[1]);
5623 /* Find current option value. */
5624 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5628 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5630 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5634 static MODCMD_FUNC(chan_opt_unreviewed)
5636 struct chanData *cData = channel->channel_info;
5637 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5643 /* The two directions can have different ACLs. */
5644 if(enabled_string(argv[1]))
5646 else if(disabled_string(argv[1]))
5650 reply("MSG_INVALID_BINARY", argv[1]);
5654 if (new_value != value)
5656 struct svccmd *subcmd;
5657 char subcmd_name[32];
5659 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5660 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5663 reply("MSG_COMMAND_DISABLED", subcmd_name);
5666 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5670 cData->flags |= CHANNEL_UNREVIEWED;
5673 free(cData->registrar);
5674 cData->registrar = strdup(user->handle_info->handle);
5675 cData->flags &= ~CHANNEL_UNREVIEWED;
5682 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5684 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5688 static MODCMD_FUNC(chan_opt_defaults)
5690 struct userData *uData;
5691 struct chanData *cData;
5692 const char *confirm;
5693 enum levelOption lvlOpt;
5694 enum charOption chOpt;
5696 cData = channel->channel_info;
5697 uData = GetChannelUser(cData, user->handle_info);
5698 if(!uData || (uData->access < UL_OWNER))
5700 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5703 confirm = make_confirmation_string(uData);
5704 if((argc < 2) || strcmp(argv[1], confirm))
5706 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5709 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5710 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5711 cData->modes = chanserv_conf.default_modes;
5712 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5713 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5714 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5715 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5716 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5721 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5723 struct chanData *cData = channel->channel_info;
5724 struct userData *uData;
5725 unsigned short value;
5729 if(!check_user_level(channel, user, option, 1, 1))
5731 reply("CSMSG_CANNOT_SET");
5734 value = user_level_from_name(argv[1], UL_OWNER+1);
5735 if(!value && strcmp(argv[1], "0"))
5737 reply("CSMSG_INVALID_ACCESS", argv[1]);
5740 uData = GetChannelUser(cData, user->handle_info);
5741 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5743 reply("CSMSG_BAD_SETLEVEL");
5749 if(value > cData->lvlOpts[lvlGiveOps])
5751 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5756 if(value < cData->lvlOpts[lvlGiveVoice])
5758 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5763 /* This test only applies to owners, since non-owners
5764 * trying to set an option to above their level get caught
5765 * by the CSMSG_BAD_SETLEVEL test above.
5767 if(value > uData->access)
5769 reply("CSMSG_BAD_SETTERS");
5776 cData->lvlOpts[option] = value;
5778 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5782 static MODCMD_FUNC(chan_opt_enfops)
5784 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5787 static MODCMD_FUNC(chan_opt_giveops)
5789 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5792 static MODCMD_FUNC(chan_opt_enfmodes)
5794 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5797 static MODCMD_FUNC(chan_opt_enftopic)
5799 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5802 static MODCMD_FUNC(chan_opt_pubcmd)
5804 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5807 static MODCMD_FUNC(chan_opt_setters)
5809 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5812 static MODCMD_FUNC(chan_opt_ctcpusers)
5814 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5817 static MODCMD_FUNC(chan_opt_userinfo)
5819 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5822 static MODCMD_FUNC(chan_opt_givevoice)
5824 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5827 static MODCMD_FUNC(chan_opt_topicsnarf)
5829 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5832 static MODCMD_FUNC(chan_opt_inviteme)
5834 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5838 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5840 struct chanData *cData = channel->channel_info;
5841 int count = charOptions[option].count, idx;
5845 idx = atoi(argv[1]);
5847 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5849 reply("CSMSG_INVALID_NUMERIC", idx);
5850 /* Show possible values. */
5851 for(idx = 0; idx < count; idx++)
5852 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5856 cData->chOpts[option] = charOptions[option].values[idx].value;
5860 /* Find current option value. */
5863 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5867 /* Somehow, the option value is corrupt; reset it to the default. */
5868 cData->chOpts[option] = charOptions[option].default_value;
5873 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5877 static MODCMD_FUNC(chan_opt_protect)
5879 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5882 static MODCMD_FUNC(chan_opt_toys)
5884 return channel_multiple_option(chToys, CSFUNC_ARGS);
5887 static MODCMD_FUNC(chan_opt_ctcpreaction)
5889 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5892 static MODCMD_FUNC(chan_opt_topicrefresh)
5894 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5897 static struct svccmd_list set_shows_list;
5900 handle_svccmd_unbind(struct svccmd *target) {
5902 for(ii=0; ii<set_shows_list.used; ++ii)
5903 if(target == set_shows_list.list[ii])
5904 set_shows_list.used = 0;
5907 static CHANSERV_FUNC(cmd_set)
5909 struct svccmd *subcmd;
5913 /* Check if we need to (re-)initialize set_shows_list. */
5914 if(!set_shows_list.used)
5916 if(!set_shows_list.size)
5918 set_shows_list.size = chanserv_conf.set_shows->used;
5919 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5921 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5923 const char *name = chanserv_conf.set_shows->list[ii];
5924 sprintf(buf, "%s %s", argv[0], name);
5925 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5928 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5931 svccmd_list_append(&set_shows_list, subcmd);
5937 reply("CSMSG_CHANNEL_OPTIONS");
5938 for(ii = 0; ii < set_shows_list.used; ii++)
5940 subcmd = set_shows_list.list[ii];
5941 subcmd->command->func(user, channel, 1, argv+1, subcmd);
5946 sprintf(buf, "%s %s", argv[0], argv[1]);
5947 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5950 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
5953 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
5955 reply("CSMSG_NO_ACCESS");
5961 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
5965 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5967 struct userData *uData;
5969 uData = GetChannelAccess(channel->channel_info, user->handle_info);
5972 reply("CSMSG_NOT_USER", channel->name);
5978 /* Just show current option value. */
5980 else if(enabled_string(argv[1]))
5982 uData->flags |= mask;
5984 else if(disabled_string(argv[1]))
5986 uData->flags &= ~mask;
5990 reply("MSG_INVALID_BINARY", argv[1]);
5994 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
5998 static MODCMD_FUNC(user_opt_noautoop)
6000 struct userData *uData;
6002 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6005 reply("CSMSG_NOT_USER", channel->name);
6008 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6009 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6011 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6014 static MODCMD_FUNC(user_opt_autoinvite)
6016 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6018 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6020 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6023 static MODCMD_FUNC(user_opt_info)
6025 struct userData *uData;
6028 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6032 /* If they got past the command restrictions (which require access)
6033 * but fail this test, we have some fool with security override on.
6035 reply("CSMSG_NOT_USER", channel->name);
6042 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6043 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6045 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6048 bp = strcspn(infoline, "\001");
6051 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6056 if(infoline[0] == '*' && infoline[1] == 0)
6059 uData->info = strdup(infoline);
6062 reply("CSMSG_USET_INFO", uData->info);
6064 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6068 struct svccmd_list uset_shows_list;
6070 static CHANSERV_FUNC(cmd_uset)
6072 struct svccmd *subcmd;
6076 /* Check if we need to (re-)initialize uset_shows_list. */
6077 if(!uset_shows_list.used)
6081 "NoAutoOp", "AutoInvite", "Info"
6084 if(!uset_shows_list.size)
6086 uset_shows_list.size = ArrayLength(options);
6087 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6089 for(ii = 0; ii < ArrayLength(options); ii++)
6091 const char *name = options[ii];
6092 sprintf(buf, "%s %s", argv[0], name);
6093 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6096 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6099 svccmd_list_append(&uset_shows_list, subcmd);
6105 /* Do this so options are presented in a consistent order. */
6106 reply("CSMSG_USER_OPTIONS");
6107 for(ii = 0; ii < uset_shows_list.used; ii++)
6108 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6112 sprintf(buf, "%s %s", argv[0], argv[1]);
6113 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6116 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6120 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6123 static CHANSERV_FUNC(cmd_giveownership)
6125 struct handle_info *new_owner_hi;
6126 struct userData *new_owner;
6127 struct userData *curr_user;
6128 struct userData *invoker;
6129 struct chanData *cData = channel->channel_info;
6130 struct do_not_register *dnr;
6131 const char *confirm;
6133 unsigned short co_access;
6134 char reason[MAXLEN];
6137 curr_user = GetChannelAccess(cData, user->handle_info);
6138 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6139 if(!curr_user || (curr_user->access != UL_OWNER))
6141 struct userData *owner = NULL;
6142 for(curr_user = channel->channel_info->users;
6144 curr_user = curr_user->next)
6146 if(curr_user->access != UL_OWNER)
6150 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6157 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6159 char delay[INTERVALLEN];
6160 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6161 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6164 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6166 if(new_owner_hi == user->handle_info)
6168 reply("CSMSG_NO_TRANSFER_SELF");
6171 new_owner = GetChannelAccess(cData, new_owner_hi);
6176 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6180 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6184 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6186 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6189 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6190 if(!IsHelping(user))
6191 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6193 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6196 invoker = GetChannelUser(cData, user->handle_info);
6197 if(invoker->access <= UL_OWNER)
6199 confirm = make_confirmation_string(curr_user);
6200 if((argc < 3) || strcmp(argv[2], confirm))
6202 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6206 if(new_owner->access >= UL_COOWNER)
6207 co_access = new_owner->access;
6209 co_access = UL_COOWNER;
6210 new_owner->access = UL_OWNER;
6212 curr_user->access = co_access;
6213 cData->ownerTransfer = now;
6214 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6215 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6216 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6220 static CHANSERV_FUNC(cmd_suspend)
6222 struct handle_info *hi;
6223 struct userData *actor, *real_actor, *target;
6224 unsigned int override = 0;
6227 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6228 actor = GetChannelUser(channel->channel_info, user->handle_info);
6229 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6230 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6232 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6235 if(target->access >= actor->access)
6237 reply("MSG_USER_OUTRANKED", hi->handle);
6240 if(target->flags & USER_SUSPENDED)
6242 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6247 target->present = 0;
6250 if(!real_actor || target->access >= real_actor->access)
6251 override = CMD_LOG_OVERRIDE;
6252 target->flags |= USER_SUSPENDED;
6253 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6254 return 1 | override;
6257 static CHANSERV_FUNC(cmd_unsuspend)
6259 struct handle_info *hi;
6260 struct userData *actor, *real_actor, *target;
6261 unsigned int override = 0;
6264 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6265 actor = GetChannelUser(channel->channel_info, user->handle_info);
6266 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6267 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6269 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6272 if(target->access >= actor->access)
6274 reply("MSG_USER_OUTRANKED", hi->handle);
6277 if(!(target->flags & USER_SUSPENDED))
6279 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6282 if(!real_actor || target->access >= real_actor->access)
6283 override = CMD_LOG_OVERRIDE;
6284 target->flags &= ~USER_SUSPENDED;
6285 scan_user_presence(target, NULL);
6286 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6287 return 1 | override;
6290 static MODCMD_FUNC(cmd_deleteme)
6292 struct handle_info *hi;
6293 struct userData *target;
6294 const char *confirm_string;
6295 unsigned short access_level;
6298 hi = user->handle_info;
6299 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6301 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6304 if(target->access == UL_OWNER)
6306 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6309 confirm_string = make_confirmation_string(target);
6310 if((argc < 2) || strcmp(argv[1], confirm_string))
6312 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6315 access_level = target->access;
6316 channel_name = strdup(channel->name);
6317 del_channel_user(target, 1);
6318 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6324 chanserv_refresh_topics(UNUSED_ARG(void *data))
6326 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6327 struct chanData *cData;
6330 for(cData = channelList; cData; cData = cData->next)
6332 if(IsSuspended(cData))
6334 opt = cData->chOpts[chTopicRefresh];
6337 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6340 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6341 cData->last_refresh = refresh_num;
6343 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6346 static CHANSERV_FUNC(cmd_unf)
6350 char response[MAXLEN];
6351 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6352 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6353 irc_privmsg(cmd->parent->bot, channel->name, response);
6356 reply("CSMSG_UNF_RESPONSE");
6360 static CHANSERV_FUNC(cmd_ping)
6364 char response[MAXLEN];
6365 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6366 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6367 irc_privmsg(cmd->parent->bot, channel->name, response);
6370 reply("CSMSG_PING_RESPONSE");
6374 static CHANSERV_FUNC(cmd_wut)
6378 char response[MAXLEN];
6379 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6380 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6381 irc_privmsg(cmd->parent->bot, channel->name, response);
6384 reply("CSMSG_WUT_RESPONSE");
6388 static CHANSERV_FUNC(cmd_8ball)
6390 unsigned int i, j, accum;
6395 for(i=1; i<argc; i++)
6396 for(j=0; argv[i][j]; j++)
6397 accum = (accum << 5) - accum + toupper(argv[i][j]);
6398 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6401 char response[MAXLEN];
6402 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6403 irc_privmsg(cmd->parent->bot, channel->name, response);
6406 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6410 static CHANSERV_FUNC(cmd_d)
6412 unsigned long sides, count, modifier, ii, total;
6413 char response[MAXLEN], *sep;
6417 if((count = strtoul(argv[1], &sep, 10)) < 1)
6427 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6428 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6432 else if((sep[0] == '-') && isdigit(sep[1]))
6433 modifier = strtoul(sep, NULL, 10);
6434 else if((sep[0] == '+') && isdigit(sep[1]))
6435 modifier = strtoul(sep+1, NULL, 10);
6442 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6447 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6450 for(total = ii = 0; ii < count; ++ii)
6451 total += (rand() % sides) + 1;
6454 if((count > 1) || modifier)
6456 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6457 sprintf(response, fmt, total, count, sides, modifier);
6461 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6462 sprintf(response, fmt, total, sides);
6465 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6467 send_message_type(4, user, cmd->parent->bot, "%s", response);
6471 static CHANSERV_FUNC(cmd_huggle)
6473 /* CTCP must be via PRIVMSG, never notice */
6475 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6477 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6482 chanserv_adjust_limit(void *data)
6484 struct mod_chanmode change;
6485 struct chanData *cData = data;
6486 struct chanNode *channel = cData->channel;
6489 if(IsSuspended(cData))
6492 cData->limitAdjusted = now;
6493 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6494 if(cData->modes.modes_set & MODE_LIMIT)
6496 if(limit > cData->modes.new_limit)
6497 limit = cData->modes.new_limit;
6498 else if(limit == cData->modes.new_limit)
6502 mod_chanmode_init(&change);
6503 change.modes_set = MODE_LIMIT;
6504 change.new_limit = limit;
6505 mod_chanmode_announce(chanserv, channel, &change);
6509 handle_new_channel(struct chanNode *channel)
6511 struct chanData *cData;
6513 if(!(cData = channel->channel_info))
6516 if(cData->modes.modes_set || cData->modes.modes_clear)
6517 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6519 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6520 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6523 /* Welcome to my worst nightmare. Warning: Read (or modify)
6524 the code below at your own risk. */
6526 handle_join(struct modeNode *mNode)
6528 struct mod_chanmode change;
6529 struct userNode *user = mNode->user;
6530 struct chanNode *channel = mNode->channel;
6531 struct chanData *cData;
6532 struct userData *uData = NULL;
6533 struct banData *bData;
6534 struct handle_info *handle;
6535 unsigned int modes = 0, info = 0;
6538 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6541 cData = channel->channel_info;
6542 if(channel->members.used > cData->max) {
6543 cData->max = channel->members.used;
6544 cData->max_time = now;
6547 for(i = 0; i < channel->invited.used; i++)
6549 if(channel->invited.list[i] == user) {
6550 userList_remove(&channel->invited, user);
6554 /* Check for bans. If they're joining through a ban, one of two
6556 * 1: Join during a netburst, by riding the break. Kick them
6557 * unless they have ops or voice in the channel.
6558 * 2: They're allowed to join through the ban (an invite in
6559 * ircu2.10, or a +e on Hybrid, or something).
6560 * If they're not joining through a ban, and the banlist is not
6561 * full, see if they're on the banlist for the channel. If so,
6564 if(user->uplink->burst && !mNode->modes)
6567 for(ii = 0; ii < channel->banlist.used; ii++)
6569 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6571 /* Riding a netburst. Naughty. */
6572 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6578 mod_chanmode_init(&change);
6580 if(channel->banlist.used < MAXBANS)
6582 /* Not joining through a ban. */
6583 for(bData = cData->bans;
6584 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6585 bData = bData->next);
6589 char kick_reason[MAXLEN];
6590 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6592 bData->triggered = now;
6593 if(bData != cData->bans)
6595 /* Shuffle the ban to the head of the list. */
6597 bData->next->prev = bData->prev;
6599 bData->prev->next = bData->next;
6602 bData->next = cData->bans;
6605 cData->bans->prev = bData;
6606 cData->bans = bData;
6609 change.args[0].mode = MODE_BAN;
6610 change.args[0].u.hostmask = bData->mask;
6611 mod_chanmode_announce(chanserv, channel, &change);
6612 KickChannelUser(user, channel, chanserv, kick_reason);
6617 /* ChanServ will not modify the limits in join-flooded channels,
6618 or when there are enough slots left below the limit. */
6619 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6620 && !channel->join_flooded
6621 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6623 /* The user count has begun "bumping" into the channel limit,
6624 so set a timer to raise the limit a bit. Any previous
6625 timers are removed so three incoming users within the delay
6626 results in one limit change, not three. */
6628 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6629 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6632 if(channel->join_flooded)
6634 /* don't automatically give ops or voice during a join flood */
6636 else if(cData->lvlOpts[lvlGiveOps] == 0)
6637 modes |= MODE_CHANOP;
6638 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6639 modes |= MODE_VOICE;
6641 greeting = cData->greeting;
6642 if(user->handle_info)
6644 handle = user->handle_info;
6646 if(IsHelper(user) && !IsHelping(user))
6649 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6651 if(channel == chanserv_conf.support_channels.list[ii])
6653 HANDLE_SET_FLAG(user->handle_info, HELPING);
6659 uData = GetTrueChannelAccess(cData, handle);
6660 if(uData && !IsUserSuspended(uData))
6662 /* Ops and above were handled by the above case. */
6663 if(IsUserAutoOp(uData))
6665 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6666 modes |= MODE_CHANOP;
6667 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6668 modes |= MODE_VOICE;
6670 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6671 cData->visited = now;
6672 if(cData->user_greeting)
6673 greeting = cData->user_greeting;
6675 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6676 && ((now - uData->seen) >= chanserv_conf.info_delay)
6684 /* If user joining normally (not during burst), apply op or voice,
6685 * and send greeting/userinfo as appropriate.
6687 if(!user->uplink->burst)
6691 if(modes & MODE_CHANOP)
6692 modes &= ~MODE_VOICE;
6693 change.args[0].mode = modes;
6694 change.args[0].u.member = mNode;
6695 mod_chanmode_announce(chanserv, channel, &change);
6698 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6699 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6700 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6706 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6708 struct mod_chanmode change;
6709 struct userData *channel;
6710 unsigned int ii, jj;
6712 if(!user->handle_info)
6715 mod_chanmode_init(&change);
6717 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6719 struct chanNode *cn;
6720 struct modeNode *mn;
6721 if(IsUserSuspended(channel)
6722 || IsSuspended(channel->channel)
6723 || !(cn = channel->channel->channel))
6726 mn = GetUserMode(cn, user);
6729 if(!IsUserSuspended(channel)
6730 && IsUserAutoInvite(channel)
6731 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6733 && !user->uplink->burst)
6734 irc_invite(chanserv, user, cn);
6738 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6739 channel->channel->visited = now;
6741 if(IsUserAutoOp(channel))
6743 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6744 change.args[0].mode = MODE_CHANOP;
6745 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6746 change.args[0].mode = MODE_VOICE;
6748 change.args[0].mode = 0;
6749 change.args[0].u.member = mn;
6750 if(change.args[0].mode)
6751 mod_chanmode_announce(chanserv, cn, &change);
6754 channel->seen = now;
6755 channel->present = 1;
6758 for(ii = 0; ii < user->channels.used; ++ii)
6760 struct chanNode *chan = user->channels.list[ii]->channel;
6761 struct banData *ban;
6763 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6764 || !chan->channel_info
6765 || IsSuspended(chan->channel_info))
6767 for(jj = 0; jj < chan->banlist.used; ++jj)
6768 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6770 if(jj < chan->banlist.used)
6772 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6774 char kick_reason[MAXLEN];
6775 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6777 change.args[0].mode = MODE_BAN;
6778 change.args[0].u.hostmask = ban->mask;
6779 mod_chanmode_announce(chanserv, chan, &change);
6780 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6781 KickChannelUser(user, chan, chanserv, kick_reason);
6782 ban->triggered = now;
6787 if(IsSupportHelper(user))
6789 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6791 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6793 HANDLE_SET_FLAG(user->handle_info, HELPING);
6801 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6803 struct chanData *cData;
6804 struct userData *uData;
6806 cData = mn->channel->channel_info;
6807 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6810 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6812 /* Allow for a bit of padding so that the limit doesn't
6813 track the user count exactly, which could get annoying. */
6814 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6816 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6817 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6821 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6823 scan_user_presence(uData, mn->user);
6825 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6826 cData->visited = now;
6829 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6832 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6833 struct chanNode *channel;
6834 struct userNode *exclude;
6835 /* When looking at the channel that is being /part'ed, we
6836 * have to skip over the client that is leaving. For
6837 * other channels, we must not do that.
6839 channel = chanserv_conf.support_channels.list[ii];
6840 exclude = (channel == mn->channel) ? mn->user : NULL;
6841 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6844 if(ii == chanserv_conf.support_channels.used)
6845 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6850 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6852 struct userData *uData;
6854 if(!channel->channel_info || !kicker || IsService(kicker)
6855 || (kicker == victim) || IsSuspended(channel->channel_info)
6856 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6859 if(protect_user(victim, kicker, channel->channel_info))
6861 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6862 KickChannelUser(kicker, channel, chanserv, reason);
6865 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6870 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6872 struct chanData *cData;
6874 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6877 cData = channel->channel_info;
6878 if(bad_topic(channel, user, channel->topic))
6880 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6881 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6882 SetChannelTopic(channel, chanserv, old_topic, 1);
6883 else if(cData->topic)
6884 SetChannelTopic(channel, chanserv, cData->topic, 1);
6887 /* With topicsnarf, grab the topic and save it as the default topic. */
6888 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6891 cData->topic = strdup(channel->topic);
6897 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6899 struct mod_chanmode *bounce = NULL;
6900 unsigned int bnc, ii;
6903 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6906 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6907 && mode_lock_violated(&channel->channel_info->modes, change))
6909 char correct[MAXLEN];
6910 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6911 mod_chanmode_format(&channel->channel_info->modes, correct);
6912 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6914 for(ii = bnc = 0; ii < change->argc; ++ii)
6916 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6918 const struct userNode *victim = change->args[ii].u.member->user;
6919 if(!protect_user(victim, user, channel->channel_info))
6922 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6925 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6926 bounce->args[bnc].u.member = GetUserMode(channel, user);
6927 if(bounce->args[bnc].u.member)
6931 bounce->args[bnc].mode = MODE_CHANOP;
6932 bounce->args[bnc].u.member = change->args[ii].u.member;
6934 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6936 else if(change->args[ii].mode & MODE_CHANOP)
6938 const struct userNode *victim = change->args[ii].u.member->user;
6939 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
6942 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6943 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6944 bounce->args[bnc].u.member = change->args[ii].u.member;
6947 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
6949 const char *ban = change->args[ii].u.hostmask;
6950 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
6953 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6954 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
6955 bounce->args[bnc].u.hostmask = strdup(ban);
6957 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
6962 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
6963 mod_chanmode_announce(chanserv, channel, bounce);
6964 for(ii = 0; ii < change->argc; ++ii)
6965 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
6966 free((char*)bounce->args[ii].u.hostmask);
6967 mod_chanmode_free(bounce);
6972 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
6974 struct chanNode *channel;
6975 struct banData *bData;
6976 struct mod_chanmode change;
6977 unsigned int ii, jj;
6978 char kick_reason[MAXLEN];
6980 mod_chanmode_init(&change);
6982 change.args[0].mode = MODE_BAN;
6983 for(ii = 0; ii < user->channels.used; ++ii)
6985 channel = user->channels.list[ii]->channel;
6986 /* Need not check for bans if they're opped or voiced. */
6987 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6989 /* Need not check for bans unless channel registration is active. */
6990 if(!channel->channel_info || IsSuspended(channel->channel_info))
6992 /* Look for a matching ban already on the channel. */
6993 for(jj = 0; jj < channel->banlist.used; ++jj)
6994 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
6996 /* Need not act if we found one. */
6997 if(jj < channel->banlist.used)
6999 /* Look for a matching ban in this channel. */
7000 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7002 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7004 change.args[0].u.hostmask = bData->mask;
7005 mod_chanmode_announce(chanserv, channel, &change);
7006 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7007 KickChannelUser(user, channel, chanserv, kick_reason);
7008 bData->triggered = now;
7009 break; /* we don't need to check any more bans in the channel */
7014 static void handle_rename(struct handle_info *handle, const char *old_handle)
7016 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7020 dict_remove2(handle_dnrs, old_handle, 1);
7021 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7022 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7027 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7029 struct userNode *h_user;
7031 if(handle->channels)
7033 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7034 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7036 while(handle->channels)
7037 del_channel_user(handle->channels, 1);
7042 handle_server_link(UNUSED_ARG(struct server *server))
7044 struct chanData *cData;
7046 for(cData = channelList; cData; cData = cData->next)
7048 if(!IsSuspended(cData))
7049 cData->may_opchan = 1;
7050 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7051 && !cData->channel->join_flooded
7052 && ((cData->channel->limit - cData->channel->members.used)
7053 < chanserv_conf.adjust_threshold))
7055 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7056 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7062 chanserv_conf_read(void)
7066 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7067 struct mod_chanmode *change;
7068 struct string_list *strlist;
7069 struct chanNode *chan;
7072 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7074 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7077 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7078 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7079 chanserv_conf.support_channels.used = 0;
7080 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7082 for(ii = 0; ii < strlist->used; ++ii)
7084 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7087 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7089 channelList_append(&chanserv_conf.support_channels, chan);
7092 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7095 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7098 chan = AddChannel(str, now, str2, NULL);
7100 channelList_append(&chanserv_conf.support_channels, chan);
7102 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7103 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7104 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7105 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7106 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7107 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7108 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7109 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7110 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7111 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7112 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7113 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7114 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7115 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7116 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7117 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7118 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7119 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7120 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7121 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7122 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7123 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7124 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7125 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7126 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7127 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7128 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7130 NickChange(chanserv, str, 0);
7131 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7132 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7133 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7134 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7135 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7136 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7137 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7138 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7139 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7140 chanserv_conf.max_owned = str ? atoi(str) : 5;
7141 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7142 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7143 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7144 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7145 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7146 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7147 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7150 safestrncpy(mode_line, str, sizeof(mode_line));
7151 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7152 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7153 && (change->argc < 2))
7155 chanserv_conf.default_modes = *change;
7156 mod_chanmode_free(change);
7158 free_string_list(chanserv_conf.set_shows);
7159 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7161 strlist = string_list_copy(strlist);
7164 static const char *list[] = {
7165 /* free form text */
7166 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7167 /* options based on user level */
7168 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7169 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7170 /* multiple choice options */
7171 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7172 /* binary options */
7173 "DynLimit", "NoDelete",
7177 strlist = alloc_string_list(ArrayLength(list)-1);
7178 for(ii=0; list[ii]; ii++)
7179 string_list_append(strlist, strdup(list[ii]));
7181 chanserv_conf.set_shows = strlist;
7182 /* We don't look things up now, in case the list refers to options
7183 * defined by modules initialized after this point. Just mark the
7184 * function list as invalid, so it will be initialized.
7186 set_shows_list.used = 0;
7187 free_string_list(chanserv_conf.eightball);
7188 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7191 strlist = string_list_copy(strlist);
7195 strlist = alloc_string_list(4);
7196 string_list_append(strlist, strdup("Yes."));
7197 string_list_append(strlist, strdup("No."));
7198 string_list_append(strlist, strdup("Maybe so."));
7200 chanserv_conf.eightball = strlist;
7201 free_string_list(chanserv_conf.old_ban_names);
7202 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7204 strlist = string_list_copy(strlist);
7206 strlist = alloc_string_list(2);
7207 chanserv_conf.old_ban_names = strlist;
7208 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7209 off_channel = str ? atoi(str) : 0;
7213 chanserv_note_type_read(const char *key, struct record_data *rd)
7216 struct note_type *ntype;
7219 if(!(obj = GET_RECORD_OBJECT(rd)))
7221 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7224 if(!(ntype = chanserv_create_note_type(key)))
7226 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7230 /* Figure out set access */
7231 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7233 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7234 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7236 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7238 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7239 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7241 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7243 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7247 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7248 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7249 ntype->set_access.min_opserv = 0;
7252 /* Figure out visibility */
7253 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7254 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7255 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7256 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7257 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7258 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7259 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7260 ntype->visible_type = NOTE_VIS_ALL;
7262 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7264 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7265 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7269 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7271 struct handle_info *handle;
7272 struct userData *uData;
7273 char *seen, *inf, *flags;
7274 unsigned long last_seen;
7275 unsigned short access_level;
7277 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7279 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7283 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7284 if(access_level > UL_OWNER)
7286 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7290 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7291 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7292 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7293 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7294 handle = get_handle_info(key);
7297 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7301 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7302 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7306 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7308 struct banData *bData;
7309 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7310 unsigned long set_time, triggered_time, expires_time;
7312 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7314 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7318 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7319 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7320 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7321 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7322 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7323 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7324 if (!reason || !owner)
7327 set_time = set ? strtoul(set, NULL, 0) : now;
7328 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7330 expires_time = strtoul(s_expires, NULL, 0);
7332 expires_time = set_time + atoi(s_duration);
7336 if(!reason || (expires_time && (expires_time < now)))
7339 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7342 static struct suspended *
7343 chanserv_read_suspended(dict_t obj)
7345 struct suspended *suspended = calloc(1, sizeof(*suspended));
7349 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7350 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7351 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7352 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7353 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7354 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7355 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7356 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7357 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7358 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7363 chanserv_channel_read(const char *key, struct record_data *hir)
7365 struct suspended *suspended;
7366 struct mod_chanmode *modes;
7367 struct chanNode *cNode;
7368 struct chanData *cData;
7369 struct dict *channel, *obj;
7370 char *str, *argv[10];
7374 channel = hir->d.object;
7376 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7379 cNode = AddChannel(key, now, NULL, NULL);
7382 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7385 cData = register_channel(cNode, str);
7388 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7392 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7394 enum levelOption lvlOpt;
7395 enum charOption chOpt;
7397 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7398 cData->flags = atoi(str);
7400 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7402 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7404 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7405 else if(levelOptions[lvlOpt].old_flag)
7407 if(cData->flags & levelOptions[lvlOpt].old_flag)
7408 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7410 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7414 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7416 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7418 cData->chOpts[chOpt] = str[0];
7421 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7423 enum levelOption lvlOpt;
7424 enum charOption chOpt;
7427 cData->flags = base64toint(str, 5);
7428 count = strlen(str += 5);
7429 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7432 if(levelOptions[lvlOpt].old_flag)
7434 if(cData->flags & levelOptions[lvlOpt].old_flag)
7435 lvl = levelOptions[lvlOpt].flag_value;
7437 lvl = levelOptions[lvlOpt].default_value;
7439 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7441 case 'c': lvl = UL_COOWNER; break;
7442 case 'm': lvl = UL_MASTER; break;
7443 case 'n': lvl = UL_OWNER+1; break;
7444 case 'o': lvl = UL_OP; break;
7445 case 'p': lvl = UL_PEON; break;
7446 case 'w': lvl = UL_OWNER; break;
7447 default: lvl = 0; break;
7449 cData->lvlOpts[lvlOpt] = lvl;
7451 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7452 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7455 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7457 suspended = chanserv_read_suspended(obj);
7458 cData->suspended = suspended;
7459 suspended->cData = cData;
7460 /* We could use suspended->expires and suspended->revoked to
7461 * set the CHANNEL_SUSPENDED flag, but we don't. */
7463 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7465 suspended = calloc(1, sizeof(*suspended));
7466 suspended->issued = 0;
7467 suspended->revoked = 0;
7468 suspended->suspender = strdup(str);
7469 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7470 suspended->expires = str ? atoi(str) : 0;
7471 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7472 suspended->reason = strdup(str ? str : "No reason");
7473 suspended->previous = NULL;
7474 cData->suspended = suspended;
7475 suspended->cData = cData;
7479 cData->flags &= ~CHANNEL_SUSPENDED;
7480 suspended = NULL; /* to squelch a warning */
7483 if(IsSuspended(cData)) {
7484 if(suspended->expires > now)
7485 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7486 else if(suspended->expires)
7487 cData->flags &= ~CHANNEL_SUSPENDED;
7490 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7491 struct mod_chanmode change;
7492 mod_chanmode_init(&change);
7494 change.args[0].mode = MODE_CHANOP;
7495 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7496 mod_chanmode_announce(chanserv, cNode, &change);
7499 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7500 cData->registered = str ? strtoul(str, NULL, 0) : now;
7501 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7502 cData->visited = str ? strtoul(str, NULL, 0) : now;
7503 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7504 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7505 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7506 cData->max = str ? atoi(str) : 0;
7507 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
7508 cData->max_time = str ? atoi(str) : 0;
7509 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7510 cData->greeting = str ? strdup(str) : NULL;
7511 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7512 cData->user_greeting = str ? strdup(str) : NULL;
7513 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7514 cData->topic_mask = str ? strdup(str) : NULL;
7515 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7516 cData->topic = str ? strdup(str) : NULL;
7518 if(!IsSuspended(cData)
7519 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7520 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7521 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7522 cData->modes = *modes;
7524 cData->modes.modes_set |= MODE_REGISTERED;
7525 if(cData->modes.argc > 1)
7526 cData->modes.argc = 1;
7527 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7528 mod_chanmode_free(modes);
7531 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7532 for(it = dict_first(obj); it; it = iter_next(it))
7533 user_read_helper(iter_key(it), iter_data(it), cData);
7535 if(!cData->users && !IsProtected(cData))
7537 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7538 unregister_channel(cData, "has empty user list.");
7542 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7543 for(it = dict_first(obj); it; it = iter_next(it))
7544 ban_read_helper(iter_key(it), iter_data(it), cData);
7546 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7547 for(it = dict_first(obj); it; it = iter_next(it))
7549 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7550 struct record_data *rd = iter_data(it);
7551 const char *note, *setter;
7553 if(rd->type != RECDB_OBJECT)
7555 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7559 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7561 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7563 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7567 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7568 if(!setter) setter = "<unknown>";
7569 chanserv_add_channel_note(cData, ntype, setter, note);
7577 chanserv_dnr_read(const char *key, struct record_data *hir)
7579 const char *setter, *reason, *str;
7580 struct do_not_register *dnr;
7581 unsigned long expiry;
7583 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7586 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7589 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7592 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7595 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7596 expiry = str ? strtoul(str, NULL, 0) : 0;
7597 if(expiry && expiry <= now)
7599 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7602 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7604 dnr->set = atoi(str);
7610 chanserv_saxdb_read(struct dict *database)
7612 struct dict *section;
7615 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7616 for(it = dict_first(section); it; it = iter_next(it))
7617 chanserv_note_type_read(iter_key(it), iter_data(it));
7619 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7620 for(it = dict_first(section); it; it = iter_next(it))
7621 chanserv_channel_read(iter_key(it), iter_data(it));
7623 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7624 for(it = dict_first(section); it; it = iter_next(it))
7625 chanserv_dnr_read(iter_key(it), iter_data(it));
7631 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7633 int high_present = 0;
7634 saxdb_start_record(ctx, KEY_USERS, 1);
7635 for(; uData; uData = uData->next)
7637 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7639 saxdb_start_record(ctx, uData->handle->handle, 0);
7640 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7641 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7643 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7645 saxdb_write_string(ctx, KEY_INFO, uData->info);
7646 saxdb_end_record(ctx);
7648 saxdb_end_record(ctx);
7649 return high_present;
7653 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7657 saxdb_start_record(ctx, KEY_BANS, 1);
7658 for(; bData; bData = bData->next)
7660 saxdb_start_record(ctx, bData->mask, 0);
7661 saxdb_write_int(ctx, KEY_SET, bData->set);
7662 if(bData->triggered)
7663 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7665 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7667 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7669 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7670 saxdb_end_record(ctx);
7672 saxdb_end_record(ctx);
7676 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7678 saxdb_start_record(ctx, name, 0);
7679 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7680 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7682 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7684 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7686 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7688 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7689 saxdb_end_record(ctx);
7693 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7697 enum levelOption lvlOpt;
7698 enum charOption chOpt;
7700 saxdb_start_record(ctx, channel->channel->name, 1);
7702 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7703 saxdb_write_int(ctx, KEY_MAX, channel->max);
7704 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
7706 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7707 if(channel->registrar)
7708 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7709 if(channel->greeting)
7710 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7711 if(channel->user_greeting)
7712 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7713 if(channel->topic_mask)
7714 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7715 if(channel->suspended)
7716 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7718 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7719 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7720 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7721 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7722 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7724 buf[0] = channel->chOpts[chOpt];
7726 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7728 saxdb_end_record(ctx);
7730 if(channel->modes.modes_set || channel->modes.modes_clear)
7732 mod_chanmode_format(&channel->modes, buf);
7733 saxdb_write_string(ctx, KEY_MODES, buf);
7736 high_present = chanserv_write_users(ctx, channel->users);
7737 chanserv_write_bans(ctx, channel->bans);
7739 if(dict_size(channel->notes))
7743 saxdb_start_record(ctx, KEY_NOTES, 1);
7744 for(it = dict_first(channel->notes); it; it = iter_next(it))
7746 struct note *note = iter_data(it);
7747 saxdb_start_record(ctx, iter_key(it), 0);
7748 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7749 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7750 saxdb_end_record(ctx);
7752 saxdb_end_record(ctx);
7755 if(channel->ownerTransfer)
7756 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7757 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7758 saxdb_end_record(ctx);
7762 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7766 saxdb_start_record(ctx, ntype->name, 0);
7767 switch(ntype->set_access_type)
7769 case NOTE_SET_CHANNEL_ACCESS:
7770 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7772 case NOTE_SET_CHANNEL_SETTER:
7773 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7775 case NOTE_SET_PRIVILEGED: default:
7776 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7779 switch(ntype->visible_type)
7781 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7782 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7783 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7785 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7786 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7787 saxdb_end_record(ctx);
7791 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7793 struct do_not_register *dnr;
7794 dict_iterator_t it, next;
7796 for(it = dict_first(dnrs); it; it = next)
7798 next = iter_next(it);
7799 dnr = iter_data(it);
7800 if(dnr->expires && dnr->expires <= now)
7802 dict_remove(dnrs, iter_key(it));
7805 saxdb_start_record(ctx, dnr->chan_name, 0);
7807 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7809 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7810 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7811 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7812 saxdb_end_record(ctx);
7817 chanserv_saxdb_write(struct saxdb_context *ctx)
7820 struct chanData *channel;
7823 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7824 for(it = dict_first(note_types); it; it = iter_next(it))
7825 chanserv_write_note_type(ctx, iter_data(it));
7826 saxdb_end_record(ctx);
7829 saxdb_start_record(ctx, KEY_DNR, 1);
7830 write_dnrs_helper(ctx, handle_dnrs);
7831 write_dnrs_helper(ctx, plain_dnrs);
7832 write_dnrs_helper(ctx, mask_dnrs);
7833 saxdb_end_record(ctx);
7836 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7837 for(channel = channelList; channel; channel = channel->next)
7838 chanserv_write_channel(ctx, channel);
7839 saxdb_end_record(ctx);
7845 chanserv_db_cleanup(void) {
7847 unreg_part_func(handle_part);
7849 unregister_channel(channelList, "terminating.");
7850 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7851 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7852 free(chanserv_conf.support_channels.list);
7853 dict_delete(handle_dnrs);
7854 dict_delete(plain_dnrs);
7855 dict_delete(mask_dnrs);
7856 dict_delete(note_types);
7857 free_string_list(chanserv_conf.eightball);
7858 free_string_list(chanserv_conf.old_ban_names);
7859 free_string_list(chanserv_conf.set_shows);
7860 free(set_shows_list.list);
7861 free(uset_shows_list.list);
7864 struct userData *helper = helperList;
7865 helperList = helperList->next;
7870 #if defined(GCC_VARMACROS)
7871 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7872 #elif defined(C99_VARMACROS)
7873 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7875 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7876 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7879 init_chanserv(const char *nick)
7881 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7882 conf_register_reload(chanserv_conf_read);
7886 reg_server_link_func(handle_server_link);
7887 reg_new_channel_func(handle_new_channel);
7888 reg_join_func(handle_join);
7889 reg_part_func(handle_part);
7890 reg_kick_func(handle_kick);
7891 reg_topic_func(handle_topic);
7892 reg_mode_change_func(handle_mode);
7893 reg_nick_change_func(handle_nick_change);
7894 reg_auth_func(handle_auth);
7897 reg_handle_rename_func(handle_rename);
7898 reg_unreg_func(handle_unreg);
7900 handle_dnrs = dict_new();
7901 dict_set_free_data(handle_dnrs, free);
7902 plain_dnrs = dict_new();
7903 dict_set_free_data(plain_dnrs, free);
7904 mask_dnrs = dict_new();
7905 dict_set_free_data(mask_dnrs, free);
7907 reg_svccmd_unbind_func(handle_svccmd_unbind);
7908 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7909 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7910 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7911 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7912 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7913 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7914 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7915 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7916 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7917 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7918 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7919 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7920 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
7922 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
7923 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
7925 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7926 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7927 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7928 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7929 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7931 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
7932 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
7933 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
7934 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7935 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7937 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7938 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
7939 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7940 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
7942 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
7943 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
7944 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7945 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
7946 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
7947 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7948 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7949 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
7951 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7952 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7953 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7954 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
7955 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
7956 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7957 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
7958 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
7959 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
7960 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
7961 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
7962 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
7963 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7964 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
7966 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
7967 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7968 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7969 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
7970 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
7972 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
7973 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
7975 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
7976 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7977 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7978 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7979 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7980 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7981 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7982 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7983 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7984 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7985 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
7987 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
7988 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
7990 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
7991 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
7992 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
7993 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
7995 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
7996 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
7997 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
7998 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
7999 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8001 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8002 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8003 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8004 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8005 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8006 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8008 /* Channel options */
8009 DEFINE_CHANNEL_OPTION(defaulttopic);
8010 DEFINE_CHANNEL_OPTION(topicmask);
8011 DEFINE_CHANNEL_OPTION(greeting);
8012 DEFINE_CHANNEL_OPTION(usergreeting);
8013 DEFINE_CHANNEL_OPTION(modes);
8014 DEFINE_CHANNEL_OPTION(enfops);
8015 DEFINE_CHANNEL_OPTION(giveops);
8016 DEFINE_CHANNEL_OPTION(protect);
8017 DEFINE_CHANNEL_OPTION(enfmodes);
8018 DEFINE_CHANNEL_OPTION(enftopic);
8019 DEFINE_CHANNEL_OPTION(pubcmd);
8020 DEFINE_CHANNEL_OPTION(givevoice);
8021 DEFINE_CHANNEL_OPTION(userinfo);
8022 DEFINE_CHANNEL_OPTION(dynlimit);
8023 DEFINE_CHANNEL_OPTION(topicsnarf);
8024 DEFINE_CHANNEL_OPTION(nodelete);
8025 DEFINE_CHANNEL_OPTION(toys);
8026 DEFINE_CHANNEL_OPTION(setters);
8027 DEFINE_CHANNEL_OPTION(topicrefresh);
8028 DEFINE_CHANNEL_OPTION(ctcpusers);
8029 DEFINE_CHANNEL_OPTION(ctcpreaction);
8030 DEFINE_CHANNEL_OPTION(inviteme);
8031 DEFINE_CHANNEL_OPTION(unreviewed);
8032 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8033 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8035 DEFINE_CHANNEL_OPTION(offchannel);
8036 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8038 /* Alias set topic to set defaulttopic for compatibility. */
8039 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8042 DEFINE_USER_OPTION(noautoop);
8043 DEFINE_USER_OPTION(autoinvite);
8044 DEFINE_USER_OPTION(info);
8046 /* Alias uset autovoice to uset autoop. */
8047 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8049 note_types = dict_new();
8050 dict_set_free_data(note_types, chanserv_deref_note_type);
8053 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8054 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8055 service_register(chanserv)->trigger = '!';
8056 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8058 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8060 if(chanserv_conf.channel_expire_frequency)
8061 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8063 if(chanserv_conf.dnr_expire_frequency)
8064 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8066 if(chanserv_conf.refresh_period)
8068 unsigned long next_refresh;
8069 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8070 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8073 reg_exit_func(chanserv_db_cleanup);
8074 message_register_table(msgtab);