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"
61 #define KEY_NEW_CHANNEL_AUTHED "new_channel_authed_join"
62 #define KEY_NEW_CHANNEL_UNAUTHED "new_channel_unauthed_join"
63 #define KEY_NEW_CHANNEL_MSG "new_channel_message"
65 /* ChanServ database */
66 #define KEY_CHANNELS "channels"
67 #define KEY_NOTE_TYPES "note_types"
69 /* Note type parameters */
70 #define KEY_NOTE_OPSERV_ACCESS "opserv_access"
71 #define KEY_NOTE_CHANNEL_ACCESS "channel_access"
72 #define KEY_NOTE_SETTER_ACCESS "setter_access"
73 #define KEY_NOTE_VISIBILITY "visibility"
74 #define KEY_NOTE_VIS_PRIVILEGED "privileged"
75 #define KEY_NOTE_VIS_CHANNEL_USERS "channel_users"
76 #define KEY_NOTE_VIS_ALL "all"
77 #define KEY_NOTE_MAX_LENGTH "max_length"
78 #define KEY_NOTE_SETTER "setter"
79 #define KEY_NOTE_NOTE "note"
81 /* Do-not-register channels */
83 #define KEY_DNR_SET "set"
84 #define KEY_DNR_SETTER "setter"
85 #define KEY_DNR_REASON "reason"
88 #define KEY_REGISTERED "registered"
89 #define KEY_REGISTRAR "registrar"
90 #define KEY_SUSPENDED "suspended"
91 #define KEY_PREVIOUS "previous"
92 #define KEY_SUSPENDER "suspender"
93 #define KEY_ISSUED "issued"
94 #define KEY_REVOKED "revoked"
95 #define KEY_SUSPEND_EXPIRES "suspend_expires"
96 #define KEY_SUSPEND_REASON "suspend_reason"
97 #define KEY_VISITED "visited"
98 #define KEY_TOPIC "topic"
99 #define KEY_GREETING "greeting"
100 #define KEY_USER_GREETING "user_greeting"
101 #define KEY_MODES "modes"
102 #define KEY_FLAGS "flags"
103 #define KEY_OPTIONS "options"
104 #define KEY_USERS "users"
105 #define KEY_BANS "bans"
106 #define KEY_MAX "max"
107 #define KEY_MAX_TIME "max_time"
108 #define KEY_NOTES "notes"
109 #define KEY_TOPIC_MASK "topic_mask"
110 #define KEY_OWNER_TRANSFER "owner_transfer"
111 #define KEY_EXPIRE "expire"
114 #define KEY_LEVEL "level"
115 #define KEY_INFO "info"
116 #define KEY_SEEN "seen"
119 #define KEY_VOTE "vote"
120 #define KEY_VOTE_START "votestart"
121 #define KEY_VOTE_OPTIONS "voptions"
122 #define KEY_VOTE_OPTION_NAME "voptionname"
123 #define KEY_VOTE_VOTED "vvoted"
124 #define KEY_VOTE_VOTEDFOR "vvotefor"
125 #define KEY_VOTE_OPTION_ID "voptionid"
126 #define KEY_VOTE_OPTION_VOTED "voptionvoted"
129 #define KEY_OWNER "owner"
130 #define KEY_REASON "reason"
131 #define KEY_SET "set"
132 #define KEY_DURATION "duration"
133 #define KEY_EXPIRES "expires"
134 #define KEY_TRIGGERED "triggered"
136 #define CHANNEL_DEFAULT_FLAGS (CHANNEL_OFFCHANNEL | CHANNEL_UNREVIEWED)
137 #define CHANNEL_PRESERVED_FLAGS (CHANNEL_UNREVIEWED)
138 #define CHANNEL_DEFAULT_OPTIONS "lmoooanpcnat"
140 /* Administrative messages */
141 static const struct message_entry msgtab[] = {
142 { "CSMSG_CHANNELS_EXPIRED", "%i channels expired." },
144 /* Channel registration */
145 { "CSMSG_REG_SUCCESS", "You now have ownership of $b%s$b." },
146 { "CSMSG_PROXY_SUCCESS", "%s now has ownership of $b%s$b." },
147 { "CSMSG_ALREADY_REGGED", "$b%s$b is registered to someone else." },
148 { "CSMSG_MUST_BE_OPPED", "You must be a channel operator in $b%s$b to register it." },
149 { "CSMSG_PROXY_FORBIDDEN", "You may not register a channel for someone else." },
150 { "CSMSG_OWN_TOO_MANY", "%s already owns enough channels (at least %d); use FORCE to override." },
152 /* Do-not-register channels */
153 { "CSMSG_NOT_DNR", "$b%s$b is not a valid channel name or *account." },
154 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
155 { "CSMSG_DNR_INFO", "$b%s$b is do-not-register (by $b%s$b): %s" },
156 { "CSMSG_DNR_INFO_SET", "$b%s$b is do-not-register (set %s by $b%s$b): %s" },
157 { "CSMSG_DNR_INFO_SET_EXPIRES", "$b%s$b is do-not-register (set %s by $b%s$b; expires %s): %s" },
158 { "CSMSG_MORE_DNRS", "%d more do-not-register entries skipped." },
159 { "CSMSG_DNR_CHANNEL", "Only network staff may register $b%s$b." },
160 { "CSMSG_DNR_CHANNEL_MOVE", "Only network staff may move $b%s$b." },
161 { "CSMSG_DNR_ACCOUNT", "Only network staff may register channels to $b%s$b." },
162 { "CSMSG_NOREGISTER_CHANNEL", "$b%s$b has been added to the do-not-register list." },
163 { "CSMSG_NO_SUCH_DNR", "$b%s$b is not in the do-not-register list." },
164 { "CSMSG_DNR_REMOVED", "$b%s$b has been removed from the do-not-register list." },
165 { "CSMSG_DNR_BAD_ACTION", "$b%s$b is not a recognized do-not-register action." },
166 { "CSMSG_DNR_SEARCH_RESULTS", "The following do-not-registers were found:" },
168 /* Channel unregistration */
169 { "CSMSG_UNREG_SUCCESS", "$b%s$b has been unregistered." },
170 { "CSMSG_UNREG_NODELETE", "$b%s$b is protected from unregistration." },
171 { "CSMSG_CHAN_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended (%s)." },
172 { "CSMSG_CONFIRM_UNREG", "To confirm this unregistration, you must use 'unregister %s'." },
175 { "CSMSG_MOVE_SUCCESS", "Channel registration has been moved to $b%s$b." },
176 { "CSMSG_MOVE_NODELETE", "$b%s$b is protected from unregistration, and cannot be moved." },
178 /* Channel merging */
179 { "CSMSG_MERGE_SUCCESS", "Channel successfully merged into $b%s$b." },
180 { "CSMSG_MERGE_SELF", "Merging cannot be performed if the source and target channels are the same." },
181 { "CSMSG_MERGE_NODELETE", "You may not merge a channel that is marked NoDelete." },
182 { "CSMSG_MERGE_SUSPENDED", "Merging cannot be performed if the source or target channel is suspended." },
183 { "CSMSG_MERGE_NOT_OWNER", "You must be the owner of the target channel (or a helper) to merge into the channel." },
185 /* Handle unregistration */
186 { "CSMSG_HANDLE_UNREGISTERED", "As a result of your account unregistration, you have been deleted from all of your channels' userlists." },
189 { "CSMSG_NOT_USER", "You lack access to $b%s$b." },
190 { "CSMSG_NO_CHAN_USER", "%s lacks access to $b%s$b." },
191 { "CSMSG_NO_ACCESS", "You lack sufficient access to use this command." },
192 { "CSMSG_NOT_REGISTERED", "$b%s$b has not been registered with $b$C$b." },
193 { "CSMSG_MAXIMUM_BANS", "This channel has reached the ban count limit of $b%d$b." },
194 { "CSMSG_MAXIMUM_USERS", "This channel has reached the user count limit of $b%d$b." },
195 { "CSMSG_ILLEGAL_CHANNEL", "$b%s$b is an illegal channel, and cannot be registered." },
196 { "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." },
197 { "CSMSG_ALREADY_OPPED", "You are already opped in $b%s$b." },
198 { "CSMSG_ALREADY_VOICED", "You are already voiced in $b%s$b." },
199 { "CSMSG_ALREADY_DOWN", "You are not opped or voiced in $b%s$b." },
200 { "CSMSG_ALREADY_OPCHANNED", "There has been no net.join since the last opchan in $b%s$b." },
201 { "CSMSG_OUT_OF_CHANNEL", "For some reason I don't seem to be in $b%s$b." },
202 { "CSMSG_OPCHAN_DONE", "I have (re-)opped myself in $b%s$b." },
204 /* Removing yourself from a channel. */
205 { "CSMSG_NO_OWNER_DELETEME", "You cannot delete your owner access in $b%s$b." },
206 { "CSMSG_CONFIRM_DELETEME", "To really remove yourself, you must use 'deleteme %s'." },
207 { "CSMSG_DELETED_YOU", "Your $b%d$b access has been deleted from $b%s$b." },
209 /* User management */
210 { "CSMSG_ADDED_USER", "Added %s to the %s user list with access %d." },
211 { "CSMSG_DELETED_USER", "Deleted %s (with access %d) from the %s user list." },
212 { "CSMSG_BAD_RANGE", "Invalid access range; minimum (%d) must be greater than maximum (%d)." },
213 { "CSMSG_DELETED_USERS", "Deleted accounts matching $b%s$b with access from $b%d$b to $b%d$b from the %s user list." },
214 { "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." },
215 { "CSMSG_INCORRECT_ACCESS", "%s has access $b%d$b, not %s." },
216 { "CSMSG_USER_EXISTS", "%s is already on the $b%s$b user list (with access %d)." },
217 { "CSMSG_CANNOT_TRIM", "You must include a minimum inactivity duration of at least 60 seconds to trim." },
219 { "CSMSG_NO_SELF_CLVL", "You cannot change your own access." },
220 { "CSMSG_NO_BUMP_ACCESS", "You cannot give users access greater than or equal to your own." },
221 { "CSMSG_MULTIPLE_OWNERS", "There is more than one owner in %s; please use $bCLVL$b, $bDELOWNER$b and/or $bADDOWNER$b instead." },
222 { "CSMSG_TRANSFER_WAIT", "You must wait %s before you can give ownership of $b%s$b to someone else." },
223 { "CSMSG_NO_TRANSFER_SELF", "You cannot give ownership to your own account." },
224 { "CSMSG_CONFIRM_GIVEOWNERSHIP", "To really give ownership to $b%1$s$b, you must use 'giveownership *%1$s %2$s'." },
225 { "CSMSG_OWNERSHIP_GIVEN", "Ownership of $b%s$b has been transferred to account $b%s$b." },
228 { "CSMSG_BAN_ADDED", "Permanently banned $b%s$b from %s." },
229 { "CSMSG_TIMED_BAN_ADDED", "Banned $b%s$b from %s for %s." },
230 { "CSMSG_KICK_BAN_DONE", "Kickbanned $b%s$b from %s." },
231 { "CSMSG_BAN_DONE", "Banned $b%s$b from %s." },
232 { "CSMSG_REASON_CHANGE", "Reason for ban $b%s$b changed." },
233 { "CSMSG_BAN_EXTENDED", "Extended ban for $b%s$b expires in %s." },
234 { "CSMSG_BAN_REMOVED", "Matching ban(s) for $b%s$b removed." },
235 { "CSMSG_TRIMMED_BANS", "Trimmed $b%d bans$b from the %s ban list that were inactive for at least %s." },
236 { "CSMSG_REDUNDANT_BAN", "$b%s$b is already banned in %s." },
237 { "CSMSG_DURATION_TOO_LOW", "Timed bans must last for at least 15 seconds." },
238 { "CSMSG_DURATION_TOO_HIGH", "Timed bans must last for less than 2 years." },
239 { "CSMSG_LAME_MASK", "$b%s$b is a little too general. Try making it more specific." },
240 { "CSMSG_MASK_PROTECTED", "Sorry, ban for $b%s$b conflicts with a protected user's hostmask." },
241 { "CSMSG_NO_MATCHING_USERS", "No one in $b%s$b has a hostmask matching $b%s$b." },
242 { "CSMSG_BAN_NOT_FOUND", "Sorry, no ban found for $b%s$b." },
243 { "CSMSG_BANLIST_FULL", "The $b%s$b channel ban list is $bfull$b." },
245 { "CSMSG_INVALID_TRIM", "$b%s$b isn't a valid trim target." },
247 /* Channel management */
248 { "CSMSG_CHANNEL_OPENED", "$b%s$b has been opened." },
249 { "CSMSG_WIPED_INFO_LINE", "Removed $b%s$b's infoline in $b%s$b." },
250 { "CSMSG_RESYNCED_USERS", "Synchronized users in $b%s$b with the userlist." },
252 { "CSMSG_TOPIC_SET", "Topic is now '%s'." },
253 { "CSMSG_NO_TOPIC", "$b%s$b does not have a default topic." },
254 { "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" },
255 { "CSMSG_TOPICMASK_CONFLICT2", "Please make sure your topic is at most %d characters and matches the topic mask pattern." },
256 { "CSMSG_TOPIC_LOCKED", "The %s topic is locked." },
257 { "CSMSG_MASK_BUT_NO_TOPIC", "Warning: $b%s$b does not have a default topic, but you just set the topic mask." },
258 { "CSMSG_TOPIC_MISMATCH", "Warning: The default topic for $b%s$b does not match the topic mask; changing it anyway." },
260 { "CSMSG_MODES_SET", "Channel modes are now $b%s$b." },
261 { "CSMSG_DEFAULTED_MODES", "Channel modes for $b%s$b are set to their defaults." },
262 { "CSMSG_NO_MODES", "$b%s$b does not have any default modes." },
263 { "CSMSG_MODE_LOCKED", "Modes conflicting with $b%s$b are not allowed in %s." },
264 { "CSMSG_CANNOT_SET", "That setting is above your current level, so you cannot change it." },
265 { "CSMSG_OWNER_DEFAULTS", "You must have access 500 in %s to reset it to the default options." },
266 { "CSMSG_CONFIRM_DEFAULTS", "To reset %s's settings to the defaults, you must use 'set defaults %s'." },
267 { "CSMSG_SETTINGS_DEFAULTED", "All settings for %s have been reset to default values." },
268 { "CSMSG_BAD_SETLEVEL", "You cannot change any setting to above your level." },
269 { "CSMSG_BAD_GIVEVOICE", "You cannot change GiveVoice to above GiveOps (%d)." },
270 { "CSMSG_BAD_GIVEOPS", "You cannot change GiveOps to below GiveVoice (%d)." },
271 { "CSMSG_BAD_SETTERS", "You cannot change Setters to above your level." },
272 { "CSMSG_INVALID_MODE_LOCK", "$b%s$b is an invalid mode lock." },
273 { "CSMSG_INVALID_NUMERIC", "$b%d$b is not a valid choice. Choose one:" },
274 { "CSMSG_SET_DEFAULT_TOPIC", "$bDefaultTopic$b %s" },
275 { "CSMSG_SET_TOPICMASK", "$bTopicMask $b %s" },
276 { "CSMSG_SET_GREETING", "$bGreeting $b %s" },
277 { "CSMSG_SET_USERGREETING", "$bUserGreeting$b %s" },
278 { "CSMSG_SET_MODES", "$bModes $b %s" },
279 { "CSMSG_SET_NODELETE", "$bNoDelete $b %s" },
280 { "CSMSG_SET_DYNLIMIT", "$bDynLimit $b %s" },
281 { "CSMSG_SET_OFFCHANNEL", "$bOffChannel $b %s" },
282 { "CSMSG_SET_USERINFO", "$bUserInfo $b %d" },
283 { "CSMSG_SET_GIVE_VOICE", "$bGiveVoice $b %d" },
284 { "CSMSG_SET_TOPICSNARF", "$bTopicSnarf $b %d" },
285 { "CSMSG_SET_VOTE", "$bVote $b %d" },
286 { "CSMSG_SET_INVITEME", "$bInviteMe $b %d" },
287 { "CSMSG_SET_ENFOPS", "$bEnfOps $b %d" },
288 { "CSMSG_SET_GIVE_OPS", "$bGiveOps $b %d" },
289 { "CSMSG_SET_ENFMODES", "$bEnfModes $b %d" },
290 { "CSMSG_SET_ENFTOPIC", "$bEnfTopic $b %d" },
291 { "CSMSG_SET_PUBCMD", "$bPubCmd $b %d" },
292 { "CSMSG_SET_SETTERS", "$bSetters $b %d" },
293 { "CSMSG_SET_CTCPUSERS", "$bCTCPUsers $b %d" },
294 { "CSMSG_SET_PROTECT", "$bProtect $b %d - %s" },
295 { "CSMSG_SET_TOYS", "$bToys $b %d - %s" },
296 { "CSMSG_SET_CTCPREACTION", "$bCTCPReaction$b %d - %s" },
297 { "CSMSG_SET_TOPICREFRESH", "$bTopicRefresh$b %d - %s" },
298 { "CSMSG_SET_UNREVIEWED", "$bUnreviewed $b %s" },
299 { "CSMSG_SET_EXPIRE", "$bExpire $b %s" },
300 { "CSMSG_SET_EXPIRE_OFF", "$bExpire $b off" },
301 { "CSMSG_USET_NOAUTOOP", "$bNoAutoOp $b %s" },
302 { "CSMSG_USET_NOAUTOVOICE", "$bNoAutoVoice $b %s" },
303 { "CSMSG_USET_AUTOINVITE", "$bAutoInvite $b %s" },
304 { "CSMSG_USET_INFO", "$bInfo $b %s" },
306 { "CSMSG_USER_PROTECTED", "Sorry, $b%s$b is protected." },
307 { "CSMSG_OPBY_LOCKED", "You may not op users who lack op or greater access." },
308 { "CSMSG_PROCESS_FAILED", "$b$C$b could not process some of the nicks you provided." },
309 { "CSMSG_OPPED_USERS", "Opped users in $b%s$b." },
310 { "CSMSG_DEOPPED_USERS", "Deopped users in $b%s$b." },
311 { "CSMSG_VOICED_USERS", "Voiced users in $b%s$b." },
312 { "CSMSG_DEVOICED_USERS", "Devoiced users in $b%s$b." },
313 { "CSMSG_PROTECT_ALL", "Non-users and users will be protected from those of equal or lower access." },
314 { "CSMSG_PROTECT_EQUAL", "Users will be protected from those of equal or lower access." },
315 { "CSMSG_PROTECT_LOWER", "Users will be protected from those of lower access." },
316 { "CSMSG_PROTECT_NONE", "No users will be protected." },
317 { "CSMSG_TOYS_DISABLED", "Toys are completely disabled." },
318 { "CSMSG_TOYS_PRIVATE", "Toys will only reply privately." },
319 { "CSMSG_TOYS_PUBLIC", "Toys will reply publicly." },
320 { "CSMSG_TOPICREFRESH_NEVER", "Never refresh topic." },
321 { "CSMSG_TOPICREFRESH_3_HOURS", "Refresh every 3 hours." },
322 { "CSMSG_TOPICREFRESH_6_HOURS", "Refresh every 6 hours." },
323 { "CSMSG_TOPICREFRESH_12_HOURS", "Refresh every 12 hours." },
324 { "CSMSG_TOPICREFRESH_24_HOURS", "Refresh every 24 hours." },
325 { "CSMSG_CTCPREACTION_KICK", "Kick on disallowed CTCPs" },
326 { "CSMSG_CTCPREACTION_KICKBAN", "Kickban on disallowed CTCPs" },
327 { "CSMSG_CTCPREACTION_SHORTBAN", "Short timed ban on disallowed CTCPs" },
328 { "CSMSG_CTCPREACTION_LONGBAN", "Long timed ban on disallowed CTCPs" },
330 { "CSMSG_INVITED_USER", "Invited $b%s$b to join %s." },
331 { "CSMSG_INVITING_YOU_REASON", "$b%s$b invites you to join %s: %s" },
332 { "CSMSG_INVITING_YOU", "$b%s$b invites you to join %s." },
333 { "CSMSG_ALREADY_PRESENT", "%s is already in $b%s$b." },
334 { "CSMSG_YOU_ALREADY_PRESENT", "You are already in $b%s$b." },
335 { "CSMSG_LOW_CHANNEL_ACCESS", "You lack sufficient access in %s for $S to invite you." },
336 { "CSMSG_INFOLINE_TOO_LONG", "Your infoline may not exceed %u characters." },
337 { "CSMSG_BAD_INFOLINE", "You may not use the character \\%03o in your infoline." },
339 { "CSMSG_KICK_DONE", "Kicked $b%s$b from %s." },
340 { "CSMSG_NO_BANS", "No channel bans found on $b%s$b." },
341 { "CSMSG_BANS_REMOVED", "Removed all channel bans from $b%s$b." },
343 /* Channel userlist */
344 { "CSMSG_ACCESS_ALL_HEADER", "%s users from level %d to %d:" },
345 { "CSMSG_ACCESS_SEARCH_HEADER", "%s users from level %d to %d matching %s:" },
346 { "CSMSG_INVALID_ACCESS", "$b%s$b is an invalid access level." },
347 { "CSMSG_CHANGED_ACCESS", "%s now has access $b%d$b in %s." },
348 { "CSMSG_TOTAL_USERS", "There are $b%d$b users in %s." },
350 /* Channel note list */
351 { "CSMSG_NOTELIST_HEADER", "Notes for $b%s$b:" },
352 { "CSMSG_REPLACED_NOTE", "Replaced old $b%s$b note on %s (set by %s): %s" },
353 { "CSMSG_NOTE_FORMAT", "%s (set by %s): %s" },
354 { "CSMSG_NOTELIST_END", "End of notes for $b%s$b." },
355 { "CSMSG_NOTELIST_EMPTY", "There are no (visible) notes for $b%s$b." },
356 { "CSMSG_NO_SUCH_NOTE", "Channel $b%s$b does not have a note named $b%s$b." },
357 { "CSMSG_BAD_NOTE_TYPE", "Note type $b%s$b does not exist." },
358 { "CSMSG_NOTE_SET", "Note $b%s$b set in channel $b%s$b." },
359 { "CSMSG_NOTE_REMOVED", "Note $b%s$b removed in channel $b%s$b." },
360 { "CSMSG_BAD_NOTE_ACCESS", "$b%s$b is not a valid note access type." },
361 { "CSMSG_BAD_MAX_LENGTH", "$b%s$b is not a valid maximum length (must be between 20 and 450 inclusive)." },
362 { "CSMSG_NOTE_MODIFIED", "Note type $b%s$b modified." },
363 { "CSMSG_NOTE_CREATED", "Note type $b%s$b created." },
364 { "CSMSG_NOTE_TYPE_USED", "Note type $b%s$b is in use; give the FORCE argument to delete it." },
365 { "CSMSG_NOTE_DELETED", "Note type $b%s$b deleted." },
367 /* Channel [un]suspension */
368 { "CSMSG_ALREADY_SUSPENDED", "$b%s$b is already suspended." },
369 { "CSMSG_NOT_SUSPENDED", "$b%s$b is not suspended." },
370 { "CSMSG_SUSPENDED", "$b$C$b access to $b%s$b has been temporarily suspended." },
371 { "CSMSG_UNSUSPENDED", "$b$C$b access to $b%s$b has been restored." },
372 { "CSMSG_SUSPEND_NODELETE", "$b%s$b is protected from unregistration, and cannot be suspended." },
373 { "CSMSG_USER_SUSPENDED", "$b%s$b's access to $b%s$b has been suspended." },
374 { "CSMSG_USER_UNSUSPENDED", "$b%s$b's access to $b%s$b has been restored." },
376 /* Access information */
377 { "CSMSG_IS_CHANSERV", "$b$C$b is the $bchannel service bot$b." },
378 { "CSMSG_MYACCESS_SELF_ONLY", "You may only see the list of infolines for yourself (by using $b%s$b with no arguments)." },
379 { "CSMSG_SQUAT_ACCESS", "$b%s$b does not have access to any channels." },
380 { "CSMSG_INFOLINE_LIST", "Showing all channel entries for account $b%s$b:" },
381 { "CSMSG_USER_NO_ACCESS", "%s lacks access to %s." },
382 { "CSMSG_USER_HAS_ACCESS", "%s has access $b%d$b in %s." },
383 { "CSMSG_HELPER_NO_ACCESS", "%s lacks access to %s but has $bsecurity override$b enabled." },
384 { "CSMSG_HELPER_HAS_ACCESS", "%s has access $b%d$b in %s and has $bsecurity override$b enabled." },
385 { "CSMSG_LAZY_SMURF_TARGET", "%s is %s ($bIRCOp$b; not logged in)." },
386 { "CSMSG_SMURF_TARGET", "%s is %s ($b%s$b)." },
387 { "CSMSG_OPERATOR_TITLE", "IRC operator" },
388 { "CSMSG_UC_H_TITLE", "network helper" },
389 { "CSMSG_LC_H_TITLE", "support helper" },
390 { "CSMSG_LAME_SMURF_TARGET", "%s is an IRC operator." },
391 { "CSMSG_MYACCESS_COUNT", "%s has access in $b%d$b channels and is owner of $b%d$b channel(s)." },
392 { "CSMSG_MYACCESS_COUNT_1", "%s has access in $b%d$b channel and is owner of $b%d$b channel(s)." },
395 /* Seen information */
396 { "CSMSG_NEVER_SEEN", "%s has never been seen in $b%s$b." },
397 { "CSMSG_USER_SEEN", "%s was last seen in $b%s$b %s ago." },
398 { "CSMSG_USER_VACATION", "%s is currently on vacation." },
399 { "CSMSG_USER_PRESENT", "%s is in the channel $bright now$b." },
401 /* Names information */
402 { "CSMSG_CHANNEL_NAMES", "Users in $b%s$b:%s" },
403 { "CSMSG_END_NAMES", "End of names in $b%s$b" },
405 /* Channel information */
406 { "CSMSG_CHANNEL_INFO", "$b%s$b Information:" },
407 { "CSMSG_CHANNEL_TOPIC", "$bDefault Topic: $b%s" },
408 { "CSMSG_CHANNEL_MODES", "$bMode Lock: $b%s" },
409 { "CSMSG_CHANNEL_NOTE", "$b%s:%*s$b%s" },
410 { "CSMSG_CHANNEL_MAX", "$bRecord Visitors: $b%i" },
411 { "CSMSG_CHANNEL_MAX_TIME", "$bRecord Visitors: $b%i (%s ago.)" },
412 { "CSMSG_CHANNEL_OWNER", "$bOwner: $b%s" },
413 { "CSMSG_CHANNEL_BANS", "$bBan Count: $b%i" },
414 { "CSMSG_CHANNEL_USERS", "$bTotal User Count: $b%i" },
415 { "CSMSG_CHANNEL_REGISTRAR", "$bRegistrar: $b%s" },
416 { "CSMSG_CHANNEL_SUSPENDED", "$b%s$b is suspended:" },
417 { "CSMSG_CHANNEL_HISTORY", "Suspension history for $b%s$b:" },
418 { "CSMSG_CHANNEL_SUSPENDED_0", " by %s: %s" },
419 { "CSMSG_CHANNEL_SUSPENDED_1", " by %s; expires in %s: %s" },
420 { "CSMSG_CHANNEL_SUSPENDED_2", " by %s; expired %s ago: %s" },
421 { "CSMSG_CHANNEL_SUSPENDED_3", " by %s; revoked %s ago: %s" },
422 { "CSMSG_CHANNEL_SUSPENDED_4", " %s ago by %s: %s" },
423 { "CSMSG_CHANNEL_SUSPENDED_5", " %s ago by %s; expires in %s: %s" },
424 { "CSMSG_CHANNEL_SUSPENDED_6", " %s ago by %s; expired %s ago: %s" },
425 { "CSMSG_CHANNEL_SUSPENDED_7", " %s ago by %s; revoked %s ago: %s" },
426 { "CSMSG_CHANNEL_REGISTERED", "$bRegistered: $b%s ago." },
427 { "CSMSG_CHANNEL_VISITED", "$bVisited: $b%s ago." },
429 { "CSMSG_PEEK_INFO", "$b%s$b Status:" },
430 { "CSMSG_PEEK_TOPIC", "$bTopic: $b%s" },
431 { "CSMSG_PEEK_MODES", "$bModes: $b%s" },
432 { "CSMSG_PEEK_USERS", "$bTotal users: $b%d (%d ops, %d voices, %d regulars)" },
433 { "CSMSG_PEEK_OPS", "$bOps:$b" },
434 { "CSMSG_PEEK_NO_OPS", "$bOps: $bNone present" },
436 /* Network information */
437 { "CSMSG_NETWORK_INFO", "Network Information:" },
438 { "CSMSG_NETWORK_SERVERS", "$bServers: $b%i" },
439 { "CSMSG_NETWORK_USERS", "$bTotal Users: $b%i" },
440 { "CSMSG_NETWORK_BANS", "$bTotal Ban Count: $b%i" },
441 { "CSMSG_NETWORK_CHANUSERS", "$bTotal User Count: $b%i" },
442 { "CSMSG_NETWORK_OPERS", "$bIRC Operators: $b%i" },
443 { "CSMSG_NETWORK_CHANNELS","$bRegistered Channels: $b%i" },
444 { "CSMSG_SERVICES_UPTIME", "$bServices Uptime: $b%s" },
445 { "CSMSG_BURST_LENGTH", "$bLast Burst Length: $b%s" },
448 { "CSMSG_NETWORK_STAFF", "$bOnline Network Staff:$b" },
449 { "CSMSG_STAFF_OPERS", "$bIRC Operators:$b" },
450 { "CSMSG_STAFF_HELPERS", "$bHelpers:$b" },
452 /* Channel searches */
453 { "CSMSG_ACTION_INVALID", "$b%s$b is not a recognized search action." },
454 { "CSMSG_UNVISITED_HEADER", "Showing a maximum of %d channels unvisited for $b%s$b:" },
455 { "CSMSG_UNVISITED_DATA", "%s: $b%s$b" },
456 { "CSMSG_CHANNEL_SEARCH_RESULTS", "The following channels were found:" },
458 /* Channel configuration */
459 { "CSMSG_INVALID_OPTION", "$b%s$b is not a valid %s option." },
460 { "CSMSG_INVALID_CFLAG", "$b%s$b is not a recognized channel flag." },
461 { "CSMSG_CHANNEL_OPTIONS", "Channel Options:" },
462 { "CSMSG_GREETING_TOO_LONG", "Your greeting ($b%d$b characters) must be shorter than $b%d$b characters." },
465 { "CSMSG_USER_OPTIONS", "User Options:" },
466 { "CSMSG_USER_PROTECTED_2", "That user is protected." },
469 { "CSMSG_UNF_RESPONSE", "I don't want to be part of your sick fantasies!" },
470 { "CSMSG_PING_RESPONSE", "Pong!" },
471 { "CSMSG_WUT_RESPONSE", "wut" },
472 { "CSMSG_BAD_NUMBER", "$b%s$b is an invalid number. Please use a number greater than 1 with this command." },
473 { "CSMSG_BAD_DIE_FORMAT", "I do not understand $b%s$b. Please use either a single number or standard 4d6+3 format." },
474 { "CSMSG_BAD_DICE_COUNT", "%lu is too many dice. Please use at most %lu." },
475 { "CSMSG_DICE_ROLL", "The total is $b%lu$b from rolling %lud%lu+%lu." },
476 { "CSMSG_DIE_ROLL", "A $b%lu$b shows on the %lu-sided die." },
477 { "CSMSG_HUGGLES_HIM", "\001ACTION huggles %s\001" },
478 { "CSMSG_HUGGLES_YOU", "\001ACTION huggles you\001" },
481 { "CSMSG_ADDVOTE_DONE", "Vote added. Use $baddoption$b to add at least 2 vote options and then $bstartvote$b to start the voting." },
482 { "CSMSG_ADDVOTE_FULL", "There is already a vote in this channel. Use $bdelvote$b to delete it." },
483 { "CSMSG_DELVOTE_DONE", "Vote deleted." },
484 { "CSMSG_NO_VOTE", "There is no vote in this channel." },
485 { "CSMSG_ADDOPTION_DONE", "Vote option added with id %i (%i - %i)." },
486 { "CSMSG_DELOPTION_DONE", "Vote option deleted." },
487 { "CSMSG_DELOPTION_NONE", "Vote option does not exist." },
488 { "CSMSG_VOTE_NEED_OPTIONS", "There must be at least 2 options in a vote." },
489 { "CSMSG_VOTE_NOT_STARTED", "The vote is not started. Use $bstartvote$b to allow voting." },
490 { "CSMSG_VOTE_QUESTION", "Question: %s" },
491 { "CSMSG_VOTE_OPTION", "$b%i$b: %s ($b%i$b votes)" },
492 { "CSMSG_VOTERES_QUESTION", "Question: %s" },
493 { "CSMSG_VOTERES_OPTION", "
\ 2%i
\ 2: %s (
\ 2%i
\ 2 votes)" },
494 { "CSMSG_STARTVOTE_TOP", "
\ 2%s
\ 2 has started a voting:" },
495 { "CSMSG_STARTVOTE_QUESTION", "
\ 2Question:
\ 2 %s" },
496 { "CSMSG_STARTVOTE_OPTION", "
\ 2%i:
\ 2 %s" },
497 { "CSMSG_STARTVOTE_ACCESS", "All channel users with at least
\ 2%i
\ 2 access can vote." },
498 { "CSMSG_STARTVOTE_HOWTO", "To vote for an option, use
\ 2vote ID
\ 2. To see the available options and the current votes, use
\ 2vote
\ 2 without parameters." },
499 { "CSMSG_STARTVOTE_RUNNING", "The vote is already running." },
500 { "CSMSG_VOTE_VOTED", "You have already voted." },
501 { "CSMSG_VOTE_INVALID", "Vote option does not exist." },
502 { "CSMSG_VOTE_DONE", "You voted for $b%s$b." },
503 { "CSMSG_ENDVOTE_DONE", "The vote has been finished." },
504 { "CSMSG_ENDVOTE_STOPPED", "The vote is not running." },
507 { "CSMSG_EVENT_SEARCH_RESULTS", "The following channel events were found:" },
511 /* eject_user and unban_user flags */
512 #define ACTION_KICK 0x0001
513 #define ACTION_BAN 0x0002
514 #define ACTION_ADD_BAN 0x0004
515 #define ACTION_ADD_TIMED_BAN 0x0008
516 #define ACTION_UNBAN 0x0010
517 #define ACTION_DEL_BAN 0x0020
519 /* The 40 allows for [+-ntlksimprD] and lots of fudge factor. */
520 #define MODELEN 40 + KEYLEN
524 #define CSFUNC_ARGS user, channel, argc, argv, cmd
526 #define CHANSERV_FUNC(NAME) MODCMD_FUNC(NAME)
527 #define CHANSERV_SYNTAX() svccmd_send_help(user, chanserv, cmd)
528 #define REQUIRE_PARAMS(N) if(argc < (N)) { \
529 reply("MSG_MISSING_PARAMS", argv[0]); \
533 DECLARE_LIST(dnrList, struct do_not_register *);
534 DEFINE_LIST(dnrList, struct do_not_register *)
536 static int eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action);
538 struct userNode *chanserv;
541 static dict_t plain_dnrs, mask_dnrs, handle_dnrs;
542 static struct log_type *CS_LOG;
546 struct channelList support_channels;
547 struct mod_chanmode default_modes;
549 unsigned long db_backup_frequency;
550 unsigned long channel_expire_frequency;
551 unsigned long dnr_expire_frequency;
553 unsigned long invited_timeout;
555 unsigned long info_delay;
556 unsigned long adjust_delay;
557 unsigned long channel_expire_delay;
558 unsigned int nodelete_level;
560 unsigned int adjust_threshold;
561 int join_flood_threshold;
563 unsigned int greeting_length;
564 unsigned int refresh_period;
565 unsigned int giveownership_period;
567 unsigned int max_owned;
568 unsigned int max_chan_users;
569 unsigned int max_chan_bans;
570 unsigned int max_userinfo_length;
572 struct string_list *set_shows;
573 struct string_list *eightball;
574 struct string_list *old_ban_names;
576 const char *ctcp_short_ban_duration;
577 const char *ctcp_long_ban_duration;
579 const char *irc_operator_epithet;
580 const char *network_helper_epithet;
581 const char *support_helper_epithet;
583 const char *new_channel_authed;
584 const char *new_channel_unauthed;
585 const char *new_channel_msg;
590 struct userNode *user;
591 struct userNode *bot;
592 struct chanNode *channel;
594 unsigned short lowest;
595 unsigned short highest;
596 struct userData **users;
597 struct helpfile_table table;
602 struct userNode *user;
603 struct chanNode *chan;
606 enum note_access_type
608 NOTE_SET_CHANNEL_ACCESS,
609 NOTE_SET_CHANNEL_SETTER,
613 enum note_visible_type
616 NOTE_VIS_CHANNEL_USERS,
622 enum note_access_type set_access_type;
624 unsigned int min_opserv;
625 unsigned short min_ulevel;
627 enum note_visible_type visible_type;
628 unsigned int max_length;
635 struct note_type *type;
636 char setter[NICKSERV_HANDLE_LEN+1];
640 static unsigned int registered_channels;
641 static unsigned int banCount;
643 static const struct {
646 unsigned short level;
649 { "peon", "Peon", UL_PEON, '+' },
650 { "op", "Op", UL_OP, '@' },
651 { "master", "Master", UL_MASTER, '%' },
652 { "coowner", "Coowner", UL_COOWNER, '*' },
653 { "owner", "Owner", UL_OWNER, '!' },
654 { "helper", "BUG:", UL_HELPER, 'X' }
657 static const struct {
660 unsigned short default_value;
661 unsigned int old_idx;
662 unsigned int old_flag;
663 unsigned short flag_value;
665 { "CSMSG_SET_GIVE_VOICE", "givevoice", 100, ~0, CHANNEL_VOICE_ALL, 0 },
666 { "CSMSG_SET_GIVE_OPS", "giveops", 200, 2, 0, 0 },
667 { "CSMSG_SET_ENFOPS", "enfops", 300, 1, 0, 0 },
668 { "CSMSG_SET_ENFMODES", "enfmodes", 200, 3, 0, 0 },
669 { "CSMSG_SET_ENFTOPIC", "enftopic", 200, 4, 0, 0 },
670 { "CSMSG_SET_PUBCMD", "pubcmd", 0, 5, 0, 0 },
671 { "CSMSG_SET_SETTERS", "setters", 400, 7, 0, 0 },
672 { "CSMSG_SET_CTCPUSERS", "ctcpusers", 0, 9, 0, 0 },
673 { "CSMSG_SET_USERINFO", "userinfo", 1, ~0, CHANNEL_INFO_LINES, 1 },
674 { "CSMSG_SET_INVITEME", "inviteme", 1, ~0, CHANNEL_PEON_INVITE, 200 },
675 { "CSMSG_SET_TOPICSNARF", "topicsnarf", 501, ~0, CHANNEL_TOPIC_SNARF, 1 },
676 { "CSMSG_SET_VOTE", "vote", 100, ~0, 0, 0 }
679 struct charOptionValues {
682 } protectValues[] = {
683 { 'a', "CSMSG_PROTECT_ALL" },
684 { 'e', "CSMSG_PROTECT_EQUAL" },
685 { 'l', "CSMSG_PROTECT_LOWER" },
686 { 'n', "CSMSG_PROTECT_NONE" }
688 { 'd', "CSMSG_TOYS_DISABLED" },
689 { 'n', "CSMSG_TOYS_PRIVATE" },
690 { 'p', "CSMSG_TOYS_PUBLIC" }
691 }, topicRefreshValues[] = {
692 { 'n', "CSMSG_TOPICREFRESH_NEVER" },
693 { '1', "CSMSG_TOPICREFRESH_3_HOURS" },
694 { '2', "CSMSG_TOPICREFRESH_6_HOURS" },
695 { '3', "CSMSG_TOPICREFRESH_12_HOURS" },
696 { '4', "CSMSG_TOPICREFRESH_24_HOURS" }
697 }, ctcpReactionValues[] = {
698 { 'k', "CSMSG_CTCPREACTION_KICK" },
699 { 'b', "CSMSG_CTCPREACTION_KICKBAN" },
700 { 't', "CSMSG_CTCPREACTION_SHORTBAN" },
701 { 'T', "CSMSG_CTCPREACTION_LONGBAN" }
704 static const struct {
708 unsigned int old_idx;
710 struct charOptionValues *values;
712 { "CSMSG_SET_PROTECT", "protect", 'l', 0, ArrayLength(protectValues), protectValues },
713 { "CSMSG_SET_TOYS", "toys", 'p', 6, ArrayLength(toysValues), toysValues },
714 { "CSMSG_SET_TOPICREFRESH", "topicrefresh", 'n', 8, ArrayLength(topicRefreshValues), topicRefreshValues },
715 { "CSMSG_SET_CTCPREACTION", "ctcpreaction", 't', 10, ArrayLength(ctcpReactionValues), ctcpReactionValues }
718 struct userData *helperList;
719 struct chanData *channelList;
720 static struct module *chanserv_module;
721 static unsigned int userCount;
723 #define GetChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 0)
724 #define GetTrueChannelAccess(channel, handle) _GetChannelUser(channel, handle, 0, 1)
725 static void unregister_channel(struct chanData *channel, const char *reason);
728 user_level_from_name(const char *name, unsigned short clamp_level)
730 unsigned int level = 0, ii;
732 level = strtoul(name, NULL, 10);
733 else for(ii = 0; (ii < ArrayLength(accessLevels)) && !level; ++ii)
734 if(!irccasecmp(name, accessLevels[ii].name))
735 level = accessLevels[ii].level;
736 if(level > clamp_level)
742 parse_level_range(unsigned short *minl, unsigned short *maxl, const char *arg)
745 *minl = strtoul(arg, &sep, 10);
753 *maxl = strtoul(sep+1, &sep, 10);
761 _GetChannelUser(struct chanData *channel, struct handle_info *handle, int override, int allow_suspended)
763 struct userData *uData, **head;
765 if(!channel || !handle)
768 if(override && HANDLE_FLAGGED(handle, HELPING)
769 && ((handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel)))
771 for(uData = helperList;
772 uData && uData->handle != handle;
773 uData = uData->next);
777 uData = calloc(1, sizeof(struct userData));
778 uData->handle = handle;
780 uData->access = UL_HELPER;
786 uData->next = helperList;
788 helperList->prev = uData;
796 for(uData = channel->users; uData; uData = uData->next)
797 if((uData->handle == handle) && (allow_suspended || !IsUserSuspended(uData)))
800 head = &(channel->users);
803 if(uData && (uData != *head))
805 /* Shuffle the user to the head of whatever list he was in. */
807 uData->next->prev = uData->prev;
809 uData->prev->next = uData->next;
815 (**head).prev = uData;
822 /* Returns non-zero if user has at least the minimum access.
823 * exempt_owner is set when handling !set, so the owner can set things
826 int check_user_level(struct chanNode *channel, struct userNode *user, enum levelOption opt, int allow_override, int exempt_owner)
828 struct userData *uData;
829 struct chanData *cData = channel->channel_info;
830 unsigned short minimum = cData->lvlOpts[opt];
833 uData = _GetChannelUser(cData, user->handle_info, allow_override, 0);
836 if(minimum <= uData->access)
838 if((minimum > UL_OWNER) && (uData->access == UL_OWNER) && exempt_owner)
843 /* Scan for other users authenticated to the same handle
844 still in the channel. If so, keep them listed as present.
846 user is optional, if not null, it skips checking that userNode
847 (for the handle_part function) */
849 scan_user_presence(struct userData *uData, struct userNode *user)
853 if(IsSuspended(uData->channel)
854 || IsUserSuspended(uData)
855 || !(mn = find_handle_in_channel(uData->channel->channel, uData->handle, user)))
867 chanserv_ctcp_check(struct userNode *user, struct chanNode *channel, const char *text, UNUSED_ARG(struct userNode *bot), UNUSED_ARG(unsigned int is_notice))
869 unsigned int eflags, argc;
871 static char *bad_ctcp_reason = "CTCPs to this channel are forbidden.";
873 /* Bail early if channel is inactive or doesn't restrict CTCPs, or sender is a service */
874 if(!channel->channel_info
875 || IsSuspended(channel->channel_info)
877 || !ircncasecmp(text, "ACTION ", 7))
879 /* Figure out the minimum level needed to CTCP the channel */
880 if(check_user_level(channel, user, lvlCTCPUsers, 1, 0))
882 /* We need to enforce against them; do so. */
884 argv[0] = (char*)text;
885 argv[1] = user->nick;
887 if(GetUserMode(channel, user))
888 eflags |= ACTION_KICK;
889 switch(channel->channel_info->chOpts[chCTCPReaction]) {
890 default: case 'k': /* just do the kick */ break;
892 eflags |= ACTION_BAN;
895 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
896 argv[argc++] = (char*)chanserv_conf.ctcp_short_ban_duration;
899 eflags |= ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN;
900 argv[argc++] = (char*)chanserv_conf.ctcp_long_ban_duration;
903 argv[argc++] = bad_ctcp_reason;
904 eject_user(chanserv, channel, argc, argv, NULL, eflags);
908 chanserv_create_note_type(const char *name)
910 struct note_type *ntype = calloc(1, sizeof(*ntype) + strlen(name));
911 strcpy(ntype->name, name);
913 dict_insert(note_types, ntype->name, ntype);
918 free_vote_options(void *data)
920 struct vote_option *vOpt = data;
922 free(vOpt->option_str);
927 chanserv_deref_note_type(void *data)
929 struct note_type *ntype = data;
931 if(--ntype->refs > 0)
937 chanserv_flush_note_type(struct note_type *ntype)
939 struct chanData *cData;
940 for(cData = channelList; cData; cData = cData->next)
941 dict_remove(cData->notes, ntype->name);
945 chanserv_truncate_notes(struct note_type *ntype)
947 struct chanData *cData;
949 unsigned int size = sizeof(*note) + ntype->max_length;
951 for(cData = channelList; cData; cData = cData->next) {
952 note = dict_find(cData->notes, ntype->name, NULL);
955 if(strlen(note->note) <= ntype->max_length)
957 dict_remove2(cData->notes, ntype->name, 1);
958 note = realloc(note, size);
959 note->note[ntype->max_length] = 0;
960 dict_insert(cData->notes, ntype->name, note);
964 static int note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user);
967 chanserv_add_channel_note(struct chanData *channel, struct note_type *type, const char *setter, const char *text)
970 unsigned int len = strlen(text);
972 if(len > type->max_length) len = type->max_length;
973 note = calloc(1, sizeof(*note) + len);
975 strncpy(note->setter, setter, sizeof(note->setter)-1);
976 memcpy(note->note, text, len);
978 dict_insert(channel->notes, type->name, note);
984 chanserv_free_note(void *data)
986 struct note *note = data;
988 chanserv_deref_note_type(note->type);
989 assert(note->type->refs > 0); /* must use delnote to remove the type */
993 static MODCMD_FUNC(cmd_createnote) {
994 struct note_type *ntype;
995 unsigned int arg = 1, existed = 0, max_length;
997 if((ntype = dict_find(note_types, argv[1], NULL)))
1000 ntype = chanserv_create_note_type(argv[arg]);
1001 if(!irccasecmp(argv[++arg], "privileged"))
1004 ntype->set_access_type = NOTE_SET_PRIVILEGED;
1005 ntype->set_access.min_opserv = strtoul(argv[arg], NULL, 0);
1007 else if(!irccasecmp(argv[arg], "channel"))
1009 unsigned short ulvl = user_level_from_name(argv[++arg], UL_OWNER);
1012 reply("CSMSG_INVALID_ACCESS", argv[arg]);
1015 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
1016 ntype->set_access.min_ulevel = ulvl;
1018 else if(!irccasecmp(argv[arg], "setter"))
1020 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
1024 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1028 if(!irccasecmp(argv[++arg], "privileged"))
1029 ntype->visible_type = NOTE_VIS_PRIVILEGED;
1030 else if(!irccasecmp(argv[arg], "channel_users"))
1031 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
1032 else if(!irccasecmp(argv[arg], "all"))
1033 ntype->visible_type = NOTE_VIS_ALL;
1035 reply("CSMSG_BAD_NOTE_ACCESS", argv[arg]);
1039 if((arg+1) >= argc) {
1040 reply("MSG_MISSING_PARAMS", argv[0]);
1043 max_length = strtoul(argv[++arg], NULL, 0);
1044 if(max_length < 20 || max_length > 450)
1046 reply("CSMSG_BAD_MAX_LENGTH", argv[arg]);
1049 if(existed && (max_length < ntype->max_length))
1051 ntype->max_length = max_length;
1052 chanserv_truncate_notes(ntype);
1054 ntype->max_length = max_length;
1057 reply("CSMSG_NOTE_MODIFIED", ntype->name);
1059 reply("CSMSG_NOTE_CREATED", ntype->name);
1064 dict_remove(note_types, ntype->name);
1068 static MODCMD_FUNC(cmd_removenote) {
1069 struct note_type *ntype;
1072 ntype = dict_find(note_types, argv[1], NULL);
1073 force = (argc > 2) && !irccasecmp(argv[2], "force");
1076 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
1083 reply("CSMSG_NOTE_TYPE_USED", ntype->name);
1086 chanserv_flush_note_type(ntype);
1088 dict_remove(note_types, argv[1]);
1089 reply("CSMSG_NOTE_DELETED", argv[1]);
1094 chanserv_expire_channel(void *data)
1096 struct chanData *channel = data;
1097 char reason[MAXLEN];
1098 sprintf(reason, "channel expired.");
1099 channel->expiry = 0;
1100 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1101 unregister_channel(channel, reason);
1104 static MODCMD_FUNC(chan_opt_expire)
1106 struct chanData *cData = channel->channel_info;
1107 unsigned long value = cData->expiry;
1111 if((!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
1113 reply("MSG_SETTING_PRIVILEGED", argv[0]);
1116 unsigned long expiry,duration;
1118 /* The two directions can have different ACLs. */
1119 if(!strcmp(argv[1], "0"))
1121 else if((duration = ParseInterval(argv[1])))
1122 expiry = now + duration;
1125 reply("MSG_INVALID_DURATION", argv[1]);
1129 if (expiry != value)
1133 timeq_del(value, chanserv_expire_channel, cData, 0);
1136 cData->expiry = value;
1139 timeq_add(expiry, chanserv_expire_channel, cData);
1144 if(cData->expiry > now) {
1145 char expirestr[INTERVALLEN];
1146 reply("CSMSG_SET_EXPIRE", intervalString(expirestr, cData->expiry - now, user->handle_info));
1148 reply("CSMSG_SET_EXPIRE_OFF");
1153 mode_lock_violated(const struct mod_chanmode *orig, const struct mod_chanmode *change)
1157 if(orig->modes_set & change->modes_clear)
1159 if(orig->modes_clear & change->modes_set)
1161 if((orig->modes_set & MODE_KEY) && (change->modes_set & MODE_KEY)
1162 && strcmp(orig->new_key, change->new_key))
1164 if((orig->modes_set & MODE_LIMIT) && (change->modes_set & MODE_LIMIT)
1165 && (orig->new_limit != change->new_limit))
1170 static char max_length_text[MAXLEN+1][16];
1172 static struct helpfile_expansion
1173 chanserv_expand_variable(const char *variable)
1175 struct helpfile_expansion exp;
1177 if(!irccasecmp(variable, "notes"))
1180 exp.type = HF_TABLE;
1181 exp.value.table.length = 1;
1182 exp.value.table.width = 3;
1183 exp.value.table.flags = 0;
1184 exp.value.table.contents = calloc(dict_size(note_types)+1, sizeof(char**));
1185 exp.value.table.contents[0] = calloc(exp.value.table.width, sizeof(char*));
1186 exp.value.table.contents[0][0] = "Note Type";
1187 exp.value.table.contents[0][1] = "Visibility";
1188 exp.value.table.contents[0][2] = "Max Length";
1189 for(it=dict_first(note_types); it; it=iter_next(it))
1191 struct note_type *ntype = iter_data(it);
1194 if(!note_type_visible_to_user(NULL, ntype, message_dest)) continue;
1195 row = exp.value.table.length++;
1196 exp.value.table.contents[row] = calloc(exp.value.table.width, sizeof(char*));
1197 exp.value.table.contents[row][0] = ntype->name;
1198 exp.value.table.contents[row][1] = (ntype->visible_type == NOTE_VIS_ALL) ? "all" :
1199 (ntype->visible_type == NOTE_VIS_CHANNEL_USERS) ? "chan users" :
1201 if(!max_length_text[ntype->max_length][0])
1202 snprintf(max_length_text[ntype->max_length], sizeof(max_length_text[ntype->max_length]), "%u", ntype->max_length);
1203 exp.value.table.contents[row][2] = max_length_text[ntype->max_length];
1208 exp.type = HF_STRING;
1209 exp.value.str = NULL;
1213 static struct chanData*
1214 register_channel(struct chanNode *cNode, char *registrar)
1216 struct chanData *channel;
1217 enum levelOption lvlOpt;
1218 enum charOption chOpt;
1220 channel = calloc(1, sizeof(struct chanData));
1222 channel->notes = dict_new();
1223 dict_set_free_data(channel->notes, chanserv_free_note);
1225 channel->registrar = strdup(registrar);
1226 channel->registered = now;
1227 channel->visited = now;
1228 channel->limitAdjusted = now;
1229 channel->ownerTransfer = now;
1230 channel->flags = CHANNEL_DEFAULT_FLAGS;
1231 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
1232 channel->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
1233 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
1234 channel->chOpts[chOpt] = charOptions[chOpt].default_value;
1236 channel->prev = NULL;
1237 channel->next = channelList;
1240 channelList->prev = channel;
1241 channelList = channel;
1242 registered_channels++;
1244 channel->channel = cNode;
1246 cNode->channel_info = channel;
1248 channel->vote = NULL;
1253 static struct userData*
1254 add_channel_user(struct chanData *channel, struct handle_info *handle, unsigned short access_level, unsigned long seen, const char *info)
1256 struct userData *ud;
1258 if(access_level > UL_OWNER)
1261 ud = calloc(1, sizeof(*ud));
1262 ud->channel = channel;
1263 ud->handle = handle;
1265 ud->access = access_level;
1266 ud->info = info ? strdup(info) : NULL;
1269 ud->next = channel->users;
1271 channel->users->prev = ud;
1272 channel->users = ud;
1274 channel->userCount++;
1278 ud->u_next = ud->handle->channels;
1280 ud->u_next->u_prev = ud;
1281 ud->handle->channels = ud;
1287 del_channel_user(struct userData *user, int do_gc)
1289 struct chanData *channel = user->channel;
1291 channel->userCount--;
1295 user->prev->next = user->next;
1297 channel->users = user->next;
1299 user->next->prev = user->prev;
1302 user->u_prev->u_next = user->u_next;
1304 user->handle->channels = user->u_next;
1306 user->u_next->u_prev = user->u_prev;
1310 if(do_gc && !channel->users && !IsProtected(channel)) {
1311 spamserv_cs_unregister(NULL, channel->channel, lost_all_users, NULL);
1312 unregister_channel(channel, "lost all users.");
1316 static void expire_ban(void *data);
1319 add_channel_ban(struct chanData *channel, const char *mask, char *owner, unsigned long set, unsigned long triggered, unsigned long expires, char *reason)
1322 unsigned int ii, l1, l2;
1327 bd = malloc(sizeof(struct banData));
1329 bd->channel = channel;
1331 bd->triggered = triggered;
1332 bd->expires = expires;
1334 for(ii = 0; ii < chanserv_conf.old_ban_names->used; ++ii)
1336 extern const char *hidden_host_suffix;
1337 const char *old_name = chanserv_conf.old_ban_names->list[ii];
1341 l2 = strlen(old_name);
1344 if(irccasecmp(mask + l1 - l2, old_name))
1346 new_mask = alloca(MAXLEN);
1347 sprintf(new_mask, "%.*s%s", (int)(l1-l2), mask, hidden_host_suffix);
1350 safestrncpy(bd->mask, mask, sizeof(bd->mask));
1352 safestrncpy(bd->owner, owner, sizeof(bd->owner));
1353 bd->reason = strdup(reason);
1356 timeq_add(expires, expire_ban, bd);
1359 bd->next = channel->bans;
1361 channel->bans->prev = bd;
1363 channel->banCount++;
1370 del_channel_ban(struct banData *ban)
1372 ban->channel->banCount--;
1376 ban->prev->next = ban->next;
1378 ban->channel->bans = ban->next;
1381 ban->next->prev = ban->prev;
1384 timeq_del(0, expire_ban, ban, TIMEQ_IGNORE_WHEN);
1393 expire_ban(void *data)
1395 struct banData *bd = data;
1396 if(!IsSuspended(bd->channel))
1398 struct banList bans;
1399 struct mod_chanmode change;
1401 bans = bd->channel->channel->banlist;
1402 mod_chanmode_init(&change);
1403 for(ii=0; ii<bans.used; ii++)
1405 if(!strcmp(bans.list[ii]->ban, bd->mask))
1408 change.args[0].mode = MODE_REMOVE|MODE_BAN;
1409 change.args[0].u.hostmask = bd->mask;
1410 mod_chanmode_announce(chanserv, bd->channel->channel, &change);
1416 del_channel_ban(bd);
1419 static void chanserv_expire_suspension(void *data);
1422 unregister_channel(struct chanData *channel, const char *reason)
1424 struct mod_chanmode change;
1425 char msgbuf[MAXLEN];
1427 /* After channel unregistration, the following must be cleaned
1429 - Channel information.
1432 - Channel suspension data.
1433 - Timeq entries. (Except timed bans, which are handled elsewhere.)
1439 timeq_del(0, NULL, channel, TIMEQ_IGNORE_FUNC | TIMEQ_IGNORE_WHEN);
1443 mod_chanmode_init(&change);
1444 change.modes_clear |= MODE_REGISTERED;
1445 mod_chanmode_announce(chanserv, channel->channel, &change);
1448 while(channel->users)
1449 del_channel_user(channel->users, 0);
1451 while(channel->bans)
1452 del_channel_ban(channel->bans);
1454 free(channel->topic);
1455 free(channel->registrar);
1456 free(channel->greeting);
1457 free(channel->user_greeting);
1458 free(channel->topic_mask);
1461 channel->prev->next = channel->next;
1463 channelList = channel->next;
1466 channel->next->prev = channel->prev;
1468 if(channel->suspended)
1470 struct chanNode *cNode = channel->channel;
1471 struct suspended *suspended, *next_suspended;
1473 for(suspended = channel->suspended; suspended; suspended = next_suspended)
1475 next_suspended = suspended->previous;
1476 free(suspended->suspender);
1477 free(suspended->reason);
1478 if(suspended->expires)
1479 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
1484 cNode->channel_info = NULL;
1487 timeq_del(channel->expiry, chanserv_expire_channel, channel, 0);
1488 channel->channel->channel_info = NULL;
1490 dict_delete(channel->notes);
1491 sprintf(msgbuf, "%s %s", channel->channel->name, reason);
1492 if(!IsSuspended(channel))
1493 DelChannelUser(chanserv, channel->channel, msgbuf, 0);
1494 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, msgbuf);
1495 UnlockChannel(channel->channel);
1497 registered_channels--;
1501 expire_channels(UNUSED_ARG(void *data))
1503 struct chanData *channel, *next;
1504 struct userData *user;
1505 char delay[INTERVALLEN], reason[INTERVALLEN + 64];
1507 intervalString(delay, chanserv_conf.channel_expire_delay, NULL);
1508 sprintf(reason, "Channel registration automatically expired after %s of disuse.", delay);
1510 for(channel = channelList; channel; channel = next)
1512 next = channel->next;
1514 /* See if the channel can be expired. */
1515 if(((now - channel->visited) <= chanserv_conf.channel_expire_delay)
1516 || IsProtected(channel))
1519 /* Make sure there are no high-ranking users still in the channel. */
1520 for(user=channel->users; user; user=user->next)
1521 if(user->present && (user->access >= UL_PRESENT) && !HANDLE_FLAGGED(user->handle, BOT))
1526 /* Unregister the channel */
1527 log_module(CS_LOG, LOG_INFO, "(%s) Channel registration expired.", channel->channel->name);
1528 spamserv_cs_unregister(NULL, channel->channel, expire, NULL);
1529 unregister_channel(channel, "registration expired.");
1532 if(chanserv_conf.channel_expire_frequency)
1533 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
1537 expire_dnrs(UNUSED_ARG(void *data))
1539 dict_iterator_t it, next;
1540 struct do_not_register *dnr;
1542 for(it = dict_first(handle_dnrs); it; it = next)
1544 dnr = iter_data(it);
1545 next = iter_next(it);
1546 if(dnr->expires && dnr->expires <= now)
1547 dict_remove(handle_dnrs, dnr->chan_name + 1);
1549 for(it = dict_first(plain_dnrs); it; it = next)
1551 dnr = iter_data(it);
1552 next = iter_next(it);
1553 if(dnr->expires && dnr->expires <= now)
1554 dict_remove(plain_dnrs, dnr->chan_name + 1);
1556 for(it = dict_first(mask_dnrs); it; it = next)
1558 dnr = iter_data(it);
1559 next = iter_next(it);
1560 if(dnr->expires && dnr->expires <= now)
1561 dict_remove(mask_dnrs, dnr->chan_name + 1);
1564 if(chanserv_conf.dnr_expire_frequency)
1565 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
1569 protect_user(const struct userNode *victim, const struct userNode *aggressor, struct chanData *channel)
1571 char protect = channel->chOpts[chProtect];
1572 struct userData *cs_victim, *cs_aggressor;
1574 /* Don't protect if no one is to be protected, someone is attacking
1575 himself, or if the aggressor is an IRC Operator. */
1576 if(protect == 'n' || victim == aggressor || IsOper(aggressor))
1579 /* Don't protect if the victim isn't authenticated (because they
1580 can't be a channel user), unless we are to protect non-users
1582 cs_victim = GetChannelAccess(channel, victim->handle_info);
1583 if(protect != 'a' && !cs_victim)
1586 /* Protect if the aggressor isn't a user because at this point,
1587 the aggressor can only be less than or equal to the victim. */
1588 cs_aggressor = GetChannelAccess(channel, aggressor->handle_info);
1592 /* If the aggressor was a user, then the victim can't be helped. */
1599 if(cs_victim->access > cs_aggressor->access)
1604 if(cs_victim->access >= cs_aggressor->access)
1613 validate_op(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1615 struct chanData *cData = channel->channel_info;
1616 struct userData *cs_victim;
1618 if((!(cs_victim = GetChannelUser(cData, victim->handle_info))
1619 || (cs_victim->access < cData->lvlOpts[lvlGiveOps]))
1620 && !check_user_level(channel, user, lvlEnfOps, 0, 0))
1622 send_message(user, chanserv, "CSMSG_OPBY_LOCKED");
1630 validate_deop(struct userNode *user, struct chanNode *channel, struct userNode *victim)
1632 if(IsService(victim))
1634 send_message(user, chanserv, "MSG_SERVICE_IMMUNE", victim->nick);
1638 if(protect_user(victim, user, channel->channel_info))
1640 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
1647 static struct do_not_register *
1648 chanserv_add_dnr(const char *chan_name, const char *setter, unsigned long expires, const char *reason)
1650 struct do_not_register *dnr = calloc(1, sizeof(*dnr)+strlen(reason));
1651 safestrncpy(dnr->chan_name, chan_name, sizeof(dnr->chan_name));
1652 safestrncpy(dnr->setter, setter, sizeof(dnr->setter));
1653 strcpy(dnr->reason, reason);
1655 dnr->expires = expires;
1656 if(dnr->chan_name[0] == '*')
1657 dict_insert(handle_dnrs, dnr->chan_name+1, dnr);
1658 else if(strpbrk(dnr->chan_name, "*?"))
1659 dict_insert(mask_dnrs, dnr->chan_name, dnr);
1661 dict_insert(plain_dnrs, dnr->chan_name, dnr);
1665 static struct dnrList
1666 chanserv_find_dnrs(const char *chan_name, const char *handle, unsigned int max)
1668 struct dnrList list;
1669 dict_iterator_t it, next;
1670 struct do_not_register *dnr;
1672 dnrList_init(&list);
1674 if(handle && (dnr = dict_find(handle_dnrs, handle, NULL)))
1676 if(dnr->expires && dnr->expires <= now)
1677 dict_remove(handle_dnrs, handle);
1678 else if(list.used < max)
1679 dnrList_append(&list, dnr);
1682 if(chan_name && (dnr = dict_find(plain_dnrs, chan_name, NULL)))
1684 if(dnr->expires && dnr->expires <= now)
1685 dict_remove(plain_dnrs, chan_name);
1686 else if(list.used < max)
1687 dnrList_append(&list, dnr);
1692 for(it = dict_first(mask_dnrs); it && list.used < max; it = next)
1694 next = iter_next(it);
1695 if(!match_ircglob(chan_name, iter_key(it)))
1697 dnr = iter_data(it);
1698 if(dnr->expires && dnr->expires <= now)
1699 dict_remove(mask_dnrs, iter_key(it));
1701 dnrList_append(&list, dnr);
1708 static int dnr_print_func(struct do_not_register *dnr, void *extra)
1710 struct userNode *user;
1711 char buf1[INTERVALLEN];
1712 char buf2[INTERVALLEN];
1719 strftime(buf1, sizeof(buf1), "%d %b %Y", localtime(&feh));
1724 strftime(buf2, sizeof(buf2), "%d %b %Y", localtime(&feh));
1725 send_message(user, chanserv, "CSMSG_DNR_INFO_SET_EXPIRES", dnr->chan_name, buf1, dnr->setter, buf2, dnr->reason);
1729 send_message(user, chanserv, "CSMSG_DNR_INFO_SET", dnr->chan_name, buf1, dnr->setter, dnr->reason);
1732 send_message(user, chanserv, "CSMSG_DNR_INFO", dnr->chan_name, dnr->setter, dnr->reason);
1737 chanserv_show_dnrs(struct userNode *user, struct svccmd *cmd, const char *chan_name, const char *handle)
1739 struct dnrList list;
1742 list = chanserv_find_dnrs(chan_name, handle, UINT_MAX);
1743 for(ii = 0; (ii < list.used) && (ii < 10); ++ii)
1744 dnr_print_func(list.list[ii], user);
1746 reply("CSMSG_MORE_DNRS", list.used - ii);
1751 struct do_not_register *
1752 chanserv_is_dnr(const char *chan_name, struct handle_info *handle)
1754 struct dnrList list;
1755 struct do_not_register *dnr;
1757 list = chanserv_find_dnrs(chan_name, handle ? handle->handle : NULL, 1);
1758 dnr = list.used ? list.list[0] : NULL;
1763 static unsigned int send_dnrs(struct userNode *user, dict_t dict)
1765 struct do_not_register *dnr;
1766 dict_iterator_t it, next;
1767 unsigned int matches = 0;
1769 for(it = dict_first(dict); it; it = next)
1771 dnr = iter_data(it);
1772 next = iter_next(it);
1773 if(dnr->expires && dnr->expires <= now)
1775 dict_remove(dict, iter_key(it));
1778 dnr_print_func(dnr, user);
1785 static CHANSERV_FUNC(cmd_noregister)
1789 unsigned long expiry, duration;
1790 unsigned int matches;
1794 reply("CSMSG_DNR_SEARCH_RESULTS");
1795 matches = send_dnrs(user, handle_dnrs);
1796 matches += send_dnrs(user, plain_dnrs);
1797 matches += send_dnrs(user, mask_dnrs);
1799 reply("MSG_MATCH_COUNT", matches);
1801 reply("MSG_NO_MATCHES");
1807 if(!IsChannelName(target) && (*target != '*'))
1809 reply("CSMSG_NOT_DNR", target);
1817 reply("MSG_INVALID_DURATION", argv[2]);
1821 if(!strcmp(argv[2], "0"))
1823 else if((duration = ParseInterval(argv[2])))
1824 expiry = now + duration;
1827 reply("MSG_INVALID_DURATION", argv[2]);
1831 reason = unsplit_string(argv + 3, argc - 3, NULL);
1832 if((*target == '*') && !get_handle_info(target + 1))
1834 reply("MSG_HANDLE_UNKNOWN", target + 1);
1837 chanserv_add_dnr(target, user->handle_info->handle, expiry, reason);
1838 reply("CSMSG_NOREGISTER_CHANNEL", target);
1842 reply("CSMSG_DNR_SEARCH_RESULTS");
1844 matches = chanserv_show_dnrs(user, cmd, NULL, target + 1);
1846 matches = chanserv_show_dnrs(user, cmd, target, NULL);
1848 reply("MSG_NO_MATCHES");
1852 static CHANSERV_FUNC(cmd_allowregister)
1854 const char *chan_name = argv[1];
1856 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
1857 || dict_remove(plain_dnrs, chan_name)
1858 || dict_remove(mask_dnrs, chan_name))
1860 reply("CSMSG_DNR_REMOVED", chan_name);
1863 reply("CSMSG_NO_SUCH_DNR", chan_name);
1868 struct userNode *source;
1872 unsigned long min_set, max_set;
1873 unsigned long min_expires, max_expires;
1878 dnr_search_matches(const struct do_not_register *dnr, const struct dnr_search *search)
1880 return !((dnr->set < search->min_set)
1881 || (dnr->set > search->max_set)
1882 || (dnr->expires < search->min_expires)
1883 || (search->max_expires
1884 && ((dnr->expires == 0)
1885 || (dnr->expires > search->max_expires)))
1886 || (search->chan_mask
1887 && !match_ircglob(dnr->chan_name, search->chan_mask))
1888 || (search->setter_mask
1889 && !match_ircglob(dnr->setter, search->setter_mask))
1890 || (search->reason_mask
1891 && !match_ircglob(dnr->reason, search->reason_mask)));
1894 static struct dnr_search *
1895 dnr_search_create(struct userNode *user, struct svccmd *cmd, unsigned int argc, char *argv[])
1897 struct dnr_search *discrim;
1900 discrim = calloc(1, sizeof(*discrim));
1901 discrim->source = user;
1902 discrim->chan_mask = NULL;
1903 discrim->setter_mask = NULL;
1904 discrim->reason_mask = NULL;
1905 discrim->max_set = INT_MAX;
1906 discrim->limit = 50;
1908 for(ii=0; ii<argc; ++ii)
1912 reply("MSG_MISSING_PARAMS", argv[ii]);
1915 else if(0 == irccasecmp(argv[ii], "channel"))
1917 discrim->chan_mask = argv[++ii];
1919 else if(0 == irccasecmp(argv[ii], "setter"))
1921 discrim->setter_mask = argv[++ii];
1923 else if(0 == irccasecmp(argv[ii], "reason"))
1925 discrim->reason_mask = argv[++ii];
1927 else if(0 == irccasecmp(argv[ii], "limit"))
1929 discrim->limit = strtoul(argv[++ii], NULL, 0);
1931 else if(0 == irccasecmp(argv[ii], "set"))
1933 const char *cmp = argv[++ii];
1936 discrim->min_set = now - ParseInterval(cmp + 2);
1938 discrim->min_set = now - (ParseInterval(cmp + 1) - 1);
1939 } else if(cmp[0] == '=') {
1940 discrim->min_set = discrim->max_set = now - ParseInterval(cmp + 1);
1941 } else if(cmp[0] == '>') {
1943 discrim->max_set = now - ParseInterval(cmp + 2);
1945 discrim->max_set = now - (ParseInterval(cmp + 1) - 1);
1947 discrim->max_set = now - (ParseInterval(cmp) - 1);
1950 else if(0 == irccasecmp(argv[ii], "expires"))
1952 const char *cmp = argv[++ii];
1955 discrim->max_expires = now + ParseInterval(cmp + 2);
1957 discrim->max_expires = now + (ParseInterval(cmp + 1) - 1);
1958 } else if(cmp[0] == '=') {
1959 discrim->min_expires = discrim->max_expires = now + ParseInterval(cmp + 1);
1960 } else if(cmp[0] == '>') {
1962 discrim->min_expires = now + ParseInterval(cmp + 2);
1964 discrim->min_expires = now + (ParseInterval(cmp + 1) - 1);
1966 discrim->min_expires = now + (ParseInterval(cmp) - 1);
1971 reply("MSG_INVALID_CRITERIA", argv[ii]);
1982 typedef int (*dnr_search_func)(struct do_not_register *match, void *extra);
1985 dnr_search(struct dnr_search *discrim, dnr_search_func dsf, void *data)
1987 struct do_not_register *dnr;
1988 dict_iterator_t next;
1993 /* Initialize local variables. */
1996 if(discrim->chan_mask)
1998 int shift = (discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*') ? 2 : 0;
1999 if('\0' == discrim->chan_mask[shift + strcspn(discrim->chan_mask+shift, "*?")])
2003 if(target_fixed && discrim->chan_mask[0] == '\\' && discrim->chan_mask[1] == '*')
2005 /* Check against account-based DNRs. */
2006 dnr = dict_find(handle_dnrs, discrim->chan_mask + 2, NULL);
2007 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2010 else if(target_fixed)
2012 /* Check against channel-based DNRs. */
2013 dnr = dict_find(plain_dnrs, discrim->chan_mask, NULL);
2014 if(dnr && dnr_search_matches(dnr, discrim) && (count++ < discrim->limit))
2019 /* Exhaustively search account DNRs. */
2020 for(it = dict_first(handle_dnrs); it; it = next)
2022 next = iter_next(it);
2023 dnr = iter_data(it);
2024 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2028 /* Do the same for channel DNRs. */
2029 for(it = dict_first(plain_dnrs); it; it = next)
2031 next = iter_next(it);
2032 dnr = iter_data(it);
2033 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2037 /* Do the same for wildcarded channel DNRs. */
2038 for(it = dict_first(mask_dnrs); it; it = next)
2040 next = iter_next(it);
2041 dnr = iter_data(it);
2042 if(dnr_search_matches(dnr, discrim) && (count++ < discrim->limit) && dsf(dnr, data))
2050 dnr_remove_func(struct do_not_register *match, void *extra)
2052 struct userNode *user;
2055 chan_name = alloca(strlen(match->chan_name) + 1);
2056 strcpy(chan_name, match->chan_name);
2058 if(((chan_name[0] == '*') && dict_remove(handle_dnrs, chan_name+1))
2059 || dict_remove(plain_dnrs, chan_name)
2060 || dict_remove(mask_dnrs, chan_name))
2062 send_message(user, chanserv, "CSMSG_DNR_REMOVED", chan_name);
2068 dnr_count_func(struct do_not_register *match, void *extra)
2070 return 0; (void)match; (void)extra;
2073 static MODCMD_FUNC(cmd_dnrsearch)
2075 struct dnr_search *discrim;
2076 dnr_search_func action;
2077 struct svccmd *subcmd;
2078 unsigned int matches;
2081 sprintf(buf, "dnrsearch %s", argv[1]);
2082 subcmd = dict_find(cmd->parent->commands, buf, NULL);
2085 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2088 if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
2090 if(!irccasecmp(argv[1], "print"))
2091 action = dnr_print_func;
2092 else if(!irccasecmp(argv[1], "remove"))
2093 action = dnr_remove_func;
2094 else if(!irccasecmp(argv[1], "count"))
2095 action = dnr_count_func;
2098 reply("CSMSG_DNR_BAD_ACTION", argv[1]);
2102 discrim = dnr_search_create(user, cmd, argc-2, argv+2);
2106 if(action == dnr_print_func)
2107 reply("CSMSG_DNR_SEARCH_RESULTS");
2108 matches = dnr_search(discrim, action, user);
2110 reply("MSG_MATCH_COUNT", matches);
2112 reply("MSG_NO_MATCHES");
2118 chanserv_get_owned_count(struct handle_info *hi)
2120 struct userData *cList;
2123 for(owned=0, cList=hi->channels; cList; cList=cList->u_next)
2124 if(cList->access == UL_OWNER)
2129 static CHANSERV_FUNC(cmd_register)
2131 struct handle_info *handle;
2132 struct chanData *cData;
2133 struct modeNode *mn;
2134 char reason[MAXLEN];
2136 unsigned int new_channel, force=0;
2137 struct do_not_register *dnr;
2141 if(channel->channel_info)
2143 reply("CSMSG_ALREADY_REGGED", channel->name);
2147 if(channel->bad_channel)
2149 reply("CSMSG_ILLEGAL_CHANNEL", channel->name);
2154 && (!(mn = GetUserMode(channel, user)) || !(mn->modes & MODE_CHANOP)))
2156 reply("CSMSG_MUST_BE_OPPED", channel->name);
2161 chan_name = channel->name;
2165 if((argc < 2) || !IsChannelName(argv[1]))
2167 reply("MSG_NOT_CHANNEL_NAME");
2171 if(opserv_bad_channel(argv[1]))
2173 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2178 chan_name = argv[1];
2181 if(argc >= (new_channel+2))
2183 if(!IsHelping(user))
2185 reply("CSMSG_PROXY_FORBIDDEN");
2189 if(!(handle = modcmd_get_handle_info(user, argv[new_channel+1])))
2191 force = (argc > (new_channel+2)) && !irccasecmp(argv[new_channel+2], "force");
2192 dnr = chanserv_is_dnr(chan_name, handle);
2196 handle = user->handle_info;
2197 dnr = chanserv_is_dnr(chan_name, handle);
2201 if(!IsHelping(user))
2202 reply("CSMSG_DNR_CHANNEL", chan_name);
2204 chanserv_show_dnrs(user, cmd, chan_name, handle->handle);
2208 if((chanserv_get_owned_count(handle) >= chanserv_conf.max_owned) && !force)
2210 reply("CSMSG_OWN_TOO_MANY", handle->handle, chanserv_conf.max_owned);
2215 channel = AddChannel(argv[1], now, NULL, NULL);
2217 cData = register_channel(channel, user->handle_info->handle);
2218 scan_user_presence(add_channel_user(cData, handle, UL_OWNER, 0, NULL), NULL);
2219 cData->modes = chanserv_conf.default_modes;
2221 cData->modes.modes_set |= MODE_REGISTERED;
2222 if (IsOffChannel(cData))
2224 mod_chanmode_announce(chanserv, channel, &cData->modes);
2228 struct mod_chanmode *change = mod_chanmode_dup(&cData->modes, 1);
2229 change->args[change->argc].mode = MODE_CHANOP;
2230 change->args[change->argc].u.member = AddChannelUser(chanserv, channel);
2232 mod_chanmode_announce(chanserv, channel, change);
2233 mod_chanmode_free(change);
2236 /* Initialize the channel's max user record. */
2237 cData->max = channel->members.used;
2238 cData->max_time = 0;
2240 if(handle != user->handle_info)
2241 reply("CSMSG_PROXY_SUCCESS", handle->handle, channel->name);
2243 reply("CSMSG_REG_SUCCESS", channel->name);
2245 sprintf(reason, "%s registered to %s by %s.", channel->name, handle->handle, user->handle_info->handle);
2246 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2251 make_confirmation_string(struct userData *uData)
2253 static char strbuf[16];
2258 for(src = uData->handle->handle; *src; )
2259 accum = accum * 31 + toupper(*src++);
2261 for(src = uData->channel->channel->name; *src; )
2262 accum = accum * 31 + toupper(*src++);
2263 sprintf(strbuf, "%08x", accum);
2267 static CHANSERV_FUNC(cmd_unregister)
2270 char reason[MAXLEN];
2271 struct chanData *cData;
2272 struct userData *uData;
2274 cData = channel->channel_info;
2277 reply("CSMSG_NOT_REGISTERED", channel->name);
2281 uData = GetChannelUser(cData, user->handle_info);
2282 if(!uData || (uData->access < UL_OWNER))
2284 reply("CSMSG_NO_ACCESS");
2288 if(IsProtected(cData))
2290 reply("CSMSG_UNREG_NODELETE", channel->name);
2294 if(!IsHelping(user))
2296 const char *confirm_string;
2297 if(IsSuspended(cData))
2299 reply("CSMSG_CHAN_SUSPENDED", channel->name, cData->suspended->reason);
2302 confirm_string = make_confirmation_string(uData);
2303 if((argc < 2) || strcmp(argv[1], confirm_string))
2305 reply("CSMSG_CONFIRM_UNREG", confirm_string);
2310 sprintf(reason, "unregistered by %s.", user->handle_info->handle);
2311 name = strdup(channel->name);
2312 unregister_channel(cData, reason);
2313 spamserv_cs_unregister(user, channel, manually, "unregistered");
2314 reply("CSMSG_UNREG_SUCCESS", name);
2320 ss_cs_join_channel(struct chanNode *channel, int spamserv_join)
2322 extern struct userNode *spamserv;
2323 struct mod_chanmode *change;
2325 if(spamserv && spamserv_join && get_chanInfo(channel->name))
2327 change = mod_chanmode_alloc(2);
2329 change->args[0].mode = MODE_CHANOP;
2330 change->args[0].u.member = AddChannelUser(chanserv, channel);
2331 change->args[1].mode = MODE_CHANOP;
2332 change->args[1].u.member = AddChannelUser(spamserv, channel);
2336 change = mod_chanmode_alloc(1);
2338 change->args[0].mode = MODE_CHANOP;
2339 change->args[0].u.member = AddChannelUser(chanserv, channel);
2342 mod_chanmode_announce(chanserv, channel, change);
2343 mod_chanmode_free(change);
2346 static CHANSERV_FUNC(cmd_move)
2348 struct mod_chanmode change;
2349 struct chanNode *target;
2350 struct modeNode *mn;
2351 struct userData *uData;
2352 char reason[MAXLEN];
2353 struct do_not_register *dnr;
2354 int chanserv_join = 0, spamserv_join;
2358 if(IsProtected(channel->channel_info))
2360 reply("CSMSG_MOVE_NODELETE", channel->name);
2364 if(!IsChannelName(argv[1]))
2366 reply("MSG_NOT_CHANNEL_NAME");
2370 if(opserv_bad_channel(argv[1]))
2372 reply("CSMSG_ILLEGAL_CHANNEL", argv[1]);
2376 if(!IsHelping(user) || (argc < 3) || irccasecmp(argv[2], "force"))
2378 for(uData = channel->channel_info->users; uData; uData = uData->next)
2380 if((uData->access == UL_OWNER) && (dnr = chanserv_is_dnr(argv[1], uData->handle)))
2382 if(!IsHelping(user))
2383 reply("CSMSG_DNR_CHANNEL_MOVE", argv[1]);
2385 chanserv_show_dnrs(user, cmd, argv[1], uData->handle->handle);
2391 mod_chanmode_init(&change);
2392 if(!(target = GetChannel(argv[1])))
2394 target = AddChannel(argv[1], now, NULL, NULL);
2395 if(!IsSuspended(channel->channel_info))
2398 else if(target->channel_info)
2400 reply("CSMSG_ALREADY_REGGED", target->name);
2403 else if((!(mn = GetUserMode(target, user)) || !(mn->modes && MODE_CHANOP))
2404 && !IsHelping(user))
2406 reply("CSMSG_MUST_BE_OPPED", target->name);
2409 else if(!IsSuspended(channel->channel_info))
2414 /* Clear MODE_REGISTERED from old channel, add it to new. */
2416 change.modes_clear = MODE_REGISTERED;
2417 mod_chanmode_announce(chanserv, channel, &change);
2418 change.modes_clear = 0;
2419 change.modes_set = MODE_REGISTERED;
2420 mod_chanmode_announce(chanserv, target, &change);
2423 /* Move the channel_info to the target channel; it
2424 shouldn't be necessary to clear timeq callbacks
2425 for the old channel. */
2426 target->channel_info = channel->channel_info;
2427 target->channel_info->channel = target;
2428 channel->channel_info = NULL;
2430 /* Check whether users are present in the new channel. */
2431 for(uData = target->channel_info->users; uData; uData = uData->next)
2432 scan_user_presence(uData, NULL);
2434 spamserv_join = spamserv_cs_move_merge(user, channel, target, 1);
2437 ss_cs_join_channel(target, spamserv_join);
2439 sprintf(reason, "%s moved to %s by %s.", channel->name, target->name, user->handle_info->handle);
2440 if(!IsSuspended(target->channel_info))
2442 char reason2[MAXLEN];
2443 sprintf(reason2, "Channel moved to %s by %s.", target->name, user->handle_info->handle);
2444 DelChannelUser(chanserv, channel, reason2, 0);
2446 UnlockChannel(channel);
2447 LockChannel(target);
2448 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
2449 reply("CSMSG_MOVE_SUCCESS", target->name);
2454 merge_users(struct chanData *source, struct chanData *target)
2456 struct userData *suData, *tuData, *next;
2462 /* Insert the source's users into the scratch area. */
2463 for(suData = source->users; suData; suData = suData->next)
2464 dict_insert(merge, suData->handle->handle, suData);
2466 /* Iterate through the target's users, looking for
2467 users common to both channels. The lower access is
2468 removed from either the scratch area or target user
2470 for(tuData = target->users; tuData; tuData = next)
2472 struct userData *choice;
2474 next = tuData->next;
2476 /* If a source user exists with the same handle as a target
2477 channel's user, resolve the conflict by removing one. */
2478 suData = dict_find(merge, tuData->handle->handle, NULL);
2482 /* Pick the data we want to keep. */
2483 /* If the access is the same, use the later seen time. */
2484 if(suData->access == tuData->access)
2485 choice = (suData->seen > tuData->seen) ? suData : tuData;
2486 else /* Otherwise, keep the higher access level. */
2487 choice = (suData->access > tuData->access) ? suData : tuData;
2488 /* Use the later seen time. */
2489 if(suData->seen < tuData->seen)
2490 suData->seen = tuData->seen;
2492 tuData->seen = suData->seen;
2494 /* Remove the user that wasn't picked. */
2495 if(choice == tuData)
2497 dict_remove(merge, suData->handle->handle);
2498 del_channel_user(suData, 0);
2501 del_channel_user(tuData, 0);
2504 /* Move the remaining users to the target channel. */
2505 for(it = dict_first(merge); it; it = iter_next(it))
2507 suData = iter_data(it);
2509 /* Insert the user into the target channel's linked list. */
2510 suData->prev = NULL;
2511 suData->next = target->users;
2512 suData->channel = target;
2515 target->users->prev = suData;
2516 target->users = suData;
2518 /* Update the user counts for the target channel; the
2519 source counts are left alone. */
2520 target->userCount++;
2522 /* Check whether the user is in the target channel. */
2523 scan_user_presence(suData, NULL);
2526 /* Possible to assert (source->users == NULL) here. */
2527 source->users = NULL;
2532 merge_bans(struct chanData *source, struct chanData *target)
2534 struct banData *sbData, *tbData, *sNext, *tNext, *tFront;
2536 /* Hold on to the original head of the target ban list
2537 to avoid comparing source bans with source bans. */
2538 tFront = target->bans;
2540 /* Perform a totally expensive O(n*m) merge, ick. */
2541 for(sbData = source->bans; sbData; sbData = sNext)
2543 /* Flag to track whether the ban's been moved
2544 to the destination yet. */
2547 /* Possible to assert (sbData->prev == NULL) here. */
2548 sNext = sbData->next;
2550 for(tbData = tFront; tbData; tbData = tNext)
2552 tNext = tbData->next;
2554 /* Perform two comparisons between each source
2555 and target ban, conflicts are resolved by
2556 keeping the broader ban and copying the later
2557 expiration and triggered time. */
2558 if(match_ircglobs(tbData->mask, sbData->mask))
2560 /* There is a broader ban in the target channel that
2561 overrides one in the source channel; remove the
2562 source ban and break. */
2563 if(sbData->expires > tbData->expires)
2564 tbData->expires = sbData->expires;
2565 if(sbData->triggered > tbData->triggered)
2566 tbData->triggered = sbData->triggered;
2567 del_channel_ban(sbData);
2570 else if(match_ircglobs(sbData->mask, tbData->mask))
2572 /* There is a broader ban in the source channel that
2573 overrides one in the target channel; remove the
2574 target ban, fall through and move the source over. */
2575 if(tbData->expires > sbData->expires)
2576 sbData->expires = tbData->expires;
2577 if(tbData->triggered > sbData->triggered)
2578 sbData->triggered = tbData->triggered;
2579 if(tbData == tFront)
2581 del_channel_ban(tbData);
2584 /* Source bans can override multiple target bans, so
2585 we allow a source to run through this loop multiple
2586 times, but we can only move it once. */
2591 /* Remove the source ban from the source ban list. */
2593 sbData->next->prev = sbData->prev;
2595 /* Modify the source ban's associated channel. */
2596 sbData->channel = target;
2598 /* Insert the ban into the target channel's linked list. */
2599 sbData->prev = NULL;
2600 sbData->next = target->bans;
2603 target->bans->prev = sbData;
2604 target->bans = sbData;
2606 /* Update the user counts for the target channel. */
2611 /* Possible to assert (source->bans == NULL) here. */
2612 source->bans = NULL;
2616 merge_data(struct chanData *source, struct chanData *target)
2618 /* Use more recent visited and owner-transfer time; use older
2619 * registered time. Bitwise or may_opchan. Use higher max.
2620 * Do not touch last_refresh, ban count or user counts.
2622 if(source->visited > target->visited)
2623 target->visited = source->visited;
2624 if(source->registered < target->registered)
2625 target->registered = source->registered;
2626 if(source->ownerTransfer > target->ownerTransfer)
2627 target->ownerTransfer = source->ownerTransfer;
2628 if(source->may_opchan)
2629 target->may_opchan = 1;
2630 if(source->max > target->max) {
2631 target->max = source->max;
2632 target->max_time = source->max_time;
2637 merge_channel(struct chanData *source, struct chanData *target)
2639 merge_users(source, target);
2640 merge_bans(source, target);
2641 merge_data(source, target);
2644 static CHANSERV_FUNC(cmd_merge)
2646 struct userData *target_user;
2647 struct chanNode *target;
2648 char reason[MAXLEN];
2652 /* Make sure the target channel exists and is registered to the user
2653 performing the command. */
2654 if(!(target = GetChannel(argv[1])))
2656 reply("MSG_INVALID_CHANNEL");
2660 if(!target->channel_info)
2662 reply("CSMSG_NOT_REGISTERED", target->name);
2666 if(IsProtected(channel->channel_info))
2668 reply("CSMSG_MERGE_NODELETE");
2672 if(IsSuspended(target->channel_info))
2674 reply("CSMSG_MERGE_SUSPENDED");
2678 if(channel == target)
2680 reply("CSMSG_MERGE_SELF");
2684 target_user = GetChannelUser(target->channel_info, user->handle_info);
2685 if(!target_user || (target_user->access < UL_OWNER))
2687 reply("CSMSG_MERGE_NOT_OWNER");
2691 /* Merge the channel structures and associated data. */
2692 merge_channel(channel->channel_info, target->channel_info);
2693 spamserv_cs_move_merge(user, channel, target, 0);
2694 sprintf(reason, "merged into %s by %s.", target->name, user->handle_info->handle);
2695 unregister_channel(channel->channel_info, reason);
2696 reply("CSMSG_MERGE_SUCCESS", target->name);
2700 static CHANSERV_FUNC(cmd_opchan)
2702 struct mod_chanmode change;
2703 if(!IsHelping(user) && !channel->channel_info->may_opchan)
2705 reply("CSMSG_ALREADY_OPCHANNED", channel->name);
2708 channel->channel_info->may_opchan = 0;
2709 mod_chanmode_init(&change);
2711 change.args[0].mode = MODE_CHANOP;
2712 change.args[0].u.member = GetUserMode(channel, chanserv);
2713 if(!change.args[0].u.member)
2715 reply("CSMSG_OUT_OF_CHANNEL", channel->name);
2718 mod_chanmode_announce(chanserv, channel, &change);
2719 reply("CSMSG_OPCHAN_DONE", channel->name);
2723 static CHANSERV_FUNC(cmd_adduser)
2725 struct userData *actee;
2726 struct userData *actor, *real_actor;
2727 struct handle_info *handle;
2728 unsigned short access_level, override = 0;
2732 if(channel->channel_info->userCount >= chanserv_conf.max_chan_users)
2734 reply("CSMSG_MAXIMUM_USERS", chanserv_conf.max_chan_users);
2738 access_level = user_level_from_name(argv[2], UL_OWNER);
2741 reply("CSMSG_INVALID_ACCESS", argv[2]);
2745 actor = GetChannelUser(channel->channel_info, user->handle_info);
2746 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2748 if(actor->access <= access_level)
2750 reply("CSMSG_NO_BUMP_ACCESS");
2754 /* Trying to add someone with equal/more access? */
2755 if (!real_actor || real_actor->access <= access_level)
2756 override = CMD_LOG_OVERRIDE;
2758 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2761 if((actee = GetTrueChannelAccess(channel->channel_info, handle)))
2763 reply("CSMSG_USER_EXISTS", handle->handle, channel->name, actee->access);
2767 actee = add_channel_user(channel->channel_info, handle, access_level, 0, NULL);
2768 scan_user_presence(actee, NULL);
2769 reply("CSMSG_ADDED_USER", handle->handle, channel->name, access_level);
2770 return 1 | override;
2773 static CHANSERV_FUNC(cmd_clvl)
2775 struct handle_info *handle;
2776 struct userData *victim;
2777 struct userData *actor, *real_actor;
2778 unsigned short new_access, override = 0;
2779 int privileged = IsHelping(user) && ((user->handle_info->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
2783 actor = GetChannelUser(channel->channel_info, user->handle_info);
2784 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2786 if(!(handle = modcmd_get_handle_info(user, argv[1])))
2789 if(handle == user->handle_info && !privileged)
2791 reply("CSMSG_NO_SELF_CLVL");
2795 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2797 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2801 if(actor->access <= victim->access && !privileged)
2803 reply("MSG_USER_OUTRANKED", handle->handle);
2807 new_access = user_level_from_name(argv[2], UL_OWNER);
2811 reply("CSMSG_INVALID_ACCESS", argv[2]);
2815 if(new_access >= actor->access && !privileged)
2817 reply("CSMSG_NO_BUMP_ACCESS");
2821 /* Trying to clvl a equal/higher user? */
2822 if(!real_actor || (real_actor->access <= victim->access && handle != user->handle_info))
2823 override = CMD_LOG_OVERRIDE;
2824 /* Trying to clvl someone to equal/higher access? */
2825 if(!real_actor || new_access >= real_actor->access)
2826 override = CMD_LOG_OVERRIDE;
2827 /* Helpers clvling themselves get caught by the "clvl someone to equal/higher access" check.
2828 * If they lower their own access it's not a big problem.
2831 victim->access = new_access;
2832 reply("CSMSG_CHANGED_ACCESS", handle->handle, new_access, channel->name);
2833 return 1 | override;
2836 static CHANSERV_FUNC(cmd_deluser)
2838 struct handle_info *handle;
2839 struct userData *victim;
2840 struct userData *actor, *real_actor;
2841 unsigned short access_level, override = 0;
2846 actor = GetChannelUser(channel->channel_info, user->handle_info);
2847 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2849 if(!(handle = modcmd_get_handle_info(user, argv[argc-1])))
2852 if(!(victim = GetTrueChannelAccess(channel->channel_info, handle)))
2854 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
2860 access_level = user_level_from_name(argv[1], UL_OWNER);
2863 reply("CSMSG_INVALID_ACCESS", argv[1]);
2866 if(access_level != victim->access)
2868 reply("CSMSG_INCORRECT_ACCESS", handle->handle, victim->access, argv[1]);
2874 access_level = victim->access;
2877 if((actor->access <= victim->access) && !IsHelping(user))
2879 reply("MSG_USER_OUTRANKED", victim->handle->handle);
2883 /* If people delete themselves it is an override, but they
2884 * could've used deleteme so we don't log it as an override
2886 if(!real_actor || (real_actor->access <= victim->access && real_actor != victim))
2887 override = CMD_LOG_OVERRIDE;
2889 chan_name = strdup(channel->name);
2890 del_channel_user(victim, 1);
2891 reply("CSMSG_DELETED_USER", handle->handle, access_level, chan_name);
2893 return 1 | override;
2897 cmd_mdel_user(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, char *mask, struct svccmd *cmd)
2899 struct userData *actor, *real_actor, *uData, *next;
2900 unsigned int override = 0;
2902 actor = GetChannelUser(channel->channel_info, user->handle_info);
2903 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
2905 if(min_access > max_access)
2907 reply("CSMSG_BAD_RANGE", min_access, max_access);
2911 if(actor->access <= max_access)
2913 reply("CSMSG_NO_ACCESS");
2917 if(!real_actor || real_actor->access <= max_access)
2918 override = CMD_LOG_OVERRIDE;
2920 for(uData = channel->channel_info->users; uData; uData = next)
2924 if((uData->access >= min_access)
2925 && (uData->access <= max_access)
2926 && match_ircglob(uData->handle->handle, mask))
2927 del_channel_user(uData, 1);
2930 reply("CSMSG_DELETED_USERS", mask, min_access, max_access, channel->name);
2931 return 1 | override;
2934 static CHANSERV_FUNC(cmd_mdelowner)
2936 return cmd_mdel_user(user, channel, UL_OWNER, UL_OWNER, argv[1], cmd);
2939 static CHANSERV_FUNC(cmd_mdelcoowner)
2941 return cmd_mdel_user(user, channel, UL_COOWNER, UL_COOWNER, argv[1], cmd);
2944 static CHANSERV_FUNC(cmd_mdelmaster)
2946 return cmd_mdel_user(user, channel, UL_MASTER, UL_MASTER, argv[1], cmd);
2949 static CHANSERV_FUNC(cmd_mdelop)
2951 return cmd_mdel_user(user, channel, UL_OP, UL_OP, argv[1], cmd);
2954 static CHANSERV_FUNC(cmd_mdelpeon)
2956 return cmd_mdel_user(user, channel, UL_PEON, UL_PEON, argv[1], cmd);
2960 cmd_trim_bans(struct userNode *user, struct chanNode *channel, unsigned long duration)
2962 struct banData *bData, *next;
2963 char interval[INTERVALLEN];
2965 unsigned long limit;
2968 limit = now - duration;
2969 for(bData = channel->channel_info->bans; bData; bData = next)
2973 if((bData->triggered && bData->triggered >= limit) || (bData->set && bData->set >= limit))
2976 del_channel_ban(bData);
2980 intervalString(interval, duration, user->handle_info);
2981 send_message(user, chanserv, "CSMSG_TRIMMED_BANS", count, channel->name, interval);
2986 cmd_trim_users(struct userNode *user, struct chanNode *channel, unsigned short min_access, unsigned short max_access, unsigned long duration, int vacation)
2988 struct userData *actor, *uData, *next;
2989 char interval[INTERVALLEN];
2991 unsigned long limit;
2993 actor = GetChannelAccess(channel->channel_info, user->handle_info);
2994 if(min_access > max_access)
2996 send_message(user, chanserv, "CSMSG_BAD_RANGE", min_access, max_access);
3000 if(!actor || actor->access <= max_access)
3002 send_message(user, chanserv, "CSMSG_NO_ACCESS");
3007 limit = now - duration;
3008 for(uData = channel->channel_info->users; uData; uData = next)
3012 if((uData->seen > limit)
3014 || (HANDLE_FLAGGED(uData->handle, FROZEN) && !vacation))
3017 if(((uData->access >= min_access) && (uData->access <= max_access))
3018 || (!max_access && (uData->access < actor->access)))
3020 del_channel_user(uData, 1);
3028 max_access = (actor->access > UL_OWNER) ? UL_OWNER : (actor->access - 1);
3030 send_message(user, chanserv, "CSMSG_TRIMMED_USERS", count, min_access, max_access, channel->name, intervalString(interval, duration, user->handle_info));
3034 static CHANSERV_FUNC(cmd_trim)
3036 unsigned long duration;
3037 unsigned short min_level, max_level;
3042 vacation = argc > 3 && !strcmp(argv[3], "vacation");
3043 duration = ParseInterval(argv[2]);
3046 reply("CSMSG_CANNOT_TRIM");
3050 if(!irccasecmp(argv[1], "bans"))
3052 cmd_trim_bans(user, channel, duration);
3055 else if(!irccasecmp(argv[1], "users"))
3057 cmd_trim_users(user, channel, 0, 0, duration, vacation);
3060 else if(parse_level_range(&min_level, &max_level, argv[1]))
3062 cmd_trim_users(user, channel, min_level, max_level, duration, vacation);
3065 else if((min_level = user_level_from_name(argv[1], UL_OWNER)))
3067 cmd_trim_users(user, channel, min_level, min_level, duration, vacation);
3072 reply("CSMSG_INVALID_TRIM", argv[1]);
3077 /* If argc is 0 in cmd_up or cmd_down, no notices will be sent
3078 to the user. cmd_all takes advantage of this. */
3079 static CHANSERV_FUNC(cmd_up)
3081 struct mod_chanmode change;
3082 struct userData *uData;
3085 mod_chanmode_init(&change);
3087 change.args[0].u.member = GetUserMode(channel, user);
3088 if(!change.args[0].u.member)
3091 reply("MSG_CHANNEL_ABSENT", channel->name);
3095 uData = GetChannelAccess(channel->channel_info, user->handle_info);
3099 reply("CSMSG_GODMODE_UP", argv[0]);
3102 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveOps])
3104 change.args[0].mode = MODE_CHANOP;
3105 errmsg = "CSMSG_ALREADY_OPPED";
3107 else if(uData->access >= channel->channel_info->lvlOpts[lvlGiveVoice])
3109 change.args[0].mode = MODE_VOICE;
3110 errmsg = "CSMSG_ALREADY_VOICED";
3115 reply("CSMSG_NO_ACCESS");
3118 change.args[0].mode &= ~change.args[0].u.member->modes;
3119 if(!change.args[0].mode)
3122 reply(errmsg, channel->name);
3125 modcmd_chanmode_announce(&change);
3129 static CHANSERV_FUNC(cmd_down)
3131 struct mod_chanmode change;
3133 mod_chanmode_init(&change);
3135 change.args[0].u.member = GetUserMode(channel, user);
3136 if(!change.args[0].u.member)
3139 reply("MSG_CHANNEL_ABSENT", channel->name);
3143 if(!change.args[0].u.member->modes)
3146 reply("CSMSG_ALREADY_DOWN", channel->name);
3150 change.args[0].mode = MODE_REMOVE | change.args[0].u.member->modes;
3151 modcmd_chanmode_announce(&change);
3155 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)
3157 struct userData *cList;
3159 for(cList = user->handle_info->channels; cList; cList = cList->u_next)
3161 if(IsSuspended(cList->channel)
3162 || IsUserSuspended(cList)
3163 || !GetUserMode(cList->channel->channel, user))
3166 mcmd(user, cList->channel->channel, 0, NULL, cmd);
3172 static CHANSERV_FUNC(cmd_upall)
3174 return cmd_all(CSFUNC_ARGS, cmd_up);
3177 static CHANSERV_FUNC(cmd_downall)
3179 return cmd_all(CSFUNC_ARGS, cmd_down);
3182 typedef int validate_func_t(struct userNode *user, struct chanNode *channel, struct userNode *victim);
3183 typedef void process_func_t(unsigned int num, struct userNode **newops, struct chanNode *channel, struct userNode *who, int announce);
3186 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)
3188 unsigned int ii, valid;
3189 struct userNode *victim;
3190 struct mod_chanmode *change;
3192 change = mod_chanmode_alloc(argc - 1);
3194 for(ii=valid=0; ++ii < argc; )
3196 if(!(victim = GetUserH(argv[ii])))
3198 change->args[valid].mode = mode;
3199 change->args[valid].u.member = GetUserMode(channel, victim);
3200 if(!change->args[valid].u.member)
3202 if(validate && !validate(user, channel, victim))
3207 change->argc = valid;
3208 if(valid < (argc-1))
3209 reply("CSMSG_PROCESS_FAILED");
3212 modcmd_chanmode_announce(change);
3213 reply(action, channel->name);
3215 mod_chanmode_free(change);
3219 static CHANSERV_FUNC(cmd_op)
3221 return modify_users(CSFUNC_ARGS, validate_op, MODE_CHANOP, "CSMSG_OPPED_USERS");
3224 static CHANSERV_FUNC(cmd_deop)
3226 return modify_users(CSFUNC_ARGS, validate_deop, MODE_REMOVE|MODE_CHANOP, "CSMSG_DEOPPED_USERS");
3229 static CHANSERV_FUNC(cmd_voice)
3231 return modify_users(CSFUNC_ARGS, NULL, MODE_VOICE, "CSMSG_VOICED_USERS");
3234 static CHANSERV_FUNC(cmd_devoice)
3236 return modify_users(CSFUNC_ARGS, NULL, MODE_REMOVE|MODE_VOICE, "CSMSG_DEVOICED_USERS");
3240 bad_channel_ban(struct chanNode *channel, struct userNode *user, const char *ban, unsigned int *victimCount, struct modeNode **victims)
3246 for(ii=0; ii<channel->members.used; ii++)
3248 struct modeNode *mn = channel->members.list[ii];
3250 if(IsService(mn->user))
3253 if(!user_matches_glob(mn->user, ban, MATCH_USENICK | MATCH_VISIBLE))
3256 if(protect_user(mn->user, user, channel->channel_info))
3260 victims[(*victimCount)++] = mn;
3266 eject_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3268 struct userNode *victim;
3269 struct modeNode **victims;
3270 unsigned int offset, n, victimCount, duration = 0;
3271 char *reason = "Bye.", *ban, *name;
3272 char interval[INTERVALLEN];
3274 offset = (action & ACTION_ADD_TIMED_BAN) ? 3 : 2;
3275 REQUIRE_PARAMS(offset);
3276 if(argc > offset && IsNetServ(user))
3278 if(*argv[offset] == '$') {
3279 struct userNode *hib;
3280 const char *accountnameb = argv[offset] + 1;
3281 if(!(hib = GetUserH(accountnameb)))
3283 reply("MSG_HANDLE_UNKNOWN", accountnameb);
3292 reason = unsplit_string(argv + offset, argc - offset, NULL);
3293 if(strlen(reason) > (TOPICLEN - (NICKLEN + 3)))
3295 /* Truncate the reason to a length of TOPICLEN, as
3296 the ircd does; however, leave room for an ellipsis
3297 and the kicker's nick. */
3298 sprintf(reason + (TOPICLEN - (NICKLEN + 6)), "...");
3302 if((victim = GetUserH(argv[1])))
3304 victims = alloca(sizeof(victims[0]));
3305 victims[0] = GetUserMode(channel, victim);
3306 /* XXX: The comparison with ACTION_KICK is just because all
3307 * other actions can work on users outside the channel, and we
3308 * want to allow those (e.g. unbans) in that case. If we add
3309 * some other ejection action for in-channel users, change
3311 victimCount = victims[0] ? 1 : 0;
3313 if(IsService(victim))
3315 reply("MSG_SERVICE_IMMUNE", victim->nick);
3319 if((action == ACTION_KICK) && !victimCount)
3321 reply("MSG_CHANNEL_USER_ABSENT", victim->nick, channel->name);
3325 if(protect_user(victim, user, channel->channel_info))
3327 reply("CSMSG_USER_PROTECTED", victim->nick);
3331 ban = generate_hostmask(victim, GENMASK_STRICT_HOST|GENMASK_ANY_IDENT);
3332 name = victim->nick;
3334 else if(!is_ircmask(argv[1]) && (*argv[1] == '*'))
3336 struct handle_info *hi;
3337 extern const char *titlehost_suffix;
3338 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3339 const char *accountname = argv[1] + 1;
3341 if(!(hi = get_handle_info(accountname)))
3343 reply("MSG_HANDLE_UNKNOWN", accountname);
3347 snprintf(banmask, sizeof(banmask), "*!*@%s.*.%s", hi->handle, titlehost_suffix);
3348 victims = alloca(sizeof(victims[0]) * channel->members.used);
3350 if(bad_channel_ban(channel, user, banmask, &victimCount, victims))
3352 reply("CSMSG_MASK_PROTECTED", banmask);
3356 if((action == ACTION_KICK) && (victimCount == 0))
3358 reply("CSMSG_NO_MATCHING_USERS", channel->name, banmask);
3362 name = ban = strdup(banmask);
3366 if(!is_ircmask(argv[1]))
3368 reply("MSG_NICK_UNKNOWN", argv[1]);
3372 victims = alloca(sizeof(victims[0]) * channel->members.used);
3374 if(bad_channel_ban(channel, user, argv[1], &victimCount, victims))
3376 reply("CSMSG_MASK_PROTECTED", argv[1]);
3380 if((victimCount > 4) && ((victimCount * 3) > channel->members.used) && !IsOper(user))
3382 reply("CSMSG_LAME_MASK", argv[1]);
3386 if((action == ACTION_KICK) && (victimCount == 0))
3388 reply("CSMSG_NO_MATCHING_USERS", channel->name, argv[1]);
3392 name = ban = strdup(argv[1]);
3395 /* Truncate the ban in place if necessary; we must ensure
3396 that 'ban' is a valid ban mask before sanitizing it. */
3397 sanitize_ircmask(ban);
3399 if(action & ACTION_ADD_BAN)
3401 struct banData *bData, *next;
3403 if(channel->channel_info->banCount >= chanserv_conf.max_chan_bans)
3405 reply("CSMSG_MAXIMUM_BANS", chanserv_conf.max_chan_bans);
3410 if(action & ACTION_ADD_TIMED_BAN)
3412 duration = ParseInterval(argv[2]);
3416 reply("CSMSG_DURATION_TOO_LOW");
3420 else if(duration > (86400 * 365 * 2))
3422 reply("CSMSG_DURATION_TOO_HIGH");
3428 for(bData = channel->channel_info->bans; bData; bData = next)
3430 if(match_ircglobs(bData->mask, ban))
3432 int exact = !irccasecmp(bData->mask, ban);
3434 /* The ban is redundant; there is already a ban
3435 with the same effect in place. */
3439 free(bData->reason);
3440 bData->reason = strdup(reason);
3441 safestrncpy(bData->owner, (user->handle_info ? user->handle_info->handle : user->nick), sizeof(bData->owner));
3443 reply("CSMSG_REASON_CHANGE", ban);
3447 if(exact && bData->expires)
3451 /* If the ban matches an existing one exactly,
3452 extend the expiration time if the provided
3453 duration is longer. */
3454 if(duration && (now + duration > bData->expires))
3456 bData->expires = now + duration;
3467 /* Delete the expiration timeq entry and
3468 requeue if necessary. */
3469 timeq_del(0, expire_ban, bData, TIMEQ_IGNORE_WHEN);
3472 timeq_add(bData->expires, expire_ban, bData);
3476 /* automated kickban */
3479 reply("CSMSG_BAN_EXTENDED", ban, intervalString(interval, duration, user->handle_info));
3481 reply("CSMSG_BAN_ADDED", name, channel->name);
3487 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3494 if(match_ircglobs(ban, bData->mask))
3496 /* The ban we are adding makes previously existing
3497 bans redundant; silently remove them. */
3498 del_channel_ban(bData);
3502 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);
3504 name = ban = strdup(bData->mask);
3508 for(n = 0; n < chanserv_conf.old_ban_names->used; ++n)
3510 extern const char *hidden_host_suffix;
3511 const char *old_name = chanserv_conf.old_ban_names->list[n];
3513 unsigned int l1, l2;
3516 l2 = strlen(old_name);
3519 if(irccasecmp(ban + l1 - l2, old_name))
3521 new_mask = malloc(MAXLEN);
3522 sprintf(new_mask, "%.*s%s", (int)(l1-l2), ban, hidden_host_suffix);
3524 name = ban = new_mask;
3529 if(action & ACTION_BAN)
3531 unsigned int exists;
3532 struct mod_chanmode *change;
3534 if(channel->banlist.used >= MAXBANS)
3537 reply("CSMSG_BANLIST_FULL", channel->name);
3542 exists = ChannelBanExists(channel, ban);
3543 change = mod_chanmode_alloc(victimCount + 1);
3544 for(n = 0; n < victimCount; ++n)
3546 change->args[n].mode = MODE_REMOVE|MODE_CHANOP|MODE_VOICE;
3547 change->args[n].u.member = victims[n];
3551 change->args[n].mode = MODE_BAN;
3552 change->args[n++].u.hostmask = ban;
3556 modcmd_chanmode_announce(change);
3558 mod_chanmode_announce(chanserv, channel, change);
3559 mod_chanmode_free(change);
3561 if(exists && (action == ACTION_BAN))
3564 reply("CSMSG_REDUNDANT_BAN", name, channel->name);
3570 if(action & ACTION_KICK)
3572 char kick_reason[MAXLEN];
3573 sprintf(kick_reason, "(%s) %s", user->nick, reason);
3575 for(n = 0; n < victimCount; n++)
3576 KickChannelUser(victims[n]->user, channel, chanserv, kick_reason);
3581 /* No response, since it was automated. */
3583 else if(action & ACTION_ADD_BAN)
3586 reply("CSMSG_TIMED_BAN_ADDED", name, channel->name, intervalString(interval, duration, user->handle_info));
3588 reply("CSMSG_BAN_ADDED", name, channel->name);
3590 else if((action & (ACTION_BAN | ACTION_KICK)) == (ACTION_BAN | ACTION_KICK))
3591 reply("CSMSG_KICK_BAN_DONE", name, channel->name);
3592 else if(action & ACTION_BAN)
3593 reply("CSMSG_BAN_DONE", name, channel->name);
3594 else if(action & ACTION_KICK && victimCount)
3595 reply("CSMSG_KICK_DONE", name, channel->name);
3601 static CHANSERV_FUNC(cmd_kickban)
3603 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN);
3606 static CHANSERV_FUNC(cmd_kick)
3608 return eject_user(CSFUNC_ARGS, ACTION_KICK);
3611 static CHANSERV_FUNC(cmd_ban)
3613 return eject_user(CSFUNC_ARGS, ACTION_BAN);
3616 static CHANSERV_FUNC(cmd_addban)
3618 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN);
3621 static CHANSERV_FUNC(cmd_addtimedban)
3623 return eject_user(CSFUNC_ARGS, ACTION_KICK | ACTION_BAN | ACTION_ADD_BAN | ACTION_ADD_TIMED_BAN);
3626 struct mod_chanmode *
3627 find_matching_bans(struct banList *bans, struct userNode *actee, const char *mask)
3629 struct mod_chanmode *change;
3630 unsigned char *match;
3631 unsigned int ii, count;
3633 match = alloca(bans->used);
3636 for(ii = count = 0; ii < bans->used; ++ii)
3638 match[ii] = user_matches_glob(actee, bans->list[ii]->ban,
3639 MATCH_USENICK | MATCH_VISIBLE);
3646 for(ii = count = 0; ii < bans->used; ++ii)
3648 match[ii] = match_ircglobs(mask, bans->list[ii]->ban);
3655 change = mod_chanmode_alloc(count);
3656 for(ii = count = 0; ii < bans->used; ++ii)
3660 change->args[count].mode = MODE_REMOVE | MODE_BAN;
3661 change->args[count++].u.hostmask = strdup(bans->list[ii]->ban);
3663 assert(count == change->argc);
3668 unban_user(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, int action)
3670 struct userNode *actee;
3676 /* may want to allow a comma delimited list of users... */
3677 if(!(actee = GetUserH(argv[1])))
3679 if(!is_ircmask(argv[1]) && *argv[1] == '*')
3681 char banmask[NICKLEN + USERLEN + HOSTLEN + 3];
3682 const char *accountname = argv[1] + 1;
3684 snprintf(banmask, sizeof(banmask), "*!*@%s.*", accountname);
3685 mask = strdup(banmask);
3687 else if(!is_ircmask(argv[1]))
3689 reply("MSG_NICK_UNKNOWN", argv[1]);
3694 mask = strdup(argv[1]);
3698 /* We don't sanitize the mask here because ircu
3700 if(action & ACTION_UNBAN)
3702 struct mod_chanmode *change;
3703 change = find_matching_bans(&channel->banlist, actee, mask);
3708 modcmd_chanmode_announce(change);
3709 for(ii = 0; ii < change->argc; ++ii)
3710 free((char*)change->args[ii].u.hostmask);
3711 mod_chanmode_free(change);
3716 if(action & ACTION_DEL_BAN)
3718 struct banData *ban, *next;
3720 ban = channel->channel_info->bans;
3724 for( ; ban && !user_matches_glob(actee, ban->mask,
3725 MATCH_USENICK | MATCH_VISIBLE);
3728 for( ; ban && !match_ircglobs(mask, ban->mask);
3733 del_channel_ban(ban);
3740 reply("CSMSG_BAN_NOT_FOUND", actee ? actee->nick : mask);
3742 reply("CSMSG_BAN_REMOVED", actee ? actee->nick : mask);
3748 static CHANSERV_FUNC(cmd_unban)
3750 return unban_user(CSFUNC_ARGS, ACTION_UNBAN);
3753 static CHANSERV_FUNC(cmd_delban)
3755 /* it doesn't necessarily have to remove the channel ban - may want
3756 to make that an option. */
3757 return unban_user(CSFUNC_ARGS, ACTION_UNBAN | ACTION_DEL_BAN);
3760 static CHANSERV_FUNC(cmd_unbanme)
3762 struct userData *uData = GetChannelUser(channel->channel_info, user->handle_info);
3763 long flags = ACTION_UNBAN;
3765 /* remove permanent bans if the user has the proper access. */
3766 if(uData->access >= UL_MASTER)
3767 flags |= ACTION_DEL_BAN;
3769 argv[1] = user->nick;
3770 return unban_user(user, channel, 2, argv, cmd, flags);
3773 static CHANSERV_FUNC(cmd_unbanall)
3775 struct mod_chanmode *change;
3778 if(!channel->banlist.used)
3780 reply("CSMSG_NO_BANS", channel->name);
3784 change = mod_chanmode_alloc(channel->banlist.used);
3785 for(ii=0; ii<channel->banlist.used; ii++)
3787 change->args[ii].mode = MODE_REMOVE | MODE_BAN;
3788 change->args[ii].u.hostmask = strdup(channel->banlist.list[ii]->ban);
3790 modcmd_chanmode_announce(change);
3791 for(ii = 0; ii < change->argc; ++ii)
3792 free((char*)change->args[ii].u.hostmask);
3793 mod_chanmode_free(change);
3794 reply("CSMSG_BANS_REMOVED", channel->name);
3798 static CHANSERV_FUNC(cmd_open)
3800 struct mod_chanmode *change;
3803 change = find_matching_bans(&channel->banlist, user, NULL);
3805 change = mod_chanmode_alloc(0);
3806 change->modes_clear |= MODE_INVITEONLY | MODE_LIMIT | MODE_KEY;
3807 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
3808 && channel->channel_info->modes.modes_set)
3809 change->modes_clear &= ~channel->channel_info->modes.modes_set;
3810 modcmd_chanmode_announce(change);
3811 reply("CSMSG_CHANNEL_OPENED", channel->name);
3812 for(ii = 0; ii < change->argc; ++ii)
3813 free((char*)change->args[ii].u.hostmask);
3814 mod_chanmode_free(change);
3818 static CHANSERV_FUNC(cmd_myaccess)
3820 static struct string_buffer sbuf;
3821 struct handle_info *target_handle;
3822 struct userData *uData;
3827 target_handle = user->handle_info;
3828 else if(!IsStaff(user))
3830 reply("CSMSG_MYACCESS_SELF_ONLY", argv[0]);
3833 else if(!(target_handle = modcmd_get_handle_info(user, argv[1])))
3836 if(!oper_outranks(user, target_handle))
3839 if(!target_handle->channels)
3841 reply("CSMSG_SQUAT_ACCESS", target_handle->handle);
3845 reply("CSMSG_INFOLINE_LIST", target_handle->handle);
3846 for(uData = target_handle->channels; uData; uData = uData->u_next)
3848 struct chanData *cData = uData->channel;
3851 if(uData->access > UL_OWNER)
3853 if(uData->access == UL_OWNER)
3856 if(IsProtected(cData)
3857 && (target_handle != user->handle_info)
3858 && !GetTrueChannelAccess(cData, user->handle_info)
3859 && !IsNetworkHelper(user))
3862 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3863 if(uData->flags != USER_AUTO_OP)
3864 string_buffer_append(&sbuf, ',');
3865 if(IsUserSuspended(uData))
3866 string_buffer_append(&sbuf, 's');
3867 if(IsUserAutoOp(uData))
3869 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3870 string_buffer_append(&sbuf, 'o');
3871 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3872 string_buffer_append(&sbuf, 'v');
3874 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3875 string_buffer_append(&sbuf, 'i');
3877 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3879 string_buffer_append_string(&sbuf, ")]");
3880 string_buffer_append(&sbuf, '\0');
3881 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3885 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3887 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3893 static CHANSERV_FUNC(cmd_access)
3895 struct userNode *target;
3896 struct handle_info *target_handle;
3897 struct userData *uData;
3899 char prefix[MAXLEN];
3904 target_handle = target->handle_info;
3906 else if((target = GetUserH(argv[1])))
3908 target_handle = target->handle_info;
3910 else if(argv[1][0] == '*')
3912 if(!(target_handle = get_handle_info(argv[1]+1)))
3914 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3920 reply("MSG_NICK_UNKNOWN", argv[1]);
3924 assert(target || target_handle);
3926 if(target == chanserv)
3928 reply("CSMSG_IS_CHANSERV");
3936 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3941 reply("MSG_USER_AUTHENTICATE", target->nick);
3944 reply("MSG_AUTHENTICATE");
3950 const char *epithet = NULL, *type = NULL;
3953 epithet = chanserv_conf.irc_operator_epithet;
3954 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3956 else if(IsNetworkHelper(target))
3958 epithet = chanserv_conf.network_helper_epithet;
3959 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3961 else if(IsSupportHelper(target))
3963 epithet = chanserv_conf.support_helper_epithet;
3964 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3968 if(target_handle->epithet)
3969 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3971 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3973 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3977 sprintf(prefix, "%s", target_handle->handle);
3980 if(!channel->channel_info)
3982 reply("CSMSG_NOT_REGISTERED", channel->name);
3986 helping = HANDLE_FLAGGED(target_handle, HELPING)
3987 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3988 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3990 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3991 /* To prevent possible information leaks, only show infolines
3992 * if the requestor is in the channel or it's their own
3994 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3996 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3998 /* Likewise, only say it's suspended if the user has active
3999 * access in that channel or it's their own entry. */
4000 if(IsUserSuspended(uData)
4001 && (GetChannelUser(channel->channel_info, user->handle_info)
4002 || (user->handle_info == uData->handle)))
4004 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4009 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4016 zoot_list(struct listData *list)
4018 struct userData *uData;
4019 unsigned int start, curr, highest, lowest;
4020 struct helpfile_table tmp_table;
4021 const char **temp, *msg;
4023 if(list->table.length == 1)
4026 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4028 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4029 msg = user_find_message(list->user, "MSG_NONE");
4030 send_message_type(4, list->user, list->bot, " %s", msg);
4032 tmp_table.width = list->table.width;
4033 tmp_table.flags = list->table.flags;
4034 list->table.contents[0][0] = " ";
4035 highest = list->highest;
4036 if(list->lowest != 0)
4037 lowest = list->lowest;
4038 else if(highest < 100)
4041 lowest = highest - 100;
4042 for(start = curr = 1; curr < list->table.length; )
4044 uData = list->users[curr-1];
4045 list->table.contents[curr++][0] = " ";
4046 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4049 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4051 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4052 temp = list->table.contents[--start];
4053 list->table.contents[start] = list->table.contents[0];
4054 tmp_table.contents = list->table.contents + start;
4055 tmp_table.length = curr - start;
4056 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4057 list->table.contents[start] = temp;
4059 highest = lowest - 1;
4060 lowest = (highest < 100) ? 0 : (highest - 99);
4066 def_list(struct listData *list)
4070 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4072 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4073 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4074 if(list->table.length == 1)
4076 msg = user_find_message(list->user, "MSG_NONE");
4077 send_message_type(4, list->user, list->bot, " %s", msg);
4082 userData_access_comp(const void *arg_a, const void *arg_b)
4084 const struct userData *a = *(struct userData**)arg_a;
4085 const struct userData *b = *(struct userData**)arg_b;
4087 if(a->access != b->access)
4088 res = b->access - a->access;
4090 res = irccasecmp(a->handle->handle, b->handle->handle);
4095 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4097 void (*send_list)(struct listData *);
4098 struct userData *uData;
4099 struct listData lData;
4100 unsigned int matches;
4104 lData.bot = cmd->parent->bot;
4105 lData.channel = channel;
4106 lData.lowest = lowest;
4107 lData.highest = highest;
4108 lData.search = (argc > 1) ? argv[1] : NULL;
4109 send_list = def_list;
4110 (void)zoot_list; /* since it doesn't show user levels */
4112 if(user->handle_info)
4114 switch(user->handle_info->userlist_style)
4116 case HI_STYLE_DEF: send_list = def_list; break;
4117 case HI_STYLE_ZOOT: send_list = def_list; break;
4121 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4123 for(uData = channel->channel_info->users; uData; uData = uData->next)
4125 if((uData->access < lowest)
4126 || (uData->access > highest)
4127 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4129 lData.users[matches++] = uData;
4131 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4133 lData.table.length = matches+1;
4134 lData.table.width = 4;
4135 lData.table.flags = TABLE_NO_FREE;
4136 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4137 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4138 lData.table.contents[0] = ary;
4141 ary[2] = "Last Seen";
4143 for(matches = 1; matches < lData.table.length; ++matches)
4145 char seen[INTERVALLEN];
4147 uData = lData.users[matches-1];
4148 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4149 lData.table.contents[matches] = ary;
4150 ary[0] = strtab(uData->access);
4151 ary[1] = uData->handle->handle;
4154 else if(!uData->seen)
4157 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4158 ary[2] = strdup(ary[2]);
4159 if(IsUserSuspended(uData))
4160 ary[3] = "Suspended";
4161 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4162 ary[3] = "Vacation";
4163 else if(HANDLE_FLAGGED(uData->handle, BOT))
4169 for(matches = 1; matches < lData.table.length; ++matches)
4171 free((char*)lData.table.contents[matches][2]);
4172 free(lData.table.contents[matches]);
4174 free(lData.table.contents[0]);
4175 free(lData.table.contents);
4176 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4180 static CHANSERV_FUNC(cmd_users)
4182 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4185 static CHANSERV_FUNC(cmd_wlist)
4187 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4190 static CHANSERV_FUNC(cmd_clist)
4192 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4195 static CHANSERV_FUNC(cmd_mlist)
4197 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4200 static CHANSERV_FUNC(cmd_olist)
4202 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4205 static CHANSERV_FUNC(cmd_plist)
4207 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4210 static CHANSERV_FUNC(cmd_bans)
4212 struct userNode *search_u = NULL;
4213 struct helpfile_table tbl;
4214 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4215 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4216 const char *msg_never, *triggered, *expires;
4217 struct banData *ban, **bans;
4221 else if(strchr(search = argv[1], '!'))
4224 search_wilds = search[strcspn(search, "?*")];
4226 else if(!(search_u = GetUserH(search)))
4227 reply("MSG_NICK_UNKNOWN", search);
4229 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4231 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4235 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4240 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4243 bans[matches++] = ban;
4248 tbl.length = matches + 1;
4249 tbl.width = 4 + timed;
4251 tbl.flags = TABLE_NO_FREE;
4252 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4253 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4254 tbl.contents[0][0] = "Mask";
4255 tbl.contents[0][1] = "Set By";
4256 tbl.contents[0][2] = "Triggered";
4259 tbl.contents[0][3] = "Expires";
4260 tbl.contents[0][4] = "Reason";
4263 tbl.contents[0][3] = "Reason";
4266 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4268 free(tbl.contents[0]);
4273 msg_never = user_find_message(user, "MSG_NEVER");
4274 for(ii = 0; ii < matches; )
4280 else if(ban->expires)
4281 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4283 expires = msg_never;
4286 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4288 triggered = msg_never;
4290 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4291 tbl.contents[ii][0] = ban->mask;
4292 tbl.contents[ii][1] = ban->owner;
4293 tbl.contents[ii][2] = strdup(triggered);
4296 tbl.contents[ii][3] = strdup(expires);
4297 tbl.contents[ii][4] = ban->reason;
4300 tbl.contents[ii][3] = ban->reason;
4302 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4303 reply("MSG_MATCH_COUNT", matches);
4304 for(ii = 1; ii < tbl.length; ++ii)
4306 free((char*)tbl.contents[ii][2]);
4308 free((char*)tbl.contents[ii][3]);
4309 free(tbl.contents[ii]);
4311 free(tbl.contents[0]);
4317 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4319 struct chanData *cData = channel->channel_info;
4320 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4322 if(cData->topic_mask)
4323 return !match_ircglob(new_topic, cData->topic_mask);
4324 else if(cData->topic)
4325 return irccasecmp(new_topic, cData->topic);
4330 static CHANSERV_FUNC(cmd_topic)
4332 struct chanData *cData;
4335 cData = channel->channel_info;
4340 SetChannelTopic(channel, chanserv, cData->topic, 1);
4341 reply("CSMSG_TOPIC_SET", cData->topic);
4345 reply("CSMSG_NO_TOPIC", channel->name);
4349 topic = unsplit_string(argv + 1, argc - 1, NULL);
4350 /* If they say "!topic *", use an empty topic. */
4351 if((topic[0] == '*') && (topic[1] == 0))
4353 if(bad_topic(channel, user, topic))
4355 char *topic_mask = cData->topic_mask;
4358 char new_topic[TOPICLEN+1], tchar;
4359 int pos=0, starpos=-1, dpos=0, len;
4361 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4368 len = strlen(topic);
4369 if((dpos + len) > TOPICLEN)
4370 len = TOPICLEN + 1 - dpos;
4371 memcpy(new_topic+dpos, topic, len);
4375 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4376 default: new_topic[dpos++] = tchar; break;
4379 if((dpos > TOPICLEN) || tchar)
4382 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4383 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4386 new_topic[dpos] = 0;
4387 SetChannelTopic(channel, chanserv, new_topic, 1);
4389 reply("CSMSG_TOPIC_LOCKED", channel->name);
4394 SetChannelTopic(channel, chanserv, topic, 1);
4396 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4398 /* Grab the topic and save it as the default topic. */
4400 cData->topic = strdup(channel->topic);
4406 static CHANSERV_FUNC(cmd_mode)
4408 struct userData *uData;
4409 struct mod_chanmode *change;
4415 change = &channel->channel_info->modes;
4416 if(change->modes_set || change->modes_clear) {
4417 modcmd_chanmode_announce(change);
4418 reply("CSMSG_DEFAULTED_MODES", channel->name);
4420 reply("CSMSG_NO_MODES", channel->name);
4424 uData = GetChannelUser(channel->channel_info, user->handle_info);
4426 base_oplevel = MAXOPLEVEL;
4427 else if (uData->access >= UL_OWNER)
4430 base_oplevel = 1 + UL_OWNER - uData->access;
4431 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4434 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4438 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4439 && mode_lock_violated(&channel->channel_info->modes, change))
4442 mod_chanmode_format(&channel->channel_info->modes, modes);
4443 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4447 modcmd_chanmode_announce(change);
4448 mod_chanmode_format(change, fmt);
4449 mod_chanmode_free(change);
4450 reply("CSMSG_MODES_SET", fmt);
4455 chanserv_del_invite_mark(void *data)
4457 struct ChanUser *chanuser = data;
4458 struct chanNode *channel = chanuser->chan;
4460 if(!channel) return;
4461 for(i = 0; i < channel->invited.used; i++)
4463 if(channel->invited.list[i] == chanuser->user) {
4464 userList_remove(&channel->invited, chanuser->user);
4470 static CHANSERV_FUNC(cmd_invite)
4472 struct userData *uData;
4473 struct userNode *invite;
4474 struct ChanUser *chanuser;
4477 uData = GetChannelUser(channel->channel_info, user->handle_info);
4481 if(!(invite = GetUserH(argv[1])))
4483 reply("MSG_NICK_UNKNOWN", argv[1]);
4490 if(GetUserMode(channel, invite))
4492 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4496 for(i = 0; i < channel->invited.used; i++)
4498 if(channel->invited.list[i] == invite) {
4499 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4508 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4509 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4512 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4514 irc_invite(chanserv, invite, channel);
4516 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4518 userList_append(&channel->invited, invite);
4519 chanuser = calloc(1, sizeof(*chanuser));
4520 chanuser->user=invite;
4521 chanuser->chan=channel;
4522 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4527 static CHANSERV_FUNC(cmd_inviteme)
4529 if(GetUserMode(channel, user))
4531 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4534 if(channel->channel_info
4535 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4537 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4540 irc_invite(cmd->parent->bot, user, channel);
4544 static CHANSERV_FUNC(cmd_invitemeall)
4546 struct handle_info *target = user->handle_info;
4547 struct userData *uData;
4549 if(!target->channels)
4551 reply("CSMSG_SQUAT_ACCESS", target->handle);
4555 for(uData = target->channels; uData; uData = uData->u_next)
4557 struct chanData *cData = uData->channel;
4558 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4560 irc_invite(cmd->parent->bot, user, cData->channel);
4567 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4570 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4572 /* We display things based on two dimensions:
4573 * - Issue time: present or absent
4574 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4575 * (in order of precedence, so something both expired and revoked
4576 * only counts as revoked)
4578 combo = (suspended->issued ? 4 : 0)
4579 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4581 case 0: /* no issue time, indefinite expiration */
4582 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4584 case 1: /* no issue time, expires in future */
4585 intervalString(buf1, suspended->expires-now, user->handle_info);
4586 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4588 case 2: /* no issue time, expired */
4589 intervalString(buf1, now-suspended->expires, user->handle_info);
4590 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4592 case 3: /* no issue time, revoked */
4593 intervalString(buf1, now-suspended->revoked, user->handle_info);
4594 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4596 case 4: /* issue time set, indefinite expiration */
4597 intervalString(buf1, now-suspended->issued, user->handle_info);
4598 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4600 case 5: /* issue time set, expires in future */
4601 intervalString(buf1, now-suspended->issued, user->handle_info);
4602 intervalString(buf2, suspended->expires-now, user->handle_info);
4603 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4605 case 6: /* issue time set, expired */
4606 intervalString(buf1, now-suspended->issued, user->handle_info);
4607 intervalString(buf2, now-suspended->expires, user->handle_info);
4608 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4610 case 7: /* issue time set, revoked */
4611 intervalString(buf1, now-suspended->issued, user->handle_info);
4612 intervalString(buf2, now-suspended->revoked, user->handle_info);
4613 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4616 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4621 static CHANSERV_FUNC(cmd_info)
4623 char modes[MAXLEN], buffer[INTERVALLEN];
4624 struct userData *uData, *owner;
4625 struct chanData *cData;
4626 struct do_not_register *dnr;
4631 cData = channel->channel_info;
4632 reply("CSMSG_CHANNEL_INFO", channel->name);
4634 uData = GetChannelUser(cData, user->handle_info);
4635 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4637 mod_chanmode_format(&cData->modes, modes);
4638 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4639 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4642 for(it = dict_first(cData->notes); it; it = iter_next(it))
4646 note = iter_data(it);
4647 if(!note_type_visible_to_user(cData, note->type, user))
4650 padding = PADLEN - 1 - strlen(iter_key(it));
4651 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4654 if(cData->max_time) {
4655 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4657 reply("CSMSG_CHANNEL_MAX", cData->max);
4659 for(owner = cData->users; owner; owner = owner->next)
4660 if(owner->access == UL_OWNER)
4661 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4662 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4663 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4664 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4666 privileged = IsStaff(user);
4668 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4669 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4670 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4672 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4673 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4675 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4677 struct suspended *suspended;
4678 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4679 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4680 show_suspension_info(cmd, user, suspended);
4682 else if(IsSuspended(cData))
4684 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4685 show_suspension_info(cmd, user, cData->suspended);
4690 static CHANSERV_FUNC(cmd_netinfo)
4692 extern unsigned long boot_time;
4693 extern unsigned long burst_length;
4694 char interval[INTERVALLEN];
4696 reply("CSMSG_NETWORK_INFO");
4697 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4698 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4699 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4700 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4701 reply("CSMSG_NETWORK_BANS", banCount);
4702 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4703 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4704 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4709 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4711 struct helpfile_table table;
4713 struct userNode *user;
4718 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4719 table.contents = alloca(list->used*sizeof(*table.contents));
4720 for(nn=0; nn<list->used; nn++)
4722 user = list->list[nn];
4723 if(user->modes & skip_flags)
4729 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4732 nick = alloca(strlen(user->nick)+3);
4733 sprintf(nick, "(%s)", user->nick);
4737 table.contents[table.length][0] = nick;
4740 table_send(chanserv, to->nick, 0, NULL, table);
4743 static CHANSERV_FUNC(cmd_ircops)
4745 reply("CSMSG_STAFF_OPERS");
4746 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4750 static CHANSERV_FUNC(cmd_helpers)
4752 reply("CSMSG_STAFF_HELPERS");
4753 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4757 static CHANSERV_FUNC(cmd_staff)
4759 reply("CSMSG_NETWORK_STAFF");
4760 cmd_ircops(CSFUNC_ARGS);
4761 cmd_helpers(CSFUNC_ARGS);
4765 static CHANSERV_FUNC(cmd_peek)
4767 struct modeNode *mn;
4768 char modes[MODELEN];
4770 struct helpfile_table table;
4771 int opcount = 0, voicecount = 0, srvcount = 0;
4773 irc_make_chanmode(channel, modes);
4775 reply("CSMSG_PEEK_INFO", channel->name);
4776 reply("CSMSG_PEEK_TOPIC", channel->topic);
4777 reply("CSMSG_PEEK_MODES", modes);
4781 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4782 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4783 for(n = 0; n < channel->members.used; n++)
4785 mn = channel->members.list[n];
4786 if(IsLocal(mn->user))
4788 else if(mn->modes & MODE_CHANOP)
4790 else if(mn->modes & MODE_VOICE)
4793 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4795 table.contents[table.length] = alloca(sizeof(**table.contents));
4796 table.contents[table.length][0] = mn->user->nick;
4800 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4801 (channel->members.used - opcount - voicecount - srvcount));
4805 reply("CSMSG_PEEK_OPS");
4806 table_send(chanserv, user->nick, 0, NULL, table);
4809 reply("CSMSG_PEEK_NO_OPS");
4813 static MODCMD_FUNC(cmd_wipeinfo)
4815 struct handle_info *victim;
4816 struct userData *ud, *actor, *real_actor;
4817 unsigned int override = 0;
4820 actor = GetChannelUser(channel->channel_info, user->handle_info);
4821 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4822 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4824 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4826 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4829 if((ud->access >= actor->access) && (ud != actor))
4831 reply("MSG_USER_OUTRANKED", victim->handle);
4834 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4835 override = CMD_LOG_OVERRIDE;
4839 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4840 return 1 | override;
4843 static CHANSERV_FUNC(cmd_resync)
4845 struct mod_chanmode *changes;
4846 struct chanData *cData = channel->channel_info;
4847 unsigned int ii, used;
4849 changes = mod_chanmode_alloc(channel->members.used * 2);
4850 for(ii = used = 0; ii < channel->members.used; ++ii)
4852 struct modeNode *mn = channel->members.list[ii];
4853 struct userData *uData;
4855 if(IsService(mn->user))
4858 uData = GetChannelAccess(cData, mn->user->handle_info);
4859 if(!cData->lvlOpts[lvlGiveOps]
4860 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4862 if(!(mn->modes & MODE_CHANOP))
4864 if(!uData || IsUserAutoOp(uData))
4866 changes->args[used].mode = MODE_CHANOP;
4867 changes->args[used++].u.member = mn;
4868 if(!(mn->modes & MODE_VOICE))
4870 changes->args[used].mode = MODE_VOICE;
4871 changes->args[used++].u.member = mn;
4876 else if(!cData->lvlOpts[lvlGiveVoice]
4877 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4879 if(mn->modes & MODE_CHANOP)
4881 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4882 changes->args[used++].u.member = mn;
4884 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4886 changes->args[used].mode = MODE_VOICE;
4887 changes->args[used++].u.member = mn;
4894 changes->args[used].mode = MODE_REMOVE | mn->modes;
4895 changes->args[used++].u.member = mn;
4899 changes->argc = used;
4900 modcmd_chanmode_announce(changes);
4901 mod_chanmode_free(changes);
4902 reply("CSMSG_RESYNCED_USERS", channel->name);
4906 static CHANSERV_FUNC(cmd_seen)
4908 struct userData *uData;
4909 struct handle_info *handle;
4910 char seen[INTERVALLEN];
4914 if(!irccasecmp(argv[1], chanserv->nick))
4916 reply("CSMSG_IS_CHANSERV");
4920 if(!(handle = get_handle_info(argv[1])))
4922 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4926 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4928 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4933 reply("CSMSG_USER_PRESENT", handle->handle);
4934 else if(uData->seen)
4935 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4937 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4939 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4940 reply("CSMSG_USER_VACATION", handle->handle);
4945 static MODCMD_FUNC(cmd_names)
4947 struct userNode *targ;
4948 struct userData *targData;
4949 unsigned int ii, pos;
4952 for(ii=pos=0; ii<channel->members.used; ++ii)
4954 targ = channel->members.list[ii]->user;
4955 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4958 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4961 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4965 if(IsUserSuspended(targData))
4967 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4970 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4971 reply("CSMSG_END_NAMES", channel->name);
4976 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4978 switch(ntype->visible_type)
4980 case NOTE_VIS_ALL: return 1;
4981 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4982 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4987 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4989 struct userData *uData;
4991 switch(ntype->set_access_type)
4993 case NOTE_SET_CHANNEL_ACCESS:
4994 if(!user->handle_info)
4996 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4998 return uData->access >= ntype->set_access.min_ulevel;
4999 case NOTE_SET_CHANNEL_SETTER:
5000 return check_user_level(channel, user, lvlSetters, 1, 0);
5001 case NOTE_SET_PRIVILEGED: default:
5002 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5006 static CHANSERV_FUNC(cmd_note)
5008 struct chanData *cData;
5010 struct note_type *ntype;
5012 cData = channel->channel_info;
5015 reply("CSMSG_NOT_REGISTERED", channel->name);
5019 /* If no arguments, show all visible notes for the channel. */
5025 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5027 note = iter_data(it);
5028 if(!note_type_visible_to_user(cData, note->type, user))
5031 reply("CSMSG_NOTELIST_HEADER", channel->name);
5032 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5035 reply("CSMSG_NOTELIST_END", channel->name);
5037 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5039 /* If one argument, show the named note. */
5042 if((note = dict_find(cData->notes, argv[1], NULL))
5043 && note_type_visible_to_user(cData, note->type, user))
5045 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5047 else if((ntype = dict_find(note_types, argv[1], NULL))
5048 && note_type_visible_to_user(NULL, ntype, user))
5050 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5055 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5059 /* Assume they're trying to set a note. */
5063 ntype = dict_find(note_types, argv[1], NULL);
5066 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5069 else if(note_type_settable_by_user(channel, ntype, user))
5071 note_text = unsplit_string(argv+2, argc-2, NULL);
5072 if((note = dict_find(cData->notes, argv[1], NULL)))
5073 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5074 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5075 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5077 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5079 /* The note is viewable to staff only, so return 0
5080 to keep the invocation from getting logged (or
5081 regular users can see it in !events). */
5087 reply("CSMSG_NO_ACCESS");
5094 static CHANSERV_FUNC(cmd_delnote)
5099 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5100 || !note_type_settable_by_user(channel, note->type, user))
5102 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5105 dict_remove(channel->channel_info->notes, note->type->name);
5106 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5110 static CHANSERV_FUNC(cmd_events)
5112 struct logSearch discrim;
5113 struct logReport report;
5114 unsigned int matches, limit;
5116 limit = (argc > 1) ? atoi(argv[1]) : 10;
5117 if(limit < 1 || limit > 200)
5120 memset(&discrim, 0, sizeof(discrim));
5121 discrim.masks.bot = chanserv;
5122 discrim.masks.channel_name = channel->name;
5124 discrim.masks.command = argv[2];
5125 discrim.limit = limit;
5126 discrim.max_time = INT_MAX;
5127 discrim.severities = 1 << LOG_COMMAND;
5128 report.reporter = chanserv;
5130 reply("CSMSG_EVENT_SEARCH_RESULTS");
5131 matches = log_entry_search(&discrim, log_report_entry, &report);
5133 reply("MSG_MATCH_COUNT", matches);
5135 reply("MSG_NO_MATCHES");
5139 static CHANSERV_FUNC(cmd_say)
5145 msg = unsplit_string(argv + 1, argc - 1, NULL);
5146 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5148 else if(*argv[1] == '*' && argv[1][1] != '\0')
5150 struct handle_info *hi;
5151 struct userNode *authed;
5154 msg = unsplit_string(argv + 2, argc - 2, NULL);
5156 if (!(hi = get_handle_info(argv[1] + 1)))
5158 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5162 for (authed = hi->users; authed; authed = authed->next_authed)
5163 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5165 else if(GetUserH(argv[1]))
5168 msg = unsplit_string(argv + 2, argc - 2, NULL);
5169 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5173 reply("MSG_NOT_TARGET_NAME");
5179 static CHANSERV_FUNC(cmd_emote)
5185 /* CTCP is so annoying. */
5186 msg = unsplit_string(argv + 1, argc - 1, NULL);
5187 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5189 else if(*argv[1] == '*' && argv[1][1] != '\0')
5191 struct handle_info *hi;
5192 struct userNode *authed;
5195 msg = unsplit_string(argv + 2, argc - 2, NULL);
5197 if (!(hi = get_handle_info(argv[1] + 1)))
5199 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5203 for (authed = hi->users; authed; authed = authed->next_authed)
5204 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5206 else if(GetUserH(argv[1]))
5208 msg = unsplit_string(argv + 2, argc - 2, NULL);
5209 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5213 reply("MSG_NOT_TARGET_NAME");
5219 struct channelList *
5220 chanserv_support_channels(void)
5222 return &chanserv_conf.support_channels;
5225 static CHANSERV_FUNC(cmd_expire)
5227 int channel_count = registered_channels;
5228 expire_channels(NULL);
5229 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5234 chanserv_expire_suspension(void *data)
5236 struct suspended *suspended = data;
5237 struct chanNode *channel;
5240 /* Update the channel registration data structure. */
5241 if(!suspended->expires || (now < suspended->expires))
5242 suspended->revoked = now;
5243 channel = suspended->cData->channel;
5244 suspended->cData->channel = channel;
5245 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5247 /* If appropriate, re-join ChanServ to the channel. */
5248 if(!IsOffChannel(suspended->cData))
5250 spamserv_cs_suspend(channel, 0, 0, NULL);
5251 ss_cs_join_channel(channel, 1);
5254 /* Mark everyone currently in the channel as present. */
5255 for(ii = 0; ii < channel->members.used; ++ii)
5257 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5266 static CHANSERV_FUNC(cmd_csuspend)
5268 struct suspended *suspended;
5269 char reason[MAXLEN];
5270 unsigned long expiry, duration;
5271 struct userData *uData;
5275 if(IsProtected(channel->channel_info))
5277 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5281 if(argv[1][0] == '!')
5283 else if(IsSuspended(channel->channel_info))
5285 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5286 show_suspension_info(cmd, user, channel->channel_info->suspended);
5290 if(!strcmp(argv[1], "0"))
5292 else if((duration = ParseInterval(argv[1])))
5293 expiry = now + duration;
5296 reply("MSG_INVALID_DURATION", argv[1]);
5300 unsplit_string(argv + 2, argc - 2, reason);
5302 suspended = calloc(1, sizeof(*suspended));
5303 suspended->revoked = 0;
5304 suspended->issued = now;
5305 suspended->suspender = strdup(user->handle_info->handle);
5306 suspended->expires = expiry;
5307 suspended->reason = strdup(reason);
5308 suspended->cData = channel->channel_info;
5309 suspended->previous = suspended->cData->suspended;
5310 suspended->cData->suspended = suspended;
5312 if(suspended->expires)
5313 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5315 if(IsSuspended(channel->channel_info))
5317 suspended->previous->revoked = now;
5318 if(suspended->previous->expires)
5319 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5320 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5321 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5325 /* Mark all users in channel as absent. */
5326 for(uData = channel->channel_info->users; uData; uData = uData->next)
5335 /* Mark the channel as suspended, then part. */
5336 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5337 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5338 DelChannelUser(chanserv, channel, suspended->reason, 0);
5339 reply("CSMSG_SUSPENDED", channel->name);
5340 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5341 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5346 static CHANSERV_FUNC(cmd_cunsuspend)
5348 struct suspended *suspended;
5349 char message[MAXLEN];
5351 if(!IsSuspended(channel->channel_info))
5353 reply("CSMSG_NOT_SUSPENDED", channel->name);
5357 suspended = channel->channel_info->suspended;
5359 /* Expire the suspension and join ChanServ to the channel. */
5360 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5361 chanserv_expire_suspension(suspended);
5362 reply("CSMSG_UNSUSPENDED", channel->name);
5363 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5364 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5368 typedef struct chanservSearch
5373 unsigned long unvisited;
5374 unsigned long registered;
5376 unsigned long flags;
5380 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5383 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5388 search = malloc(sizeof(struct chanservSearch));
5389 memset(search, 0, sizeof(*search));
5392 for(i = 0; i < argc; i++)
5394 /* Assume all criteria require arguments. */
5397 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5401 if(!irccasecmp(argv[i], "name"))
5402 search->name = argv[++i];
5403 else if(!irccasecmp(argv[i], "registrar"))
5404 search->registrar = argv[++i];
5405 else if(!irccasecmp(argv[i], "unvisited"))
5406 search->unvisited = ParseInterval(argv[++i]);
5407 else if(!irccasecmp(argv[i], "registered"))
5408 search->registered = ParseInterval(argv[++i]);
5409 else if(!irccasecmp(argv[i], "flags"))
5412 if(!irccasecmp(argv[i], "nodelete"))
5413 search->flags |= CHANNEL_NODELETE;
5414 else if(!irccasecmp(argv[i], "suspended"))
5415 search->flags |= CHANNEL_SUSPENDED;
5416 else if(!irccasecmp(argv[i], "unreviewed"))
5417 search->flags |= CHANNEL_UNREVIEWED;
5420 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5424 else if(!irccasecmp(argv[i], "limit"))
5425 search->limit = strtoul(argv[++i], NULL, 10);
5428 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5433 if(search->name && !strcmp(search->name, "*"))
5435 if(search->registrar && !strcmp(search->registrar, "*"))
5436 search->registrar = 0;
5445 chanserv_channel_match(struct chanData *channel, search_t search)
5447 const char *name = channel->channel->name;
5448 if((search->name && !match_ircglob(name, search->name)) ||
5449 (search->registrar && !channel->registrar) ||
5450 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5451 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5452 (search->registered && (now - channel->registered) > search->registered) ||
5453 (search->flags && ((search->flags & channel->flags) != search->flags)))
5460 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5462 struct chanData *channel;
5463 unsigned int matches = 0;
5465 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5467 if(!chanserv_channel_match(channel, search))
5477 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5482 search_print(struct chanData *channel, void *data)
5484 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5487 static CHANSERV_FUNC(cmd_search)
5490 unsigned int matches;
5491 channel_search_func action;
5495 if(!irccasecmp(argv[1], "count"))
5496 action = search_count;
5497 else if(!irccasecmp(argv[1], "print"))
5498 action = search_print;
5501 reply("CSMSG_ACTION_INVALID", argv[1]);
5505 search = chanserv_search_create(user, argc - 2, argv + 2);
5509 if(action == search_count)
5510 search->limit = INT_MAX;
5512 if(action == search_print)
5513 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5515 matches = chanserv_channel_search(search, action, user);
5518 reply("MSG_MATCH_COUNT", matches);
5520 reply("MSG_NO_MATCHES");
5526 static CHANSERV_FUNC(cmd_unvisited)
5528 struct chanData *cData;
5529 unsigned long interval = chanserv_conf.channel_expire_delay;
5530 char buffer[INTERVALLEN];
5531 unsigned int limit = 25, matches = 0;
5535 interval = ParseInterval(argv[1]);
5537 limit = atoi(argv[2]);
5540 intervalString(buffer, interval, user->handle_info);
5541 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5543 for(cData = channelList; cData && matches < limit; cData = cData->next)
5545 if((now - cData->visited) < interval)
5548 intervalString(buffer, now - cData->visited, user->handle_info);
5549 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5556 static MODCMD_FUNC(chan_opt_defaulttopic)
5562 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5564 reply("CSMSG_TOPIC_LOCKED", channel->name);
5568 topic = unsplit_string(argv+1, argc-1, NULL);
5570 free(channel->channel_info->topic);
5571 if(topic[0] == '*' && topic[1] == 0)
5573 topic = channel->channel_info->topic = NULL;
5577 topic = channel->channel_info->topic = strdup(topic);
5578 if(channel->channel_info->topic_mask
5579 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5580 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5582 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5585 if(channel->channel_info->topic)
5586 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5588 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5592 static MODCMD_FUNC(chan_opt_topicmask)
5596 struct chanData *cData = channel->channel_info;
5599 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5601 reply("CSMSG_TOPIC_LOCKED", channel->name);
5605 mask = unsplit_string(argv+1, argc-1, NULL);
5607 if(cData->topic_mask)
5608 free(cData->topic_mask);
5609 if(mask[0] == '*' && mask[1] == 0)
5611 cData->topic_mask = 0;
5615 cData->topic_mask = strdup(mask);
5617 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5618 else if(!match_ircglob(cData->topic, cData->topic_mask))
5619 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5623 if(channel->channel_info->topic_mask)
5624 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5626 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5630 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5634 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5638 if(greeting[0] == '*' && greeting[1] == 0)
5642 unsigned int length = strlen(greeting);
5643 if(length > chanserv_conf.greeting_length)
5645 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5648 *data = strdup(greeting);
5657 reply(name, user_find_message(user, "MSG_NONE"));
5661 static MODCMD_FUNC(chan_opt_greeting)
5663 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5666 static MODCMD_FUNC(chan_opt_usergreeting)
5668 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5671 static MODCMD_FUNC(chan_opt_modes)
5673 struct mod_chanmode *new_modes;
5678 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5680 reply("CSMSG_NO_ACCESS");
5683 if(argv[1][0] == '*' && argv[1][1] == 0)
5685 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5687 else if(!(new_modes = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS|(IsOper(user) && IsHelping(user) ? MCP_OPERMODE : 0), 0)))
5689 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5692 else if(new_modes->argc > 1)
5694 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5695 mod_chanmode_free(new_modes);
5700 channel->channel_info->modes = *new_modes;
5701 modcmd_chanmode_announce(new_modes);
5702 mod_chanmode_free(new_modes);
5706 mod_chanmode_format(&channel->channel_info->modes, modes);
5708 reply("CSMSG_SET_MODES", modes);
5710 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5714 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5716 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5718 struct chanData *cData = channel->channel_info;
5723 /* Set flag according to value. */
5724 if(enabled_string(argv[1]))
5726 cData->flags |= mask;
5729 else if(disabled_string(argv[1]))
5731 cData->flags &= ~mask;
5736 reply("MSG_INVALID_BINARY", argv[1]);
5742 /* Find current option value. */
5743 value = (cData->flags & mask) ? 1 : 0;
5747 reply(name, user_find_message(user, "MSG_ON"));
5749 reply(name, user_find_message(user, "MSG_OFF"));
5753 static MODCMD_FUNC(chan_opt_nodelete)
5755 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5757 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5761 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5764 static MODCMD_FUNC(chan_opt_dynlimit)
5766 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5769 static MODCMD_FUNC(chan_opt_offchannel)
5771 struct chanData *cData = channel->channel_info;
5776 /* Set flag according to value. */
5777 if(enabled_string(argv[1]))
5779 if(!IsOffChannel(cData))
5780 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5781 cData->flags |= CHANNEL_OFFCHANNEL;
5784 else if(disabled_string(argv[1]))
5786 if(IsOffChannel(cData))
5788 struct mod_chanmode change;
5789 mod_chanmode_init(&change);
5791 change.args[0].mode = MODE_CHANOP;
5792 change.args[0].u.member = AddChannelUser(chanserv, channel);
5793 mod_chanmode_announce(chanserv, channel, &change);
5795 cData->flags &= ~CHANNEL_OFFCHANNEL;
5800 reply("MSG_INVALID_BINARY", argv[1]);
5806 /* Find current option value. */
5807 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5811 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5813 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5817 static MODCMD_FUNC(chan_opt_unreviewed)
5819 struct chanData *cData = channel->channel_info;
5820 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5826 /* The two directions can have different ACLs. */
5827 if(enabled_string(argv[1]))
5829 else if(disabled_string(argv[1]))
5833 reply("MSG_INVALID_BINARY", argv[1]);
5837 if (new_value != value)
5839 struct svccmd *subcmd;
5840 char subcmd_name[32];
5842 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5843 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5846 reply("MSG_COMMAND_DISABLED", subcmd_name);
5849 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5853 cData->flags |= CHANNEL_UNREVIEWED;
5856 free(cData->registrar);
5857 cData->registrar = strdup(user->handle_info->handle);
5858 cData->flags &= ~CHANNEL_UNREVIEWED;
5865 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5867 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5871 static MODCMD_FUNC(chan_opt_defaults)
5873 struct userData *uData;
5874 struct chanData *cData;
5875 const char *confirm;
5876 enum levelOption lvlOpt;
5877 enum charOption chOpt;
5879 cData = channel->channel_info;
5880 uData = GetChannelUser(cData, user->handle_info);
5881 if(!uData || (uData->access < UL_OWNER))
5883 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5886 confirm = make_confirmation_string(uData);
5887 if((argc < 2) || strcmp(argv[1], confirm))
5889 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5892 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5893 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5894 cData->modes = chanserv_conf.default_modes;
5895 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5896 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5897 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5898 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5899 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5904 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5906 struct chanData *cData = channel->channel_info;
5907 struct userData *uData;
5908 unsigned short value;
5912 if(!check_user_level(channel, user, option, 1, 1))
5914 reply("CSMSG_CANNOT_SET");
5917 value = user_level_from_name(argv[1], UL_OWNER+1);
5918 if(!value && strcmp(argv[1], "0"))
5920 reply("CSMSG_INVALID_ACCESS", argv[1]);
5923 uData = GetChannelUser(cData, user->handle_info);
5924 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5926 reply("CSMSG_BAD_SETLEVEL");
5932 if(value > cData->lvlOpts[lvlGiveOps])
5934 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5939 if(value < cData->lvlOpts[lvlGiveVoice])
5941 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5946 /* This test only applies to owners, since non-owners
5947 * trying to set an option to above their level get caught
5948 * by the CSMSG_BAD_SETLEVEL test above.
5950 if(value > uData->access)
5952 reply("CSMSG_BAD_SETTERS");
5959 cData->lvlOpts[option] = value;
5961 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5965 static MODCMD_FUNC(chan_opt_enfops)
5967 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5970 static MODCMD_FUNC(chan_opt_giveops)
5972 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5975 static MODCMD_FUNC(chan_opt_enfmodes)
5977 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5980 static MODCMD_FUNC(chan_opt_enftopic)
5982 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5985 static MODCMD_FUNC(chan_opt_pubcmd)
5987 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5990 static MODCMD_FUNC(chan_opt_setters)
5992 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5995 static MODCMD_FUNC(chan_opt_ctcpusers)
5997 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
6000 static MODCMD_FUNC(chan_opt_userinfo)
6002 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6005 static MODCMD_FUNC(chan_opt_givevoice)
6007 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6010 static MODCMD_FUNC(chan_opt_topicsnarf)
6012 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6015 static MODCMD_FUNC(chan_opt_vote)
6017 return channel_level_option(lvlVote, CSFUNC_ARGS);
6020 static MODCMD_FUNC(chan_opt_inviteme)
6022 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6026 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6028 struct chanData *cData = channel->channel_info;
6029 int count = charOptions[option].count, idx;
6033 idx = atoi(argv[1]);
6035 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6037 reply("CSMSG_INVALID_NUMERIC", idx);
6038 /* Show possible values. */
6039 for(idx = 0; idx < count; idx++)
6040 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6044 cData->chOpts[option] = charOptions[option].values[idx].value;
6048 /* Find current option value. */
6051 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6055 /* Somehow, the option value is corrupt; reset it to the default. */
6056 cData->chOpts[option] = charOptions[option].default_value;
6061 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6065 static MODCMD_FUNC(chan_opt_protect)
6067 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6070 static MODCMD_FUNC(chan_opt_toys)
6072 return channel_multiple_option(chToys, CSFUNC_ARGS);
6075 static MODCMD_FUNC(chan_opt_ctcpreaction)
6077 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6080 static MODCMD_FUNC(chan_opt_topicrefresh)
6082 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6085 static struct svccmd_list set_shows_list;
6088 handle_svccmd_unbind(struct svccmd *target) {
6090 for(ii=0; ii<set_shows_list.used; ++ii)
6091 if(target == set_shows_list.list[ii])
6092 set_shows_list.used = 0;
6095 static CHANSERV_FUNC(cmd_set)
6097 struct svccmd *subcmd;
6101 /* Check if we need to (re-)initialize set_shows_list. */
6102 if(!set_shows_list.used)
6104 if(!set_shows_list.size)
6106 set_shows_list.size = chanserv_conf.set_shows->used;
6107 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6109 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6111 const char *name = chanserv_conf.set_shows->list[ii];
6112 sprintf(buf, "%s %s", argv[0], name);
6113 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6116 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6119 svccmd_list_append(&set_shows_list, subcmd);
6125 reply("CSMSG_CHANNEL_OPTIONS");
6126 for(ii = 0; ii < set_shows_list.used; ii++)
6128 subcmd = set_shows_list.list[ii];
6129 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6134 sprintf(buf, "%s %s", argv[0], argv[1]);
6135 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6138 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6141 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6143 reply("CSMSG_NO_ACCESS");
6149 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6153 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6155 struct userData *uData;
6157 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6160 reply("CSMSG_NOT_USER", channel->name);
6166 /* Just show current option value. */
6168 else if(enabled_string(argv[1]))
6170 uData->flags |= mask;
6172 else if(disabled_string(argv[1]))
6174 uData->flags &= ~mask;
6178 reply("MSG_INVALID_BINARY", argv[1]);
6182 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6186 static MODCMD_FUNC(user_opt_noautoop)
6188 struct userData *uData;
6190 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6193 reply("CSMSG_NOT_USER", channel->name);
6196 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6197 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6199 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6202 static MODCMD_FUNC(user_opt_autoinvite)
6204 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6206 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6208 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6211 static MODCMD_FUNC(user_opt_info)
6213 struct userData *uData;
6216 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6220 /* If they got past the command restrictions (which require access)
6221 * but fail this test, we have some fool with security override on.
6223 reply("CSMSG_NOT_USER", channel->name);
6230 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6231 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6233 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6236 bp = strcspn(infoline, "\001");
6239 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6244 if(infoline[0] == '*' && infoline[1] == 0)
6247 uData->info = strdup(infoline);
6250 reply("CSMSG_USET_INFO", uData->info);
6252 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6256 struct svccmd_list uset_shows_list;
6258 static CHANSERV_FUNC(cmd_uset)
6260 struct svccmd *subcmd;
6264 /* Check if we need to (re-)initialize uset_shows_list. */
6265 if(!uset_shows_list.used)
6269 "NoAutoOp", "AutoInvite", "Info"
6272 if(!uset_shows_list.size)
6274 uset_shows_list.size = ArrayLength(options);
6275 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6277 for(ii = 0; ii < ArrayLength(options); ii++)
6279 const char *name = options[ii];
6280 sprintf(buf, "%s %s", argv[0], name);
6281 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6284 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6287 svccmd_list_append(&uset_shows_list, subcmd);
6293 /* Do this so options are presented in a consistent order. */
6294 reply("CSMSG_USER_OPTIONS");
6295 for(ii = 0; ii < uset_shows_list.used; ii++)
6296 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6300 sprintf(buf, "%s %s", argv[0], argv[1]);
6301 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6304 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6308 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6311 static CHANSERV_FUNC(cmd_giveownership)
6313 struct handle_info *new_owner_hi;
6314 struct userData *new_owner;
6315 struct userData *curr_user;
6316 struct userData *invoker;
6317 struct chanData *cData = channel->channel_info;
6318 struct do_not_register *dnr;
6319 const char *confirm;
6321 unsigned short co_access;
6322 char reason[MAXLEN];
6325 curr_user = GetChannelAccess(cData, user->handle_info);
6326 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6327 if(!curr_user || (curr_user->access != UL_OWNER))
6329 struct userData *owner = NULL;
6330 for(curr_user = channel->channel_info->users;
6332 curr_user = curr_user->next)
6334 if(curr_user->access != UL_OWNER)
6338 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6345 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6347 char delay[INTERVALLEN];
6348 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6349 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6352 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6354 if(new_owner_hi == user->handle_info)
6356 reply("CSMSG_NO_TRANSFER_SELF");
6359 new_owner = GetChannelAccess(cData, new_owner_hi);
6364 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6368 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6372 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6374 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6377 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6378 if(!IsHelping(user))
6379 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6381 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6384 invoker = GetChannelUser(cData, user->handle_info);
6385 if(invoker->access <= UL_OWNER)
6387 confirm = make_confirmation_string(curr_user);
6388 if((argc < 3) || strcmp(argv[2], confirm))
6390 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6394 if(new_owner->access >= UL_COOWNER)
6395 co_access = new_owner->access;
6397 co_access = UL_COOWNER;
6398 new_owner->access = UL_OWNER;
6400 curr_user->access = co_access;
6401 cData->ownerTransfer = now;
6402 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6403 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6404 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6408 static CHANSERV_FUNC(cmd_suspend)
6410 struct handle_info *hi;
6411 struct userData *actor, *real_actor, *target;
6412 unsigned int override = 0;
6415 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6416 actor = GetChannelUser(channel->channel_info, user->handle_info);
6417 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6418 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6420 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6423 if(target->access >= actor->access)
6425 reply("MSG_USER_OUTRANKED", hi->handle);
6428 if(target->flags & USER_SUSPENDED)
6430 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6435 target->present = 0;
6438 if(!real_actor || target->access >= real_actor->access)
6439 override = CMD_LOG_OVERRIDE;
6440 target->flags |= USER_SUSPENDED;
6441 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6442 return 1 | override;
6445 static CHANSERV_FUNC(cmd_unsuspend)
6447 struct handle_info *hi;
6448 struct userData *actor, *real_actor, *target;
6449 unsigned int override = 0;
6452 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6453 actor = GetChannelUser(channel->channel_info, user->handle_info);
6454 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6455 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6457 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6460 if(target->access >= actor->access)
6462 reply("MSG_USER_OUTRANKED", hi->handle);
6465 if(!(target->flags & USER_SUSPENDED))
6467 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6470 if(!real_actor || target->access >= real_actor->access)
6471 override = CMD_LOG_OVERRIDE;
6472 target->flags &= ~USER_SUSPENDED;
6473 scan_user_presence(target, NULL);
6474 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6475 return 1 | override;
6478 static MODCMD_FUNC(cmd_deleteme)
6480 struct handle_info *hi;
6481 struct userData *target;
6482 const char *confirm_string;
6483 unsigned short access_level;
6486 hi = user->handle_info;
6487 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6489 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6492 if(target->access == UL_OWNER)
6494 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6497 confirm_string = make_confirmation_string(target);
6498 if((argc < 2) || strcmp(argv[1], confirm_string))
6500 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6503 access_level = target->access;
6504 channel_name = strdup(channel->name);
6505 del_channel_user(target, 1);
6506 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6511 static CHANSERV_FUNC(cmd_addvote)
6513 struct chanData *cData = channel->channel_info;
6514 struct userData *uData, *target;
6515 struct handle_info *hi;
6516 if (!cData) return 0;
6518 hi = user->handle_info;
6519 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6521 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6524 if(target->access < 300) {
6525 reply("CSMSG_NO_ACCESS");
6529 reply("CSMSG_ADDVOTE_FULL");
6533 msg = unsplit_string(argv + 1, argc - 1, NULL);
6534 cData->vote = strdup(msg);
6535 cData->vote_start=0;
6536 dict_delete(cData->vote_options);
6537 cData->vote_options = dict_new();
6538 dict_set_free_data(cData->vote_options, free_vote_options);
6539 for(uData = channel->channel_info->users; uData; uData = uData->next)
6544 reply("CSMSG_ADDVOTE_DONE");
6548 static CHANSERV_FUNC(cmd_delvote)
6550 struct chanData *cData = channel->channel_info;
6551 struct userData *target;
6552 struct handle_info *hi;
6553 if (!cData) return 0;
6554 hi = user->handle_info;
6555 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6557 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6560 if(target->access < 300) {
6561 reply("CSMSG_NO_ACCESS");
6565 reply("CSMSG_NO_VOTE");
6570 reply("CSMSG_DELVOTE_DONE");
6574 static CHANSERV_FUNC(cmd_addoption)
6576 struct chanData *cData = channel->channel_info;
6577 struct userData *target;
6578 struct handle_info *hi;
6579 if (!cData) return 0;
6581 hi = user->handle_info;
6582 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6584 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6587 if(target->access < 300) {
6588 reply("CSMSG_NO_ACCESS");
6592 reply("CSMSG_NO_VOTE");
6598 msg = unsplit_string(argv + 1, argc - 1, NULL);
6601 unsigned int lastid = 1;
6602 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6603 struct vote_option *cvOpt = iter_data(it);
6604 if(cvOpt->option_id > lastid)
6605 lastid = cvOpt->option_id;
6607 struct vote_option *vOpt;
6608 vOpt = calloc(1, sizeof(*vOpt));
6609 vOpt->name = strdup(msg);
6610 vOpt->option_id = (lastid + 1);
6612 sprintf(str,"%i",(lastid + 1));
6613 vOpt->option_str = strdup(str);
6615 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6617 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6621 static CHANSERV_FUNC(cmd_deloption)
6623 struct chanData *cData = channel->channel_info;
6624 struct userData *uData, *target;
6625 struct handle_info *hi;
6626 if (!cData) return 0;
6628 hi = user->handle_info;
6629 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6631 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6634 if(target->access < 300) {
6635 reply("CSMSG_NO_ACCESS");
6639 reply("CSMSG_NO_VOTE");
6642 if(cData->vote_start) {
6643 if(dict_size(cData->vote_options) < 3) {
6644 reply("CSMSG_VOTE_NEED_OPTIONS");
6649 int find_id = atoi(argv[1]);
6651 unsigned int found = 0;
6654 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6656 if (find_id == ii) {
6657 struct vote_option *vOpt = iter_data(it);
6658 found = vOpt->option_id;
6660 sprintf(str,"%i",vOpt->option_id);
6661 dict_remove(cData->vote_options, str);
6666 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6667 if(uData->votefor == found) {
6672 reply("CSMSG_DELOPTION_DONE");
6675 reply("CSMSG_DELOPTION_NONE");
6680 static CHANSERV_FUNC(cmd_vote)
6682 struct chanData *cData = channel->channel_info;
6683 struct userData *target;
6684 struct handle_info *hi;
6685 unsigned int votedfor = 0;
6686 char *votedfor_str = NULL;
6688 if (!cData || !cData->vote) {
6689 reply("CSMSG_NO_VOTE");
6692 if(argc > 1 && cData->vote_start) {
6693 hi = user->handle_info;
6694 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6696 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6699 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6700 reply("CSMSG_NO_ACCESS");
6704 reply("CSMSG_VOTE_VOTED");
6707 int find_id = atoi(argv[1]);
6710 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6712 if (find_id == ii) {
6713 struct vote_option *vOpt = iter_data(it);
6716 target->votefor = vOpt->option_id;
6717 votedfor = vOpt->option_id;
6718 votedfor_str = vOpt->name;
6722 reply("CSMSG_VOTE_INVALID");
6726 if (!cData->vote_start) {
6727 reply("CSMSG_VOTE_NOT_STARTED");
6729 reply("CSMSG_VOTE_QUESTION",cData->vote);
6731 unsigned int voteid = 0;
6734 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6735 struct vote_option *vOpt = iter_data(it);
6737 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6739 if(argc > 1 && cData->vote_start && votedfor_str) {
6740 reply("CSMSG_VOTE_DONE",votedfor_str);
6745 static CHANSERV_FUNC(cmd_startvote)
6747 struct chanData *cData = channel->channel_info;
6748 struct userData *target;
6749 struct handle_info *hi;
6750 if (!cData) return 0;
6751 hi = user->handle_info;
6752 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6754 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6757 if(target->access < 300) {
6758 reply("CSMSG_NO_ACCESS");
6762 reply("CSMSG_NO_VOTE");
6765 if(cData->vote_start) {
6766 reply("CSMSG_STARTVOTE_RUNNING");
6769 if(dict_size(cData->vote_options) < 2) {
6770 reply("CSMSG_VOTE_NEED_OPTIONS");
6773 cData->vote_start = 1;
6774 char response[MAXLEN];
6775 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6776 irc_privmsg(cmd->parent->bot, channel->name, response);
6777 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6778 irc_privmsg(cmd->parent->bot, channel->name, response);
6779 unsigned int voteid = 0;
6781 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6782 struct vote_option *vOpt = iter_data(it);
6784 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6785 irc_privmsg(cmd->parent->bot, channel->name, response);
6787 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6788 irc_privmsg(cmd->parent->bot, channel->name, response);
6789 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6790 irc_privmsg(cmd->parent->bot, channel->name, response);
6794 static CHANSERV_FUNC(cmd_endvote)
6796 struct chanData *cData = channel->channel_info;
6797 struct userData *target;
6798 struct handle_info *hi;
6799 if (!cData) return 0;
6800 hi = user->handle_info;
6801 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6803 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6806 if(target->access < 300) {
6807 reply("CSMSG_NO_ACCESS");
6811 reply("CSMSG_NO_VOTE");
6814 if(!cData->vote_start) {
6815 reply("CSMSG_ENDVOTE_STOPPED");
6818 cData->vote_start = 0;
6819 reply("CSMSG_ENDVOTE_DONE");
6823 static CHANSERV_FUNC(cmd_voteresults)
6825 struct chanData *cData = channel->channel_info;
6826 struct userData *target;
6827 struct handle_info *hi;
6828 if (!cData) return 0;
6830 reply("CSMSG_NO_VOTE");
6833 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6834 hi = user->handle_info;
6835 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6837 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6840 if(target->access < 300) {
6841 reply("CSMSG_NO_ACCESS");
6844 char response[MAXLEN];
6845 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6846 irc_privmsg(cmd->parent->bot, channel->name, response);
6847 unsigned int voteid = 0;
6849 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6850 struct vote_option *vOpt = iter_data(it);
6852 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6853 irc_privmsg(cmd->parent->bot, channel->name, response);
6856 reply("CSMSG_VOTE_QUESTION",cData->vote);
6857 unsigned int voteid = 0;
6859 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6860 struct vote_option *vOpt = iter_data(it);
6862 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6869 chanserv_refresh_topics(UNUSED_ARG(void *data))
6871 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6872 struct chanData *cData;
6875 for(cData = channelList; cData; cData = cData->next)
6877 if(IsSuspended(cData))
6879 opt = cData->chOpts[chTopicRefresh];
6882 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6885 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6886 cData->last_refresh = refresh_num;
6888 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6891 static CHANSERV_FUNC(cmd_unf)
6895 char response[MAXLEN];
6896 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6897 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6898 irc_privmsg(cmd->parent->bot, channel->name, response);
6901 reply("CSMSG_UNF_RESPONSE");
6905 static CHANSERV_FUNC(cmd_ping)
6909 char response[MAXLEN];
6910 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6911 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6912 irc_privmsg(cmd->parent->bot, channel->name, response);
6915 reply("CSMSG_PING_RESPONSE");
6919 static CHANSERV_FUNC(cmd_wut)
6923 char response[MAXLEN];
6924 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6925 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6926 irc_privmsg(cmd->parent->bot, channel->name, response);
6929 reply("CSMSG_WUT_RESPONSE");
6933 static CHANSERV_FUNC(cmd_8ball)
6935 unsigned int i, j, accum;
6940 for(i=1; i<argc; i++)
6941 for(j=0; argv[i][j]; j++)
6942 accum = (accum << 5) - accum + toupper(argv[i][j]);
6943 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6946 char response[MAXLEN];
6947 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6948 irc_privmsg(cmd->parent->bot, channel->name, response);
6951 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6955 static CHANSERV_FUNC(cmd_d)
6957 unsigned long sides, count, modifier, ii, total;
6958 char response[MAXLEN], *sep;
6962 if((count = strtoul(argv[1], &sep, 10)) < 1)
6972 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6973 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6977 else if((sep[0] == '-') && isdigit(sep[1]))
6978 modifier = strtoul(sep, NULL, 10);
6979 else if((sep[0] == '+') && isdigit(sep[1]))
6980 modifier = strtoul(sep+1, NULL, 10);
6987 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6992 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6995 for(total = ii = 0; ii < count; ++ii)
6996 total += (rand() % sides) + 1;
6999 if((count > 1) || modifier)
7001 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7002 sprintf(response, fmt, total, count, sides, modifier);
7006 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7007 sprintf(response, fmt, total, sides);
7010 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7012 send_message_type(4, user, cmd->parent->bot, "%s", response);
7016 static CHANSERV_FUNC(cmd_huggle)
7018 /* CTCP must be via PRIVMSG, never notice */
7020 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7022 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7027 chanserv_adjust_limit(void *data)
7029 struct mod_chanmode change;
7030 struct chanData *cData = data;
7031 struct chanNode *channel = cData->channel;
7034 if(IsSuspended(cData))
7037 cData->limitAdjusted = now;
7038 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7039 if(cData->modes.modes_set & MODE_LIMIT)
7041 if(limit > cData->modes.new_limit)
7042 limit = cData->modes.new_limit;
7043 else if(limit == cData->modes.new_limit)
7047 mod_chanmode_init(&change);
7048 change.modes_set = MODE_LIMIT;
7049 change.new_limit = limit;
7050 mod_chanmode_announce(chanserv, channel, &change);
7054 handle_new_channel(struct chanNode *channel)
7056 struct chanData *cData;
7058 if(!(cData = channel->channel_info))
7061 if(cData->modes.modes_set || cData->modes.modes_clear)
7062 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7064 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7065 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7068 void handle_new_channel_created(char *chan, struct userNode *user) {
7069 if(user->handle_info && chanserv_conf.new_channel_authed) {
7070 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7071 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7072 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7074 if(chanserv_conf.new_channel_msg)
7075 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7078 /* Welcome to my worst nightmare. Warning: Read (or modify)
7079 the code below at your own risk. */
7081 handle_join(struct modeNode *mNode)
7083 struct mod_chanmode change;
7084 struct userNode *user = mNode->user;
7085 struct chanNode *channel = mNode->channel;
7086 struct chanData *cData;
7087 struct userData *uData = NULL;
7088 struct banData *bData;
7089 struct handle_info *handle;
7090 unsigned int modes = 0, info = 0;
7094 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7097 cData = channel->channel_info;
7098 if(channel->members.used > cData->max) {
7099 cData->max = channel->members.used;
7100 cData->max_time = now;
7103 for(i = 0; i < channel->invited.used; i++)
7105 if(channel->invited.list[i] == user) {
7106 userList_remove(&channel->invited, user);
7110 /* Check for bans. If they're joining through a ban, one of two
7112 * 1: Join during a netburst, by riding the break. Kick them
7113 * unless they have ops or voice in the channel.
7114 * 2: They're allowed to join through the ban (an invite in
7115 * ircu2.10, or a +e on Hybrid, or something).
7116 * If they're not joining through a ban, and the banlist is not
7117 * full, see if they're on the banlist for the channel. If so,
7120 if(user->uplink->burst && !mNode->modes)
7123 for(ii = 0; ii < channel->banlist.used; ii++)
7125 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7127 /* Riding a netburst. Naughty. */
7128 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7134 mod_chanmode_init(&change);
7136 if(channel->banlist.used < MAXBANS)
7138 /* Not joining through a ban. */
7139 for(bData = cData->bans;
7140 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7141 bData = bData->next);
7145 char kick_reason[MAXLEN];
7146 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7148 bData->triggered = now;
7149 if(bData != cData->bans)
7151 /* Shuffle the ban to the head of the list. */
7153 bData->next->prev = bData->prev;
7155 bData->prev->next = bData->next;
7158 bData->next = cData->bans;
7161 cData->bans->prev = bData;
7162 cData->bans = bData;
7165 change.args[0].mode = MODE_BAN;
7166 change.args[0].u.hostmask = bData->mask;
7167 mod_chanmode_announce(chanserv, channel, &change);
7168 KickChannelUser(user, channel, chanserv, kick_reason);
7173 /* ChanServ will not modify the limits in join-flooded channels,
7174 or when there are enough slots left below the limit. */
7175 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7176 && !channel->join_flooded
7177 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7179 /* The user count has begun "bumping" into the channel limit,
7180 so set a timer to raise the limit a bit. Any previous
7181 timers are removed so three incoming users within the delay
7182 results in one limit change, not three. */
7184 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7185 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7188 if(channel->join_flooded)
7190 /* don't automatically give ops or voice during a join flood */
7192 else if(cData->lvlOpts[lvlGiveOps] == 0)
7193 modes |= MODE_CHANOP;
7194 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7195 modes |= MODE_VOICE;
7197 greeting = cData->greeting;
7198 if(user->handle_info)
7200 handle = user->handle_info;
7202 if(IsHelper(user) && !IsHelping(user))
7205 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7207 if(channel == chanserv_conf.support_channels.list[ii])
7209 HANDLE_SET_FLAG(user->handle_info, HELPING);
7215 uData = GetTrueChannelAccess(cData, handle);
7216 if(uData && !IsUserSuspended(uData))
7218 /* Ops and above were handled by the above case. */
7219 if(IsUserAutoOp(uData))
7221 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7222 modes |= MODE_CHANOP;
7223 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7224 modes |= MODE_VOICE;
7226 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7227 cData->visited = now;
7228 if(cData->user_greeting)
7229 greeting = cData->user_greeting;
7231 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7232 && ((now - uData->seen) >= chanserv_conf.info_delay)
7240 /* If user joining normally (not during burst), apply op or voice,
7241 * and send greeting/userinfo as appropriate.
7243 if(!user->uplink->burst)
7247 if(modes & MODE_CHANOP)
7248 modes &= ~MODE_VOICE;
7249 change.args[0].mode = modes;
7250 change.args[0].u.member = mNode;
7251 mod_chanmode_announce(chanserv, channel, &change);
7254 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7255 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7256 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7262 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7264 struct mod_chanmode change;
7265 struct userData *channel;
7266 unsigned int ii, jj;
7268 if(!user->handle_info)
7271 mod_chanmode_init(&change);
7273 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7275 struct chanNode *cn;
7276 struct modeNode *mn;
7277 if(IsUserSuspended(channel)
7278 || IsSuspended(channel->channel)
7279 || !(cn = channel->channel->channel))
7282 mn = GetUserMode(cn, user);
7285 if(!IsUserSuspended(channel)
7286 && IsUserAutoInvite(channel)
7287 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7289 && !user->uplink->burst)
7290 irc_invite(chanserv, user, cn);
7294 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7295 channel->channel->visited = now;
7297 if(IsUserAutoOp(channel))
7299 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7300 change.args[0].mode = MODE_CHANOP;
7301 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7302 change.args[0].mode = MODE_VOICE;
7304 change.args[0].mode = 0;
7305 change.args[0].u.member = mn;
7306 if(change.args[0].mode)
7307 mod_chanmode_announce(chanserv, cn, &change);
7310 channel->seen = now;
7311 channel->present = 1;
7314 for(ii = 0; ii < user->channels.used; ++ii)
7316 struct chanNode *chan = user->channels.list[ii]->channel;
7317 struct banData *ban;
7319 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7320 || !chan->channel_info
7321 || IsSuspended(chan->channel_info))
7323 for(jj = 0; jj < chan->banlist.used; ++jj)
7324 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7326 if(jj < chan->banlist.used)
7328 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7330 char kick_reason[MAXLEN];
7331 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7333 change.args[0].mode = MODE_BAN;
7334 change.args[0].u.hostmask = ban->mask;
7335 mod_chanmode_announce(chanserv, chan, &change);
7336 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7337 KickChannelUser(user, chan, chanserv, kick_reason);
7338 ban->triggered = now;
7343 if(IsSupportHelper(user))
7345 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7347 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7349 HANDLE_SET_FLAG(user->handle_info, HELPING);
7357 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7359 struct chanData *cData;
7360 struct userData *uData;
7362 cData = mn->channel->channel_info;
7363 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7366 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7368 /* Allow for a bit of padding so that the limit doesn't
7369 track the user count exactly, which could get annoying. */
7370 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7372 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7373 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7377 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7379 scan_user_presence(uData, mn->user);
7381 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7382 cData->visited = now;
7385 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7388 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7389 struct chanNode *channel;
7390 struct userNode *exclude;
7391 /* When looking at the channel that is being /part'ed, we
7392 * have to skip over the client that is leaving. For
7393 * other channels, we must not do that.
7395 channel = chanserv_conf.support_channels.list[ii];
7396 exclude = (channel == mn->channel) ? mn->user : NULL;
7397 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7400 if(ii == chanserv_conf.support_channels.used)
7401 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7406 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7408 struct userData *uData;
7410 if(!channel->channel_info || !kicker || IsService(kicker)
7411 || (kicker == victim) || IsSuspended(channel->channel_info)
7412 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7415 if(protect_user(victim, kicker, channel->channel_info))
7417 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7418 KickChannelUser(kicker, channel, chanserv, reason);
7421 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7426 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7428 struct chanData *cData;
7430 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7433 cData = channel->channel_info;
7434 if(bad_topic(channel, user, channel->topic))
7436 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7437 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7438 SetChannelTopic(channel, chanserv, old_topic, 1);
7439 else if(cData->topic)
7440 SetChannelTopic(channel, chanserv, cData->topic, 1);
7443 /* With topicsnarf, grab the topic and save it as the default topic. */
7444 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7447 cData->topic = strdup(channel->topic);
7453 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7455 struct mod_chanmode *bounce = NULL;
7456 unsigned int bnc, ii;
7459 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7462 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7463 && mode_lock_violated(&channel->channel_info->modes, change))
7465 char correct[MAXLEN];
7466 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7467 mod_chanmode_format(&channel->channel_info->modes, correct);
7468 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7470 for(ii = bnc = 0; ii < change->argc; ++ii)
7472 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7474 const struct userNode *victim = change->args[ii].u.member->user;
7475 if(!protect_user(victim, user, channel->channel_info))
7478 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7481 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7482 bounce->args[bnc].u.member = GetUserMode(channel, user);
7483 if(bounce->args[bnc].u.member)
7487 bounce->args[bnc].mode = MODE_CHANOP;
7488 bounce->args[bnc].u.member = change->args[ii].u.member;
7490 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7492 else if(change->args[ii].mode & MODE_CHANOP)
7494 const struct userNode *victim = change->args[ii].u.member->user;
7495 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7498 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7499 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7500 bounce->args[bnc].u.member = change->args[ii].u.member;
7503 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7505 const char *ban = change->args[ii].u.hostmask;
7506 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7509 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7510 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7511 bounce->args[bnc].u.hostmask = strdup(ban);
7513 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7518 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7519 mod_chanmode_announce(chanserv, channel, bounce);
7520 for(ii = 0; ii < change->argc; ++ii)
7521 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7522 free((char*)bounce->args[ii].u.hostmask);
7523 mod_chanmode_free(bounce);
7528 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7530 struct chanNode *channel;
7531 struct banData *bData;
7532 struct mod_chanmode change;
7533 unsigned int ii, jj;
7534 char kick_reason[MAXLEN];
7536 mod_chanmode_init(&change);
7538 change.args[0].mode = MODE_BAN;
7539 for(ii = 0; ii < user->channels.used; ++ii)
7541 channel = user->channels.list[ii]->channel;
7542 /* Need not check for bans if they're opped or voiced. */
7543 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7545 /* Need not check for bans unless channel registration is active. */
7546 if(!channel->channel_info || IsSuspended(channel->channel_info))
7548 /* Look for a matching ban already on the channel. */
7549 for(jj = 0; jj < channel->banlist.used; ++jj)
7550 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7552 /* Need not act if we found one. */
7553 if(jj < channel->banlist.used)
7555 /* Look for a matching ban in this channel. */
7556 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7558 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7560 change.args[0].u.hostmask = bData->mask;
7561 mod_chanmode_announce(chanserv, channel, &change);
7562 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7563 KickChannelUser(user, channel, chanserv, kick_reason);
7564 bData->triggered = now;
7565 break; /* we don't need to check any more bans in the channel */
7570 static void handle_rename(struct handle_info *handle, const char *old_handle)
7572 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7576 dict_remove2(handle_dnrs, old_handle, 1);
7577 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7578 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7583 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7585 struct userNode *h_user;
7587 if(handle->channels)
7589 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7590 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7592 while(handle->channels)
7593 del_channel_user(handle->channels, 1);
7598 handle_server_link(UNUSED_ARG(struct server *server))
7600 struct chanData *cData;
7602 for(cData = channelList; cData; cData = cData->next)
7604 if(!IsSuspended(cData))
7605 cData->may_opchan = 1;
7606 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7607 && !cData->channel->join_flooded
7608 && ((cData->channel->limit - cData->channel->members.used)
7609 < chanserv_conf.adjust_threshold))
7611 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7612 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7618 chanserv_conf_read(void)
7622 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7623 struct mod_chanmode *change;
7624 struct string_list *strlist;
7625 struct chanNode *chan;
7628 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7630 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7633 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7634 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7635 chanserv_conf.support_channels.used = 0;
7636 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7638 for(ii = 0; ii < strlist->used; ++ii)
7640 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7643 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7645 channelList_append(&chanserv_conf.support_channels, chan);
7648 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7651 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7654 chan = AddChannel(str, now, str2, NULL);
7656 channelList_append(&chanserv_conf.support_channels, chan);
7658 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7659 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7660 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7661 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7662 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7663 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7664 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7665 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7666 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7667 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7668 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7669 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7670 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7671 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7672 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7673 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7674 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7675 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7676 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7677 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7678 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7679 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7680 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7681 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7682 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7683 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7684 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7686 NickChange(chanserv, str, 0);
7687 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7688 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7689 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7690 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7691 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7692 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7693 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7694 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7695 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7696 chanserv_conf.max_owned = str ? atoi(str) : 5;
7697 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7698 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7699 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7700 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7701 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7702 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7703 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7704 chanserv_conf.new_channel_authed = str ? str : NULL;
7705 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7706 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7707 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7708 chanserv_conf.new_channel_msg = str ? str : NULL;
7709 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7712 safestrncpy(mode_line, str, sizeof(mode_line));
7713 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7714 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7715 && (change->argc < 2))
7717 chanserv_conf.default_modes = *change;
7718 mod_chanmode_free(change);
7720 free_string_list(chanserv_conf.set_shows);
7721 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7723 strlist = string_list_copy(strlist);
7726 static const char *list[] = {
7727 /* free form text */
7728 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7729 /* options based on user level */
7730 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7731 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7732 /* multiple choice options */
7733 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7734 /* binary options */
7735 "DynLimit", "NoDelete", "expire", "Vote",
7739 strlist = alloc_string_list(ArrayLength(list)-1);
7740 for(ii=0; list[ii]; ii++)
7741 string_list_append(strlist, strdup(list[ii]));
7743 chanserv_conf.set_shows = strlist;
7744 /* We don't look things up now, in case the list refers to options
7745 * defined by modules initialized after this point. Just mark the
7746 * function list as invalid, so it will be initialized.
7748 set_shows_list.used = 0;
7749 free_string_list(chanserv_conf.eightball);
7750 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7753 strlist = string_list_copy(strlist);
7757 strlist = alloc_string_list(4);
7758 string_list_append(strlist, strdup("Yes."));
7759 string_list_append(strlist, strdup("No."));
7760 string_list_append(strlist, strdup("Maybe so."));
7762 chanserv_conf.eightball = strlist;
7763 free_string_list(chanserv_conf.old_ban_names);
7764 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7766 strlist = string_list_copy(strlist);
7768 strlist = alloc_string_list(2);
7769 chanserv_conf.old_ban_names = strlist;
7770 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7771 off_channel = str ? atoi(str) : 0;
7775 chanserv_note_type_read(const char *key, struct record_data *rd)
7778 struct note_type *ntype;
7781 if(!(obj = GET_RECORD_OBJECT(rd)))
7783 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7786 if(!(ntype = chanserv_create_note_type(key)))
7788 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7792 /* Figure out set access */
7793 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7795 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7796 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7798 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7800 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7801 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7803 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7805 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7809 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7810 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7811 ntype->set_access.min_opserv = 0;
7814 /* Figure out visibility */
7815 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7816 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7817 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7818 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7819 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7820 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7821 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7822 ntype->visible_type = NOTE_VIS_ALL;
7824 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7826 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7827 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7831 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7833 struct vote_option *vOpt;
7836 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7838 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7842 vOpt = calloc(1, sizeof(*vOpt));
7843 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7844 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7845 vOpt->voted = str ? atoi(str) : 0;
7846 vOpt->option_id = str ? atoi(key) : 0;
7847 vOpt->option_str = strdup(key);
7848 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7852 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7854 struct handle_info *handle;
7855 struct userData *uData;
7856 char *seen, *inf, *flags, *voted, *votefor;
7857 unsigned long last_seen;
7858 unsigned short access_level;
7860 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7862 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7866 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7867 if(access_level > UL_OWNER)
7869 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7873 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7874 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7875 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7876 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7877 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7878 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7879 handle = get_handle_info(key);
7882 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7886 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7887 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7889 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7890 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7898 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7900 struct banData *bData;
7901 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7902 unsigned long set_time, triggered_time, expires_time;
7904 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7906 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7910 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7911 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7912 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7913 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7914 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7915 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7916 if (!reason || !owner)
7919 set_time = set ? strtoul(set, NULL, 0) : now;
7920 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7922 expires_time = strtoul(s_expires, NULL, 0);
7924 expires_time = set_time + atoi(s_duration);
7928 if(!reason || (expires_time && (expires_time < now)))
7931 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7934 static struct suspended *
7935 chanserv_read_suspended(dict_t obj)
7937 struct suspended *suspended = calloc(1, sizeof(*suspended));
7941 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7942 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7943 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7944 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7945 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7946 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7947 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7948 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7949 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7950 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7955 chanserv_channel_read(const char *key, struct record_data *hir)
7957 struct suspended *suspended;
7958 struct mod_chanmode *modes;
7959 struct chanNode *cNode;
7960 struct chanData *cData;
7961 struct dict *channel, *obj;
7962 char *str, *argv[10];
7966 channel = hir->d.object;
7968 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7971 cNode = AddChannel(key, now, NULL, NULL);
7974 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7977 cData = register_channel(cNode, str);
7980 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7984 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7986 enum levelOption lvlOpt;
7987 enum charOption chOpt;
7989 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7990 cData->flags = atoi(str);
7992 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7994 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7996 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7997 else if(levelOptions[lvlOpt].old_flag)
7999 if(cData->flags & levelOptions[lvlOpt].old_flag)
8000 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8002 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8006 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8008 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8010 cData->chOpts[chOpt] = str[0];
8013 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8015 enum levelOption lvlOpt;
8016 enum charOption chOpt;
8019 cData->flags = base64toint(str, 5);
8020 count = strlen(str += 5);
8021 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8024 if(levelOptions[lvlOpt].old_flag)
8026 if(cData->flags & levelOptions[lvlOpt].old_flag)
8027 lvl = levelOptions[lvlOpt].flag_value;
8029 lvl = levelOptions[lvlOpt].default_value;
8031 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8033 case 'c': lvl = UL_COOWNER; break;
8034 case 'm': lvl = UL_MASTER; break;
8035 case 'n': lvl = UL_OWNER+1; break;
8036 case 'o': lvl = UL_OP; break;
8037 case 'p': lvl = UL_PEON; break;
8038 case 'w': lvl = UL_OWNER; break;
8039 default: lvl = 0; break;
8041 cData->lvlOpts[lvlOpt] = lvl;
8043 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8044 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8047 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8049 cData->expiry = atoi(str);
8050 if(cData->expiry > 0) {
8051 if(cData->expiry > now) {
8052 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8054 timeq_add(1, chanserv_expire_channel, cData);
8061 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8063 suspended = chanserv_read_suspended(obj);
8064 cData->suspended = suspended;
8065 suspended->cData = cData;
8066 /* We could use suspended->expires and suspended->revoked to
8067 * set the CHANNEL_SUSPENDED flag, but we don't. */
8069 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8071 suspended = calloc(1, sizeof(*suspended));
8072 suspended->issued = 0;
8073 suspended->revoked = 0;
8074 suspended->suspender = strdup(str);
8075 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8076 suspended->expires = str ? atoi(str) : 0;
8077 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8078 suspended->reason = strdup(str ? str : "No reason");
8079 suspended->previous = NULL;
8080 cData->suspended = suspended;
8081 suspended->cData = cData;
8085 cData->flags &= ~CHANNEL_SUSPENDED;
8086 suspended = NULL; /* to squelch a warning */
8089 if(IsSuspended(cData)) {
8090 if(suspended->expires > now)
8091 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8092 else if(suspended->expires)
8093 cData->flags &= ~CHANNEL_SUSPENDED;
8096 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8097 struct mod_chanmode change;
8098 mod_chanmode_init(&change);
8100 change.args[0].mode = MODE_CHANOP;
8101 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8102 mod_chanmode_announce(chanserv, cNode, &change);
8105 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8106 cData->registered = str ? strtoul(str, NULL, 0) : now;
8107 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8108 cData->visited = str ? strtoul(str, NULL, 0) : now;
8109 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8110 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8111 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8112 cData->max = str ? atoi(str) : 0;
8113 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8114 cData->max_time = str ? atoi(str) : 0;
8115 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8116 cData->greeting = str ? strdup(str) : NULL;
8117 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8118 cData->user_greeting = str ? strdup(str) : NULL;
8119 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8120 cData->topic_mask = str ? strdup(str) : NULL;
8121 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8122 cData->topic = str ? strdup(str) : NULL;
8124 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8126 cData->vote = str ? strdup(str) : NULL;
8127 dict_delete(cData->vote_options);
8128 cData->vote_options = dict_new();
8129 dict_set_free_data(cData->vote_options, free_vote_options);
8130 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8131 cData->vote_start = str ? atoi(str) : 0;
8132 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8133 for(it = dict_first(obj); it; it = iter_next(it)) {
8134 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8138 if(!IsSuspended(cData)
8139 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8140 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8141 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8142 cData->modes = *modes;
8144 cData->modes.modes_set |= MODE_REGISTERED;
8145 if(cData->modes.argc > 1)
8146 cData->modes.argc = 1;
8147 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8148 mod_chanmode_free(modes);
8151 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8152 for(it = dict_first(obj); it; it = iter_next(it))
8153 user_read_helper(iter_key(it), iter_data(it), cData);
8155 if(!cData->users && !IsProtected(cData))
8157 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8158 unregister_channel(cData, "has empty user list.");
8162 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8163 for(it = dict_first(obj); it; it = iter_next(it))
8164 ban_read_helper(iter_key(it), iter_data(it), cData);
8166 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8167 for(it = dict_first(obj); it; it = iter_next(it))
8169 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8170 struct record_data *rd = iter_data(it);
8171 const char *note, *setter;
8173 if(rd->type != RECDB_OBJECT)
8175 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8179 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8181 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8183 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8187 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8188 if(!setter) setter = "<unknown>";
8189 chanserv_add_channel_note(cData, ntype, setter, note);
8197 chanserv_dnr_read(const char *key, struct record_data *hir)
8199 const char *setter, *reason, *str;
8200 struct do_not_register *dnr;
8201 unsigned long expiry;
8203 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8206 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8209 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8212 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8215 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8216 expiry = str ? strtoul(str, NULL, 0) : 0;
8217 if(expiry && expiry <= now)
8219 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8222 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8224 dnr->set = atoi(str);
8230 chanserv_saxdb_read(struct dict *database)
8232 struct dict *section;
8235 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8236 for(it = dict_first(section); it; it = iter_next(it))
8237 chanserv_note_type_read(iter_key(it), iter_data(it));
8239 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8240 for(it = dict_first(section); it; it = iter_next(it))
8241 chanserv_channel_read(iter_key(it), iter_data(it));
8243 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8244 for(it = dict_first(section); it; it = iter_next(it))
8245 chanserv_dnr_read(iter_key(it), iter_data(it));
8251 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8253 int high_present = 0;
8254 saxdb_start_record(ctx, KEY_USERS, 1);
8255 for(; uData; uData = uData->next)
8257 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8259 saxdb_start_record(ctx, uData->handle->handle, 0);
8260 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8261 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8263 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8264 if(uData->channel->vote && uData->voted)
8265 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8266 if(uData->channel->vote && uData->votefor)
8267 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8269 saxdb_write_string(ctx, KEY_INFO, uData->info);
8270 saxdb_end_record(ctx);
8272 saxdb_end_record(ctx);
8273 return high_present;
8277 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8281 saxdb_start_record(ctx, KEY_BANS, 1);
8282 for(; bData; bData = bData->next)
8284 saxdb_start_record(ctx, bData->mask, 0);
8285 saxdb_write_int(ctx, KEY_SET, bData->set);
8286 if(bData->triggered)
8287 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8289 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8291 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8293 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8294 saxdb_end_record(ctx);
8296 saxdb_end_record(ctx);
8300 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8302 saxdb_start_record(ctx, name, 0);
8303 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8304 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8306 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8308 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8310 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8312 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8313 saxdb_end_record(ctx);
8317 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8321 enum levelOption lvlOpt;
8322 enum charOption chOpt;
8325 saxdb_start_record(ctx, channel->channel->name, 1);
8327 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8328 saxdb_write_int(ctx, KEY_MAX, channel->max);
8329 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8331 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8332 if(channel->registrar)
8333 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8334 if(channel->greeting)
8335 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8336 if(channel->user_greeting)
8337 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8338 if(channel->topic_mask)
8339 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8340 if(channel->suspended)
8341 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8343 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8346 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8347 if(channel->vote_start)
8348 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8349 if (dict_size(channel->vote_options)) {
8350 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8351 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8352 struct vote_option *vOpt = iter_data(it);
8354 sprintf(str,"%i",vOpt->option_id);
8355 saxdb_start_record(ctx, str, 0);
8357 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8359 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8360 saxdb_end_record(ctx);
8362 saxdb_end_record(ctx);
8366 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8367 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8368 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8369 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8370 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8372 buf[0] = channel->chOpts[chOpt];
8374 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8376 saxdb_end_record(ctx);
8378 if(channel->modes.modes_set || channel->modes.modes_clear)
8380 mod_chanmode_format(&channel->modes, buf);
8381 saxdb_write_string(ctx, KEY_MODES, buf);
8384 high_present = chanserv_write_users(ctx, channel->users);
8385 chanserv_write_bans(ctx, channel->bans);
8387 if(dict_size(channel->notes))
8391 saxdb_start_record(ctx, KEY_NOTES, 1);
8392 for(it = dict_first(channel->notes); it; it = iter_next(it))
8394 struct note *note = iter_data(it);
8395 saxdb_start_record(ctx, iter_key(it), 0);
8396 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8397 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8398 saxdb_end_record(ctx);
8400 saxdb_end_record(ctx);
8403 if(channel->ownerTransfer)
8404 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8405 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8406 saxdb_end_record(ctx);
8410 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8414 saxdb_start_record(ctx, ntype->name, 0);
8415 switch(ntype->set_access_type)
8417 case NOTE_SET_CHANNEL_ACCESS:
8418 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8420 case NOTE_SET_CHANNEL_SETTER:
8421 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8423 case NOTE_SET_PRIVILEGED: default:
8424 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8427 switch(ntype->visible_type)
8429 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8430 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8431 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8433 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8434 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8435 saxdb_end_record(ctx);
8439 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8441 struct do_not_register *dnr;
8442 dict_iterator_t it, next;
8444 for(it = dict_first(dnrs); it; it = next)
8446 next = iter_next(it);
8447 dnr = iter_data(it);
8448 if(dnr->expires && dnr->expires <= now)
8450 dict_remove(dnrs, iter_key(it));
8453 saxdb_start_record(ctx, dnr->chan_name, 0);
8455 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8457 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8458 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8459 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8460 saxdb_end_record(ctx);
8465 chanserv_saxdb_write(struct saxdb_context *ctx)
8468 struct chanData *channel;
8471 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8472 for(it = dict_first(note_types); it; it = iter_next(it))
8473 chanserv_write_note_type(ctx, iter_data(it));
8474 saxdb_end_record(ctx);
8477 saxdb_start_record(ctx, KEY_DNR, 1);
8478 write_dnrs_helper(ctx, handle_dnrs);
8479 write_dnrs_helper(ctx, plain_dnrs);
8480 write_dnrs_helper(ctx, mask_dnrs);
8481 saxdb_end_record(ctx);
8484 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8485 for(channel = channelList; channel; channel = channel->next)
8486 chanserv_write_channel(ctx, channel);
8487 saxdb_end_record(ctx);
8493 chanserv_db_cleanup(void) {
8495 unreg_part_func(handle_part);
8497 unregister_channel(channelList, "terminating.");
8498 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8499 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8500 free(chanserv_conf.support_channels.list);
8501 dict_delete(handle_dnrs);
8502 dict_delete(plain_dnrs);
8503 dict_delete(mask_dnrs);
8504 dict_delete(note_types);
8505 free_string_list(chanserv_conf.eightball);
8506 free_string_list(chanserv_conf.old_ban_names);
8507 free_string_list(chanserv_conf.set_shows);
8508 free(set_shows_list.list);
8509 free(uset_shows_list.list);
8512 struct userData *helper = helperList;
8513 helperList = helperList->next;
8518 #if defined(GCC_VARMACROS)
8519 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8520 #elif defined(C99_VARMACROS)
8521 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8523 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8524 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8527 init_chanserv(const char *nick)
8529 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8530 conf_register_reload(chanserv_conf_read);
8534 reg_server_link_func(handle_server_link);
8535 reg_new_channel_func(handle_new_channel);
8536 reg_join_func(handle_join);
8537 reg_part_func(handle_part);
8538 reg_kick_func(handle_kick);
8539 reg_topic_func(handle_topic);
8540 reg_mode_change_func(handle_mode);
8541 reg_nick_change_func(handle_nick_change);
8542 reg_auth_func(handle_auth);
8545 reg_handle_rename_func(handle_rename);
8546 reg_unreg_func(handle_unreg);
8548 handle_dnrs = dict_new();
8549 dict_set_free_data(handle_dnrs, free);
8550 plain_dnrs = dict_new();
8551 dict_set_free_data(plain_dnrs, free);
8552 mask_dnrs = dict_new();
8553 dict_set_free_data(mask_dnrs, free);
8555 reg_svccmd_unbind_func(handle_svccmd_unbind);
8556 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8557 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8558 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8559 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8560 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8561 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8562 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8563 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8564 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8565 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8566 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8567 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8568 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8570 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8571 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8573 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8574 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8575 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8576 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8577 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8579 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8580 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8581 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8582 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8583 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8585 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8586 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8587 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8588 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8590 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8591 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8592 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8593 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8594 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8595 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8596 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8597 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8599 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8600 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8601 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8602 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8603 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8604 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8605 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8606 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8607 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8608 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8609 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8610 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8611 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8612 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8613 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8615 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8616 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8617 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8618 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8619 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8621 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8622 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8624 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8625 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8627 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8628 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8629 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8630 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8631 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8632 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8633 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8634 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8636 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8637 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8639 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8640 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8641 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8642 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8644 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8645 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8646 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8647 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8648 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8650 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8651 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8652 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8653 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8654 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8655 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8657 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8658 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8659 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8660 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8661 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8662 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8663 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8664 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8666 /* Channel options */
8667 DEFINE_CHANNEL_OPTION(defaulttopic);
8668 DEFINE_CHANNEL_OPTION(topicmask);
8669 DEFINE_CHANNEL_OPTION(greeting);
8670 DEFINE_CHANNEL_OPTION(usergreeting);
8671 DEFINE_CHANNEL_OPTION(modes);
8672 DEFINE_CHANNEL_OPTION(enfops);
8673 DEFINE_CHANNEL_OPTION(giveops);
8674 DEFINE_CHANNEL_OPTION(protect);
8675 DEFINE_CHANNEL_OPTION(enfmodes);
8676 DEFINE_CHANNEL_OPTION(enftopic);
8677 DEFINE_CHANNEL_OPTION(pubcmd);
8678 DEFINE_CHANNEL_OPTION(givevoice);
8679 DEFINE_CHANNEL_OPTION(userinfo);
8680 DEFINE_CHANNEL_OPTION(dynlimit);
8681 DEFINE_CHANNEL_OPTION(topicsnarf);
8682 DEFINE_CHANNEL_OPTION(vote);
8683 DEFINE_CHANNEL_OPTION(nodelete);
8684 DEFINE_CHANNEL_OPTION(toys);
8685 DEFINE_CHANNEL_OPTION(setters);
8686 DEFINE_CHANNEL_OPTION(topicrefresh);
8687 DEFINE_CHANNEL_OPTION(ctcpusers);
8688 DEFINE_CHANNEL_OPTION(ctcpreaction);
8689 DEFINE_CHANNEL_OPTION(inviteme);
8690 DEFINE_CHANNEL_OPTION(unreviewed);
8691 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8692 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8693 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8695 DEFINE_CHANNEL_OPTION(offchannel);
8696 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8698 /* Alias set topic to set defaulttopic for compatibility. */
8699 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8702 DEFINE_USER_OPTION(noautoop);
8703 DEFINE_USER_OPTION(autoinvite);
8704 DEFINE_USER_OPTION(info);
8706 /* Alias uset autovoice to uset autoop. */
8707 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8709 note_types = dict_new();
8710 dict_set_free_data(note_types, chanserv_deref_note_type);
8713 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8714 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8715 service_register(chanserv)->trigger = '!';
8716 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8718 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8720 if(chanserv_conf.channel_expire_frequency)
8721 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8723 if(chanserv_conf.dnr_expire_frequency)
8724 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8726 if(chanserv_conf.refresh_period)
8728 unsigned long next_refresh;
8729 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8730 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8733 reg_exit_func(chanserv_db_cleanup);
8734 message_register_table(msgtab);