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"
108 #define KEY_EXPIRE "expire"
111 #define KEY_LEVEL "level"
112 #define KEY_INFO "info"
113 #define KEY_SEEN "seen"
116 #define KEY_OWNER "owner"
117 #define KEY_REASON "reason"
118 #define KEY_SET "set"
119 #define KEY_DURATION "duration"
120 #define KEY_EXPIRES "expires"
121 #define KEY_TRIGGERED "triggered"
123 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
124 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
125 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
127 /* Administrative messages */
128 static const struct message_entry msgtab[] = {
129 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
131 /* Channel registration */
132 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
133 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
134 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
135 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
136 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
137 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
139 /* Do-not-register channels */
140 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
141 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
142 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
143 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
144 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
145 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
146 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
147 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
148 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
149 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
150 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
151 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
152 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
153 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
155 /* Channel unregistration */
156 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
157 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
158 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
159 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
162 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
163 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
165 /* Channel merging */
166 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
167 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
168 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
169 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
170 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
172 /* Handle unregistration */
173 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
176 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
177 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
178 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
179 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
180 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
181 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
182 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
183 { "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." },
184 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
185 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
186 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
187 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
188 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
189 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
191 /* Removing yourself from a channel. */
192 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
193 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
194 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
196 /* User management */
197 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
198 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
199 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
200 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
201 { "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." },
202 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
203 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
204 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
206 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
207 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
208 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
209 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
210 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
211 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
212 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
215 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
216 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
217 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
218 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
219 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
220 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
221 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
222 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
223 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
224 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
225 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
226 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
227 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
228 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
229 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
230 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
232 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
234 /* Channel management */
235 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
236 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
237 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
239 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
240 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
241 { "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" },
242 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
243 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
244 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
245 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
247 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
248 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
249 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
250 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
251 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
252 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
253 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
254 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
255 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
256 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
257 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
258 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
259 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
260 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
261 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
262 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
263 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
264 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
265 { "CSMSG_SET_MODES", "$bModes $b %s" },
266 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
267 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
268 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
269 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
270 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
271 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
272 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
273 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
274 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
275 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
276 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
277 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
278 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
279 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
280 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
281 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
282 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
283 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
284 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
285 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
286 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
287 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
288 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
289 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
290 { "CSMSG_USET_INFO", "$bInfo $b %s" },
292 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
293 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
294 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
295 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
296 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
297 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
298 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
299 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
300 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
301 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
302 { "CSMSG_PROTECT_NONE", "No users will be protected." },
303 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
304 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
305 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
306 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
307 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
308 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
309 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
310 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
311 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
312 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
313 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
314 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
316 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
317 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
318 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
319 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
320 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
321 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
322 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
323 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
325 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
326 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
327 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
329 /* Channel userlist */
330 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
331 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
332 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
333 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
335 /* Channel note list */
336 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
337 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
338 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
339 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
340 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
341 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
342 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
343 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
344 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
345 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
346 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
347 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
348 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
349 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
350 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
352 /* Channel [un]suspension */
353 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
354 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
355 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
356 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
357 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
358 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
359 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
361 /* Access information */
362 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
363 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
364 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
365 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
366 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
367 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
368 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
369 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
370 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
371 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
372 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
373 { "CSMSG_UC_H_TITLE", "network helper" },
374 { "CSMSG_LC_H_TITLE", "support helper" },
375 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
377 /* Seen information */
378 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
379 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
380 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
381 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
383 /* Names information */
384 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
385 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
387 /* Channel information */
388 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
389 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
390 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
391 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
392 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
393 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
394 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
395 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
396 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
397 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
398 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
399 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
400 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
401 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
402 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
403 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
404 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
405 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
406 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
407 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
408 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
409 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
411 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
412 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
413 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
414 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
415 { "CSMSG_PEEK_OPS", "$bOps:$b" },
416 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
418 /* Network information */
419 { "CSMSG_NETWORK_INFO", "Network Information:" },
420 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
421 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
422 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
423 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
424 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
425 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
426 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
427 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
430 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
431 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
432 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
434 /* Channel searches */
435 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
436 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
437 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
438 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
440 /* Channel configuration */
441 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
442 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
443 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
444 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
447 { "CSMSG_USER_OPTIONS", "User Options:" },
448 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
451 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
452 { "CSMSG_PING_RESPONSE", "Pong!" },
453 { "CSMSG_WUT_RESPONSE", "wut" },
454 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
455 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
456 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
457 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
458 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
459 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
460 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
463 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
467 /* eject_user and unban_user flags */
468 #define ACTION_KICK 0x0001
469 #define ACTION_BAN 0x0002
470 #define ACTION_ADD_BAN 0x0004
471 #define ACTION_ADD_TIMED_BAN 0x0008
472 #define ACTION_UNBAN 0x0010
473 #define ACTION_DEL_BAN 0x0020
475 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
476 #define MODELEN 40 + KEYLEN
480 #define CSFUNC_ARGS user, channel, argc, argv, cmd
482 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
483 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
484 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
485 reply("MSG_MISSING_PARAMS", argv[0]); \
489 DECLARE_LIST(dnrList, struct do_not_register *);
490 DEFINE_LIST(dnrList, struct do_not_register *)
492 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
494 struct userNode *chanserv;
497 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
498 static struct log_type *CS_LOG;
502 struct channelList support_channels;
503 struct mod_chanmode default_modes;
505 unsigned long db_backup_frequency;
506 unsigned long channel_expire_frequency;
507 unsigned long dnr_expire_frequency;
509 unsigned long invited_timeout;
511 unsigned long info_delay;
512 unsigned long adjust_delay;
513 unsigned long channel_expire_delay;
514 unsigned int nodelete_level;
516 unsigned int adjust_threshold;
517 int join_flood_threshold;
519 unsigned int greeting_length;
520 unsigned int refresh_period;
521 unsigned int giveownership_period;
523 unsigned int max_owned;
524 unsigned int max_chan_users;
525 unsigned int max_chan_bans;
526 unsigned int max_userinfo_length;
528 struct string_list *set_shows;
529 struct string_list *eightball;
530 struct string_list *old_ban_names;
532 const char *ctcp_short_ban_duration;
533 const char *ctcp_long_ban_duration;
535 const char *irc_operator_epithet;
536 const char *network_helper_epithet;
537 const char *support_helper_epithet;
542 struct userNode *user;
543 struct userNode *bot;
544 struct chanNode *channel;
546 unsigned short lowest;
547 unsigned short highest;
548 struct userData **users;
549 struct helpfile_table table;
554 struct userNode *user;
555 struct chanNode *chan;
558 enum note_access_type
560 NOTE_SET_CHANNEL_ACCESS,
561 NOTE_SET_CHANNEL_SETTER,
565 enum note_visible_type
568 NOTE_VIS_CHANNEL_USERS,
574 enum note_access_type set_access_type;
576 unsigned int min_opserv;
577 unsigned short min_ulevel;
579 enum note_visible_type visible_type;
580 unsigned int max_length;
587 struct note_type *type;
588 char setter[NICKSERV_HANDLE_LEN+1];
592 static unsigned int registered_channels;
593 static unsigned int banCount;
595 static const struct {
598 unsigned short level;
601 { "peon", "Peon", UL_PEON, '+' },
602 { "op", "Op", UL_OP, '@' },
603 { "master", "Master", UL_MASTER, '%' },
604 { "coowner", "Coowner", UL_COOWNER, '*' },
605 { "owner", "Owner", UL_OWNER, '!' },
606 { "helper", "BUG:", UL_HELPER, 'X' }
609 static const struct {
612 unsigned short default_value;
613 unsigned int old_idx;
614 unsigned int old_flag;
615 unsigned short flag_value;
617 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
618 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
619 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
620 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
621 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
622 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
623 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
624 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
625 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
626 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
627 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 }
630 struct charOptionValues {
633 } protectValues[] = {
634 { 'a', "CSMSG_PROTECT_ALL" },
635 { 'e', "CSMSG_PROTECT_EQUAL" },
636 { 'l', "CSMSG_PROTECT_LOWER" },
637 { 'n', "CSMSG_PROTECT_NONE" }
639 { 'd', "CSMSG_TOYS_DISABLED" },
640 { 'n', "CSMSG_TOYS_PRIVATE" },
641 { 'p', "CSMSG_TOYS_PUBLIC" }
642 }, topicRefreshValues[] = {
643 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
644 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
645 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
646 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
647 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
648 }, ctcpReactionValues[] = {
649 { 'k', "CSMSG_CTCPREACTION_KICK" },
650 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
651 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
652 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
655 static const struct {
659 unsigned int old_idx;
661 struct charOptionValues *values;
663 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
664 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
665 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
666 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
669 struct userData *helperList;
670 struct chanData *channelList;
671 static struct module *chanserv_module;
672 static unsigned int userCount;
674 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
675 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
678 user_level_from_name(const char *name, unsigned short clamp_level)
680 unsigned int level = 0, ii;
682 level = strtoul(name, NULL, 10);
683 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
684 if(!irccasecmp(name, accessLevels[ii].name))
685 level = accessLevels[ii].level;
686 if(level > clamp_level)
692 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
695 *minl = strtoul(arg, &sep, 10);
703 *maxl = strtoul(sep+1, &sep, 10);
711 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
713 struct userData *uData, **head;
715 if(!channel || !handle)
718 if(override && HANDLE_FLAGGED(handle, HELPING)
719 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
721 for(uData = helperList;
722 uData && uData->handle != handle;
723 uData = uData->next);
727 uData = calloc(1, sizeof(struct userData));
728 uData->handle = handle;
730 uData->access = UL_HELPER;
736 uData->next = helperList;
738 helperList->prev = uData;
746 for(uData = channel->users; uData; uData = uData->next)
747 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
750 head = &(channel->users);
753 if(uData && (uData != *head))
755 /* Shuffle the user to the head of whatever list he was in. */
757 uData->next->prev = uData->prev;
759 uData->prev->next = uData->next;
765 (**head).prev = uData;
772 /* Returns non-zero if user has at least the minimum access.
773 * exempt_owner is set when handling !set, so the owner can set things
776 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
778 struct userData *uData;
779 struct chanData *cData = channel->channel_info;
780 unsigned short minimum = cData->lvlOpts[opt];
783 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
786 if(minimum <= uData->access)
788 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
793 /* Scan for other users authenticated to the same handle
794 still in the channel. If so, keep them listed as present.
796 user is optional, if not null, it skips checking that userNode
797 (for the handle_part function) */
799 scan_user_presence(struct userData *uData, struct userNode *user)
803 if(IsSuspended(uData->channel)
804 || IsUserSuspended(uData)
805 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
817 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
819 unsigned int eflags, argc;
821 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
823 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
824 if(!channel->channel_info
825 || IsSuspended(channel->channel_info)
827 || !ircncasecmp(text, "ACTION ", 7))
829 /* Figure out the minimum level needed to CTCP the channel */
830 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
832 /* We need to enforce against them; do so. */
834 argv[0] = (char*)text;
835 argv[1] = user->nick;
837 if(GetUserMode(channel, user))
838 eflags |= ACTION_KICK;
839 switch(channel->channel_info->chOpts[chCTCPReaction]) {
840 default: case 'k': /* just do the kick */ break;
842 eflags |= ACTION_BAN;
845 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
846 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
849 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
850 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
853 argv[argc++] = bad_ctcp_reason;
854 eject_user(chanserv, channel, argc, argv, NULL, eflags);
858 chanserv_create_note_type(const char *name)
860 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
861 strcpy(ntype->name, name);
863 dict_insert(note_types, ntype->name, ntype);
868 chanserv_deref_note_type(void *data)
870 struct note_type *ntype = data;
872 if(--ntype->refs > 0)
878 chanserv_flush_note_type(struct note_type *ntype)
880 struct chanData *cData;
881 for(cData = channelList; cData; cData = cData->next)
882 dict_remove(cData->notes, ntype->name);
886 chanserv_truncate_notes(struct note_type *ntype)
888 struct chanData *cData;
890 unsigned int size = sizeof(*note) + ntype->max_length;
892 for(cData = channelList; cData; cData = cData->next) {
893 note = dict_find(cData->notes, ntype->name, NULL);
896 if(strlen(note->note) <= ntype->max_length)
898 dict_remove2(cData->notes, ntype->name, 1);
899 note = realloc(note, size);
900 note->note[ntype->max_length] = 0;
901 dict_insert(cData->notes, ntype->name, note);
905 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
908 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
911 unsigned int len = strlen(text);
913 if(len > type->max_length) len = type->max_length;
914 note = calloc(1, sizeof(*note) + len);
916 strncpy(note->setter, setter, sizeof(note->setter)-1);
917 memcpy(note->note, text, len);
919 dict_insert(channel->notes, type->name, note);
925 chanserv_free_note(void *data)
927 struct note *note = data;
929 chanserv_deref_note_type(note->type);
930 assert(note->type->refs > 0); /* must use delnote to remove the type */
934 static MODCMD_FUNC(cmd_createnote) {
935 struct note_type *ntype;
936 unsigned int arg = 1, existed = 0, max_length;
938 if((ntype = dict_find(note_types, argv[1], NULL)))
941 ntype = chanserv_create_note_type(argv[arg]);
942 if(!irccasecmp(argv[++arg], "privileged"))
945 ntype->set_access_type = NOTE_SET_PRIVILEGED;
946 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
948 else if(!irccasecmp(argv[arg], "channel"))
950 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
953 reply("CSMSG_INVALID_ACCESS", argv[arg]);
956 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
957 ntype->set_access.min_ulevel = ulvl;
959 else if(!irccasecmp(argv[arg], "setter"))
961 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
965 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
969 if(!irccasecmp(argv[++arg], "privileged"))
970 ntype->visible_type = NOTE_VIS_PRIVILEGED;
971 else if(!irccasecmp(argv[arg], "channel_users"))
972 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
973 else if(!irccasecmp(argv[arg], "all"))
974 ntype->visible_type = NOTE_VIS_ALL;
976 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
980 if((arg+1) >= argc) {
981 reply("MSG_MISSING_PARAMS", argv[0]);
984 max_length = strtoul(argv[++arg], NULL, 0);
985 if(max_length < 20 || max_length > 450)
987 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
990 if(existed && (max_length < ntype->max_length))
992 ntype->max_length = max_length;
993 chanserv_truncate_notes(ntype);
995 ntype->max_length = max_length;
998 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1000 reply("CSMSG_NOTE_CREATED", ntype->name);
1005 dict_remove(note_types, ntype->name);
1009 static MODCMD_FUNC(cmd_removenote) {
1010 struct note_type *ntype;
1013 ntype = dict_find(note_types, argv[1], NULL);
1014 force = (argc > 2) && !irccasecmp(argv[2], "force");
1017 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1024 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1027 chanserv_flush_note_type(ntype);
1029 dict_remove(note_types, argv[1]);
1030 reply("CSMSG_NOTE_DELETED", argv[1]);
1035 chanserv_expire_channel(void *data)
1037 struct chanData *channel = data;
1038 char reason[MAXLEN];
1039 sprintf(reason, "channel expired.");
1040 channel->expiry = 0;
1041 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1042 unregister_channel(channel, reason);
1045 static MODCMD_FUNC(chan_opt_expire)
1047 struct chanData *cData = channel->channel_info;
1048 unsigned long value = cData->expiry;
1052 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1054 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1057 unsigned long expiry,duration;
1059 /* The two directions can have different ACLs. */
1060 if(!strcmp(argv[1], "0"))
1062 else if((duration = ParseInterval(argv[1])))
1063 expiry = now + duration;
1066 reply("MSG_INVALID_DURATION", argv[1]);
1070 if (expiry != value)
1074 timeq_del(value, chanserv_expire_channel, cData, 0);
1077 cData->expiry = value;
1080 timeq_add(expiry, chanserv_expire_channel, cData);
1085 if(cData->expiry > now) {
1086 char expirestr[INTERVALLEN];
1087 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1089 reply("CSMSG_SET_EXPIRE_OFF");
1094 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1098 if(orig->modes_set & change->modes_clear)
1100 if(orig->modes_clear & change->modes_set)
1102 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1103 && strcmp(orig->new_key, change->new_key))
1105 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1106 && (orig->new_limit != change->new_limit))
1111 static char max_length_text[MAXLEN+1][16];
1113 static struct helpfile_expansion
1114 chanserv_expand_variable(const char *variable)
1116 struct helpfile_expansion exp;
1118 if(!irccasecmp(variable, "notes"))
1121 exp.type = HF_TABLE;
1122 exp.value.table.length = 1;
1123 exp.value.table.width = 3;
1124 exp.value.table.flags = 0;
1125 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1126 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1127 exp.value.table.contents[0][0] = "Note Type";
1128 exp.value.table.contents[0][1] = "Visibility";
1129 exp.value.table.contents[0][2] = "Max Length";
1130 for(it=dict_first(note_types); it; it=iter_next(it))
1132 struct note_type *ntype = iter_data(it);
1135 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1136 row = exp.value.table.length++;
1137 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1138 exp.value.table.contents[row][0] = ntype->name;
1139 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1140 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1142 if(!max_length_text[ntype->max_length][0])
1143 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1144 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1149 exp.type = HF_STRING;
1150 exp.value.str = NULL;
1154 static struct chanData*
1155 register_channel(struct chanNode *cNode, char *registrar)
1157 struct chanData *channel;
1158 enum levelOption lvlOpt;
1159 enum charOption chOpt;
1161 channel = calloc(1, sizeof(struct chanData));
1163 channel->notes = dict_new();
1164 dict_set_free_data(channel->notes, chanserv_free_note);
1166 channel->registrar = strdup(registrar);
1167 channel->registered = now;
1168 channel->visited = now;
1169 channel->limitAdjusted = now;
1170 channel->ownerTransfer = now;
1171 channel->flags = CHANNEL_DEFAULT_FLAGS;
1172 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1173 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1174 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1175 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1177 channel->prev = NULL;
1178 channel->next = channelList;
1181 channelList->prev = channel;
1182 channelList = channel;
1183 registered_channels++;
1185 channel->channel = cNode;
1187 cNode->channel_info = channel;
1192 static struct userData*
1193 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1195 struct userData *ud;
1197 if(access_level > UL_OWNER)
1200 ud = calloc(1, sizeof(*ud));
1201 ud->channel = channel;
1202 ud->handle = handle;
1204 ud->access = access_level;
1205 ud->info = info ? strdup(info) : NULL;
1208 ud->next = channel->users;
1210 channel->users->prev = ud;
1211 channel->users = ud;
1213 channel->userCount++;
1217 ud->u_next = ud->handle->channels;
1219 ud->u_next->u_prev = ud;
1220 ud->handle->channels = ud;
1226 del_channel_user(struct userData *user, int do_gc)
1228 struct chanData *channel = user->channel;
1230 channel->userCount--;
1234 user->prev->next = user->next;
1236 channel->users = user->next;
1238 user->next->prev = user->prev;
1241 user->u_prev->u_next = user->u_next;
1243 user->handle->channels = user->u_next;
1245 user->u_next->u_prev = user->u_prev;
1249 if(do_gc && !channel->users && !IsProtected(channel)) {
1250 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1251 unregister_channel(channel, "lost all users.");
1255 static void expire_ban(void *data);
1258 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1261 unsigned int ii, l1, l2;
1266 bd = malloc(sizeof(struct banData));
1268 bd->channel = channel;
1270 bd->triggered = triggered;
1271 bd->expires = expires;
1273 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1275 extern const char *hidden_host_suffix;
1276 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1280 l2 = strlen(old_name);
1283 if(irccasecmp(mask + l1 - l2, old_name))
1285 new_mask = alloca(MAXLEN);
1286 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1289 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1291 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1292 bd->reason = strdup(reason);
1295 timeq_add(expires, expire_ban, bd);
1298 bd->next = channel->bans;
1300 channel->bans->prev = bd;
1302 channel->banCount++;
1309 del_channel_ban(struct banData *ban)
1311 ban->channel->banCount--;
1315 ban->prev->next = ban->next;
1317 ban->channel->bans = ban->next;
1320 ban->next->prev = ban->prev;
1323 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1332 expire_ban(void *data)
1334 struct banData *bd = data;
1335 if(!IsSuspended(bd->channel))
1337 struct banList bans;
1338 struct mod_chanmode change;
1340 bans = bd->channel->channel->banlist;
1341 mod_chanmode_init(&change);
1342 for(ii=0; ii<bans.used; ii++)
1344 if(!strcmp(bans.list[ii]->ban, bd->mask))
1347 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1348 change.args[0].u.hostmask = bd->mask;
1349 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1355 del_channel_ban(bd);
1358 static void chanserv_expire_suspension(void *data);
1361 unregister_channel(struct chanData *channel, const char *reason)
1363 struct mod_chanmode change;
1364 char msgbuf[MAXLEN];
1366 /* After channel unregistration, the following must be cleaned
1368 - Channel information.
1371 - Channel suspension data.
1372 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1378 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1382 mod_chanmode_init(&change);
1383 change.modes_clear |= MODE_REGISTERED;
1384 mod_chanmode_announce(chanserv, channel->channel, &change);
1387 while(channel->users)
1388 del_channel_user(channel->users, 0);
1390 while(channel->bans)
1391 del_channel_ban(channel->bans);
1393 free(channel->topic);
1394 free(channel->registrar);
1395 free(channel->greeting);
1396 free(channel->user_greeting);
1397 free(channel->topic_mask);
1400 channel->prev->next = channel->next;
1402 channelList = channel->next;
1405 channel->next->prev = channel->prev;
1407 if(channel->suspended)
1409 struct chanNode *cNode = channel->channel;
1410 struct suspended *suspended, *next_suspended;
1412 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1414 next_suspended = suspended->previous;
1415 free(suspended->suspender);
1416 free(suspended->reason);
1417 if(suspended->expires)
1418 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1423 cNode->channel_info = NULL;
1426 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1427 channel->channel->channel_info = NULL;
1429 dict_delete(channel->notes);
1430 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1431 if(!IsSuspended(channel))
1432 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1433 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1434 UnlockChannel(channel->channel);
1436 registered_channels--;
1440 expire_channels(UNUSED_ARG(void *data))
1442 struct chanData *channel, *next;
1443 struct userData *user;
1444 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1446 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1447 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1449 for(channel = channelList; channel; channel = next)
1451 next = channel->next;
1453 /* See if the channel can be expired. */
1454 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1455 || IsProtected(channel))
1458 /* Make sure there are no high-ranking users still in the channel. */
1459 for(user=channel->users; user; user=user->next)
1460 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1465 /* Unregister the channel */
1466 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1467 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1468 unregister_channel(channel, "registration expired.");
1471 if(chanserv_conf.channel_expire_frequency)
1472 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1476 expire_dnrs(UNUSED_ARG(void *data))
1478 dict_iterator_t it, next;
1479 struct do_not_register *dnr;
1481 for(it = dict_first(handle_dnrs); it; it = next)
1483 dnr = iter_data(it);
1484 next = iter_next(it);
1485 if(dnr->expires && dnr->expires <= now)
1486 dict_remove(handle_dnrs, dnr->chan_name + 1);
1488 for(it = dict_first(plain_dnrs); it; it = next)
1490 dnr = iter_data(it);
1491 next = iter_next(it);
1492 if(dnr->expires && dnr->expires <= now)
1493 dict_remove(plain_dnrs, dnr->chan_name + 1);
1495 for(it = dict_first(mask_dnrs); it; it = next)
1497 dnr = iter_data(it);
1498 next = iter_next(it);
1499 if(dnr->expires && dnr->expires <= now)
1500 dict_remove(mask_dnrs, dnr->chan_name + 1);
1503 if(chanserv_conf.dnr_expire_frequency)
1504 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1508 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1510 char protect = channel->chOpts[chProtect];
1511 struct userData *cs_victim, *cs_aggressor;
1513 /* Don't protect if no one is to be protected, someone is attacking
1514 himself, or if the aggressor is an IRC Operator. */
1515 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1518 /* Don't protect if the victim isn't authenticated (because they
1519 can't be a channel user), unless we are to protect non-users
1521 cs_victim = GetChannelAccess(channel, victim->handle_info);
1522 if(protect != 'a' && !cs_victim)
1525 /* Protect if the aggressor isn't a user because at this point,
1526 the aggressor can only be less than or equal to the victim. */
1527 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1531 /* If the aggressor was a user, then the victim can't be helped. */
1538 if(cs_victim->access > cs_aggressor->access)
1543 if(cs_victim->access >= cs_aggressor->access)
1552 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1554 struct chanData *cData = channel->channel_info;
1555 struct userData *cs_victim;
1557 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1558 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1559 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1561 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1569 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1571 if(IsService(victim))
1573 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1577 if(protect_user(victim, user, channel->channel_info))
1579 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1586 static struct do_not_register *
1587 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1589 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1590 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1591 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1592 strcpy(dnr->reason, reason);
1594 dnr->expires = expires;
1595 if(dnr->chan_name[0] == '*')
1596 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1597 else if(strpbrk(dnr->chan_name, "*?"))
1598 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1600 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1604 static struct dnrList
1605 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1607 struct dnrList list;
1608 dict_iterator_t it, next;
1609 struct do_not_register *dnr;
1611 dnrList_init(&list);
1613 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1615 if(dnr->expires && dnr->expires <= now)
1616 dict_remove(handle_dnrs, handle);
1617 else if(list.used < max)
1618 dnrList_append(&list, dnr);
1621 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1623 if(dnr->expires && dnr->expires <= now)
1624 dict_remove(plain_dnrs, chan_name);
1625 else if(list.used < max)
1626 dnrList_append(&list, dnr);
1631 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1633 next = iter_next(it);
1634 if(!match_ircglob(chan_name, iter_key(it)))
1636 dnr = iter_data(it);
1637 if(dnr->expires && dnr->expires <= now)
1638 dict_remove(mask_dnrs, iter_key(it));
1640 dnrList_append(&list, dnr);
1647 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1649 struct userNode *user;
1650 char buf1[INTERVALLEN];
1651 char buf2[INTERVALLEN];
1658 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1663 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1664 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1668 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1671 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1676 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1678 struct dnrList list;
1681 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1682 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1683 dnr_print_func(list.list[ii], user);
1685 reply("CSMSG_MORE_DNRS", list.used - ii);
1690 struct do_not_register *
1691 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1693 struct dnrList list;
1694 struct do_not_register *dnr;
1696 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1697 dnr = list.used ? list.list[0] : NULL;
1702 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1704 struct do_not_register *dnr;
1705 dict_iterator_t it, next;
1706 unsigned int matches = 0;
1708 for(it = dict_first(dict); it; it = next)
1710 dnr = iter_data(it);
1711 next = iter_next(it);
1712 if(dnr->expires && dnr->expires <= now)
1714 dict_remove(dict, iter_key(it));
1717 dnr_print_func(dnr, user);
1724 static CHANSERV_FUNC(cmd_noregister)
1728 unsigned long expiry, duration;
1729 unsigned int matches;
1733 reply("CSMSG_DNR_SEARCH_RESULTS");
1734 matches = send_dnrs(user, handle_dnrs);
1735 matches += send_dnrs(user, plain_dnrs);
1736 matches += send_dnrs(user, mask_dnrs);
1738 reply("MSG_MATCH_COUNT", matches);
1740 reply("MSG_NO_MATCHES");
1746 if(!IsChannelName(target) && (*target != '*'))
1748 reply("CSMSG_NOT_DNR", target);
1756 reply("MSG_INVALID_DURATION", argv[2]);
1760 if(!strcmp(argv[2], "0"))
1762 else if((duration = ParseInterval(argv[2])))
1763 expiry = now + duration;
1766 reply("MSG_INVALID_DURATION", argv[2]);
1770 reason = unsplit_string(argv + 3, argc - 3, NULL);
1771 if((*target == '*') && !get_handle_info(target + 1))
1773 reply("MSG_HANDLE_UNKNOWN", target + 1);
1776 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1777 reply("CSMSG_NOREGISTER_CHANNEL", target);
1781 reply("CSMSG_DNR_SEARCH_RESULTS");
1783 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1785 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1787 reply("MSG_NO_MATCHES");
1791 static CHANSERV_FUNC(cmd_allowregister)
1793 const char *chan_name = argv[1];
1795 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1796 || dict_remove(plain_dnrs, chan_name)
1797 || dict_remove(mask_dnrs, chan_name))
1799 reply("CSMSG_DNR_REMOVED", chan_name);
1802 reply("CSMSG_NO_SUCH_DNR", chan_name);
1807 struct userNode *source;
1811 unsigned long min_set, max_set;
1812 unsigned long min_expires, max_expires;
1817 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1819 return !((dnr->set < search->min_set)
1820 || (dnr->set > search->max_set)
1821 || (dnr->expires < search->min_expires)
1822 || (search->max_expires
1823 && ((dnr->expires == 0)
1824 || (dnr->expires > search->max_expires)))
1825 || (search->chan_mask
1826 && !match_ircglob(dnr->chan_name, search->chan_mask))
1827 || (search->setter_mask
1828 && !match_ircglob(dnr->setter, search->setter_mask))
1829 || (search->reason_mask
1830 && !match_ircglob(dnr->reason, search->reason_mask)));
1833 static struct dnr_search *
1834 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1836 struct dnr_search *discrim;
1839 discrim = calloc(1, sizeof(*discrim));
1840 discrim->source = user;
1841 discrim->chan_mask = NULL;
1842 discrim->setter_mask = NULL;
1843 discrim->reason_mask = NULL;
1844 discrim->max_set = INT_MAX;
1845 discrim->limit = 50;
1847 for(ii=0; ii<argc; ++ii)
1851 reply("MSG_MISSING_PARAMS", argv[ii]);
1854 else if(0 == irccasecmp(argv[ii], "channel"))
1856 discrim->chan_mask = argv[++ii];
1858 else if(0 == irccasecmp(argv[ii], "setter"))
1860 discrim->setter_mask = argv[++ii];
1862 else if(0 == irccasecmp(argv[ii], "reason"))
1864 discrim->reason_mask = argv[++ii];
1866 else if(0 == irccasecmp(argv[ii], "limit"))
1868 discrim->limit = strtoul(argv[++ii], NULL, 0);
1870 else if(0 == irccasecmp(argv[ii], "set"))
1872 const char *cmp = argv[++ii];
1875 discrim->min_set = now - ParseInterval(cmp + 2);
1877 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1878 } else if(cmp[0] == '=') {
1879 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1880 } else if(cmp[0] == '>') {
1882 discrim->max_set = now - ParseInterval(cmp + 2);
1884 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1886 discrim->max_set = now - (ParseInterval(cmp) - 1);
1889 else if(0 == irccasecmp(argv[ii], "expires"))
1891 const char *cmp = argv[++ii];
1894 discrim->max_expires = now + ParseInterval(cmp + 2);
1896 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1897 } else if(cmp[0] == '=') {
1898 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1899 } else if(cmp[0] == '>') {
1901 discrim->min_expires = now + ParseInterval(cmp + 2);
1903 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1905 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1910 reply("MSG_INVALID_CRITERIA", argv[ii]);
1921 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1924 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1926 struct do_not_register *dnr;
1927 dict_iterator_t next;
1932 /* Initialize local variables. */
1935 if(discrim->chan_mask)
1937 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1938 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
1942 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
1944 /* Check against account-based DNRs. */
1945 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
1946 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1949 else if(target_fixed)
1951 /* Check against channel-based DNRs. */
1952 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
1953 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
1958 /* Exhaustively search account DNRs. */
1959 for(it = dict_first(handle_dnrs); it; it = next)
1961 next = iter_next(it);
1962 dnr = iter_data(it);
1963 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1967 /* Do the same for channel DNRs. */
1968 for(it = dict_first(plain_dnrs); it; it = next)
1970 next = iter_next(it);
1971 dnr = iter_data(it);
1972 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1976 /* Do the same for wildcarded channel DNRs. */
1977 for(it = dict_first(mask_dnrs); it; it = next)
1979 next = iter_next(it);
1980 dnr = iter_data(it);
1981 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
1989 dnr_remove_func(struct do_not_register *match, void *extra)
1991 struct userNode *user;
1994 chan_name = alloca(strlen(match->chan_name) + 1);
1995 strcpy(chan_name, match->chan_name);
1997 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1998 || dict_remove(plain_dnrs, chan_name)
1999 || dict_remove(mask_dnrs, chan_name))
2001 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2007 dnr_count_func(struct do_not_register *match, void *extra)
2009 return 0; (void)match; (void)extra;
2012 static MODCMD_FUNC(cmd_dnrsearch)
2014 struct dnr_search *discrim;
2015 dnr_search_func action;
2016 struct svccmd *subcmd;
2017 unsigned int matches;
2020 sprintf(buf, "dnrsearch %s", argv[1]);
2021 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2024 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2027 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2029 if(!irccasecmp(argv[1], "print"))
2030 action = dnr_print_func;
2031 else if(!irccasecmp(argv[1], "remove"))
2032 action = dnr_remove_func;
2033 else if(!irccasecmp(argv[1], "count"))
2034 action = dnr_count_func;
2037 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2041 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2045 if(action == dnr_print_func)
2046 reply("CSMSG_DNR_SEARCH_RESULTS");
2047 matches = dnr_search(discrim, action, user);
2049 reply("MSG_MATCH_COUNT", matches);
2051 reply("MSG_NO_MATCHES");
2057 chanserv_get_owned_count(struct handle_info *hi)
2059 struct userData *cList;
2062 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2063 if(cList->access == UL_OWNER)
2068 static CHANSERV_FUNC(cmd_register)
2070 struct handle_info *handle;
2071 struct chanData *cData;
2072 struct modeNode *mn;
2073 char reason[MAXLEN];
2075 unsigned int new_channel, force=0;
2076 struct do_not_register *dnr;
2080 if(channel->channel_info)
2082 reply("CSMSG_ALREADY_REGGED", channel->name);
2086 if(channel->bad_channel)
2088 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2093 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2095 reply("CSMSG_MUST_BE_OPPED", channel->name);
2100 chan_name = channel->name;
2104 if((argc < 2) || !IsChannelName(argv[1]))
2106 reply("MSG_NOT_CHANNEL_NAME");
2110 if(opserv_bad_channel(argv[1]))
2112 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2117 chan_name = argv[1];
2120 if(argc >= (new_channel+2))
2122 if(!IsHelping(user))
2124 reply("CSMSG_PROXY_FORBIDDEN");
2128 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2130 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2131 dnr = chanserv_is_dnr(chan_name, handle);
2135 handle = user->handle_info;
2136 dnr = chanserv_is_dnr(chan_name, handle);
2140 if(!IsHelping(user))
2141 reply("CSMSG_DNR_CHANNEL", chan_name);
2143 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2147 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2149 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2154 channel = AddChannel(argv[1], now, NULL, NULL);
2156 cData = register_channel(channel, user->handle_info->handle);
2157 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2158 cData->modes = chanserv_conf.default_modes;
2160 cData->modes.modes_set |= MODE_REGISTERED;
2161 if (IsOffChannel(cData))
2163 mod_chanmode_announce(chanserv, channel, &cData->modes);
2167 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2168 change->args[change->argc].mode = MODE_CHANOP;
2169 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2171 mod_chanmode_announce(chanserv, channel, change);
2172 mod_chanmode_free(change);
2175 /* Initialize the channel's max user record. */
2176 cData->max = channel->members.used;
2177 cData->max_time = 0;
2179 if(handle != user->handle_info)
2180 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2182 reply("CSMSG_REG_SUCCESS", channel->name);
2184 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2185 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2190 make_confirmation_string(struct userData *uData)
2192 static char strbuf[16];
2197 for(src = uData->handle->handle; *src; )
2198 accum = accum * 31 + toupper(*src++);
2200 for(src = uData->channel->channel->name; *src; )
2201 accum = accum * 31 + toupper(*src++);
2202 sprintf(strbuf, "%08x", accum);
2206 static CHANSERV_FUNC(cmd_unregister)
2209 char reason[MAXLEN];
2210 struct chanData *cData;
2211 struct userData *uData;
2213 cData = channel->channel_info;
2216 reply("CSMSG_NOT_REGISTERED", channel->name);
2220 uData = GetChannelUser(cData, user->handle_info);
2221 if(!uData || (uData->access < UL_OWNER))
2223 reply("CSMSG_NO_ACCESS");
2227 if(IsProtected(cData))
2229 reply("CSMSG_UNREG_NODELETE", channel->name);
2233 if(!IsHelping(user))
2235 const char *confirm_string;
2236 if(IsSuspended(cData))
2238 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2241 confirm_string = make_confirmation_string(uData);
2242 if((argc < 2) || strcmp(argv[1], confirm_string))
2244 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2249 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2250 name = strdup(channel->name);
2251 unregister_channel(cData, reason);
2252 spamserv_cs_unregister(user, channel, manually, "unregistered");
2253 reply("CSMSG_UNREG_SUCCESS", name);
2259 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2261 extern struct userNode *spamserv;
2262 struct mod_chanmode *change;
2264 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2266 change = mod_chanmode_alloc(2);
2268 change->args[0].mode = MODE_CHANOP;
2269 change->args[0].u.member = AddChannelUser(chanserv, channel);
2270 change->args[1].mode = MODE_CHANOP;
2271 change->args[1].u.member = AddChannelUser(spamserv, channel);
2275 change = mod_chanmode_alloc(1);
2277 change->args[0].mode = MODE_CHANOP;
2278 change->args[0].u.member = AddChannelUser(chanserv, channel);
2281 mod_chanmode_announce(chanserv, channel, change);
2282 mod_chanmode_free(change);
2285 static CHANSERV_FUNC(cmd_move)
2287 struct mod_chanmode change;
2288 struct chanNode *target;
2289 struct modeNode *mn;
2290 struct userData *uData;
2291 char reason[MAXLEN];
2292 struct do_not_register *dnr;
2293 int chanserv_join = 0, spamserv_join;
2297 if(IsProtected(channel->channel_info))
2299 reply("CSMSG_MOVE_NODELETE", channel->name);
2303 if(!IsChannelName(argv[1]))
2305 reply("MSG_NOT_CHANNEL_NAME");
2309 if(opserv_bad_channel(argv[1]))
2311 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2315 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2317 for(uData = channel->channel_info->users; uData; uData = uData->next)
2319 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2321 if(!IsHelping(user))
2322 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2324 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2330 mod_chanmode_init(&change);
2331 if(!(target = GetChannel(argv[1])))
2333 target = AddChannel(argv[1], now, NULL, NULL);
2334 if(!IsSuspended(channel->channel_info))
2337 else if(target->channel_info)
2339 reply("CSMSG_ALREADY_REGGED", target->name);
2342 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2343 && !IsHelping(user))
2345 reply("CSMSG_MUST_BE_OPPED", target->name);
2348 else if(!IsSuspended(channel->channel_info))
2353 /* Clear MODE_REGISTERED from old channel, add it to new. */
2355 change.modes_clear = MODE_REGISTERED;
2356 mod_chanmode_announce(chanserv, channel, &change);
2357 change.modes_clear = 0;
2358 change.modes_set = MODE_REGISTERED;
2359 mod_chanmode_announce(chanserv, target, &change);
2362 /* Move the channel_info to the target channel; it
2363 shouldn't be necessary to clear timeq callbacks
2364 for the old channel. */
2365 target->channel_info = channel->channel_info;
2366 target->channel_info->channel = target;
2367 channel->channel_info = NULL;
2369 /* Check whether users are present in the new channel. */
2370 for(uData = target->channel_info->users; uData; uData = uData->next)
2371 scan_user_presence(uData, NULL);
2373 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2376 ss_cs_join_channel(target, spamserv_join);
2378 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2379 if(!IsSuspended(target->channel_info))
2381 char reason2[MAXLEN];
2382 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2383 DelChannelUser(chanserv, channel, reason2, 0);
2385 UnlockChannel(channel);
2386 LockChannel(target);
2387 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2388 reply("CSMSG_MOVE_SUCCESS", target->name);
2393 merge_users(struct chanData *source, struct chanData *target)
2395 struct userData *suData, *tuData, *next;
2401 /* Insert the source's users into the scratch area. */
2402 for(suData = source->users; suData; suData = suData->next)
2403 dict_insert(merge, suData->handle->handle, suData);
2405 /* Iterate through the target's users, looking for
2406 users common to both channels. The lower access is
2407 removed from either the scratch area or target user
2409 for(tuData = target->users; tuData; tuData = next)
2411 struct userData *choice;
2413 next = tuData->next;
2415 /* If a source user exists with the same handle as a target
2416 channel's user, resolve the conflict by removing one. */
2417 suData = dict_find(merge, tuData->handle->handle, NULL);
2421 /* Pick the data we want to keep. */
2422 /* If the access is the same, use the later seen time. */
2423 if(suData->access == tuData->access)
2424 choice = (suData->seen > tuData->seen) ? suData : tuData;
2425 else /* Otherwise, keep the higher access level. */
2426 choice = (suData->access > tuData->access) ? suData : tuData;
2427 /* Use the later seen time. */
2428 if(suData->seen < tuData->seen)
2429 suData->seen = tuData->seen;
2431 tuData->seen = suData->seen;
2433 /* Remove the user that wasn't picked. */
2434 if(choice == tuData)
2436 dict_remove(merge, suData->handle->handle);
2437 del_channel_user(suData, 0);
2440 del_channel_user(tuData, 0);
2443 /* Move the remaining users to the target channel. */
2444 for(it = dict_first(merge); it; it = iter_next(it))
2446 suData = iter_data(it);
2448 /* Insert the user into the target channel's linked list. */
2449 suData->prev = NULL;
2450 suData->next = target->users;
2451 suData->channel = target;
2454 target->users->prev = suData;
2455 target->users = suData;
2457 /* Update the user counts for the target channel; the
2458 source counts are left alone. */
2459 target->userCount++;
2461 /* Check whether the user is in the target channel. */
2462 scan_user_presence(suData, NULL);
2465 /* Possible to assert (source->users == NULL) here. */
2466 source->users = NULL;
2471 merge_bans(struct chanData *source, struct chanData *target)
2473 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2475 /* Hold on to the original head of the target ban list
2476 to avoid comparing source bans with source bans. */
2477 tFront = target->bans;
2479 /* Perform a totally expensive O(n*m) merge, ick. */
2480 for(sbData = source->bans; sbData; sbData = sNext)
2482 /* Flag to track whether the ban's been moved
2483 to the destination yet. */
2486 /* Possible to assert (sbData->prev == NULL) here. */
2487 sNext = sbData->next;
2489 for(tbData = tFront; tbData; tbData = tNext)
2491 tNext = tbData->next;
2493 /* Perform two comparisons between each source
2494 and target ban, conflicts are resolved by
2495 keeping the broader ban and copying the later
2496 expiration and triggered time. */
2497 if(match_ircglobs(tbData->mask, sbData->mask))
2499 /* There is a broader ban in the target channel that
2500 overrides one in the source channel; remove the
2501 source ban and break. */
2502 if(sbData->expires > tbData->expires)
2503 tbData->expires = sbData->expires;
2504 if(sbData->triggered > tbData->triggered)
2505 tbData->triggered = sbData->triggered;
2506 del_channel_ban(sbData);
2509 else if(match_ircglobs(sbData->mask, tbData->mask))
2511 /* There is a broader ban in the source channel that
2512 overrides one in the target channel; remove the
2513 target ban, fall through and move the source over. */
2514 if(tbData->expires > sbData->expires)
2515 sbData->expires = tbData->expires;
2516 if(tbData->triggered > sbData->triggered)
2517 sbData->triggered = tbData->triggered;
2518 if(tbData == tFront)
2520 del_channel_ban(tbData);
2523 /* Source bans can override multiple target bans, so
2524 we allow a source to run through this loop multiple
2525 times, but we can only move it once. */
2530 /* Remove the source ban from the source ban list. */
2532 sbData->next->prev = sbData->prev;
2534 /* Modify the source ban's associated channel. */
2535 sbData->channel = target;
2537 /* Insert the ban into the target channel's linked list. */
2538 sbData->prev = NULL;
2539 sbData->next = target->bans;
2542 target->bans->prev = sbData;
2543 target->bans = sbData;
2545 /* Update the user counts for the target channel. */
2550 /* Possible to assert (source->bans == NULL) here. */
2551 source->bans = NULL;
2555 merge_data(struct chanData *source, struct chanData *target)
2557 /* Use more recent visited and owner-transfer time; use older
2558 * registered time. Bitwise or may_opchan. Use higher max.
2559 * Do not touch last_refresh, ban count or user counts.
2561 if(source->visited > target->visited)
2562 target->visited = source->visited;
2563 if(source->registered < target->registered)
2564 target->registered = source->registered;
2565 if(source->ownerTransfer > target->ownerTransfer)
2566 target->ownerTransfer = source->ownerTransfer;
2567 if(source->may_opchan)
2568 target->may_opchan = 1;
2569 if(source->max > target->max) {
2570 target->max = source->max;
2571 target->max_time = source->max_time;
2576 merge_channel(struct chanData *source, struct chanData *target)
2578 merge_users(source, target);
2579 merge_bans(source, target);
2580 merge_data(source, target);
2583 static CHANSERV_FUNC(cmd_merge)
2585 struct userData *target_user;
2586 struct chanNode *target;
2587 char reason[MAXLEN];
2591 /* Make sure the target channel exists and is registered to the user
2592 performing the command. */
2593 if(!(target = GetChannel(argv[1])))
2595 reply("MSG_INVALID_CHANNEL");
2599 if(!target->channel_info)
2601 reply("CSMSG_NOT_REGISTERED", target->name);
2605 if(IsProtected(channel->channel_info))
2607 reply("CSMSG_MERGE_NODELETE");
2611 if(IsSuspended(target->channel_info))
2613 reply("CSMSG_MERGE_SUSPENDED");
2617 if(channel == target)
2619 reply("CSMSG_MERGE_SELF");
2623 target_user = GetChannelUser(target->channel_info, user->handle_info);
2624 if(!target_user || (target_user->access < UL_OWNER))
2626 reply("CSMSG_MERGE_NOT_OWNER");
2630 /* Merge the channel structures and associated data. */
2631 merge_channel(channel->channel_info, target->channel_info);
2632 spamserv_cs_move_merge(user, channel, target, 0);
2633 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2634 unregister_channel(channel->channel_info, reason);
2635 reply("CSMSG_MERGE_SUCCESS", target->name);
2639 static CHANSERV_FUNC(cmd_opchan)
2641 struct mod_chanmode change;
2642 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2644 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2647 channel->channel_info->may_opchan = 0;
2648 mod_chanmode_init(&change);
2650 change.args[0].mode = MODE_CHANOP;
2651 change.args[0].u.member = GetUserMode(channel, chanserv);
2652 if(!change.args[0].u.member)
2654 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2657 mod_chanmode_announce(chanserv, channel, &change);
2658 reply("CSMSG_OPCHAN_DONE", channel->name);
2662 static CHANSERV_FUNC(cmd_adduser)
2664 struct userData *actee;
2665 struct userData *actor, *real_actor;
2666 struct handle_info *handle;
2667 unsigned short access_level, override = 0;
2671 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2673 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2677 access_level = user_level_from_name(argv[2], UL_OWNER);
2680 reply("CSMSG_INVALID_ACCESS", argv[2]);
2684 actor = GetChannelUser(channel->channel_info, user->handle_info);
2685 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2687 if(actor->access <= access_level)
2689 reply("CSMSG_NO_BUMP_ACCESS");
2693 /* Trying to add someone with equal/more access? */
2694 if (!real_actor || real_actor->access <= access_level)
2695 override = CMD_LOG_OVERRIDE;
2697 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2700 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2702 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2706 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2707 scan_user_presence(actee, NULL);
2708 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2709 return 1 | override;
2712 static CHANSERV_FUNC(cmd_clvl)
2714 struct handle_info *handle;
2715 struct userData *victim;
2716 struct userData *actor, *real_actor;
2717 unsigned short new_access, override = 0;
2718 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2722 actor = GetChannelUser(channel->channel_info, user->handle_info);
2723 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2725 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2728 if(handle == user->handle_info && !privileged)
2730 reply("CSMSG_NO_SELF_CLVL");
2734 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2736 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2740 if(actor->access <= victim->access && !privileged)
2742 reply("MSG_USER_OUTRANKED", handle->handle);
2746 new_access = user_level_from_name(argv[2], UL_OWNER);
2750 reply("CSMSG_INVALID_ACCESS", argv[2]);
2754 if(new_access >= actor->access && !privileged)
2756 reply("CSMSG_NO_BUMP_ACCESS");
2760 /* Trying to clvl a equal/higher user? */
2761 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2762 override = CMD_LOG_OVERRIDE;
2763 /* Trying to clvl someone to equal/higher access? */
2764 if(!real_actor || new_access >= real_actor->access)
2765 override = CMD_LOG_OVERRIDE;
2766 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2767 * If they lower their own access it's not a big problem.
2770 victim->access = new_access;
2771 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2772 return 1 | override;
2775 static CHANSERV_FUNC(cmd_deluser)
2777 struct handle_info *handle;
2778 struct userData *victim;
2779 struct userData *actor, *real_actor;
2780 unsigned short access_level, override = 0;
2785 actor = GetChannelUser(channel->channel_info, user->handle_info);
2786 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2788 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2791 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2793 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2799 access_level = user_level_from_name(argv[1], UL_OWNER);
2802 reply("CSMSG_INVALID_ACCESS", argv[1]);
2805 if(access_level != victim->access)
2807 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2813 access_level = victim->access;
2816 if((actor->access <= victim->access) && !IsHelping(user))
2818 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2822 /* If people delete themselves it is an override, but they
2823 * could've used deleteme so we don't log it as an override
2825 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2826 override = CMD_LOG_OVERRIDE;
2828 chan_name = strdup(channel->name);
2829 del_channel_user(victim, 1);
2830 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2832 return 1 | override;
2836 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2838 struct userData *actor, *real_actor, *uData, *next;
2839 unsigned int override = 0;
2841 actor = GetChannelUser(channel->channel_info, user->handle_info);
2842 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2844 if(min_access > max_access)
2846 reply("CSMSG_BAD_RANGE", min_access, max_access);
2850 if(actor->access <= max_access)
2852 reply("CSMSG_NO_ACCESS");
2856 if(!real_actor || real_actor->access <= max_access)
2857 override = CMD_LOG_OVERRIDE;
2859 for(uData = channel->channel_info->users; uData; uData = next)
2863 if((uData->access >= min_access)
2864 && (uData->access <= max_access)
2865 && match_ircglob(uData->handle->handle, mask))
2866 del_channel_user(uData, 1);
2869 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2870 return 1 | override;
2873 static CHANSERV_FUNC(cmd_mdelowner)
2875 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2878 static CHANSERV_FUNC(cmd_mdelcoowner)
2880 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2883 static CHANSERV_FUNC(cmd_mdelmaster)
2885 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2888 static CHANSERV_FUNC(cmd_mdelop)
2890 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2893 static CHANSERV_FUNC(cmd_mdelpeon)
2895 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2899 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2901 struct banData *bData, *next;
2902 char interval[INTERVALLEN];
2904 unsigned long limit;
2907 limit = now - duration;
2908 for(bData = channel->channel_info->bans; bData; bData = next)
2912 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2915 del_channel_ban(bData);
2919 intervalString(interval, duration, user->handle_info);
2920 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2925 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2927 struct userData *actor, *uData, *next;
2928 char interval[INTERVALLEN];
2930 unsigned long limit;
2932 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2933 if(min_access > max_access)
2935 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
2939 if(!actor || actor->access <= max_access)
2941 send_message(user, chanserv, "CSMSG_NO_ACCESS");
2946 limit = now - duration;
2947 for(uData = channel->channel_info->users; uData; uData = next)
2951 if((uData->seen > limit)
2953 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
2956 if(((uData->access >= min_access) && (uData->access <= max_access))
2957 || (!max_access && (uData->access < actor->access)))
2959 del_channel_user(uData, 1);
2967 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
2969 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
2973 static CHANSERV_FUNC(cmd_trim)
2975 unsigned long duration;
2976 unsigned short min_level, max_level;
2981 vacation = argc > 3 && !strcmp(argv[3], "vacation");
2982 duration = ParseInterval(argv[2]);
2985 reply("CSMSG_CANNOT_TRIM");
2989 if(!irccasecmp(argv[1], "bans"))
2991 cmd_trim_bans(user, channel, duration);
2994 else if(!irccasecmp(argv[1], "users"))
2996 cmd_trim_users(user, channel, 0, 0, duration, vacation);
2999 else if(parse_level_range(&min_level, &max_level, argv[1]))
3001 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3004 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3006 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3011 reply("CSMSG_INVALID_TRIM", argv[1]);
3016 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3017 to the user. cmd_all takes advantage of this. */
3018 static CHANSERV_FUNC(cmd_up)
3020 struct mod_chanmode change;
3021 struct userData *uData;
3024 mod_chanmode_init(&change);
3026 change.args[0].u.member = GetUserMode(channel, user);
3027 if(!change.args[0].u.member)
3030 reply("MSG_CHANNEL_ABSENT", channel->name);
3034 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3038 reply("CSMSG_GODMODE_UP", argv[0]);
3041 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3043 change.args[0].mode = MODE_CHANOP;
3044 errmsg = "CSMSG_ALREADY_OPPED";
3046 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3048 change.args[0].mode = MODE_VOICE;
3049 errmsg = "CSMSG_ALREADY_VOICED";
3054 reply("CSMSG_NO_ACCESS");
3057 change.args[0].mode &= ~change.args[0].u.member->modes;
3058 if(!change.args[0].mode)
3061 reply(errmsg, channel->name);
3064 modcmd_chanmode_announce(&change);
3068 static CHANSERV_FUNC(cmd_down)
3070 struct mod_chanmode change;
3072 mod_chanmode_init(&change);
3074 change.args[0].u.member = GetUserMode(channel, user);
3075 if(!change.args[0].u.member)
3078 reply("MSG_CHANNEL_ABSENT", channel->name);
3082 if(!change.args[0].u.member->modes)
3085 reply("CSMSG_ALREADY_DOWN", channel->name);
3089 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3090 modcmd_chanmode_announce(&change);
3094 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)
3096 struct userData *cList;
3098 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3100 if(IsSuspended(cList->channel)
3101 || IsUserSuspended(cList)
3102 || !GetUserMode(cList->channel->channel, user))
3105 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3111 static CHANSERV_FUNC(cmd_upall)
3113 return cmd_all(CSFUNC_ARGS, cmd_up);
3116 static CHANSERV_FUNC(cmd_downall)
3118 return cmd_all(CSFUNC_ARGS, cmd_down);
3121 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3122 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3125 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)
3127 unsigned int ii, valid;
3128 struct userNode *victim;
3129 struct mod_chanmode *change;
3131 change = mod_chanmode_alloc(argc - 1);
3133 for(ii=valid=0; ++ii < argc; )
3135 if(!(victim = GetUserH(argv[ii])))
3137 change->args[valid].mode = mode;
3138 change->args[valid].u.member = GetUserMode(channel, victim);
3139 if(!change->args[valid].u.member)
3141 if(validate && !validate(user, channel, victim))
3146 change->argc = valid;
3147 if(valid < (argc-1))
3148 reply("CSMSG_PROCESS_FAILED");
3151 modcmd_chanmode_announce(change);
3152 reply(action, channel->name);
3154 mod_chanmode_free(change);
3158 static CHANSERV_FUNC(cmd_op)
3160 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3163 static CHANSERV_FUNC(cmd_deop)
3165 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3168 static CHANSERV_FUNC(cmd_voice)
3170 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3173 static CHANSERV_FUNC(cmd_devoice)
3175 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3179 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3185 for(ii=0; ii<channel->members.used; ii++)
3187 struct modeNode *mn = channel->members.list[ii];
3189 if(IsService(mn->user))
3192 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3195 if(protect_user(mn->user, user, channel->channel_info))
3199 victims[(*victimCount)++] = mn;
3205 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3207 struct userNode *victim;
3208 struct modeNode **victims;
3209 unsigned int offset, n, victimCount, duration = 0;
3210 char *reason = "Bye.", *ban, *name;
3211 char interval[INTERVALLEN];
3213 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3214 REQUIRE_PARAMS(offset);
3217 reason = unsplit_string(argv + offset, argc - offset, NULL);
3218 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3220 /* Truncate the reason to a length of TOPICLEN, as
3221 the ircd does; however, leave room for an ellipsis
3222 and the kicker's nick. */
3223 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3227 if((victim = GetUserH(argv[1])))
3229 victims = alloca(sizeof(victims[0]));
3230 victims[0] = GetUserMode(channel, victim);
3231 /* XXX: The comparison with ACTION_KICK is just because all
3232 * other actions can work on users outside the channel, and we
3233 * want to allow those (e.g. unbans) in that case. If we add
3234 * some other ejection action for in-channel users, change
3236 victimCount = victims[0] ? 1 : 0;
3238 if(IsService(victim))
3240 reply("MSG_SERVICE_IMMUNE", victim->nick);
3244 if((action == ACTION_KICK) && !victimCount)
3246 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3250 if(protect_user(victim, user, channel->channel_info))
3252 reply("CSMSG_USER_PROTECTED", victim->nick);
3256 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3257 name = victim->nick;
3259 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3261 struct handle_info *hi;
3262 extern const char *titlehost_suffix;
3263 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3264 const char *accountname = argv[1] + 1;
3266 if(!(hi = get_handle_info(accountname)))
3268 reply("MSG_HANDLE_UNKNOWN", accountname);
3272 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3273 victims = alloca(sizeof(victims[0]) * channel->members.used);
3275 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3277 reply("CSMSG_MASK_PROTECTED", banmask);
3281 if((action == ACTION_KICK) && (victimCount == 0))
3283 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3287 name = ban = strdup(banmask);
3291 if(!is_ircmask(argv[1]))
3293 reply("MSG_NICK_UNKNOWN", argv[1]);
3297 victims = alloca(sizeof(victims[0]) * channel->members.used);
3299 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3301 reply("CSMSG_MASK_PROTECTED", argv[1]);
3305 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3307 reply("CSMSG_LAME_MASK", argv[1]);
3311 if((action == ACTION_KICK) && (victimCount == 0))
3313 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3317 name = ban = strdup(argv[1]);
3320 /* Truncate the ban in place if necessary; we must ensure
3321 that 'ban' is a valid ban mask before sanitizing it. */
3322 sanitize_ircmask(ban);
3324 if(action & ACTION_ADD_BAN)
3326 struct banData *bData, *next;
3328 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3330 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3335 if(action & ACTION_ADD_TIMED_BAN)
3337 duration = ParseInterval(argv[2]);
3341 reply("CSMSG_DURATION_TOO_LOW");
3345 else if(duration > (86400 * 365 * 2))
3347 reply("CSMSG_DURATION_TOO_HIGH");
3353 for(bData = channel->channel_info->bans; bData; bData = next)
3355 if(match_ircglobs(bData->mask, ban))
3357 int exact = !irccasecmp(bData->mask, ban);
3359 /* The ban is redundant; there is already a ban
3360 with the same effect in place. */
3364 free(bData->reason);
3365 bData->reason = strdup(reason);
3366 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3368 reply("CSMSG_REASON_CHANGE", ban);
3372 if(exact && bData->expires)
3376 /* If the ban matches an existing one exactly,
3377 extend the expiration time if the provided
3378 duration is longer. */
3379 if(duration && (now + duration > bData->expires))
3381 bData->expires = now + duration;
3392 /* Delete the expiration timeq entry and
3393 requeue if necessary. */
3394 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3397 timeq_add(bData->expires, expire_ban, bData);
3401 /* automated kickban */
3404 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3406 reply("CSMSG_BAN_ADDED", name, channel->name);
3412 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3419 if(match_ircglobs(ban, bData->mask))
3421 /* The ban we are adding makes previously existing
3422 bans redundant; silently remove them. */
3423 del_channel_ban(bData);
3427 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);
3429 name = ban = strdup(bData->mask);
3433 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3435 extern const char *hidden_host_suffix;
3436 const char *old_name = chanserv_conf.old_ban_names->list[n];
3438 unsigned int l1, l2;
3441 l2 = strlen(old_name);
3444 if(irccasecmp(ban + l1 - l2, old_name))
3446 new_mask = malloc(MAXLEN);
3447 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3449 name = ban = new_mask;
3454 if(action & ACTION_BAN)
3456 unsigned int exists;
3457 struct mod_chanmode *change;
3459 if(channel->banlist.used >= MAXBANS)
3462 reply("CSMSG_BANLIST_FULL", channel->name);
3467 exists = ChannelBanExists(channel, ban);
3468 change = mod_chanmode_alloc(victimCount + 1);
3469 for(n = 0; n < victimCount; ++n)
3471 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3472 change->args[n].u.member = victims[n];
3476 change->args[n].mode = MODE_BAN;
3477 change->args[n++].u.hostmask = ban;
3481 modcmd_chanmode_announce(change);
3483 mod_chanmode_announce(chanserv, channel, change);
3484 mod_chanmode_free(change);
3486 if(exists && (action == ACTION_BAN))
3489 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3495 if(action & ACTION_KICK)
3497 char kick_reason[MAXLEN];
3498 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3500 for(n = 0; n < victimCount; n++)
3501 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3506 /* No response, since it was automated. */
3508 else if(action & ACTION_ADD_BAN)
3511 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3513 reply("CSMSG_BAN_ADDED", name, channel->name);
3515 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3516 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3517 else if(action & ACTION_BAN)
3518 reply("CSMSG_BAN_DONE", name, channel->name);
3519 else if(action & ACTION_KICK && victimCount)
3520 reply("CSMSG_KICK_DONE", name, channel->name);
3526 static CHANSERV_FUNC(cmd_kickban)
3528 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3531 static CHANSERV_FUNC(cmd_kick)
3533 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3536 static CHANSERV_FUNC(cmd_ban)
3538 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3541 static CHANSERV_FUNC(cmd_addban)
3543 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3546 static CHANSERV_FUNC(cmd_addtimedban)
3548 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3551 static struct mod_chanmode *
3552 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3554 struct mod_chanmode *change;
3555 unsigned char *match;
3556 unsigned int ii, count;
3558 match = alloca(bans->used);
3561 for(ii = count = 0; ii < bans->used; ++ii)
3563 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3564 MATCH_USENICK | MATCH_VISIBLE);
3571 for(ii = count = 0; ii < bans->used; ++ii)
3573 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3580 change = mod_chanmode_alloc(count);
3581 for(ii = count = 0; ii < bans->used; ++ii)
3585 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3586 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3588 assert(count == change->argc);
3593 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3595 struct userNode *actee;
3601 /* may want to allow a comma delimited list of users... */
3602 if(!(actee = GetUserH(argv[1])))
3604 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3606 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3607 const char *accountname = argv[1] + 1;
3609 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3610 mask = strdup(banmask);
3612 else if(!is_ircmask(argv[1]))
3614 reply("MSG_NICK_UNKNOWN", argv[1]);
3619 mask = strdup(argv[1]);
3623 /* We don't sanitize the mask here because ircu
3625 if(action & ACTION_UNBAN)
3627 struct mod_chanmode *change;
3628 change = find_matching_bans(&channel->banlist, actee, mask);
3633 modcmd_chanmode_announce(change);
3634 for(ii = 0; ii < change->argc; ++ii)
3635 free((char*)change->args[ii].u.hostmask);
3636 mod_chanmode_free(change);
3641 if(action & ACTION_DEL_BAN)
3643 struct banData *ban, *next;
3645 ban = channel->channel_info->bans;
3649 for( ; ban && !user_matches_glob(actee, ban->mask,
3650 MATCH_USENICK | MATCH_VISIBLE);
3653 for( ; ban && !match_ircglobs(mask, ban->mask);
3658 del_channel_ban(ban);
3665 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3667 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3673 static CHANSERV_FUNC(cmd_unban)
3675 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3678 static CHANSERV_FUNC(cmd_delban)
3680 /* it doesn't necessarily have to remove the channel ban - may want
3681 to make that an option. */
3682 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3685 static CHANSERV_FUNC(cmd_unbanme)
3687 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3688 long flags = ACTION_UNBAN;
3690 /* remove permanent bans if the user has the proper access. */
3691 if(uData->access >= UL_MASTER)
3692 flags |= ACTION_DEL_BAN;
3694 argv[1] = user->nick;
3695 return unban_user(user, channel, 2, argv, cmd, flags);
3698 static CHANSERV_FUNC(cmd_unbanall)
3700 struct mod_chanmode *change;
3703 if(!channel->banlist.used)
3705 reply("CSMSG_NO_BANS", channel->name);
3709 change = mod_chanmode_alloc(channel->banlist.used);
3710 for(ii=0; ii<channel->banlist.used; ii++)
3712 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3713 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3715 modcmd_chanmode_announce(change);
3716 for(ii = 0; ii < change->argc; ++ii)
3717 free((char*)change->args[ii].u.hostmask);
3718 mod_chanmode_free(change);
3719 reply("CSMSG_BANS_REMOVED", channel->name);
3723 static CHANSERV_FUNC(cmd_open)
3725 struct mod_chanmode *change;
3728 change = find_matching_bans(&channel->banlist, user, NULL);
3730 change = mod_chanmode_alloc(0);
3731 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3732 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3733 && channel->channel_info->modes.modes_set)
3734 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3735 modcmd_chanmode_announce(change);
3736 reply("CSMSG_CHANNEL_OPENED", channel->name);
3737 for(ii = 0; ii < change->argc; ++ii)
3738 free((char*)change->args[ii].u.hostmask);
3739 mod_chanmode_free(change);
3743 static CHANSERV_FUNC(cmd_myaccess)
3745 static struct string_buffer sbuf;
3746 struct handle_info *target_handle;
3747 struct userData *uData;
3750 target_handle = user->handle_info;
3751 else if(!IsStaff(user))
3753 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3756 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3759 if(!oper_outranks(user, target_handle))
3762 if(!target_handle->channels)
3764 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3768 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3769 for(uData = target_handle->channels; uData; uData = uData->u_next)
3771 struct chanData *cData = uData->channel;
3773 if(uData->access > UL_OWNER)
3775 if(IsProtected(cData)
3776 && (target_handle != user->handle_info)
3777 && !GetTrueChannelAccess(cData, user->handle_info))
3780 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3781 if(uData->flags != USER_AUTO_OP)
3782 string_buffer_append(&sbuf, ',');
3783 if(IsUserSuspended(uData))
3784 string_buffer_append(&sbuf, 's');
3785 if(IsUserAutoOp(uData))
3787 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3788 string_buffer_append(&sbuf, 'o');
3789 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3790 string_buffer_append(&sbuf, 'v');
3792 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3793 string_buffer_append(&sbuf, 'i');
3795 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3797 string_buffer_append_string(&sbuf, ")]");
3798 string_buffer_append(&sbuf, '\0');
3799 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3805 static CHANSERV_FUNC(cmd_access)
3807 struct userNode *target;
3808 struct handle_info *target_handle;
3809 struct userData *uData;
3811 char prefix[MAXLEN];
3816 target_handle = target->handle_info;
3818 else if((target = GetUserH(argv[1])))
3820 target_handle = target->handle_info;
3822 else if(argv[1][0] == '*')
3824 if(!(target_handle = get_handle_info(argv[1]+1)))
3826 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3832 reply("MSG_NICK_UNKNOWN", argv[1]);
3836 assert(target || target_handle);
3838 if(target == chanserv)
3840 reply("CSMSG_IS_CHANSERV");
3848 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3853 reply("MSG_USER_AUTHENTICATE", target->nick);
3856 reply("MSG_AUTHENTICATE");
3862 const char *epithet = NULL, *type = NULL;
3865 epithet = chanserv_conf.irc_operator_epithet;
3866 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3868 else if(IsNetworkHelper(target))
3870 epithet = chanserv_conf.network_helper_epithet;
3871 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3873 else if(IsSupportHelper(target))
3875 epithet = chanserv_conf.support_helper_epithet;
3876 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3880 if(target_handle->epithet)
3881 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3883 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3885 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3889 sprintf(prefix, "%s", target_handle->handle);
3892 if(!channel->channel_info)
3894 reply("CSMSG_NOT_REGISTERED", channel->name);
3898 helping = HANDLE_FLAGGED(target_handle, HELPING)
3899 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3900 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3902 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3903 /* To prevent possible information leaks, only show infolines
3904 * if the requestor is in the channel or it's their own
3906 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3908 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3910 /* Likewise, only say it's suspended if the user has active
3911 * access in that channel or it's their own entry. */
3912 if(IsUserSuspended(uData)
3913 && (GetChannelUser(channel->channel_info, user->handle_info)
3914 || (user->handle_info == uData->handle)))
3916 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
3921 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
3928 zoot_list(struct listData *list)
3930 struct userData *uData;
3931 unsigned int start, curr, highest, lowest;
3932 struct helpfile_table tmp_table;
3933 const char **temp, *msg;
3935 if(list->table.length == 1)
3938 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3940 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3941 msg = user_find_message(list->user, "MSG_NONE");
3942 send_message_type(4, list->user, list->bot, " %s", msg);
3944 tmp_table.width = list->table.width;
3945 tmp_table.flags = list->table.flags;
3946 list->table.contents[0][0] = " ";
3947 highest = list->highest;
3948 if(list->lowest != 0)
3949 lowest = list->lowest;
3950 else if(highest < 100)
3953 lowest = highest - 100;
3954 for(start = curr = 1; curr < list->table.length; )
3956 uData = list->users[curr-1];
3957 list->table.contents[curr++][0] = " ";
3958 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
3961 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
3963 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
3964 temp = list->table.contents[--start];
3965 list->table.contents[start] = list->table.contents[0];
3966 tmp_table.contents = list->table.contents + start;
3967 tmp_table.length = curr - start;
3968 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
3969 list->table.contents[start] = temp;
3971 highest = lowest - 1;
3972 lowest = (highest < 100) ? 0 : (highest - 99);
3978 def_list(struct listData *list)
3982 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
3984 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
3985 table_send(list->bot, list->user->nick, 0, NULL, list->table);
3986 if(list->table.length == 1)
3988 msg = user_find_message(list->user, "MSG_NONE");
3989 send_message_type(4, list->user, list->bot, " %s", msg);
3994 userData_access_comp(const void *arg_a, const void *arg_b)
3996 const struct userData *a = *(struct userData**)arg_a;
3997 const struct userData *b = *(struct userData**)arg_b;
3999 if(a->access != b->access)
4000 res = b->access - a->access;
4002 res = irccasecmp(a->handle->handle, b->handle->handle);
4007 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4009 void (*send_list)(struct listData *);
4010 struct userData *uData;
4011 struct listData lData;
4012 unsigned int matches;
4016 lData.bot = cmd->parent->bot;
4017 lData.channel = channel;
4018 lData.lowest = lowest;
4019 lData.highest = highest;
4020 lData.search = (argc > 1) ? argv[1] : NULL;
4021 send_list = def_list;
4022 (void)zoot_list; /* since it doesn't show user levels */
4024 if(user->handle_info)
4026 switch(user->handle_info->userlist_style)
4028 case HI_STYLE_DEF: send_list = def_list; break;
4029 case HI_STYLE_ZOOT: send_list = def_list; break;
4033 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4035 for(uData = channel->channel_info->users; uData; uData = uData->next)
4037 if((uData->access < lowest)
4038 || (uData->access > highest)
4039 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4041 lData.users[matches++] = uData;
4043 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4045 lData.table.length = matches+1;
4046 lData.table.width = 4;
4047 lData.table.flags = TABLE_NO_FREE;
4048 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4049 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4050 lData.table.contents[0] = ary;
4053 ary[2] = "Last Seen";
4055 for(matches = 1; matches < lData.table.length; ++matches)
4057 char seen[INTERVALLEN];
4059 uData = lData.users[matches-1];
4060 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4061 lData.table.contents[matches] = ary;
4062 ary[0] = strtab(uData->access);
4063 ary[1] = uData->handle->handle;
4066 else if(!uData->seen)
4069 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4070 ary[2] = strdup(ary[2]);
4071 if(IsUserSuspended(uData))
4072 ary[3] = "Suspended";
4073 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4074 ary[3] = "Vacation";
4075 else if(HANDLE_FLAGGED(uData->handle, BOT))
4081 for(matches = 1; matches < lData.table.length; ++matches)
4083 free((char*)lData.table.contents[matches][2]);
4084 free(lData.table.contents[matches]);
4086 free(lData.table.contents[0]);
4087 free(lData.table.contents);
4091 static CHANSERV_FUNC(cmd_users)
4093 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4096 static CHANSERV_FUNC(cmd_wlist)
4098 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4101 static CHANSERV_FUNC(cmd_clist)
4103 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4106 static CHANSERV_FUNC(cmd_mlist)
4108 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4111 static CHANSERV_FUNC(cmd_olist)
4113 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4116 static CHANSERV_FUNC(cmd_plist)
4118 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4121 static CHANSERV_FUNC(cmd_bans)
4123 struct userNode *search_u = NULL;
4124 struct helpfile_table tbl;
4125 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4126 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4127 const char *msg_never, *triggered, *expires;
4128 struct banData *ban, **bans;
4132 else if(strchr(search = argv[1], '!'))
4135 search_wilds = search[strcspn(search, "?*")];
4137 else if(!(search_u = GetUserH(search)))
4138 reply("MSG_NICK_UNKNOWN", search);
4140 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4142 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4146 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4151 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4154 bans[matches++] = ban;
4159 tbl.length = matches + 1;
4160 tbl.width = 4 + timed;
4162 tbl.flags = TABLE_NO_FREE;
4163 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4164 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4165 tbl.contents[0][0] = "Mask";
4166 tbl.contents[0][1] = "Set By";
4167 tbl.contents[0][2] = "Triggered";
4170 tbl.contents[0][3] = "Expires";
4171 tbl.contents[0][4] = "Reason";
4174 tbl.contents[0][3] = "Reason";
4177 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4179 free(tbl.contents[0]);
4184 msg_never = user_find_message(user, "MSG_NEVER");
4185 for(ii = 0; ii < matches; )
4191 else if(ban->expires)
4192 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4194 expires = msg_never;
4197 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4199 triggered = msg_never;
4201 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4202 tbl.contents[ii][0] = ban->mask;
4203 tbl.contents[ii][1] = ban->owner;
4204 tbl.contents[ii][2] = strdup(triggered);
4207 tbl.contents[ii][3] = strdup(expires);
4208 tbl.contents[ii][4] = ban->reason;
4211 tbl.contents[ii][3] = ban->reason;
4213 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4214 reply("MSG_MATCH_COUNT", matches);
4215 for(ii = 1; ii < tbl.length; ++ii)
4217 free((char*)tbl.contents[ii][2]);
4219 free((char*)tbl.contents[ii][3]);
4220 free(tbl.contents[ii]);
4222 free(tbl.contents[0]);
4228 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4230 struct chanData *cData = channel->channel_info;
4231 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4233 if(cData->topic_mask)
4234 return !match_ircglob(new_topic, cData->topic_mask);
4235 else if(cData->topic)
4236 return irccasecmp(new_topic, cData->topic);
4241 static CHANSERV_FUNC(cmd_topic)
4243 struct chanData *cData;
4246 cData = channel->channel_info;
4251 SetChannelTopic(channel, chanserv, cData->topic, 1);
4252 reply("CSMSG_TOPIC_SET", cData->topic);
4256 reply("CSMSG_NO_TOPIC", channel->name);
4260 topic = unsplit_string(argv + 1, argc - 1, NULL);
4261 /* If they say "!topic *", use an empty topic. */
4262 if((topic[0] == '*') && (topic[1] == 0))
4264 if(bad_topic(channel, user, topic))
4266 char *topic_mask = cData->topic_mask;
4269 char new_topic[TOPICLEN+1], tchar;
4270 int pos=0, starpos=-1, dpos=0, len;
4272 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4279 len = strlen(topic);
4280 if((dpos + len) > TOPICLEN)
4281 len = TOPICLEN + 1 - dpos;
4282 memcpy(new_topic+dpos, topic, len);
4286 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4287 default: new_topic[dpos++] = tchar; break;
4290 if((dpos > TOPICLEN) || tchar)
4293 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4294 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4297 new_topic[dpos] = 0;
4298 SetChannelTopic(channel, chanserv, new_topic, 1);
4300 reply("CSMSG_TOPIC_LOCKED", channel->name);
4305 SetChannelTopic(channel, chanserv, topic, 1);
4307 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4309 /* Grab the topic and save it as the default topic. */
4311 cData->topic = strdup(channel->topic);
4317 static CHANSERV_FUNC(cmd_mode)
4319 struct userData *uData;
4320 struct mod_chanmode *change;
4326 change = &channel->channel_info->modes;
4327 if(change->modes_set || change->modes_clear) {
4328 modcmd_chanmode_announce(change);
4329 reply("CSMSG_DEFAULTED_MODES", channel->name);
4331 reply("CSMSG_NO_MODES", channel->name);
4335 uData = GetChannelUser(channel->channel_info, user->handle_info);
4337 base_oplevel = MAXOPLEVEL;
4338 else if (uData->access >= UL_OWNER)
4341 base_oplevel = 1 + UL_OWNER - uData->access;
4342 change = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, base_oplevel);
4345 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4349 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4350 && mode_lock_violated(&channel->channel_info->modes, change))
4353 mod_chanmode_format(&channel->channel_info->modes, modes);
4354 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4358 modcmd_chanmode_announce(change);
4359 mod_chanmode_format(change, fmt);
4360 mod_chanmode_free(change);
4361 reply("CSMSG_MODES_SET", fmt);
4366 chanserv_del_invite_mark(void *data)
4368 struct ChanUser *chanuser = data;
4369 struct chanNode *channel = chanuser->chan;
4371 if(!channel) return;
4372 for(i = 0; i < channel->invited.used; i++)
4374 if(channel->invited.list[i] == chanuser->user) {
4375 userList_remove(&channel->invited, chanuser->user);
4381 static CHANSERV_FUNC(cmd_invite)
4383 struct userData *uData;
4384 struct userNode *invite;
4385 struct ChanUser *chanuser;
4388 uData = GetChannelUser(channel->channel_info, user->handle_info);
4392 if(!(invite = GetUserH(argv[1])))
4394 reply("MSG_NICK_UNKNOWN", argv[1]);
4401 if(GetUserMode(channel, invite))
4403 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4407 for(i = 0; i < channel->invited.used; i++)
4409 if(channel->invited.list[i] == invite) {
4410 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4419 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4420 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4423 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4425 irc_invite(chanserv, invite, channel);
4427 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4429 userList_append(&channel->invited, invite);
4430 chanuser = calloc(1, sizeof(*chanuser));
4431 chanuser->user=invite;
4432 chanuser->chan=channel;
4433 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4438 static CHANSERV_FUNC(cmd_inviteme)
4440 if(GetUserMode(channel, user))
4442 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4445 if(channel->channel_info
4446 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4448 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4451 irc_invite(cmd->parent->bot, user, channel);
4456 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4459 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4461 /* We display things based on two dimensions:
4462 * - Issue time: present or absent
4463 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4464 * (in order of precedence, so something both expired and revoked
4465 * only counts as revoked)
4467 combo = (suspended->issued ? 4 : 0)
4468 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4470 case 0: /* no issue time, indefinite expiration */
4471 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4473 case 1: /* no issue time, expires in future */
4474 intervalString(buf1, suspended->expires-now, user->handle_info);
4475 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4477 case 2: /* no issue time, expired */
4478 intervalString(buf1, now-suspended->expires, user->handle_info);
4479 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4481 case 3: /* no issue time, revoked */
4482 intervalString(buf1, now-suspended->revoked, user->handle_info);
4483 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4485 case 4: /* issue time set, indefinite expiration */
4486 intervalString(buf1, now-suspended->issued, user->handle_info);
4487 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4489 case 5: /* issue time set, expires in future */
4490 intervalString(buf1, now-suspended->issued, user->handle_info);
4491 intervalString(buf2, suspended->expires-now, user->handle_info);
4492 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4494 case 6: /* issue time set, expired */
4495 intervalString(buf1, now-suspended->issued, user->handle_info);
4496 intervalString(buf2, now-suspended->expires, user->handle_info);
4497 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4499 case 7: /* issue time set, revoked */
4500 intervalString(buf1, now-suspended->issued, user->handle_info);
4501 intervalString(buf2, now-suspended->revoked, user->handle_info);
4502 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4505 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4510 static CHANSERV_FUNC(cmd_info)
4512 char modes[MAXLEN], buffer[INTERVALLEN];
4513 struct userData *uData, *owner;
4514 struct chanData *cData;
4515 struct do_not_register *dnr;
4520 cData = channel->channel_info;
4521 reply("CSMSG_CHANNEL_INFO", channel->name);
4523 uData = GetChannelUser(cData, user->handle_info);
4524 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4526 mod_chanmode_format(&cData->modes, modes);
4527 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4528 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4531 for(it = dict_first(cData->notes); it; it = iter_next(it))
4535 note = iter_data(it);
4536 if(!note_type_visible_to_user(cData, note->type, user))
4539 padding = PADLEN - 1 - strlen(iter_key(it));
4540 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4543 if(cData->max_time) {
4544 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4546 reply("CSMSG_CHANNEL_MAX", cData->max);
4548 for(owner = cData->users; owner; owner = owner->next)
4549 if(owner->access == UL_OWNER)
4550 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4551 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4552 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4553 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4555 privileged = IsStaff(user);
4557 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4558 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4559 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4561 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4562 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4564 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4566 struct suspended *suspended;
4567 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4568 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4569 show_suspension_info(cmd, user, suspended);
4571 else if(IsSuspended(cData))
4573 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4574 show_suspension_info(cmd, user, cData->suspended);
4579 static CHANSERV_FUNC(cmd_netinfo)
4581 extern unsigned long boot_time;
4582 extern unsigned long burst_length;
4583 char interval[INTERVALLEN];
4585 reply("CSMSG_NETWORK_INFO");
4586 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4587 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4588 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4589 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4590 reply("CSMSG_NETWORK_BANS", banCount);
4591 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4592 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4593 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4598 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4600 struct helpfile_table table;
4602 struct userNode *user;
4607 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4608 table.contents = alloca(list->used*sizeof(*table.contents));
4609 for(nn=0; nn<list->used; nn++)
4611 user = list->list[nn];
4612 if(user->modes & skip_flags)
4616 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4619 nick = alloca(strlen(user->nick)+3);
4620 sprintf(nick, "(%s)", user->nick);
4624 table.contents[table.length][0] = nick;
4627 table_send(chanserv, to->nick, 0, NULL, table);
4630 static CHANSERV_FUNC(cmd_ircops)
4632 reply("CSMSG_STAFF_OPERS");
4633 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4637 static CHANSERV_FUNC(cmd_helpers)
4639 reply("CSMSG_STAFF_HELPERS");
4640 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4644 static CHANSERV_FUNC(cmd_staff)
4646 reply("CSMSG_NETWORK_STAFF");
4647 cmd_ircops(CSFUNC_ARGS);
4648 cmd_helpers(CSFUNC_ARGS);
4652 static CHANSERV_FUNC(cmd_peek)
4654 struct modeNode *mn;
4655 char modes[MODELEN];
4657 struct helpfile_table table;
4658 int opcount = 0, voicecount = 0, srvcount = 0;
4660 irc_make_chanmode(channel, modes);
4662 reply("CSMSG_PEEK_INFO", channel->name);
4663 reply("CSMSG_PEEK_TOPIC", channel->topic);
4664 reply("CSMSG_PEEK_MODES", modes);
4668 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4669 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4670 for(n = 0; n < channel->members.used; n++)
4672 mn = channel->members.list[n];
4673 if(IsLocal(mn->user))
4675 else if(mn->modes & MODE_CHANOP)
4677 else if(mn->modes & MODE_VOICE)
4680 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4682 table.contents[table.length] = alloca(sizeof(**table.contents));
4683 table.contents[table.length][0] = mn->user->nick;
4687 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4688 (channel->members.used - opcount - voicecount - srvcount));
4692 reply("CSMSG_PEEK_OPS");
4693 table_send(chanserv, user->nick, 0, NULL, table);
4696 reply("CSMSG_PEEK_NO_OPS");
4700 static MODCMD_FUNC(cmd_wipeinfo)
4702 struct handle_info *victim;
4703 struct userData *ud, *actor, *real_actor;
4704 unsigned int override = 0;
4707 actor = GetChannelUser(channel->channel_info, user->handle_info);
4708 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4709 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4711 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4713 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4716 if((ud->access >= actor->access) && (ud != actor))
4718 reply("MSG_USER_OUTRANKED", victim->handle);
4721 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4722 override = CMD_LOG_OVERRIDE;
4726 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4727 return 1 | override;
4730 static CHANSERV_FUNC(cmd_resync)
4732 struct mod_chanmode *changes;
4733 struct chanData *cData = channel->channel_info;
4734 unsigned int ii, used;
4736 changes = mod_chanmode_alloc(channel->members.used * 2);
4737 for(ii = used = 0; ii < channel->members.used; ++ii)
4739 struct modeNode *mn = channel->members.list[ii];
4740 struct userData *uData;
4742 if(IsService(mn->user))
4745 uData = GetChannelAccess(cData, mn->user->handle_info);
4746 if(!cData->lvlOpts[lvlGiveOps]
4747 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4749 if(!(mn->modes & MODE_CHANOP))
4751 changes->args[used].mode = MODE_CHANOP;
4752 changes->args[used++].u.member = mn;
4755 else if(!cData->lvlOpts[lvlGiveVoice]
4756 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4758 if(mn->modes & MODE_CHANOP)
4760 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4761 changes->args[used++].u.member = mn;
4763 if(!(mn->modes & MODE_VOICE))
4765 changes->args[used].mode = MODE_VOICE;
4766 changes->args[used++].u.member = mn;
4773 changes->args[used].mode = MODE_REMOVE | mn->modes;
4774 changes->args[used++].u.member = mn;
4778 changes->argc = used;
4779 modcmd_chanmode_announce(changes);
4780 mod_chanmode_free(changes);
4781 reply("CSMSG_RESYNCED_USERS", channel->name);
4785 static CHANSERV_FUNC(cmd_seen)
4787 struct userData *uData;
4788 struct handle_info *handle;
4789 char seen[INTERVALLEN];
4793 if(!irccasecmp(argv[1], chanserv->nick))
4795 reply("CSMSG_IS_CHANSERV");
4799 if(!(handle = get_handle_info(argv[1])))
4801 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4805 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4807 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4812 reply("CSMSG_USER_PRESENT", handle->handle);
4813 else if(uData->seen)
4814 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4816 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4818 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4819 reply("CSMSG_USER_VACATION", handle->handle);
4824 static MODCMD_FUNC(cmd_names)
4826 struct userNode *targ;
4827 struct userData *targData;
4828 unsigned int ii, pos;
4831 for(ii=pos=0; ii<channel->members.used; ++ii)
4833 targ = channel->members.list[ii]->user;
4834 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4837 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4840 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4844 if(IsUserSuspended(targData))
4846 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4849 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4850 reply("CSMSG_END_NAMES", channel->name);
4855 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4857 switch(ntype->visible_type)
4859 case NOTE_VIS_ALL: return 1;
4860 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4861 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4866 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4868 struct userData *uData;
4870 switch(ntype->set_access_type)
4872 case NOTE_SET_CHANNEL_ACCESS:
4873 if(!user->handle_info)
4875 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4877 return uData->access >= ntype->set_access.min_ulevel;
4878 case NOTE_SET_CHANNEL_SETTER:
4879 return check_user_level(channel, user, lvlSetters, 1, 0);
4880 case NOTE_SET_PRIVILEGED: default:
4881 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
4885 static CHANSERV_FUNC(cmd_note)
4887 struct chanData *cData;
4889 struct note_type *ntype;
4891 cData = channel->channel_info;
4894 reply("CSMSG_NOT_REGISTERED", channel->name);
4898 /* If no arguments, show all visible notes for the channel. */
4904 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
4906 note = iter_data(it);
4907 if(!note_type_visible_to_user(cData, note->type, user))
4910 reply("CSMSG_NOTELIST_HEADER", channel->name);
4911 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
4914 reply("CSMSG_NOTELIST_END", channel->name);
4916 reply("CSMSG_NOTELIST_EMPTY", channel->name);
4918 /* If one argument, show the named note. */
4921 if((note = dict_find(cData->notes, argv[1], NULL))
4922 && note_type_visible_to_user(cData, note->type, user))
4924 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
4926 else if((ntype = dict_find(note_types, argv[1], NULL))
4927 && note_type_visible_to_user(NULL, ntype, user))
4929 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
4934 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4938 /* Assume they're trying to set a note. */
4942 ntype = dict_find(note_types, argv[1], NULL);
4945 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
4948 else if(note_type_settable_by_user(channel, ntype, user))
4950 note_text = unsplit_string(argv+2, argc-2, NULL);
4951 if((note = dict_find(cData->notes, argv[1], NULL)))
4952 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
4953 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
4954 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
4956 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
4958 /* The note is viewable to staff only, so return 0
4959 to keep the invocation from getting logged (or
4960 regular users can see it in !events). */
4966 reply("CSMSG_NO_ACCESS");
4973 static CHANSERV_FUNC(cmd_delnote)
4978 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
4979 || !note_type_settable_by_user(channel, note->type, user))
4981 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
4984 dict_remove(channel->channel_info->notes, note->type->name);
4985 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
4989 static CHANSERV_FUNC(cmd_events)
4991 struct logSearch discrim;
4992 struct logReport report;
4993 unsigned int matches, limit;
4995 limit = (argc > 1) ? atoi(argv[1]) : 10;
4996 if(limit < 1 || limit > 200)
4999 memset(&discrim, 0, sizeof(discrim));
5000 discrim.masks.bot = chanserv;
5001 discrim.masks.channel_name = channel->name;
5003 discrim.masks.command = argv[2];
5004 discrim.limit = limit;
5005 discrim.max_time = INT_MAX;
5006 discrim.severities = 1 << LOG_COMMAND;
5007 report.reporter = chanserv;
5009 reply("CSMSG_EVENT_SEARCH_RESULTS");
5010 matches = log_entry_search(&discrim, log_report_entry, &report);
5012 reply("MSG_MATCH_COUNT", matches);
5014 reply("MSG_NO_MATCHES");
5018 static CHANSERV_FUNC(cmd_say)
5024 msg = unsplit_string(argv + 1, argc - 1, NULL);
5025 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5027 else if(*argv[1] == '*' && argv[1][1] != '\0')
5029 struct handle_info *hi;
5030 struct userNode *authed;
5033 msg = unsplit_string(argv + 2, argc - 2, NULL);
5035 if (!(hi = get_handle_info(argv[1] + 1)))
5037 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5041 for (authed = hi->users; authed; authed = authed->next_authed)
5042 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5044 else if(GetUserH(argv[1]))
5047 msg = unsplit_string(argv + 2, argc - 2, NULL);
5048 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5052 reply("MSG_NOT_TARGET_NAME");
5058 static CHANSERV_FUNC(cmd_emote)
5064 /* CTCP is so annoying. */
5065 msg = unsplit_string(argv + 1, argc - 1, NULL);
5066 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5068 else if(*argv[1] == '*' && argv[1][1] != '\0')
5070 struct handle_info *hi;
5071 struct userNode *authed;
5074 msg = unsplit_string(argv + 2, argc - 2, NULL);
5076 if (!(hi = get_handle_info(argv[1] + 1)))
5078 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5082 for (authed = hi->users; authed; authed = authed->next_authed)
5083 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5085 else if(GetUserH(argv[1]))
5087 msg = unsplit_string(argv + 2, argc - 2, NULL);
5088 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5092 reply("MSG_NOT_TARGET_NAME");
5098 struct channelList *
5099 chanserv_support_channels(void)
5101 return &chanserv_conf.support_channels;
5104 static CHANSERV_FUNC(cmd_expire)
5106 int channel_count = registered_channels;
5107 expire_channels(NULL);
5108 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5113 chanserv_expire_suspension(void *data)
5115 struct suspended *suspended = data;
5116 struct chanNode *channel;
5119 /* Update the channel registration data structure. */
5120 if(!suspended->expires || (now < suspended->expires))
5121 suspended->revoked = now;
5122 channel = suspended->cData->channel;
5123 suspended->cData->channel = channel;
5124 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5126 /* If appropriate, re-join ChanServ to the channel. */
5127 if(!IsOffChannel(suspended->cData))
5129 spamserv_cs_suspend(channel, 0, 0, NULL);
5130 ss_cs_join_channel(channel, 1);
5133 /* Mark everyone currently in the channel as present. */
5134 for(ii = 0; ii < channel->members.used; ++ii)
5136 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5145 static CHANSERV_FUNC(cmd_csuspend)
5147 struct suspended *suspended;
5148 char reason[MAXLEN];
5149 unsigned long expiry, duration;
5150 struct userData *uData;
5154 if(IsProtected(channel->channel_info))
5156 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5160 if(argv[1][0] == '!')
5162 else if(IsSuspended(channel->channel_info))
5164 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5165 show_suspension_info(cmd, user, channel->channel_info->suspended);
5169 if(!strcmp(argv[1], "0"))
5171 else if((duration = ParseInterval(argv[1])))
5172 expiry = now + duration;
5175 reply("MSG_INVALID_DURATION", argv[1]);
5179 unsplit_string(argv + 2, argc - 2, reason);
5181 suspended = calloc(1, sizeof(*suspended));
5182 suspended->revoked = 0;
5183 suspended->issued = now;
5184 suspended->suspender = strdup(user->handle_info->handle);
5185 suspended->expires = expiry;
5186 suspended->reason = strdup(reason);
5187 suspended->cData = channel->channel_info;
5188 suspended->previous = suspended->cData->suspended;
5189 suspended->cData->suspended = suspended;
5191 if(suspended->expires)
5192 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5194 if(IsSuspended(channel->channel_info))
5196 suspended->previous->revoked = now;
5197 if(suspended->previous->expires)
5198 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5199 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5200 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5204 /* Mark all users in channel as absent. */
5205 for(uData = channel->channel_info->users; uData; uData = uData->next)
5214 /* Mark the channel as suspended, then part. */
5215 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5216 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5217 DelChannelUser(chanserv, channel, suspended->reason, 0);
5218 reply("CSMSG_SUSPENDED", channel->name);
5219 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5220 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5225 static CHANSERV_FUNC(cmd_cunsuspend)
5227 struct suspended *suspended;
5228 char message[MAXLEN];
5230 if(!IsSuspended(channel->channel_info))
5232 reply("CSMSG_NOT_SUSPENDED", channel->name);
5236 suspended = channel->channel_info->suspended;
5238 /* Expire the suspension and join ChanServ to the channel. */
5239 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5240 chanserv_expire_suspension(suspended);
5241 reply("CSMSG_UNSUSPENDED", channel->name);
5242 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5243 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5247 typedef struct chanservSearch
5252 unsigned long unvisited;
5253 unsigned long registered;
5255 unsigned long flags;
5259 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5262 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5267 search = malloc(sizeof(struct chanservSearch));
5268 memset(search, 0, sizeof(*search));
5271 for(i = 0; i < argc; i++)
5273 /* Assume all criteria require arguments. */
5276 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5280 if(!irccasecmp(argv[i], "name"))
5281 search->name = argv[++i];
5282 else if(!irccasecmp(argv[i], "registrar"))
5283 search->registrar = argv[++i];
5284 else if(!irccasecmp(argv[i], "unvisited"))
5285 search->unvisited = ParseInterval(argv[++i]);
5286 else if(!irccasecmp(argv[i], "registered"))
5287 search->registered = ParseInterval(argv[++i]);
5288 else if(!irccasecmp(argv[i], "flags"))
5291 if(!irccasecmp(argv[i], "nodelete"))
5292 search->flags |= CHANNEL_NODELETE;
5293 else if(!irccasecmp(argv[i], "suspended"))
5294 search->flags |= CHANNEL_SUSPENDED;
5295 else if(!irccasecmp(argv[i], "unreviewed"))
5296 search->flags |= CHANNEL_UNREVIEWED;
5299 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5303 else if(!irccasecmp(argv[i], "limit"))
5304 search->limit = strtoul(argv[++i], NULL, 10);
5307 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5312 if(search->name && !strcmp(search->name, "*"))
5314 if(search->registrar && !strcmp(search->registrar, "*"))
5315 search->registrar = 0;
5324 chanserv_channel_match(struct chanData *channel, search_t search)
5326 const char *name = channel->channel->name;
5327 if((search->name && !match_ircglob(name, search->name)) ||
5328 (search->registrar && !channel->registrar) ||
5329 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5330 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5331 (search->registered && (now - channel->registered) > search->registered) ||
5332 (search->flags && ((search->flags & channel->flags) != search->flags)))
5339 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5341 struct chanData *channel;
5342 unsigned int matches = 0;
5344 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5346 if(!chanserv_channel_match(channel, search))
5356 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5361 search_print(struct chanData *channel, void *data)
5363 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5366 static CHANSERV_FUNC(cmd_search)
5369 unsigned int matches;
5370 channel_search_func action;
5374 if(!irccasecmp(argv[1], "count"))
5375 action = search_count;
5376 else if(!irccasecmp(argv[1], "print"))
5377 action = search_print;
5380 reply("CSMSG_ACTION_INVALID", argv[1]);
5384 search = chanserv_search_create(user, argc - 2, argv + 2);
5388 if(action == search_count)
5389 search->limit = INT_MAX;
5391 if(action == search_print)
5392 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5394 matches = chanserv_channel_search(search, action, user);
5397 reply("MSG_MATCH_COUNT", matches);
5399 reply("MSG_NO_MATCHES");
5405 static CHANSERV_FUNC(cmd_unvisited)
5407 struct chanData *cData;
5408 unsigned long interval = chanserv_conf.channel_expire_delay;
5409 char buffer[INTERVALLEN];
5410 unsigned int limit = 25, matches = 0;
5414 interval = ParseInterval(argv[1]);
5416 limit = atoi(argv[2]);
5419 intervalString(buffer, interval, user->handle_info);
5420 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5422 for(cData = channelList; cData && matches < limit; cData = cData->next)
5424 if((now - cData->visited) < interval)
5427 intervalString(buffer, now - cData->visited, user->handle_info);
5428 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5435 static MODCMD_FUNC(chan_opt_defaulttopic)
5441 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5443 reply("CSMSG_TOPIC_LOCKED", channel->name);
5447 topic = unsplit_string(argv+1, argc-1, NULL);
5449 free(channel->channel_info->topic);
5450 if(topic[0] == '*' && topic[1] == 0)
5452 topic = channel->channel_info->topic = NULL;
5456 topic = channel->channel_info->topic = strdup(topic);
5457 if(channel->channel_info->topic_mask
5458 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5459 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5461 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5464 if(channel->channel_info->topic)
5465 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5467 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5471 static MODCMD_FUNC(chan_opt_topicmask)
5475 struct chanData *cData = channel->channel_info;
5478 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5480 reply("CSMSG_TOPIC_LOCKED", channel->name);
5484 mask = unsplit_string(argv+1, argc-1, NULL);
5486 if(cData->topic_mask)
5487 free(cData->topic_mask);
5488 if(mask[0] == '*' && mask[1] == 0)
5490 cData->topic_mask = 0;
5494 cData->topic_mask = strdup(mask);
5496 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5497 else if(!match_ircglob(cData->topic, cData->topic_mask))
5498 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5502 if(channel->channel_info->topic_mask)
5503 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5505 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5509 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5513 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5517 if(greeting[0] == '*' && greeting[1] == 0)
5521 unsigned int length = strlen(greeting);
5522 if(length > chanserv_conf.greeting_length)
5524 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5527 *data = strdup(greeting);
5536 reply(name, user_find_message(user, "MSG_NONE"));
5540 static MODCMD_FUNC(chan_opt_greeting)
5542 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5545 static MODCMD_FUNC(chan_opt_usergreeting)
5547 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5550 static MODCMD_FUNC(chan_opt_modes)
5552 struct mod_chanmode *new_modes;
5557 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5559 reply("CSMSG_NO_ACCESS");
5562 if(argv[1][0] == '*' && argv[1][1] == 0)
5564 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5566 else if(!(new_modes = mod_chanmode_parse(channel, argv+1, argc-1, MCP_KEY_FREE|MCP_REGISTERED|MCP_NO_APASS, 0)))
5568 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5571 else if(new_modes->argc > 1)
5573 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5574 mod_chanmode_free(new_modes);
5579 channel->channel_info->modes = *new_modes;
5580 modcmd_chanmode_announce(new_modes);
5581 mod_chanmode_free(new_modes);
5585 mod_chanmode_format(&channel->channel_info->modes, modes);
5587 reply("CSMSG_SET_MODES", modes);
5589 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5593 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5595 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5597 struct chanData *cData = channel->channel_info;
5602 /* Set flag according to value. */
5603 if(enabled_string(argv[1]))
5605 cData->flags |= mask;
5608 else if(disabled_string(argv[1]))
5610 cData->flags &= ~mask;
5615 reply("MSG_INVALID_BINARY", argv[1]);
5621 /* Find current option value. */
5622 value = (cData->flags & mask) ? 1 : 0;
5626 reply(name, user_find_message(user, "MSG_ON"));
5628 reply(name, user_find_message(user, "MSG_OFF"));
5632 static MODCMD_FUNC(chan_opt_nodelete)
5634 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5636 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5640 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5643 static MODCMD_FUNC(chan_opt_dynlimit)
5645 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5648 static MODCMD_FUNC(chan_opt_offchannel)
5650 struct chanData *cData = channel->channel_info;
5655 /* Set flag according to value. */
5656 if(enabled_string(argv[1]))
5658 if(!IsOffChannel(cData))
5659 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5660 cData->flags |= CHANNEL_OFFCHANNEL;
5663 else if(disabled_string(argv[1]))
5665 if(IsOffChannel(cData))
5667 struct mod_chanmode change;
5668 mod_chanmode_init(&change);
5670 change.args[0].mode = MODE_CHANOP;
5671 change.args[0].u.member = AddChannelUser(chanserv, channel);
5672 mod_chanmode_announce(chanserv, channel, &change);
5674 cData->flags &= ~CHANNEL_OFFCHANNEL;
5679 reply("MSG_INVALID_BINARY", argv[1]);
5685 /* Find current option value. */
5686 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5690 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5692 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5696 static MODCMD_FUNC(chan_opt_unreviewed)
5698 struct chanData *cData = channel->channel_info;
5699 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5705 /* The two directions can have different ACLs. */
5706 if(enabled_string(argv[1]))
5708 else if(disabled_string(argv[1]))
5712 reply("MSG_INVALID_BINARY", argv[1]);
5716 if (new_value != value)
5718 struct svccmd *subcmd;
5719 char subcmd_name[32];
5721 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5722 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5725 reply("MSG_COMMAND_DISABLED", subcmd_name);
5728 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5732 cData->flags |= CHANNEL_UNREVIEWED;
5735 free(cData->registrar);
5736 cData->registrar = strdup(user->handle_info->handle);
5737 cData->flags &= ~CHANNEL_UNREVIEWED;
5744 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5746 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5750 static MODCMD_FUNC(chan_opt_defaults)
5752 struct userData *uData;
5753 struct chanData *cData;
5754 const char *confirm;
5755 enum levelOption lvlOpt;
5756 enum charOption chOpt;
5758 cData = channel->channel_info;
5759 uData = GetChannelUser(cData, user->handle_info);
5760 if(!uData || (uData->access < UL_OWNER))
5762 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5765 confirm = make_confirmation_string(uData);
5766 if((argc < 2) || strcmp(argv[1], confirm))
5768 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5771 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5772 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5773 cData->modes = chanserv_conf.default_modes;
5774 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5775 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5776 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5777 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5778 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5783 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5785 struct chanData *cData = channel->channel_info;
5786 struct userData *uData;
5787 unsigned short value;
5791 if(!check_user_level(channel, user, option, 1, 1))
5793 reply("CSMSG_CANNOT_SET");
5796 value = user_level_from_name(argv[1], UL_OWNER+1);
5797 if(!value && strcmp(argv[1], "0"))
5799 reply("CSMSG_INVALID_ACCESS", argv[1]);
5802 uData = GetChannelUser(cData, user->handle_info);
5803 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5805 reply("CSMSG_BAD_SETLEVEL");
5811 if(value > cData->lvlOpts[lvlGiveOps])
5813 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5818 if(value < cData->lvlOpts[lvlGiveVoice])
5820 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5825 /* This test only applies to owners, since non-owners
5826 * trying to set an option to above their level get caught
5827 * by the CSMSG_BAD_SETLEVEL test above.
5829 if(value > uData->access)
5831 reply("CSMSG_BAD_SETTERS");
5838 cData->lvlOpts[option] = value;
5840 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5844 static MODCMD_FUNC(chan_opt_enfops)
5846 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5849 static MODCMD_FUNC(chan_opt_giveops)
5851 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5854 static MODCMD_FUNC(chan_opt_enfmodes)
5856 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5859 static MODCMD_FUNC(chan_opt_enftopic)
5861 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5864 static MODCMD_FUNC(chan_opt_pubcmd)
5866 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5869 static MODCMD_FUNC(chan_opt_setters)
5871 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5874 static MODCMD_FUNC(chan_opt_ctcpusers)
5876 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5879 static MODCMD_FUNC(chan_opt_userinfo)
5881 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
5884 static MODCMD_FUNC(chan_opt_givevoice)
5886 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
5889 static MODCMD_FUNC(chan_opt_topicsnarf)
5891 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
5894 static MODCMD_FUNC(chan_opt_inviteme)
5896 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
5900 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5902 struct chanData *cData = channel->channel_info;
5903 int count = charOptions[option].count, idx;
5907 idx = atoi(argv[1]);
5909 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
5911 reply("CSMSG_INVALID_NUMERIC", idx);
5912 /* Show possible values. */
5913 for(idx = 0; idx < count; idx++)
5914 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5918 cData->chOpts[option] = charOptions[option].values[idx].value;
5922 /* Find current option value. */
5925 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
5929 /* Somehow, the option value is corrupt; reset it to the default. */
5930 cData->chOpts[option] = charOptions[option].default_value;
5935 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
5939 static MODCMD_FUNC(chan_opt_protect)
5941 return channel_multiple_option(chProtect, CSFUNC_ARGS);
5944 static MODCMD_FUNC(chan_opt_toys)
5946 return channel_multiple_option(chToys, CSFUNC_ARGS);
5949 static MODCMD_FUNC(chan_opt_ctcpreaction)
5951 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
5954 static MODCMD_FUNC(chan_opt_topicrefresh)
5956 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
5959 static struct svccmd_list set_shows_list;
5962 handle_svccmd_unbind(struct svccmd *target) {
5964 for(ii=0; ii<set_shows_list.used; ++ii)
5965 if(target == set_shows_list.list[ii])
5966 set_shows_list.used = 0;
5969 static CHANSERV_FUNC(cmd_set)
5971 struct svccmd *subcmd;
5975 /* Check if we need to (re-)initialize set_shows_list. */
5976 if(!set_shows_list.used)
5978 if(!set_shows_list.size)
5980 set_shows_list.size = chanserv_conf.set_shows->used;
5981 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
5983 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
5985 const char *name = chanserv_conf.set_shows->list[ii];
5986 sprintf(buf, "%s %s", argv[0], name);
5987 subcmd = dict_find(cmd->parent->commands, buf, NULL);
5990 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
5993 svccmd_list_append(&set_shows_list, subcmd);
5999 reply("CSMSG_CHANNEL_OPTIONS");
6000 for(ii = 0; ii < set_shows_list.used; ii++)
6002 subcmd = set_shows_list.list[ii];
6003 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6008 sprintf(buf, "%s %s", argv[0], argv[1]);
6009 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6012 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6015 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6017 reply("CSMSG_NO_ACCESS");
6023 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6027 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6029 struct userData *uData;
6031 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6034 reply("CSMSG_NOT_USER", channel->name);
6040 /* Just show current option value. */
6042 else if(enabled_string(argv[1]))
6044 uData->flags |= mask;
6046 else if(disabled_string(argv[1]))
6048 uData->flags &= ~mask;
6052 reply("MSG_INVALID_BINARY", argv[1]);
6056 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6060 static MODCMD_FUNC(user_opt_noautoop)
6062 struct userData *uData;
6064 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6067 reply("CSMSG_NOT_USER", channel->name);
6070 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6071 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6073 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6076 static MODCMD_FUNC(user_opt_autoinvite)
6078 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6080 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6082 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6085 static MODCMD_FUNC(user_opt_info)
6087 struct userData *uData;
6090 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6094 /* If they got past the command restrictions (which require access)
6095 * but fail this test, we have some fool with security override on.
6097 reply("CSMSG_NOT_USER", channel->name);
6104 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6105 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6107 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6110 bp = strcspn(infoline, "\001");
6113 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6118 if(infoline[0] == '*' && infoline[1] == 0)
6121 uData->info = strdup(infoline);
6124 reply("CSMSG_USET_INFO", uData->info);
6126 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6130 struct svccmd_list uset_shows_list;
6132 static CHANSERV_FUNC(cmd_uset)
6134 struct svccmd *subcmd;
6138 /* Check if we need to (re-)initialize uset_shows_list. */
6139 if(!uset_shows_list.used)
6143 "NoAutoOp", "AutoInvite", "Info"
6146 if(!uset_shows_list.size)
6148 uset_shows_list.size = ArrayLength(options);
6149 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6151 for(ii = 0; ii < ArrayLength(options); ii++)
6153 const char *name = options[ii];
6154 sprintf(buf, "%s %s", argv[0], name);
6155 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6158 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6161 svccmd_list_append(&uset_shows_list, subcmd);
6167 /* Do this so options are presented in a consistent order. */
6168 reply("CSMSG_USER_OPTIONS");
6169 for(ii = 0; ii < uset_shows_list.used; ii++)
6170 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6174 sprintf(buf, "%s %s", argv[0], argv[1]);
6175 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6178 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6182 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6185 static CHANSERV_FUNC(cmd_giveownership)
6187 struct handle_info *new_owner_hi;
6188 struct userData *new_owner;
6189 struct userData *curr_user;
6190 struct userData *invoker;
6191 struct chanData *cData = channel->channel_info;
6192 struct do_not_register *dnr;
6193 const char *confirm;
6195 unsigned short co_access;
6196 char reason[MAXLEN];
6199 curr_user = GetChannelAccess(cData, user->handle_info);
6200 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6201 if(!curr_user || (curr_user->access != UL_OWNER))
6203 struct userData *owner = NULL;
6204 for(curr_user = channel->channel_info->users;
6206 curr_user = curr_user->next)
6208 if(curr_user->access != UL_OWNER)
6212 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6219 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6221 char delay[INTERVALLEN];
6222 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6223 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6226 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6228 if(new_owner_hi == user->handle_info)
6230 reply("CSMSG_NO_TRANSFER_SELF");
6233 new_owner = GetChannelAccess(cData, new_owner_hi);
6238 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6242 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6246 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6248 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6251 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6252 if(!IsHelping(user))
6253 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6255 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6258 invoker = GetChannelUser(cData, user->handle_info);
6259 if(invoker->access <= UL_OWNER)
6261 confirm = make_confirmation_string(curr_user);
6262 if((argc < 3) || strcmp(argv[2], confirm))
6264 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6268 if(new_owner->access >= UL_COOWNER)
6269 co_access = new_owner->access;
6271 co_access = UL_COOWNER;
6272 new_owner->access = UL_OWNER;
6274 curr_user->access = co_access;
6275 cData->ownerTransfer = now;
6276 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6277 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6278 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6282 static CHANSERV_FUNC(cmd_suspend)
6284 struct handle_info *hi;
6285 struct userData *actor, *real_actor, *target;
6286 unsigned int override = 0;
6289 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6290 actor = GetChannelUser(channel->channel_info, user->handle_info);
6291 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6292 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6294 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6297 if(target->access >= actor->access)
6299 reply("MSG_USER_OUTRANKED", hi->handle);
6302 if(target->flags & USER_SUSPENDED)
6304 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6309 target->present = 0;
6312 if(!real_actor || target->access >= real_actor->access)
6313 override = CMD_LOG_OVERRIDE;
6314 target->flags |= USER_SUSPENDED;
6315 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6316 return 1 | override;
6319 static CHANSERV_FUNC(cmd_unsuspend)
6321 struct handle_info *hi;
6322 struct userData *actor, *real_actor, *target;
6323 unsigned int override = 0;
6326 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6327 actor = GetChannelUser(channel->channel_info, user->handle_info);
6328 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6329 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6331 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6334 if(target->access >= actor->access)
6336 reply("MSG_USER_OUTRANKED", hi->handle);
6339 if(!(target->flags & USER_SUSPENDED))
6341 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6344 if(!real_actor || target->access >= real_actor->access)
6345 override = CMD_LOG_OVERRIDE;
6346 target->flags &= ~USER_SUSPENDED;
6347 scan_user_presence(target, NULL);
6348 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6349 return 1 | override;
6352 static MODCMD_FUNC(cmd_deleteme)
6354 struct handle_info *hi;
6355 struct userData *target;
6356 const char *confirm_string;
6357 unsigned short access_level;
6360 hi = user->handle_info;
6361 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6363 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6366 if(target->access == UL_OWNER)
6368 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6371 confirm_string = make_confirmation_string(target);
6372 if((argc < 2) || strcmp(argv[1], confirm_string))
6374 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6377 access_level = target->access;
6378 channel_name = strdup(channel->name);
6379 del_channel_user(target, 1);
6380 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6386 chanserv_refresh_topics(UNUSED_ARG(void *data))
6388 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6389 struct chanData *cData;
6392 for(cData = channelList; cData; cData = cData->next)
6394 if(IsSuspended(cData))
6396 opt = cData->chOpts[chTopicRefresh];
6399 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6402 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6403 cData->last_refresh = refresh_num;
6405 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6408 static CHANSERV_FUNC(cmd_unf)
6412 char response[MAXLEN];
6413 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6414 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6415 irc_privmsg(cmd->parent->bot, channel->name, response);
6418 reply("CSMSG_UNF_RESPONSE");
6422 static CHANSERV_FUNC(cmd_ping)
6426 char response[MAXLEN];
6427 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6428 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6429 irc_privmsg(cmd->parent->bot, channel->name, response);
6432 reply("CSMSG_PING_RESPONSE");
6436 static CHANSERV_FUNC(cmd_wut)
6440 char response[MAXLEN];
6441 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6442 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6443 irc_privmsg(cmd->parent->bot, channel->name, response);
6446 reply("CSMSG_WUT_RESPONSE");
6450 static CHANSERV_FUNC(cmd_8ball)
6452 unsigned int i, j, accum;
6457 for(i=1; i<argc; i++)
6458 for(j=0; argv[i][j]; j++)
6459 accum = (accum << 5) - accum + toupper(argv[i][j]);
6460 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6463 char response[MAXLEN];
6464 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6465 irc_privmsg(cmd->parent->bot, channel->name, response);
6468 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6472 static CHANSERV_FUNC(cmd_d)
6474 unsigned long sides, count, modifier, ii, total;
6475 char response[MAXLEN], *sep;
6479 if((count = strtoul(argv[1], &sep, 10)) < 1)
6489 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6490 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6494 else if((sep[0] == '-') && isdigit(sep[1]))
6495 modifier = strtoul(sep, NULL, 10);
6496 else if((sep[0] == '+') && isdigit(sep[1]))
6497 modifier = strtoul(sep+1, NULL, 10);
6504 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6509 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6512 for(total = ii = 0; ii < count; ++ii)
6513 total += (rand() % sides) + 1;
6516 if((count > 1) || modifier)
6518 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
6519 sprintf(response, fmt, total, count, sides, modifier);
6523 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
6524 sprintf(response, fmt, total, sides);
6527 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
6529 send_message_type(4, user, cmd->parent->bot, "%s", response);
6533 static CHANSERV_FUNC(cmd_huggle)
6535 /* CTCP must be via PRIVMSG, never notice */
6537 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
6539 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
6544 chanserv_adjust_limit(void *data)
6546 struct mod_chanmode change;
6547 struct chanData *cData = data;
6548 struct chanNode *channel = cData->channel;
6551 if(IsSuspended(cData))
6554 cData->limitAdjusted = now;
6555 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
6556 if(cData->modes.modes_set & MODE_LIMIT)
6558 if(limit > cData->modes.new_limit)
6559 limit = cData->modes.new_limit;
6560 else if(limit == cData->modes.new_limit)
6564 mod_chanmode_init(&change);
6565 change.modes_set = MODE_LIMIT;
6566 change.new_limit = limit;
6567 mod_chanmode_announce(chanserv, channel, &change);
6571 handle_new_channel(struct chanNode *channel)
6573 struct chanData *cData;
6575 if(!(cData = channel->channel_info))
6578 if(cData->modes.modes_set || cData->modes.modes_clear)
6579 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
6581 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
6582 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
6585 /* Welcome to my worst nightmare. Warning: Read (or modify)
6586 the code below at your own risk. */
6588 handle_join(struct modeNode *mNode)
6590 struct mod_chanmode change;
6591 struct userNode *user = mNode->user;
6592 struct chanNode *channel = mNode->channel;
6593 struct chanData *cData;
6594 struct userData *uData = NULL;
6595 struct banData *bData;
6596 struct handle_info *handle;
6597 unsigned int modes = 0, info = 0;
6600 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
6603 cData = channel->channel_info;
6604 if(channel->members.used > cData->max) {
6605 cData->max = channel->members.used;
6606 cData->max_time = now;
6609 for(i = 0; i < channel->invited.used; i++)
6611 if(channel->invited.list[i] == user) {
6612 userList_remove(&channel->invited, user);
6616 /* Check for bans. If they're joining through a ban, one of two
6618 * 1: Join during a netburst, by riding the break. Kick them
6619 * unless they have ops or voice in the channel.
6620 * 2: They're allowed to join through the ban (an invite in
6621 * ircu2.10, or a +e on Hybrid, or something).
6622 * If they're not joining through a ban, and the banlist is not
6623 * full, see if they're on the banlist for the channel. If so,
6626 if(user->uplink->burst && !mNode->modes)
6629 for(ii = 0; ii < channel->banlist.used; ii++)
6631 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
6633 /* Riding a netburst. Naughty. */
6634 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
6640 mod_chanmode_init(&change);
6642 if(channel->banlist.used < MAXBANS)
6644 /* Not joining through a ban. */
6645 for(bData = cData->bans;
6646 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
6647 bData = bData->next);
6651 char kick_reason[MAXLEN];
6652 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
6654 bData->triggered = now;
6655 if(bData != cData->bans)
6657 /* Shuffle the ban to the head of the list. */
6659 bData->next->prev = bData->prev;
6661 bData->prev->next = bData->next;
6664 bData->next = cData->bans;
6667 cData->bans->prev = bData;
6668 cData->bans = bData;
6671 change.args[0].mode = MODE_BAN;
6672 change.args[0].u.hostmask = bData->mask;
6673 mod_chanmode_announce(chanserv, channel, &change);
6674 KickChannelUser(user, channel, chanserv, kick_reason);
6679 /* ChanServ will not modify the limits in join-flooded channels,
6680 or when there are enough slots left below the limit. */
6681 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
6682 && !channel->join_flooded
6683 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
6685 /* The user count has begun "bumping" into the channel limit,
6686 so set a timer to raise the limit a bit. Any previous
6687 timers are removed so three incoming users within the delay
6688 results in one limit change, not three. */
6690 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6691 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6694 if(channel->join_flooded)
6696 /* don't automatically give ops or voice during a join flood */
6698 else if(cData->lvlOpts[lvlGiveOps] == 0)
6699 modes |= MODE_CHANOP;
6700 else if(cData->lvlOpts[lvlGiveVoice] == 0)
6701 modes |= MODE_VOICE;
6703 greeting = cData->greeting;
6704 if(user->handle_info)
6706 handle = user->handle_info;
6708 if(IsHelper(user) && !IsHelping(user))
6711 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6713 if(channel == chanserv_conf.support_channels.list[ii])
6715 HANDLE_SET_FLAG(user->handle_info, HELPING);
6721 uData = GetTrueChannelAccess(cData, handle);
6722 if(uData && !IsUserSuspended(uData))
6724 /* Ops and above were handled by the above case. */
6725 if(IsUserAutoOp(uData))
6727 if(uData->access >= cData->lvlOpts[lvlGiveOps])
6728 modes |= MODE_CHANOP;
6729 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
6730 modes |= MODE_VOICE;
6732 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6733 cData->visited = now;
6734 if(cData->user_greeting)
6735 greeting = cData->user_greeting;
6737 && (uData->access >= cData->lvlOpts[lvlUserInfo])
6738 && ((now - uData->seen) >= chanserv_conf.info_delay)
6746 /* If user joining normally (not during burst), apply op or voice,
6747 * and send greeting/userinfo as appropriate.
6749 if(!user->uplink->burst)
6753 if(modes & MODE_CHANOP)
6754 modes &= ~MODE_VOICE;
6755 change.args[0].mode = modes;
6756 change.args[0].u.member = mNode;
6757 mod_chanmode_announce(chanserv, channel, &change);
6760 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
6761 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
6762 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
6768 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
6770 struct mod_chanmode change;
6771 struct userData *channel;
6772 unsigned int ii, jj;
6774 if(!user->handle_info)
6777 mod_chanmode_init(&change);
6779 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
6781 struct chanNode *cn;
6782 struct modeNode *mn;
6783 if(IsUserSuspended(channel)
6784 || IsSuspended(channel->channel)
6785 || !(cn = channel->channel->channel))
6788 mn = GetUserMode(cn, user);
6791 if(!IsUserSuspended(channel)
6792 && IsUserAutoInvite(channel)
6793 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
6795 && !user->uplink->burst)
6796 irc_invite(chanserv, user, cn);
6800 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
6801 channel->channel->visited = now;
6803 if(IsUserAutoOp(channel))
6805 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
6806 change.args[0].mode = MODE_CHANOP;
6807 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
6808 change.args[0].mode = MODE_VOICE;
6810 change.args[0].mode = 0;
6811 change.args[0].u.member = mn;
6812 if(change.args[0].mode)
6813 mod_chanmode_announce(chanserv, cn, &change);
6816 channel->seen = now;
6817 channel->present = 1;
6820 for(ii = 0; ii < user->channels.used; ++ii)
6822 struct chanNode *chan = user->channels.list[ii]->channel;
6823 struct banData *ban;
6825 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
6826 || !chan->channel_info
6827 || IsSuspended(chan->channel_info))
6829 for(jj = 0; jj < chan->banlist.used; ++jj)
6830 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
6832 if(jj < chan->banlist.used)
6834 for(ban = chan->channel_info->bans; ban; ban = ban->next)
6836 char kick_reason[MAXLEN];
6837 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
6839 change.args[0].mode = MODE_BAN;
6840 change.args[0].u.hostmask = ban->mask;
6841 mod_chanmode_announce(chanserv, chan, &change);
6842 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
6843 KickChannelUser(user, chan, chanserv, kick_reason);
6844 ban->triggered = now;
6849 if(IsSupportHelper(user))
6851 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
6853 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
6855 HANDLE_SET_FLAG(user->handle_info, HELPING);
6863 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
6865 struct chanData *cData;
6866 struct userData *uData;
6868 cData = mn->channel->channel_info;
6869 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
6872 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
6874 /* Allow for a bit of padding so that the limit doesn't
6875 track the user count exactly, which could get annoying. */
6876 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
6878 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
6879 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
6883 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
6885 scan_user_presence(uData, mn->user);
6887 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
6888 cData->visited = now;
6891 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
6894 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
6895 struct chanNode *channel;
6896 struct userNode *exclude;
6897 /* When looking at the channel that is being /part'ed, we
6898 * have to skip over the client that is leaving. For
6899 * other channels, we must not do that.
6901 channel = chanserv_conf.support_channels.list[ii];
6902 exclude = (channel == mn->channel) ? mn->user : NULL;
6903 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
6906 if(ii == chanserv_conf.support_channels.used)
6907 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
6912 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
6914 struct userData *uData;
6916 if(!channel->channel_info || !kicker || IsService(kicker)
6917 || (kicker == victim) || IsSuspended(channel->channel_info)
6918 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
6921 if(protect_user(victim, kicker, channel->channel_info))
6923 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
6924 KickChannelUser(kicker, channel, chanserv, reason);
6927 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
6932 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
6934 struct chanData *cData;
6936 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
6939 cData = channel->channel_info;
6940 if(bad_topic(channel, user, channel->topic))
6942 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
6943 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
6944 SetChannelTopic(channel, chanserv, old_topic, 1);
6945 else if(cData->topic)
6946 SetChannelTopic(channel, chanserv, cData->topic, 1);
6949 /* With topicsnarf, grab the topic and save it as the default topic. */
6950 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
6953 cData->topic = strdup(channel->topic);
6959 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
6961 struct mod_chanmode *bounce = NULL;
6962 unsigned int bnc, ii;
6965 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
6968 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
6969 && mode_lock_violated(&channel->channel_info->modes, change))
6971 char correct[MAXLEN];
6972 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
6973 mod_chanmode_format(&channel->channel_info->modes, correct);
6974 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
6976 for(ii = bnc = 0; ii < change->argc; ++ii)
6978 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
6980 const struct userNode *victim = change->args[ii].u.member->user;
6981 if(!protect_user(victim, user, channel->channel_info))
6984 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
6987 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
6988 bounce->args[bnc].u.member = GetUserMode(channel, user);
6989 if(bounce->args[bnc].u.member)
6993 bounce->args[bnc].mode = MODE_CHANOP;
6994 bounce->args[bnc].u.member = change->args[ii].u.member;
6996 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
6998 else if(change->args[ii].mode & MODE_CHANOP)
7000 const struct userNode *victim = change->args[ii].u.member->user;
7001 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7004 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7005 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7006 bounce->args[bnc].u.member = change->args[ii].u.member;
7009 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7011 const char *ban = change->args[ii].u.hostmask;
7012 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7015 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7016 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7017 bounce->args[bnc].u.hostmask = strdup(ban);
7019 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7024 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7025 mod_chanmode_announce(chanserv, channel, bounce);
7026 for(ii = 0; ii < change->argc; ++ii)
7027 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7028 free((char*)bounce->args[ii].u.hostmask);
7029 mod_chanmode_free(bounce);
7034 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7036 struct chanNode *channel;
7037 struct banData *bData;
7038 struct mod_chanmode change;
7039 unsigned int ii, jj;
7040 char kick_reason[MAXLEN];
7042 mod_chanmode_init(&change);
7044 change.args[0].mode = MODE_BAN;
7045 for(ii = 0; ii < user->channels.used; ++ii)
7047 channel = user->channels.list[ii]->channel;
7048 /* Need not check for bans if they're opped or voiced. */
7049 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7051 /* Need not check for bans unless channel registration is active. */
7052 if(!channel->channel_info || IsSuspended(channel->channel_info))
7054 /* Look for a matching ban already on the channel. */
7055 for(jj = 0; jj < channel->banlist.used; ++jj)
7056 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7058 /* Need not act if we found one. */
7059 if(jj < channel->banlist.used)
7061 /* Look for a matching ban in this channel. */
7062 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7064 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7066 change.args[0].u.hostmask = bData->mask;
7067 mod_chanmode_announce(chanserv, channel, &change);
7068 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7069 KickChannelUser(user, channel, chanserv, kick_reason);
7070 bData->triggered = now;
7071 break; /* we don't need to check any more bans in the channel */
7076 static void handle_rename(struct handle_info *handle, const char *old_handle)
7078 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7082 dict_remove2(handle_dnrs, old_handle, 1);
7083 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7084 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7089 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7091 struct userNode *h_user;
7093 if(handle->channels)
7095 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7096 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7098 while(handle->channels)
7099 del_channel_user(handle->channels, 1);
7104 handle_server_link(UNUSED_ARG(struct server *server))
7106 struct chanData *cData;
7108 for(cData = channelList; cData; cData = cData->next)
7110 if(!IsSuspended(cData))
7111 cData->may_opchan = 1;
7112 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7113 && !cData->channel->join_flooded
7114 && ((cData->channel->limit - cData->channel->members.used)
7115 < chanserv_conf.adjust_threshold))
7117 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7118 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7124 chanserv_conf_read(void)
7128 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7129 struct mod_chanmode *change;
7130 struct string_list *strlist;
7131 struct chanNode *chan;
7134 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7136 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7139 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7140 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7141 chanserv_conf.support_channels.used = 0;
7142 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7144 for(ii = 0; ii < strlist->used; ++ii)
7146 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7149 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7151 channelList_append(&chanserv_conf.support_channels, chan);
7154 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7157 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7160 chan = AddChannel(str, now, str2, NULL);
7162 channelList_append(&chanserv_conf.support_channels, chan);
7164 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7165 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7166 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7167 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7168 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7169 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7170 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7171 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7172 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7173 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7174 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7175 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7176 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7177 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7178 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7179 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7180 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7181 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7182 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7183 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7184 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7185 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7186 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7187 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7188 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7189 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7190 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7192 NickChange(chanserv, str, 0);
7193 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7194 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7195 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7196 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7197 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7198 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7199 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7200 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7201 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7202 chanserv_conf.max_owned = str ? atoi(str) : 5;
7203 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7204 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7205 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7206 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7207 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7208 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7209 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7212 safestrncpy(mode_line, str, sizeof(mode_line));
7213 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7214 if((change = mod_chanmode_parse(NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7215 && (change->argc < 2))
7217 chanserv_conf.default_modes = *change;
7218 mod_chanmode_free(change);
7220 free_string_list(chanserv_conf.set_shows);
7221 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7223 strlist = string_list_copy(strlist);
7226 static const char *list[] = {
7227 /* free form text */
7228 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7229 /* options based on user level */
7230 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7231 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7232 /* multiple choice options */
7233 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7234 /* binary options */
7235 "DynLimit", "NoDelete", "expire",
7239 strlist = alloc_string_list(ArrayLength(list)-1);
7240 for(ii=0; list[ii]; ii++)
7241 string_list_append(strlist, strdup(list[ii]));
7243 chanserv_conf.set_shows = strlist;
7244 /* We don't look things up now, in case the list refers to options
7245 * defined by modules initialized after this point. Just mark the
7246 * function list as invalid, so it will be initialized.
7248 set_shows_list.used = 0;
7249 free_string_list(chanserv_conf.eightball);
7250 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7253 strlist = string_list_copy(strlist);
7257 strlist = alloc_string_list(4);
7258 string_list_append(strlist, strdup("Yes."));
7259 string_list_append(strlist, strdup("No."));
7260 string_list_append(strlist, strdup("Maybe so."));
7262 chanserv_conf.eightball = strlist;
7263 free_string_list(chanserv_conf.old_ban_names);
7264 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7266 strlist = string_list_copy(strlist);
7268 strlist = alloc_string_list(2);
7269 chanserv_conf.old_ban_names = strlist;
7270 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7271 off_channel = str ? atoi(str) : 0;
7275 chanserv_note_type_read(const char *key, struct record_data *rd)
7278 struct note_type *ntype;
7281 if(!(obj = GET_RECORD_OBJECT(rd)))
7283 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7286 if(!(ntype = chanserv_create_note_type(key)))
7288 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7292 /* Figure out set access */
7293 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7295 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7296 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7298 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7300 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7301 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7303 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7305 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7309 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7310 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7311 ntype->set_access.min_opserv = 0;
7314 /* Figure out visibility */
7315 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7316 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7317 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7318 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7319 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7320 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7321 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7322 ntype->visible_type = NOTE_VIS_ALL;
7324 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7326 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7327 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7331 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7333 struct handle_info *handle;
7334 struct userData *uData;
7335 char *seen, *inf, *flags;
7336 unsigned long last_seen;
7337 unsigned short access_level;
7339 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7341 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7345 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7346 if(access_level > UL_OWNER)
7348 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7352 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7353 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7354 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7355 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7356 handle = get_handle_info(key);
7359 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7363 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7364 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7368 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7370 struct banData *bData;
7371 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7372 unsigned long set_time, triggered_time, expires_time;
7374 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7376 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7380 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7381 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7382 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7383 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7384 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7385 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7386 if (!reason || !owner)
7389 set_time = set ? strtoul(set, NULL, 0) : now;
7390 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7392 expires_time = strtoul(s_expires, NULL, 0);
7394 expires_time = set_time + atoi(s_duration);
7398 if(!reason || (expires_time && (expires_time < now)))
7401 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7404 static struct suspended *
7405 chanserv_read_suspended(dict_t obj)
7407 struct suspended *suspended = calloc(1, sizeof(*suspended));
7411 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7412 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7413 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7414 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7415 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7416 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7417 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7418 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7419 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7420 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7425 chanserv_channel_read(const char *key, struct record_data *hir)
7427 struct suspended *suspended;
7428 struct mod_chanmode *modes;
7429 struct chanNode *cNode;
7430 struct chanData *cData;
7431 struct dict *channel, *obj;
7432 char *str, *argv[10];
7436 channel = hir->d.object;
7438 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7441 cNode = AddChannel(key, now, NULL, NULL);
7444 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7447 cData = register_channel(cNode, str);
7450 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7454 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7456 enum levelOption lvlOpt;
7457 enum charOption chOpt;
7459 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7460 cData->flags = atoi(str);
7462 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7464 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7466 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7467 else if(levelOptions[lvlOpt].old_flag)
7469 if(cData->flags & levelOptions[lvlOpt].old_flag)
7470 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
7472 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
7476 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7478 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
7480 cData->chOpts[chOpt] = str[0];
7483 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
7485 enum levelOption lvlOpt;
7486 enum charOption chOpt;
7489 cData->flags = base64toint(str, 5);
7490 count = strlen(str += 5);
7491 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7494 if(levelOptions[lvlOpt].old_flag)
7496 if(cData->flags & levelOptions[lvlOpt].old_flag)
7497 lvl = levelOptions[lvlOpt].flag_value;
7499 lvl = levelOptions[lvlOpt].default_value;
7501 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
7503 case 'c': lvl = UL_COOWNER; break;
7504 case 'm': lvl = UL_MASTER; break;
7505 case 'n': lvl = UL_OWNER+1; break;
7506 case 'o': lvl = UL_OP; break;
7507 case 'p': lvl = UL_PEON; break;
7508 case 'w': lvl = UL_OWNER; break;
7509 default: lvl = 0; break;
7511 cData->lvlOpts[lvlOpt] = lvl;
7513 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7514 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
7517 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
7519 cData->expiry = atoi(str);
7520 if(cData->expiry > 0) {
7521 if(cData->expiry > now) {
7522 timeq_add(cData->expiry, chanserv_expire_channel, cData);
7524 timeq_add(1, chanserv_expire_channel, cData);
7531 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
7533 suspended = chanserv_read_suspended(obj);
7534 cData->suspended = suspended;
7535 suspended->cData = cData;
7536 /* We could use suspended->expires and suspended->revoked to
7537 * set the CHANNEL_SUSPENDED flag, but we don't. */
7539 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
7541 suspended = calloc(1, sizeof(*suspended));
7542 suspended->issued = 0;
7543 suspended->revoked = 0;
7544 suspended->suspender = strdup(str);
7545 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
7546 suspended->expires = str ? atoi(str) : 0;
7547 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
7548 suspended->reason = strdup(str ? str : "No reason");
7549 suspended->previous = NULL;
7550 cData->suspended = suspended;
7551 suspended->cData = cData;
7555 cData->flags &= ~CHANNEL_SUSPENDED;
7556 suspended = NULL; /* to squelch a warning */
7559 if(IsSuspended(cData)) {
7560 if(suspended->expires > now)
7561 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
7562 else if(suspended->expires)
7563 cData->flags &= ~CHANNEL_SUSPENDED;
7566 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
7567 struct mod_chanmode change;
7568 mod_chanmode_init(&change);
7570 change.args[0].mode = MODE_CHANOP;
7571 change.args[0].u.member = AddChannelUser(chanserv, cNode);
7572 mod_chanmode_announce(chanserv, cNode, &change);
7575 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
7576 cData->registered = str ? strtoul(str, NULL, 0) : now;
7577 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
7578 cData->visited = str ? strtoul(str, NULL, 0) : now;
7579 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
7580 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
7581 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
7582 cData->max = str ? atoi(str) : 0;
7583 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
7584 cData->max_time = str ? atoi(str) : 0;
7585 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
7586 cData->greeting = str ? strdup(str) : NULL;
7587 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
7588 cData->user_greeting = str ? strdup(str) : NULL;
7589 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
7590 cData->topic_mask = str ? strdup(str) : NULL;
7591 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
7592 cData->topic = str ? strdup(str) : NULL;
7594 if(!IsSuspended(cData)
7595 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
7596 && (argc = split_line(str, 0, ArrayLength(argv), argv))
7597 && (modes = mod_chanmode_parse(cNode, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
7598 cData->modes = *modes;
7600 cData->modes.modes_set |= MODE_REGISTERED;
7601 if(cData->modes.argc > 1)
7602 cData->modes.argc = 1;
7603 mod_chanmode_announce(chanserv, cNode, &cData->modes);
7604 mod_chanmode_free(modes);
7607 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
7608 for(it = dict_first(obj); it; it = iter_next(it))
7609 user_read_helper(iter_key(it), iter_data(it), cData);
7611 if(!cData->users && !IsProtected(cData))
7613 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
7614 unregister_channel(cData, "has empty user list.");
7618 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
7619 for(it = dict_first(obj); it; it = iter_next(it))
7620 ban_read_helper(iter_key(it), iter_data(it), cData);
7622 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
7623 for(it = dict_first(obj); it; it = iter_next(it))
7625 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
7626 struct record_data *rd = iter_data(it);
7627 const char *note, *setter;
7629 if(rd->type != RECDB_OBJECT)
7631 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
7635 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
7637 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
7639 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
7643 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
7644 if(!setter) setter = "<unknown>";
7645 chanserv_add_channel_note(cData, ntype, setter, note);
7653 chanserv_dnr_read(const char *key, struct record_data *hir)
7655 const char *setter, *reason, *str;
7656 struct do_not_register *dnr;
7657 unsigned long expiry;
7659 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
7662 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
7665 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
7668 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
7671 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
7672 expiry = str ? strtoul(str, NULL, 0) : 0;
7673 if(expiry && expiry <= now)
7675 dnr = chanserv_add_dnr(key, setter, expiry, reason);
7678 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
7680 dnr->set = atoi(str);
7686 chanserv_saxdb_read(struct dict *database)
7688 struct dict *section;
7691 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
7692 for(it = dict_first(section); it; it = iter_next(it))
7693 chanserv_note_type_read(iter_key(it), iter_data(it));
7695 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
7696 for(it = dict_first(section); it; it = iter_next(it))
7697 chanserv_channel_read(iter_key(it), iter_data(it));
7699 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
7700 for(it = dict_first(section); it; it = iter_next(it))
7701 chanserv_dnr_read(iter_key(it), iter_data(it));
7707 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
7709 int high_present = 0;
7710 saxdb_start_record(ctx, KEY_USERS, 1);
7711 for(; uData; uData = uData->next)
7713 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
7715 saxdb_start_record(ctx, uData->handle->handle, 0);
7716 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
7717 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
7719 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
7721 saxdb_write_string(ctx, KEY_INFO, uData->info);
7722 saxdb_end_record(ctx);
7724 saxdb_end_record(ctx);
7725 return high_present;
7729 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
7733 saxdb_start_record(ctx, KEY_BANS, 1);
7734 for(; bData; bData = bData->next)
7736 saxdb_start_record(ctx, bData->mask, 0);
7737 saxdb_write_int(ctx, KEY_SET, bData->set);
7738 if(bData->triggered)
7739 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
7741 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
7743 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
7745 saxdb_write_string(ctx, KEY_REASON, bData->reason);
7746 saxdb_end_record(ctx);
7748 saxdb_end_record(ctx);
7752 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
7754 saxdb_start_record(ctx, name, 0);
7755 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
7756 saxdb_write_string(ctx, KEY_REASON, susp->reason);
7758 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
7760 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
7762 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
7764 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
7765 saxdb_end_record(ctx);
7769 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
7773 enum levelOption lvlOpt;
7774 enum charOption chOpt;
7776 saxdb_start_record(ctx, channel->channel->name, 1);
7778 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
7779 saxdb_write_int(ctx, KEY_MAX, channel->max);
7780 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
7782 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
7783 if(channel->registrar)
7784 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
7785 if(channel->greeting)
7786 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
7787 if(channel->user_greeting)
7788 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
7789 if(channel->topic_mask)
7790 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
7791 if(channel->suspended)
7792 chanserv_write_suspended(ctx, "suspended", channel->suspended);
7794 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
7796 saxdb_start_record(ctx, KEY_OPTIONS, 0);
7797 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
7798 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7799 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
7800 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
7802 buf[0] = channel->chOpts[chOpt];
7804 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
7806 saxdb_end_record(ctx);
7808 if(channel->modes.modes_set || channel->modes.modes_clear)
7810 mod_chanmode_format(&channel->modes, buf);
7811 saxdb_write_string(ctx, KEY_MODES, buf);
7814 high_present = chanserv_write_users(ctx, channel->users);
7815 chanserv_write_bans(ctx, channel->bans);
7817 if(dict_size(channel->notes))
7821 saxdb_start_record(ctx, KEY_NOTES, 1);
7822 for(it = dict_first(channel->notes); it; it = iter_next(it))
7824 struct note *note = iter_data(it);
7825 saxdb_start_record(ctx, iter_key(it), 0);
7826 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
7827 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
7828 saxdb_end_record(ctx);
7830 saxdb_end_record(ctx);
7833 if(channel->ownerTransfer)
7834 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
7835 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
7836 saxdb_end_record(ctx);
7840 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
7844 saxdb_start_record(ctx, ntype->name, 0);
7845 switch(ntype->set_access_type)
7847 case NOTE_SET_CHANNEL_ACCESS:
7848 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
7850 case NOTE_SET_CHANNEL_SETTER:
7851 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
7853 case NOTE_SET_PRIVILEGED: default:
7854 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
7857 switch(ntype->visible_type)
7859 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
7860 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
7861 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
7863 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
7864 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
7865 saxdb_end_record(ctx);
7869 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
7871 struct do_not_register *dnr;
7872 dict_iterator_t it, next;
7874 for(it = dict_first(dnrs); it; it = next)
7876 next = iter_next(it);
7877 dnr = iter_data(it);
7878 if(dnr->expires && dnr->expires <= now)
7880 dict_remove(dnrs, iter_key(it));
7883 saxdb_start_record(ctx, dnr->chan_name, 0);
7885 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
7887 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
7888 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
7889 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
7890 saxdb_end_record(ctx);
7895 chanserv_saxdb_write(struct saxdb_context *ctx)
7898 struct chanData *channel;
7901 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
7902 for(it = dict_first(note_types); it; it = iter_next(it))
7903 chanserv_write_note_type(ctx, iter_data(it));
7904 saxdb_end_record(ctx);
7907 saxdb_start_record(ctx, KEY_DNR, 1);
7908 write_dnrs_helper(ctx, handle_dnrs);
7909 write_dnrs_helper(ctx, plain_dnrs);
7910 write_dnrs_helper(ctx, mask_dnrs);
7911 saxdb_end_record(ctx);
7914 saxdb_start_record(ctx, KEY_CHANNELS, 1);
7915 for(channel = channelList; channel; channel = channel->next)
7916 chanserv_write_channel(ctx, channel);
7917 saxdb_end_record(ctx);
7923 chanserv_db_cleanup(void) {
7925 unreg_part_func(handle_part);
7927 unregister_channel(channelList, "terminating.");
7928 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7929 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7930 free(chanserv_conf.support_channels.list);
7931 dict_delete(handle_dnrs);
7932 dict_delete(plain_dnrs);
7933 dict_delete(mask_dnrs);
7934 dict_delete(note_types);
7935 free_string_list(chanserv_conf.eightball);
7936 free_string_list(chanserv_conf.old_ban_names);
7937 free_string_list(chanserv_conf.set_shows);
7938 free(set_shows_list.list);
7939 free(uset_shows_list.list);
7942 struct userData *helper = helperList;
7943 helperList = helperList->next;
7948 #if defined(GCC_VARMACROS)
7949 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
7950 #elif defined(C99_VARMACROS)
7951 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
7953 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
7954 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
7957 init_chanserv(const char *nick)
7959 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
7960 conf_register_reload(chanserv_conf_read);
7964 reg_server_link_func(handle_server_link);
7965 reg_new_channel_func(handle_new_channel);
7966 reg_join_func(handle_join);
7967 reg_part_func(handle_part);
7968 reg_kick_func(handle_kick);
7969 reg_topic_func(handle_topic);
7970 reg_mode_change_func(handle_mode);
7971 reg_nick_change_func(handle_nick_change);
7972 reg_auth_func(handle_auth);
7975 reg_handle_rename_func(handle_rename);
7976 reg_unreg_func(handle_unreg);
7978 handle_dnrs = dict_new();
7979 dict_set_free_data(handle_dnrs, free);
7980 plain_dnrs = dict_new();
7981 dict_set_free_data(plain_dnrs, free);
7982 mask_dnrs = dict_new();
7983 dict_set_free_data(mask_dnrs, free);
7985 reg_svccmd_unbind_func(handle_svccmd_unbind);
7986 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
7987 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
7988 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
7989 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
7990 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
7991 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
7992 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
7993 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
7994 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
7995 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7996 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
7997 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
7998 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8000 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8001 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8003 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8004 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8005 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8006 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8007 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8009 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8010 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8011 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8012 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8013 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8015 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8016 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8017 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8018 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8020 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8021 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8022 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8023 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8024 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8025 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8026 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8027 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8029 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8030 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8031 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8032 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8033 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8034 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8035 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8036 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8037 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8038 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8039 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8040 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8041 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8042 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8044 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8045 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8046 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8047 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8048 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8050 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8051 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8053 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8054 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8055 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8056 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8057 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8058 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8059 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8060 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8061 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8062 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8063 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8065 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8066 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8068 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8069 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8070 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8071 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8073 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8074 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8075 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8076 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8077 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8079 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8080 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8081 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8082 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8083 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8084 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8086 /* Channel options */
8087 DEFINE_CHANNEL_OPTION(defaulttopic);
8088 DEFINE_CHANNEL_OPTION(topicmask);
8089 DEFINE_CHANNEL_OPTION(greeting);
8090 DEFINE_CHANNEL_OPTION(usergreeting);
8091 DEFINE_CHANNEL_OPTION(modes);
8092 DEFINE_CHANNEL_OPTION(enfops);
8093 DEFINE_CHANNEL_OPTION(giveops);
8094 DEFINE_CHANNEL_OPTION(protect);
8095 DEFINE_CHANNEL_OPTION(enfmodes);
8096 DEFINE_CHANNEL_OPTION(enftopic);
8097 DEFINE_CHANNEL_OPTION(pubcmd);
8098 DEFINE_CHANNEL_OPTION(givevoice);
8099 DEFINE_CHANNEL_OPTION(userinfo);
8100 DEFINE_CHANNEL_OPTION(dynlimit);
8101 DEFINE_CHANNEL_OPTION(topicsnarf);
8102 DEFINE_CHANNEL_OPTION(nodelete);
8103 DEFINE_CHANNEL_OPTION(toys);
8104 DEFINE_CHANNEL_OPTION(setters);
8105 DEFINE_CHANNEL_OPTION(topicrefresh);
8106 DEFINE_CHANNEL_OPTION(ctcpusers);
8107 DEFINE_CHANNEL_OPTION(ctcpreaction);
8108 DEFINE_CHANNEL_OPTION(inviteme);
8109 DEFINE_CHANNEL_OPTION(unreviewed);
8110 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8111 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8112 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8114 DEFINE_CHANNEL_OPTION(offchannel);
8115 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8117 /* Alias set topic to set defaulttopic for compatibility. */
8118 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8121 DEFINE_USER_OPTION(noautoop);
8122 DEFINE_USER_OPTION(autoinvite);
8123 DEFINE_USER_OPTION(info);
8125 /* Alias uset autovoice to uset autoop. */
8126 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8128 note_types = dict_new();
8129 dict_set_free_data(note_types, chanserv_deref_note_type);
8132 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8133 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8134 service_register(chanserv)->trigger = '!';
8135 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8137 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8139 if(chanserv_conf.channel_expire_frequency)
8140 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8142 if(chanserv_conf.dnr_expire_frequency)
8143 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8145 if(chanserv_conf.refresh_period)
8147 unsigned long next_refresh;
8148 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8149 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8152 reg_exit_func(chanserv_db_cleanup);
8153 message_register_table(msgtab);