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))
3861 string_buffer_append_printf(&sbuf, "[%s (%d", cData->channel->name, uData->access);
3862 if(uData->flags != USER_AUTO_OP)
3863 string_buffer_append(&sbuf, ',');
3864 if(IsUserSuspended(uData))
3865 string_buffer_append(&sbuf, 's');
3866 if(IsUserAutoOp(uData))
3868 if(uData->access >= cData->lvlOpts[lvlGiveOps])
3869 string_buffer_append(&sbuf, 'o');
3870 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
3871 string_buffer_append(&sbuf, 'v');
3873 if(IsUserAutoInvite(uData) && (uData->access >= cData->lvlOpts[lvlInviteMe]))
3874 string_buffer_append(&sbuf, 'i');
3876 string_buffer_append_printf(&sbuf, ")] %s", uData->info);
3878 string_buffer_append_string(&sbuf, ")]");
3879 string_buffer_append(&sbuf, '\0');
3880 send_message_type(4, user, cmd->parent->bot, "%s", sbuf.list);
3884 reply("CSMSG_MYACCESS_COUNT_1", target_handle->handle, ccount, ocount);
3886 reply("CSMSG_MYACCESS_COUNT", target_handle->handle, ccount, ocount);
3892 static CHANSERV_FUNC(cmd_access)
3894 struct userNode *target;
3895 struct handle_info *target_handle;
3896 struct userData *uData;
3898 char prefix[MAXLEN];
3903 target_handle = target->handle_info;
3905 else if((target = GetUserH(argv[1])))
3907 target_handle = target->handle_info;
3909 else if(argv[1][0] == '*')
3911 if(!(target_handle = get_handle_info(argv[1]+1)))
3913 reply("MSG_HANDLE_UNKNOWN", argv[1]+1);
3919 reply("MSG_NICK_UNKNOWN", argv[1]);
3923 assert(target || target_handle);
3925 if(target == chanserv)
3927 reply("CSMSG_IS_CHANSERV");
3935 reply("CSMSG_LAZY_SMURF_TARGET", target->nick, chanserv_conf.irc_operator_epithet);
3940 reply("MSG_USER_AUTHENTICATE", target->nick);
3943 reply("MSG_AUTHENTICATE");
3949 const char *epithet = NULL, *type = NULL;
3952 epithet = chanserv_conf.irc_operator_epithet;
3953 type = user_find_message(user, "CSMSG_OPERATOR_TITLE");
3955 else if(IsNetworkHelper(target))
3957 epithet = chanserv_conf.network_helper_epithet;
3958 type = user_find_message(user, "CSMSG_UC_H_TITLE");
3960 else if(IsSupportHelper(target))
3962 epithet = chanserv_conf.support_helper_epithet;
3963 type = user_find_message(user, "CSMSG_LC_H_TITLE");
3967 if(target_handle->epithet)
3968 reply("CSMSG_SMURF_TARGET", target->nick, target_handle->epithet, type);
3970 reply("CSMSG_SMURF_TARGET", target->nick, epithet, type);
3972 sprintf(prefix, "%s (%s)", target->nick, target_handle->handle);
3976 sprintf(prefix, "%s", target_handle->handle);
3979 if(!channel->channel_info)
3981 reply("CSMSG_NOT_REGISTERED", channel->name);
3985 helping = HANDLE_FLAGGED(target_handle, HELPING)
3986 && ((target_handle->opserv_level >= chanserv_conf.nodelete_level) || !IsProtected(channel->channel_info));
3987 if((uData = GetTrueChannelAccess(channel->channel_info, target_handle)))
3989 reply((helping ? "CSMSG_HELPER_HAS_ACCESS" : "CSMSG_USER_HAS_ACCESS"), prefix, uData->access, channel->name);
3990 /* To prevent possible information leaks, only show infolines
3991 * if the requestor is in the channel or it's their own
3993 if(uData->info && (GetUserMode(channel, user) || (target_handle == user->handle_info)))
3995 send_message_type(4, user, cmd->parent->bot, "[%s] %s", (target ? target->nick : target_handle->handle), uData->info);
3997 /* Likewise, only say it's suspended if the user has active
3998 * access in that channel or it's their own entry. */
3999 if(IsUserSuspended(uData)
4000 && (GetChannelUser(channel->channel_info, user->handle_info)
4001 || (user->handle_info == uData->handle)))
4003 reply("CSMSG_USER_SUSPENDED", (target ? target->nick : target_handle->handle), channel->name);
4008 reply((helping ? "CSMSG_HELPER_NO_ACCESS" : "CSMSG_USER_NO_ACCESS"), prefix, channel->name);
4015 zoot_list(struct listData *list)
4017 struct userData *uData;
4018 unsigned int start, curr, highest, lowest;
4019 struct helpfile_table tmp_table;
4020 const char **temp, *msg;
4022 if(list->table.length == 1)
4025 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4027 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4028 msg = user_find_message(list->user, "MSG_NONE");
4029 send_message_type(4, list->user, list->bot, " %s", msg);
4031 tmp_table.width = list->table.width;
4032 tmp_table.flags = list->table.flags;
4033 list->table.contents[0][0] = " ";
4034 highest = list->highest;
4035 if(list->lowest != 0)
4036 lowest = list->lowest;
4037 else if(highest < 100)
4040 lowest = highest - 100;
4041 for(start = curr = 1; curr < list->table.length; )
4043 uData = list->users[curr-1];
4044 list->table.contents[curr++][0] = " ";
4045 if((curr == list->table.length) || (list->users[curr-1]->access < lowest))
4048 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, lowest, highest, list->search);
4050 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, lowest, highest);
4051 temp = list->table.contents[--start];
4052 list->table.contents[start] = list->table.contents[0];
4053 tmp_table.contents = list->table.contents + start;
4054 tmp_table.length = curr - start;
4055 table_send(list->bot, list->user->nick, 0, NULL, tmp_table);
4056 list->table.contents[start] = temp;
4058 highest = lowest - 1;
4059 lowest = (highest < 100) ? 0 : (highest - 99);
4065 def_list(struct listData *list)
4069 send_message(list->user, list->bot, "CSMSG_ACCESS_SEARCH_HEADER", list->channel->name, list->lowest, list->highest, list->search);
4071 send_message(list->user, list->bot, "CSMSG_ACCESS_ALL_HEADER", list->channel->name, list->lowest, list->highest);
4072 table_send(list->bot, list->user->nick, 0, NULL, list->table);
4073 if(list->table.length == 1)
4075 msg = user_find_message(list->user, "MSG_NONE");
4076 send_message_type(4, list->user, list->bot, " %s", msg);
4081 userData_access_comp(const void *arg_a, const void *arg_b)
4083 const struct userData *a = *(struct userData**)arg_a;
4084 const struct userData *b = *(struct userData**)arg_b;
4086 if(a->access != b->access)
4087 res = b->access - a->access;
4089 res = irccasecmp(a->handle->handle, b->handle->handle);
4094 cmd_list_users(struct userNode *user, struct chanNode *channel, unsigned int argc, char *argv[], struct svccmd *cmd, unsigned short lowest, unsigned short highest)
4096 void (*send_list)(struct listData *);
4097 struct userData *uData;
4098 struct listData lData;
4099 unsigned int matches;
4103 lData.bot = cmd->parent->bot;
4104 lData.channel = channel;
4105 lData.lowest = lowest;
4106 lData.highest = highest;
4107 lData.search = (argc > 1) ? argv[1] : NULL;
4108 send_list = def_list;
4109 (void)zoot_list; /* since it doesn't show user levels */
4111 if(user->handle_info)
4113 switch(user->handle_info->userlist_style)
4115 case HI_STYLE_DEF: send_list = def_list; break;
4116 case HI_STYLE_ZOOT: send_list = def_list; break;
4120 lData.users = alloca(channel->channel_info->userCount * sizeof(struct userData *));
4122 for(uData = channel->channel_info->users; uData; uData = uData->next)
4124 if((uData->access < lowest)
4125 || (uData->access > highest)
4126 || (lData.search && !match_ircglob(uData->handle->handle, lData.search)))
4128 lData.users[matches++] = uData;
4130 qsort(lData.users, matches, sizeof(lData.users[0]), userData_access_comp);
4132 lData.table.length = matches+1;
4133 lData.table.width = 4;
4134 lData.table.flags = TABLE_NO_FREE;
4135 lData.table.contents = malloc(lData.table.length*sizeof(*lData.table.contents));
4136 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4137 lData.table.contents[0] = ary;
4140 ary[2] = "Last Seen";
4142 for(matches = 1; matches < lData.table.length; ++matches)
4144 char seen[INTERVALLEN];
4146 uData = lData.users[matches-1];
4147 ary = malloc(lData.table.width*sizeof(**lData.table.contents));
4148 lData.table.contents[matches] = ary;
4149 ary[0] = strtab(uData->access);
4150 ary[1] = uData->handle->handle;
4153 else if(!uData->seen)
4156 ary[2] = intervalString(seen, now - uData->seen, user->handle_info);
4157 ary[2] = strdup(ary[2]);
4158 if(IsUserSuspended(uData))
4159 ary[3] = "Suspended";
4160 else if(HANDLE_FLAGGED(uData->handle, FROZEN))
4161 ary[3] = "Vacation";
4162 else if(HANDLE_FLAGGED(uData->handle, BOT))
4168 for(matches = 1; matches < lData.table.length; ++matches)
4170 free((char*)lData.table.contents[matches][2]);
4171 free(lData.table.contents[matches]);
4173 free(lData.table.contents[0]);
4174 free(lData.table.contents);
4175 reply("CSMSG_TOTAL_USERS",(lData.table.length - 1),channel->name);
4179 static CHANSERV_FUNC(cmd_users)
4181 return cmd_list_users(CSFUNC_ARGS, 1, UL_OWNER);
4184 static CHANSERV_FUNC(cmd_wlist)
4186 return cmd_list_users(CSFUNC_ARGS, UL_OWNER, UL_OWNER);
4189 static CHANSERV_FUNC(cmd_clist)
4191 return cmd_list_users(CSFUNC_ARGS, UL_COOWNER, UL_OWNER-1);
4194 static CHANSERV_FUNC(cmd_mlist)
4196 return cmd_list_users(CSFUNC_ARGS, UL_MASTER, UL_COOWNER-1);
4199 static CHANSERV_FUNC(cmd_olist)
4201 return cmd_list_users(CSFUNC_ARGS, UL_OP, UL_MASTER-1);
4204 static CHANSERV_FUNC(cmd_plist)
4206 return cmd_list_users(CSFUNC_ARGS, 1, UL_OP-1);
4209 static CHANSERV_FUNC(cmd_bans)
4211 struct userNode *search_u = NULL;
4212 struct helpfile_table tbl;
4213 unsigned int matches = 0, timed = 0, search_wilds = 0, ii;
4214 char t_buffer[INTERVALLEN], e_buffer[INTERVALLEN], *search;
4215 const char *msg_never, *triggered, *expires;
4216 struct banData *ban, **bans;
4220 else if(strchr(search = argv[1], '!'))
4223 search_wilds = search[strcspn(search, "?*")];
4225 else if(!(search_u = GetUserH(search)))
4226 reply("MSG_NICK_UNKNOWN", search);
4228 bans = alloca(channel->channel_info->banCount * sizeof(struct banData *));
4230 for(ban = channel->channel_info->bans; ban; ban = ban->next)
4234 if(!user_matches_glob(search_u, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
4239 if(search_wilds ? !match_ircglobs(search, ban->mask) : !match_ircglob(search, ban->mask))
4242 bans[matches++] = ban;
4247 tbl.length = matches + 1;
4248 tbl.width = 4 + timed;
4250 tbl.flags = TABLE_NO_FREE;
4251 tbl.contents = malloc(tbl.length * sizeof(tbl.contents[0]));
4252 tbl.contents[0] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4253 tbl.contents[0][0] = "Mask";
4254 tbl.contents[0][1] = "Set By";
4255 tbl.contents[0][2] = "Triggered";
4258 tbl.contents[0][3] = "Expires";
4259 tbl.contents[0][4] = "Reason";
4262 tbl.contents[0][3] = "Reason";
4265 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4267 free(tbl.contents[0]);
4272 msg_never = user_find_message(user, "MSG_NEVER");
4273 for(ii = 0; ii < matches; )
4279 else if(ban->expires)
4280 expires = intervalString(e_buffer, ban->expires - now, user->handle_info);
4282 expires = msg_never;
4285 triggered = intervalString(t_buffer, now - ban->triggered, user->handle_info);
4287 triggered = msg_never;
4289 tbl.contents[++ii] = malloc(tbl.width * sizeof(tbl.contents[0][0]));
4290 tbl.contents[ii][0] = ban->mask;
4291 tbl.contents[ii][1] = ban->owner;
4292 tbl.contents[ii][2] = strdup(triggered);
4295 tbl.contents[ii][3] = strdup(expires);
4296 tbl.contents[ii][4] = ban->reason;
4299 tbl.contents[ii][3] = ban->reason;
4301 table_send(cmd->parent->bot, user->nick, 0, NULL, tbl);
4302 reply("MSG_MATCH_COUNT", matches);
4303 for(ii = 1; ii < tbl.length; ++ii)
4305 free((char*)tbl.contents[ii][2]);
4307 free((char*)tbl.contents[ii][3]);
4308 free(tbl.contents[ii]);
4310 free(tbl.contents[0]);
4316 bad_topic(struct chanNode *channel, struct userNode *user, const char *new_topic)
4318 struct chanData *cData = channel->channel_info;
4319 if(check_user_level(channel, user, lvlEnfTopic, 1, 0))
4321 if(cData->topic_mask)
4322 return !match_ircglob(new_topic, cData->topic_mask);
4323 else if(cData->topic)
4324 return irccasecmp(new_topic, cData->topic);
4329 static CHANSERV_FUNC(cmd_topic)
4331 struct chanData *cData;
4334 cData = channel->channel_info;
4339 SetChannelTopic(channel, chanserv, cData->topic, 1);
4340 reply("CSMSG_TOPIC_SET", cData->topic);
4344 reply("CSMSG_NO_TOPIC", channel->name);
4348 topic = unsplit_string(argv + 1, argc - 1, NULL);
4349 /* If they say "!topic *", use an empty topic. */
4350 if((topic[0] == '*') && (topic[1] == 0))
4352 if(bad_topic(channel, user, topic))
4354 char *topic_mask = cData->topic_mask;
4357 char new_topic[TOPICLEN+1], tchar;
4358 int pos=0, starpos=-1, dpos=0, len;
4360 while((tchar = topic_mask[pos++]) && (dpos <= TOPICLEN))
4367 len = strlen(topic);
4368 if((dpos + len) > TOPICLEN)
4369 len = TOPICLEN + 1 - dpos;
4370 memcpy(new_topic+dpos, topic, len);
4374 case '\\': tchar = topic_mask[pos++]; /* and fall through */
4375 default: new_topic[dpos++] = tchar; break;
4378 if((dpos > TOPICLEN) || tchar)
4381 reply("CSMSG_TOPICMASK_CONFLICT1", channel->name, topic_mask);
4382 reply("CSMSG_TOPICMASK_CONFLICT2", TOPICLEN);
4385 new_topic[dpos] = 0;
4386 SetChannelTopic(channel, chanserv, new_topic, 1);
4388 reply("CSMSG_TOPIC_LOCKED", channel->name);
4393 SetChannelTopic(channel, chanserv, topic, 1);
4395 if(check_user_level(channel, user, lvlTopicSnarf, 1, 0))
4397 /* Grab the topic and save it as the default topic. */
4399 cData->topic = strdup(channel->topic);
4405 static CHANSERV_FUNC(cmd_mode)
4407 struct userData *uData;
4408 struct mod_chanmode *change;
4414 change = &channel->channel_info->modes;
4415 if(change->modes_set || change->modes_clear) {
4416 modcmd_chanmode_announce(change);
4417 reply("CSMSG_DEFAULTED_MODES", channel->name);
4419 reply("CSMSG_NO_MODES", channel->name);
4423 uData = GetChannelUser(channel->channel_info, user->handle_info);
4425 base_oplevel = MAXOPLEVEL;
4426 else if (uData->access >= UL_OWNER)
4429 base_oplevel = 1 + UL_OWNER - uData->access;
4430 change = mod_chanmode_parse(channel, user, argv+1, argc-1, MCP_KEY_FREE|MCP_IGN_REGISTERED|MCP_NO_APASS, base_oplevel);
4433 reply("MSG_INVALID_MODES", unsplit_string(argv+1, argc-1, NULL));
4437 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
4438 && mode_lock_violated(&channel->channel_info->modes, change))
4441 mod_chanmode_format(&channel->channel_info->modes, modes);
4442 reply("CSMSG_MODE_LOCKED", modes, channel->name);
4446 modcmd_chanmode_announce(change);
4447 mod_chanmode_format(change, fmt);
4448 mod_chanmode_free(change);
4449 reply("CSMSG_MODES_SET", fmt);
4454 chanserv_del_invite_mark(void *data)
4456 struct ChanUser *chanuser = data;
4457 struct chanNode *channel = chanuser->chan;
4459 if(!channel) return;
4460 for(i = 0; i < channel->invited.used; i++)
4462 if(channel->invited.list[i] == chanuser->user) {
4463 userList_remove(&channel->invited, chanuser->user);
4469 static CHANSERV_FUNC(cmd_invite)
4471 struct userData *uData;
4472 struct userNode *invite;
4473 struct ChanUser *chanuser;
4476 uData = GetChannelUser(channel->channel_info, user->handle_info);
4480 if(!(invite = GetUserH(argv[1])))
4482 reply("MSG_NICK_UNKNOWN", argv[1]);
4489 if(GetUserMode(channel, invite))
4491 reply("CSMSG_ALREADY_PRESENT", invite->nick, channel->name);
4495 for(i = 0; i < channel->invited.used; i++)
4497 if(channel->invited.list[i] == invite) {
4498 reply("CSMSG_ALREADY_INVITED", invite->nick, channel->name);
4507 char *reason = unsplit_string(argv + 2, argc - 2, NULL);
4508 send_message(invite, chanserv, "CSMSG_INVITING_YOU_REASON", user->nick, channel->name, reason);
4511 send_message(invite, chanserv, "CSMSG_INVITING_YOU", user->nick, channel->name);
4513 irc_invite(chanserv, invite, channel);
4515 reply("CSMSG_INVITED_USER", argv[1], channel->name);
4517 userList_append(&channel->invited, invite);
4518 chanuser = calloc(1, sizeof(*chanuser));
4519 chanuser->user=invite;
4520 chanuser->chan=channel;
4521 timeq_add(now + chanserv_conf.invited_timeout, chanserv_del_invite_mark, chanuser);
4526 static CHANSERV_FUNC(cmd_inviteme)
4528 if(GetUserMode(channel, user))
4530 reply("CSMSG_YOU_ALREADY_PRESENT", channel->name);
4533 if(channel->channel_info
4534 && !check_user_level(channel, user, lvlInviteMe, 1, 0))
4536 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
4539 irc_invite(cmd->parent->bot, user, channel);
4543 static CHANSERV_FUNC(cmd_invitemeall)
4545 struct handle_info *target = user->handle_info;
4546 struct userData *uData;
4548 if(!target->channels)
4550 reply("CSMSG_SQUAT_ACCESS", target->handle);
4554 for(uData = target->channels; uData; uData = uData->u_next)
4556 struct chanData *cData = uData->channel;
4557 if(uData->access >= cData->lvlOpts[lvlInviteMe])
4559 irc_invite(cmd->parent->bot, user, cData->channel);
4566 show_suspension_info(struct svccmd *cmd, struct userNode *user, struct suspended *suspended)
4569 char buf1[INTERVALLEN], buf2[INTERVALLEN];
4571 /* We display things based on two dimensions:
4572 * - Issue time: present or absent
4573 * - Expiration: revoked, expired, expires in future, or indefinite expiration
4574 * (in order of precedence, so something both expired and revoked
4575 * only counts as revoked)
4577 combo = (suspended->issued ? 4 : 0)
4578 + (suspended->revoked ? 3 : suspended->expires ? ((suspended->expires < now) ? 2 : 1) : 0);
4580 case 0: /* no issue time, indefinite expiration */
4581 reply("CSMSG_CHANNEL_SUSPENDED_0", suspended->suspender, suspended->reason);
4583 case 1: /* no issue time, expires in future */
4584 intervalString(buf1, suspended->expires-now, user->handle_info);
4585 reply("CSMSG_CHANNEL_SUSPENDED_1", suspended->suspender, buf1, suspended->reason);
4587 case 2: /* no issue time, expired */
4588 intervalString(buf1, now-suspended->expires, user->handle_info);
4589 reply("CSMSG_CHANNEL_SUSPENDED_2", suspended->suspender, buf1, suspended->reason);
4591 case 3: /* no issue time, revoked */
4592 intervalString(buf1, now-suspended->revoked, user->handle_info);
4593 reply("CSMSG_CHANNEL_SUSPENDED_3", suspended->suspender, buf1, suspended->reason);
4595 case 4: /* issue time set, indefinite expiration */
4596 intervalString(buf1, now-suspended->issued, user->handle_info);
4597 reply("CSMSG_CHANNEL_SUSPENDED_4", buf1, suspended->suspender, suspended->reason);
4599 case 5: /* issue time set, expires in future */
4600 intervalString(buf1, now-suspended->issued, user->handle_info);
4601 intervalString(buf2, suspended->expires-now, user->handle_info);
4602 reply("CSMSG_CHANNEL_SUSPENDED_5", buf1, suspended->suspender, buf2, suspended->reason);
4604 case 6: /* issue time set, expired */
4605 intervalString(buf1, now-suspended->issued, user->handle_info);
4606 intervalString(buf2, now-suspended->expires, user->handle_info);
4607 reply("CSMSG_CHANNEL_SUSPENDED_6", buf1, suspended->suspender, buf2, suspended->reason);
4609 case 7: /* issue time set, revoked */
4610 intervalString(buf1, now-suspended->issued, user->handle_info);
4611 intervalString(buf2, now-suspended->revoked, user->handle_info);
4612 reply("CSMSG_CHANNEL_SUSPENDED_7", buf1, suspended->suspender, buf2, suspended->reason);
4615 log_module(CS_LOG, LOG_ERROR, "Invalid combo value %d in show_suspension_info()", combo);
4620 static CHANSERV_FUNC(cmd_info)
4622 char modes[MAXLEN], buffer[INTERVALLEN];
4623 struct userData *uData, *owner;
4624 struct chanData *cData;
4625 struct do_not_register *dnr;
4630 cData = channel->channel_info;
4631 reply("CSMSG_CHANNEL_INFO", channel->name);
4633 uData = GetChannelUser(cData, user->handle_info);
4634 if(uData && (uData->access >= cData->lvlOpts[lvlGiveOps]))
4636 mod_chanmode_format(&cData->modes, modes);
4637 reply("CSMSG_CHANNEL_TOPIC", cData->topic);
4638 reply("CSMSG_CHANNEL_MODES", modes[0] ? modes : user_find_message(user, "MSG_NONE"));
4641 for(it = dict_first(cData->notes); it; it = iter_next(it))
4645 note = iter_data(it);
4646 if(!note_type_visible_to_user(cData, note->type, user))
4649 padding = PADLEN - 1 - strlen(iter_key(it));
4650 reply("CSMSG_CHANNEL_NOTE", iter_key(it), padding > 0 ? padding : 1, "", note->note);
4653 if(cData->max_time) {
4654 reply("CSMSG_CHANNEL_MAX_TIME", cData->max, intervalString(buffer, now - cData->max_time, user->handle_info));
4656 reply("CSMSG_CHANNEL_MAX", cData->max);
4658 for(owner = cData->users; owner; owner = owner->next)
4659 if(owner->access == UL_OWNER)
4660 reply("CSMSG_CHANNEL_OWNER", owner->handle->handle);
4661 reply("CSMSG_CHANNEL_USERS", cData->userCount);
4662 reply("CSMSG_CHANNEL_BANS", cData->banCount);
4663 reply("CSMSG_CHANNEL_VISITED", intervalString(buffer, now - cData->visited, user->handle_info));
4665 privileged = IsStaff(user);
4667 reply("CSMSG_CHANNEL_REGISTERED", intervalString(buffer, now - cData->registered, user->handle_info));
4668 if(((uData && uData->access >= UL_COOWNER) || privileged) && cData->registrar)
4669 reply("CSMSG_CHANNEL_REGISTRAR", cData->registrar);
4671 if(privileged && (dnr = chanserv_is_dnr(channel->name, NULL)))
4672 chanserv_show_dnrs(user, cmd, channel->name, NULL);
4674 if(cData->suspended && ((uData && (uData->access >= UL_COOWNER)) || IsHelping(user)))
4676 struct suspended *suspended;
4677 reply((IsSuspended(cData) ? "CSMSG_CHANNEL_SUSPENDED" : "CSMSG_CHANNEL_HISTORY"), channel->name);
4678 for(suspended = cData->suspended; suspended; suspended = suspended->previous)
4679 show_suspension_info(cmd, user, suspended);
4681 else if(IsSuspended(cData))
4683 reply("CSMSG_CHANNEL_SUSPENDED", channel->name);
4684 show_suspension_info(cmd, user, cData->suspended);
4689 static CHANSERV_FUNC(cmd_netinfo)
4691 extern unsigned long boot_time;
4692 extern unsigned long burst_length;
4693 char interval[INTERVALLEN];
4695 reply("CSMSG_NETWORK_INFO");
4696 reply("CSMSG_NETWORK_SERVERS", dict_size(servers));
4697 reply("CSMSG_NETWORK_USERS", dict_size(clients));
4698 reply("CSMSG_NETWORK_OPERS", curr_opers.used);
4699 reply("CSMSG_NETWORK_CHANNELS", registered_channels);
4700 reply("CSMSG_NETWORK_BANS", banCount);
4701 reply("CSMSG_NETWORK_CHANUSERS", userCount);
4702 reply("CSMSG_SERVICES_UPTIME", intervalString(interval, time(NULL) - boot_time, user->handle_info));
4703 reply("CSMSG_BURST_LENGTH", intervalString(interval, burst_length, user->handle_info));
4708 send_staff_list(struct userNode *to, struct userList *list, int skip_flags)
4710 struct helpfile_table table;
4712 struct userNode *user;
4717 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4718 table.contents = alloca(list->used*sizeof(*table.contents));
4719 for(nn=0; nn<list->used; nn++)
4721 user = list->list[nn];
4722 if(user->modes & skip_flags)
4728 table.contents[table.length] = alloca(table.width*sizeof(**table.contents));
4731 nick = alloca(strlen(user->nick)+3);
4732 sprintf(nick, "(%s)", user->nick);
4736 table.contents[table.length][0] = nick;
4739 table_send(chanserv, to->nick, 0, NULL, table);
4742 static CHANSERV_FUNC(cmd_ircops)
4744 reply("CSMSG_STAFF_OPERS");
4745 send_staff_list(user, &curr_opers, FLAGS_SERVICE);
4749 static CHANSERV_FUNC(cmd_helpers)
4751 reply("CSMSG_STAFF_HELPERS");
4752 send_staff_list(user, &curr_helpers, FLAGS_OPER);
4756 static CHANSERV_FUNC(cmd_staff)
4758 reply("CSMSG_NETWORK_STAFF");
4759 cmd_ircops(CSFUNC_ARGS);
4760 cmd_helpers(CSFUNC_ARGS);
4764 static CHANSERV_FUNC(cmd_peek)
4766 struct modeNode *mn;
4767 char modes[MODELEN];
4769 struct helpfile_table table;
4770 int opcount = 0, voicecount = 0, srvcount = 0;
4772 irc_make_chanmode(channel, modes);
4774 reply("CSMSG_PEEK_INFO", channel->name);
4775 reply("CSMSG_PEEK_TOPIC", channel->topic);
4776 reply("CSMSG_PEEK_MODES", modes);
4780 table.flags = TABLE_REPEAT_ROWS | TABLE_NO_FREE | TABLE_NO_HEADERS;
4781 table.contents = alloca(channel->members.used*sizeof(*table.contents));
4782 for(n = 0; n < channel->members.used; n++)
4784 mn = channel->members.list[n];
4785 if(IsLocal(mn->user))
4787 else if(mn->modes & MODE_CHANOP)
4789 else if(mn->modes & MODE_VOICE)
4792 if(!(mn->modes & MODE_CHANOP) || IsLocal(mn->user))
4794 table.contents[table.length] = alloca(sizeof(**table.contents));
4795 table.contents[table.length][0] = mn->user->nick;
4799 reply("CSMSG_PEEK_USERS", channel->members.used, opcount, voicecount,
4800 (channel->members.used - opcount - voicecount - srvcount));
4804 reply("CSMSG_PEEK_OPS");
4805 table_send(chanserv, user->nick, 0, NULL, table);
4808 reply("CSMSG_PEEK_NO_OPS");
4812 static MODCMD_FUNC(cmd_wipeinfo)
4814 struct handle_info *victim;
4815 struct userData *ud, *actor, *real_actor;
4816 unsigned int override = 0;
4819 actor = GetChannelUser(channel->channel_info, user->handle_info);
4820 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
4821 if(!(victim = modcmd_get_handle_info(user, argv[1])))
4823 if(!(ud = GetTrueChannelAccess(channel->channel_info, victim)))
4825 reply("CSMSG_NO_CHAN_USER", argv[1], channel->name);
4828 if((ud->access >= actor->access) && (ud != actor))
4830 reply("MSG_USER_OUTRANKED", victim->handle);
4833 if((ud != real_actor) && (!real_actor || (ud->access >= real_actor->access)))
4834 override = CMD_LOG_OVERRIDE;
4838 reply("CSMSG_WIPED_INFO_LINE", argv[1], channel->name);
4839 return 1 | override;
4842 static CHANSERV_FUNC(cmd_resync)
4844 struct mod_chanmode *changes;
4845 struct chanData *cData = channel->channel_info;
4846 unsigned int ii, used;
4848 changes = mod_chanmode_alloc(channel->members.used * 2);
4849 for(ii = used = 0; ii < channel->members.used; ++ii)
4851 struct modeNode *mn = channel->members.list[ii];
4852 struct userData *uData;
4854 if(IsService(mn->user))
4857 uData = GetChannelAccess(cData, mn->user->handle_info);
4858 if(!cData->lvlOpts[lvlGiveOps]
4859 || (uData && uData->access >= cData->lvlOpts[lvlGiveOps]))
4861 if(!(mn->modes & MODE_CHANOP))
4863 if(!uData || IsUserAutoOp(uData))
4865 changes->args[used].mode = MODE_CHANOP;
4866 changes->args[used++].u.member = mn;
4867 if(!(mn->modes & MODE_VOICE))
4869 changes->args[used].mode = MODE_VOICE;
4870 changes->args[used++].u.member = mn;
4875 else if(!cData->lvlOpts[lvlGiveVoice]
4876 || (uData && uData->access >= cData->lvlOpts[lvlGiveVoice]))
4878 if(mn->modes & MODE_CHANOP)
4880 changes->args[used].mode = MODE_REMOVE | (mn->modes & ~MODE_VOICE);
4881 changes->args[used++].u.member = mn;
4883 if(!(mn->modes & MODE_VOICE) && (!uData || IsUserAutoOp(uData)))
4885 changes->args[used].mode = MODE_VOICE;
4886 changes->args[used++].u.member = mn;
4893 changes->args[used].mode = MODE_REMOVE | mn->modes;
4894 changes->args[used++].u.member = mn;
4898 changes->argc = used;
4899 modcmd_chanmode_announce(changes);
4900 mod_chanmode_free(changes);
4901 reply("CSMSG_RESYNCED_USERS", channel->name);
4905 static CHANSERV_FUNC(cmd_seen)
4907 struct userData *uData;
4908 struct handle_info *handle;
4909 char seen[INTERVALLEN];
4913 if(!irccasecmp(argv[1], chanserv->nick))
4915 reply("CSMSG_IS_CHANSERV");
4919 if(!(handle = get_handle_info(argv[1])))
4921 reply("MSG_HANDLE_UNKNOWN", argv[1]);
4925 if(!(uData = GetTrueChannelAccess(channel->channel_info, handle)))
4927 reply("CSMSG_NO_CHAN_USER", handle->handle, channel->name);
4932 reply("CSMSG_USER_PRESENT", handle->handle);
4933 else if(uData->seen)
4934 reply("CSMSG_USER_SEEN", handle->handle, channel->name, intervalString(seen, now - uData->seen, user->handle_info));
4936 reply("CSMSG_NEVER_SEEN", handle->handle, channel->name);
4938 if(!uData->present && HANDLE_FLAGGED(handle, FROZEN))
4939 reply("CSMSG_USER_VACATION", handle->handle);
4944 static MODCMD_FUNC(cmd_names)
4946 struct userNode *targ;
4947 struct userData *targData;
4948 unsigned int ii, pos;
4951 for(ii=pos=0; ii<channel->members.used; ++ii)
4953 targ = channel->members.list[ii]->user;
4954 targData = GetTrueChannelAccess(channel->channel_info, targ->handle_info);
4957 if(pos + strlen(targ->nick) + strlen(targ->handle_info->handle) + 8 > sizeof(buf))
4960 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4964 if(IsUserSuspended(targData))
4966 pos += sprintf(buf+pos, "%d:%s(%s)", targData->access, targ->nick, targ->handle_info->handle);
4969 reply("CSMSG_CHANNEL_NAMES", channel->name, buf);
4970 reply("CSMSG_END_NAMES", channel->name);
4975 note_type_visible_to_user(struct chanData *channel, struct note_type *ntype, struct userNode *user)
4977 switch(ntype->visible_type)
4979 case NOTE_VIS_ALL: return 1;
4980 case NOTE_VIS_CHANNEL_USERS: return !channel || !user || (user->handle_info && GetChannelUser(channel, user->handle_info));
4981 case NOTE_VIS_PRIVILEGED: default: return user && (IsOper(user) || IsSupportHelper(user) || IsNetworkHelper(user));
4986 note_type_settable_by_user(struct chanNode *channel, struct note_type *ntype, struct userNode *user)
4988 struct userData *uData;
4990 switch(ntype->set_access_type)
4992 case NOTE_SET_CHANNEL_ACCESS:
4993 if(!user->handle_info)
4995 if(!(uData = GetChannelUser(channel->channel_info, user->handle_info)))
4997 return uData->access >= ntype->set_access.min_ulevel;
4998 case NOTE_SET_CHANNEL_SETTER:
4999 return check_user_level(channel, user, lvlSetters, 1, 0);
5000 case NOTE_SET_PRIVILEGED: default:
5001 return IsHelping(user) && (user->handle_info->opserv_level >= ntype->set_access.min_opserv);
5005 static CHANSERV_FUNC(cmd_note)
5007 struct chanData *cData;
5009 struct note_type *ntype;
5011 cData = channel->channel_info;
5014 reply("CSMSG_NOT_REGISTERED", channel->name);
5018 /* If no arguments, show all visible notes for the channel. */
5024 for(count=0, it=dict_first(cData->notes); it; it=iter_next(it))
5026 note = iter_data(it);
5027 if(!note_type_visible_to_user(cData, note->type, user))
5030 reply("CSMSG_NOTELIST_HEADER", channel->name);
5031 reply("CSMSG_NOTE_FORMAT", iter_key(it), note->setter, note->note);
5034 reply("CSMSG_NOTELIST_END", channel->name);
5036 reply("CSMSG_NOTELIST_EMPTY", channel->name);
5038 /* If one argument, show the named note. */
5041 if((note = dict_find(cData->notes, argv[1], NULL))
5042 && note_type_visible_to_user(cData, note->type, user))
5044 reply("CSMSG_NOTE_FORMAT", note->type->name, note->setter, note->note);
5046 else if((ntype = dict_find(note_types, argv[1], NULL))
5047 && note_type_visible_to_user(NULL, ntype, user))
5049 reply("CSMSG_NO_SUCH_NOTE", channel->name, ntype->name);
5054 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5058 /* Assume they're trying to set a note. */
5062 ntype = dict_find(note_types, argv[1], NULL);
5065 reply("CSMSG_BAD_NOTE_TYPE", argv[1]);
5068 else if(note_type_settable_by_user(channel, ntype, user))
5070 note_text = unsplit_string(argv+2, argc-2, NULL);
5071 if((note = dict_find(cData->notes, argv[1], NULL)))
5072 reply("CSMSG_REPLACED_NOTE", ntype->name, channel->name, note->setter, note->note);
5073 chanserv_add_channel_note(cData, ntype, user->handle_info->handle, note_text);
5074 reply("CSMSG_NOTE_SET", ntype->name, channel->name);
5076 if(ntype->visible_type == NOTE_VIS_PRIVILEGED)
5078 /* The note is viewable to staff only, so return 0
5079 to keep the invocation from getting logged (or
5080 regular users can see it in !events). */
5086 reply("CSMSG_NO_ACCESS");
5093 static CHANSERV_FUNC(cmd_delnote)
5098 if(!(note = dict_find(channel->channel_info->notes, argv[1], NULL))
5099 || !note_type_settable_by_user(channel, note->type, user))
5101 reply("CSMSG_NO_SUCH_NOTE", channel->name, argv[1]);
5104 dict_remove(channel->channel_info->notes, note->type->name);
5105 reply("CSMSG_NOTE_REMOVED", argv[1], channel->name);
5109 static CHANSERV_FUNC(cmd_events)
5111 struct logSearch discrim;
5112 struct logReport report;
5113 unsigned int matches, limit;
5115 limit = (argc > 1) ? atoi(argv[1]) : 10;
5116 if(limit < 1 || limit > 200)
5119 memset(&discrim, 0, sizeof(discrim));
5120 discrim.masks.bot = chanserv;
5121 discrim.masks.channel_name = channel->name;
5123 discrim.masks.command = argv[2];
5124 discrim.limit = limit;
5125 discrim.max_time = INT_MAX;
5126 discrim.severities = 1 << LOG_COMMAND;
5127 report.reporter = chanserv;
5129 reply("CSMSG_EVENT_SEARCH_RESULTS");
5130 matches = log_entry_search(&discrim, log_report_entry, &report);
5132 reply("MSG_MATCH_COUNT", matches);
5134 reply("MSG_NO_MATCHES");
5138 static CHANSERV_FUNC(cmd_say)
5144 msg = unsplit_string(argv + 1, argc - 1, NULL);
5145 send_channel_message(channel, cmd->parent->bot, "%s", msg);
5147 else if(*argv[1] == '*' && argv[1][1] != '\0')
5149 struct handle_info *hi;
5150 struct userNode *authed;
5153 msg = unsplit_string(argv + 2, argc - 2, NULL);
5155 if (!(hi = get_handle_info(argv[1] + 1)))
5157 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5161 for (authed = hi->users; authed; authed = authed->next_authed)
5162 send_target_message(5, authed->nick, cmd->parent->bot, "%s", msg);
5164 else if(GetUserH(argv[1]))
5167 msg = unsplit_string(argv + 2, argc - 2, NULL);
5168 send_target_message(5, argv[1], cmd->parent->bot, "%s", msg);
5172 reply("MSG_NOT_TARGET_NAME");
5178 static CHANSERV_FUNC(cmd_emote)
5184 /* CTCP is so annoying. */
5185 msg = unsplit_string(argv + 1, argc - 1, NULL);
5186 send_channel_message(channel, cmd->parent->bot, "\001ACTION %s\001", msg);
5188 else if(*argv[1] == '*' && argv[1][1] != '\0')
5190 struct handle_info *hi;
5191 struct userNode *authed;
5194 msg = unsplit_string(argv + 2, argc - 2, NULL);
5196 if (!(hi = get_handle_info(argv[1] + 1)))
5198 reply("MSG_HANDLE_UNKNOWN", argv[1] + 1);
5202 for (authed = hi->users; authed; authed = authed->next_authed)
5203 send_target_message(5, authed->nick, cmd->parent->bot, "\001ACTION %s\001", msg);
5205 else if(GetUserH(argv[1]))
5207 msg = unsplit_string(argv + 2, argc - 2, NULL);
5208 send_target_message(5, argv[1], cmd->parent->bot, "\001ACTION %s\001", msg);
5212 reply("MSG_NOT_TARGET_NAME");
5218 struct channelList *
5219 chanserv_support_channels(void)
5221 return &chanserv_conf.support_channels;
5224 static CHANSERV_FUNC(cmd_expire)
5226 int channel_count = registered_channels;
5227 expire_channels(NULL);
5228 reply("CSMSG_CHANNELS_EXPIRED", channel_count - registered_channels);
5233 chanserv_expire_suspension(void *data)
5235 struct suspended *suspended = data;
5236 struct chanNode *channel;
5239 /* Update the channel registration data structure. */
5240 if(!suspended->expires || (now < suspended->expires))
5241 suspended->revoked = now;
5242 channel = suspended->cData->channel;
5243 suspended->cData->channel = channel;
5244 suspended->cData->flags &= ~CHANNEL_SUSPENDED;
5246 /* If appropriate, re-join ChanServ to the channel. */
5247 if(!IsOffChannel(suspended->cData))
5249 spamserv_cs_suspend(channel, 0, 0, NULL);
5250 ss_cs_join_channel(channel, 1);
5253 /* Mark everyone currently in the channel as present. */
5254 for(ii = 0; ii < channel->members.used; ++ii)
5256 struct userData *uData = GetChannelAccess(suspended->cData, channel->members.list[ii]->user->handle_info);
5265 static CHANSERV_FUNC(cmd_csuspend)
5267 struct suspended *suspended;
5268 char reason[MAXLEN];
5269 unsigned long expiry, duration;
5270 struct userData *uData;
5274 if(IsProtected(channel->channel_info))
5276 reply("CSMSG_SUSPEND_NODELETE", channel->name);
5280 if(argv[1][0] == '!')
5282 else if(IsSuspended(channel->channel_info))
5284 reply("CSMSG_ALREADY_SUSPENDED", channel->name);
5285 show_suspension_info(cmd, user, channel->channel_info->suspended);
5289 if(!strcmp(argv[1], "0"))
5291 else if((duration = ParseInterval(argv[1])))
5292 expiry = now + duration;
5295 reply("MSG_INVALID_DURATION", argv[1]);
5299 unsplit_string(argv + 2, argc - 2, reason);
5301 suspended = calloc(1, sizeof(*suspended));
5302 suspended->revoked = 0;
5303 suspended->issued = now;
5304 suspended->suspender = strdup(user->handle_info->handle);
5305 suspended->expires = expiry;
5306 suspended->reason = strdup(reason);
5307 suspended->cData = channel->channel_info;
5308 suspended->previous = suspended->cData->suspended;
5309 suspended->cData->suspended = suspended;
5311 if(suspended->expires)
5312 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
5314 if(IsSuspended(channel->channel_info))
5316 suspended->previous->revoked = now;
5317 if(suspended->previous->expires)
5318 timeq_del(suspended->previous->expires, chanserv_expire_suspension, suspended->previous, 0);
5319 sprintf(reason, "%s suspension modified by %s.", channel->name, suspended->suspender);
5320 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5324 /* Mark all users in channel as absent. */
5325 for(uData = channel->channel_info->users; uData; uData = uData->next)
5334 /* Mark the channel as suspended, then part. */
5335 channel->channel_info->flags |= CHANNEL_SUSPENDED;
5336 spamserv_cs_suspend(channel, expiry, 1, suspended->reason);
5337 DelChannelUser(chanserv, channel, suspended->reason, 0);
5338 reply("CSMSG_SUSPENDED", channel->name);
5339 sprintf(reason, "%s suspended by %s.", channel->name, suspended->suspender);
5340 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
5345 static CHANSERV_FUNC(cmd_cunsuspend)
5347 struct suspended *suspended;
5348 char message[MAXLEN];
5350 if(!IsSuspended(channel->channel_info))
5352 reply("CSMSG_NOT_SUSPENDED", channel->name);
5356 suspended = channel->channel_info->suspended;
5358 /* Expire the suspension and join ChanServ to the channel. */
5359 timeq_del(suspended->expires, chanserv_expire_suspension, suspended, 0);
5360 chanserv_expire_suspension(suspended);
5361 reply("CSMSG_UNSUSPENDED", channel->name);
5362 sprintf(message, "%s unsuspended by %s.", channel->name, user->handle_info->handle);
5363 global_message(MESSAGE_RECIPIENT_OPERS|MESSAGE_RECIPIENT_HELPERS, message);
5367 typedef struct chanservSearch
5372 unsigned long unvisited;
5373 unsigned long registered;
5375 unsigned long flags;
5379 typedef void (*channel_search_func)(struct chanData *channel, void *data);
5382 chanserv_search_create(struct userNode *user, unsigned int argc, char *argv[])
5387 search = malloc(sizeof(struct chanservSearch));
5388 memset(search, 0, sizeof(*search));
5391 for(i = 0; i < argc; i++)
5393 /* Assume all criteria require arguments. */
5396 send_message(user, chanserv, "MSG_MISSING_PARAMS", argv[i]);
5400 if(!irccasecmp(argv[i], "name"))
5401 search->name = argv[++i];
5402 else if(!irccasecmp(argv[i], "registrar"))
5403 search->registrar = argv[++i];
5404 else if(!irccasecmp(argv[i], "unvisited"))
5405 search->unvisited = ParseInterval(argv[++i]);
5406 else if(!irccasecmp(argv[i], "registered"))
5407 search->registered = ParseInterval(argv[++i]);
5408 else if(!irccasecmp(argv[i], "flags"))
5411 if(!irccasecmp(argv[i], "nodelete"))
5412 search->flags |= CHANNEL_NODELETE;
5413 else if(!irccasecmp(argv[i], "suspended"))
5414 search->flags |= CHANNEL_SUSPENDED;
5415 else if(!irccasecmp(argv[i], "unreviewed"))
5416 search->flags |= CHANNEL_UNREVIEWED;
5419 send_message(user, chanserv, "CSMSG_INVALID_CFLAG", argv[i]);
5423 else if(!irccasecmp(argv[i], "limit"))
5424 search->limit = strtoul(argv[++i], NULL, 10);
5427 send_message(user, chanserv, "MSG_INVALID_CRITERIA", argv[i]);
5432 if(search->name && !strcmp(search->name, "*"))
5434 if(search->registrar && !strcmp(search->registrar, "*"))
5435 search->registrar = 0;
5444 chanserv_channel_match(struct chanData *channel, search_t search)
5446 const char *name = channel->channel->name;
5447 if((search->name && !match_ircglob(name, search->name)) ||
5448 (search->registrar && !channel->registrar) ||
5449 (search->registrar && !match_ircglob(channel->registrar, search->registrar)) ||
5450 (search->unvisited && (now - channel->visited) < search->unvisited) ||
5451 (search->registered && (now - channel->registered) > search->registered) ||
5452 (search->flags && ((search->flags & channel->flags) != search->flags)))
5459 chanserv_channel_search(search_t search, channel_search_func smf, void *data)
5461 struct chanData *channel;
5462 unsigned int matches = 0;
5464 for(channel = channelList; channel && matches < search->limit; channel = channel->next)
5466 if(!chanserv_channel_match(channel, search))
5476 search_count(UNUSED_ARG(struct chanData *channel), UNUSED_ARG(void *data))
5481 search_print(struct chanData *channel, void *data)
5483 send_message_type(4, data, chanserv, "%s", channel->channel->name);
5486 static CHANSERV_FUNC(cmd_search)
5489 unsigned int matches;
5490 channel_search_func action;
5494 if(!irccasecmp(argv[1], "count"))
5495 action = search_count;
5496 else if(!irccasecmp(argv[1], "print"))
5497 action = search_print;
5500 reply("CSMSG_ACTION_INVALID", argv[1]);
5504 search = chanserv_search_create(user, argc - 2, argv + 2);
5508 if(action == search_count)
5509 search->limit = INT_MAX;
5511 if(action == search_print)
5512 reply("CSMSG_CHANNEL_SEARCH_RESULTS");
5514 matches = chanserv_channel_search(search, action, user);
5517 reply("MSG_MATCH_COUNT", matches);
5519 reply("MSG_NO_MATCHES");
5525 static CHANSERV_FUNC(cmd_unvisited)
5527 struct chanData *cData;
5528 unsigned long interval = chanserv_conf.channel_expire_delay;
5529 char buffer[INTERVALLEN];
5530 unsigned int limit = 25, matches = 0;
5534 interval = ParseInterval(argv[1]);
5536 limit = atoi(argv[2]);
5539 intervalString(buffer, interval, user->handle_info);
5540 reply("CSMSG_UNVISITED_HEADER", limit, buffer);
5542 for(cData = channelList; cData && matches < limit; cData = cData->next)
5544 if((now - cData->visited) < interval)
5547 intervalString(buffer, now - cData->visited, user->handle_info);
5548 reply("CSMSG_UNVISITED_DATA", cData->channel->name, buffer);
5555 static MODCMD_FUNC(chan_opt_defaulttopic)
5561 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5563 reply("CSMSG_TOPIC_LOCKED", channel->name);
5567 topic = unsplit_string(argv+1, argc-1, NULL);
5569 free(channel->channel_info->topic);
5570 if(topic[0] == '*' && topic[1] == 0)
5572 topic = channel->channel_info->topic = NULL;
5576 topic = channel->channel_info->topic = strdup(topic);
5577 if(channel->channel_info->topic_mask
5578 && !match_ircglob(channel->channel_info->topic, channel->channel_info->topic_mask))
5579 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5581 SetChannelTopic(channel, chanserv, topic ? topic : "", 1);
5584 if(channel->channel_info->topic)
5585 reply("CSMSG_SET_DEFAULT_TOPIC", channel->channel_info->topic);
5587 reply("CSMSG_SET_DEFAULT_TOPIC", user_find_message(user, "MSG_NONE"));
5591 static MODCMD_FUNC(chan_opt_topicmask)
5595 struct chanData *cData = channel->channel_info;
5598 if(!check_user_level(channel, user, lvlEnfTopic, 1, 0))
5600 reply("CSMSG_TOPIC_LOCKED", channel->name);
5604 mask = unsplit_string(argv+1, argc-1, NULL);
5606 if(cData->topic_mask)
5607 free(cData->topic_mask);
5608 if(mask[0] == '*' && mask[1] == 0)
5610 cData->topic_mask = 0;
5614 cData->topic_mask = strdup(mask);
5616 reply("CSMSG_MASK_BUT_NO_TOPIC", channel->name);
5617 else if(!match_ircglob(cData->topic, cData->topic_mask))
5618 reply("CSMSG_TOPIC_MISMATCH", channel->name);
5622 if(channel->channel_info->topic_mask)
5623 reply("CSMSG_SET_TOPICMASK", channel->channel_info->topic_mask);
5625 reply("CSMSG_SET_TOPICMASK", user_find_message(user, "MSG_NONE"));
5629 int opt_greeting_common(struct userNode *user, struct svccmd *cmd, int argc, char *argv[], char *name, char **data)
5633 char *greeting = unsplit_string(argv+1, argc-1, NULL);
5637 if(greeting[0] == '*' && greeting[1] == 0)
5641 unsigned int length = strlen(greeting);
5642 if(length > chanserv_conf.greeting_length)
5644 reply("CSMSG_GREETING_TOO_LONG", length, chanserv_conf.greeting_length);
5647 *data = strdup(greeting);
5656 reply(name, user_find_message(user, "MSG_NONE"));
5660 static MODCMD_FUNC(chan_opt_greeting)
5662 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_GREETING", &channel->channel_info->greeting);
5665 static MODCMD_FUNC(chan_opt_usergreeting)
5667 return opt_greeting_common(user, cmd, argc, argv, "CSMSG_SET_USERGREETING", &channel->channel_info->user_greeting);
5670 static MODCMD_FUNC(chan_opt_modes)
5672 struct mod_chanmode *new_modes;
5677 if(!check_user_level(channel, user, lvlEnfModes, 1, 0))
5679 reply("CSMSG_NO_ACCESS");
5682 if(argv[1][0] == '*' && argv[1][1] == 0)
5684 memset(&channel->channel_info->modes, 0, sizeof(channel->channel_info->modes));
5686 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)))
5688 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5691 else if(new_modes->argc > 1)
5693 reply("CSMSG_INVALID_MODE_LOCK", unsplit_string(argv+1, argc-1, NULL));
5694 mod_chanmode_free(new_modes);
5699 channel->channel_info->modes = *new_modes;
5700 modcmd_chanmode_announce(new_modes);
5701 mod_chanmode_free(new_modes);
5705 mod_chanmode_format(&channel->channel_info->modes, modes);
5707 reply("CSMSG_SET_MODES", modes);
5709 reply("CSMSG_SET_MODES", user_find_message(user, "MSG_NONE"));
5713 #define CHANNEL_BINARY_OPTION(MSG, FLAG) return channel_binary_option(MSG, FLAG, CSFUNC_ARGS);
5715 channel_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5717 struct chanData *cData = channel->channel_info;
5722 /* Set flag according to value. */
5723 if(enabled_string(argv[1]))
5725 cData->flags |= mask;
5728 else if(disabled_string(argv[1]))
5730 cData->flags &= ~mask;
5735 reply("MSG_INVALID_BINARY", argv[1]);
5741 /* Find current option value. */
5742 value = (cData->flags & mask) ? 1 : 0;
5746 reply(name, user_find_message(user, "MSG_ON"));
5748 reply(name, user_find_message(user, "MSG_OFF"));
5752 static MODCMD_FUNC(chan_opt_nodelete)
5754 if((argc > 1) && (!IsOper(user) || !user->handle_info || (user->handle_info->opserv_level < chanserv_conf.nodelete_level)))
5756 reply("MSG_SETTING_PRIVILEGED", argv[0]);
5760 CHANNEL_BINARY_OPTION("CSMSG_SET_NODELETE", CHANNEL_NODELETE);
5763 static MODCMD_FUNC(chan_opt_dynlimit)
5765 CHANNEL_BINARY_OPTION("CSMSG_SET_DYNLIMIT", CHANNEL_DYNAMIC_LIMIT);
5768 static MODCMD_FUNC(chan_opt_offchannel)
5770 struct chanData *cData = channel->channel_info;
5775 /* Set flag according to value. */
5776 if(enabled_string(argv[1]))
5778 if(!IsOffChannel(cData))
5779 DelChannelUser(chanserv, channel, "Going off-channel.", 0);
5780 cData->flags |= CHANNEL_OFFCHANNEL;
5783 else if(disabled_string(argv[1]))
5785 if(IsOffChannel(cData))
5787 struct mod_chanmode change;
5788 mod_chanmode_init(&change);
5790 change.args[0].mode = MODE_CHANOP;
5791 change.args[0].u.member = AddChannelUser(chanserv, channel);
5792 mod_chanmode_announce(chanserv, channel, &change);
5794 cData->flags &= ~CHANNEL_OFFCHANNEL;
5799 reply("MSG_INVALID_BINARY", argv[1]);
5805 /* Find current option value. */
5806 value = (cData->flags & CHANNEL_OFFCHANNEL) ? 1 : 0;
5810 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_ON"));
5812 reply("CSMSG_SET_OFFCHANNEL", user_find_message(user, "MSG_OFF"));
5816 static MODCMD_FUNC(chan_opt_unreviewed)
5818 struct chanData *cData = channel->channel_info;
5819 int value = (cData->flags & CHANNEL_UNREVIEWED) ? 1 : 0;
5825 /* The two directions can have different ACLs. */
5826 if(enabled_string(argv[1]))
5828 else if(disabled_string(argv[1]))
5832 reply("MSG_INVALID_BINARY", argv[1]);
5836 if (new_value != value)
5838 struct svccmd *subcmd;
5839 char subcmd_name[32];
5841 snprintf(subcmd_name, sizeof(subcmd_name), "%s %s", argv[0], (new_value ? "on" : "off"));
5842 subcmd = dict_find(cmd->parent->commands, subcmd_name, NULL);
5845 reply("MSG_COMMAND_DISABLED", subcmd_name);
5848 else if(!svccmd_can_invoke(user, cmd->parent->bot, subcmd, channel, SVCCMD_NOISY))
5852 cData->flags |= CHANNEL_UNREVIEWED;
5855 free(cData->registrar);
5856 cData->registrar = strdup(user->handle_info->handle);
5857 cData->flags &= ~CHANNEL_UNREVIEWED;
5864 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_ON"));
5866 reply("CSMSG_SET_UNREVIEWED", user_find_message(user, "MSG_OFF"));
5870 static MODCMD_FUNC(chan_opt_defaults)
5872 struct userData *uData;
5873 struct chanData *cData;
5874 const char *confirm;
5875 enum levelOption lvlOpt;
5876 enum charOption chOpt;
5878 cData = channel->channel_info;
5879 uData = GetChannelUser(cData, user->handle_info);
5880 if(!uData || (uData->access < UL_OWNER))
5882 reply("CSMSG_OWNER_DEFAULTS", channel->name);
5885 confirm = make_confirmation_string(uData);
5886 if((argc < 2) || strcmp(argv[1], confirm))
5888 reply("CSMSG_CONFIRM_DEFAULTS", channel->name, confirm);
5891 cData->flags = (CHANNEL_DEFAULT_FLAGS & ~CHANNEL_PRESERVED_FLAGS)
5892 | (cData->flags & CHANNEL_PRESERVED_FLAGS);
5893 cData->modes = chanserv_conf.default_modes;
5894 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
5895 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
5896 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
5897 cData->chOpts[chOpt] = charOptions[chOpt].default_value;
5898 reply("CSMSG_SETTINGS_DEFAULTED", channel->name);
5903 channel_level_option(enum levelOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
5905 struct chanData *cData = channel->channel_info;
5906 struct userData *uData;
5907 unsigned short value;
5911 if(!check_user_level(channel, user, option, 1, 1))
5913 reply("CSMSG_CANNOT_SET");
5916 value = user_level_from_name(argv[1], UL_OWNER+1);
5917 if(!value && strcmp(argv[1], "0"))
5919 reply("CSMSG_INVALID_ACCESS", argv[1]);
5922 uData = GetChannelUser(cData, user->handle_info);
5923 if(!uData || ((uData->access < UL_OWNER) && (value > uData->access)))
5925 reply("CSMSG_BAD_SETLEVEL");
5931 if(value > cData->lvlOpts[lvlGiveOps])
5933 reply("CSMSG_BAD_GIVEVOICE", cData->lvlOpts[lvlGiveOps]);
5938 if(value < cData->lvlOpts[lvlGiveVoice])
5940 reply("CSMSG_BAD_GIVEOPS", cData->lvlOpts[lvlGiveVoice]);
5945 /* This test only applies to owners, since non-owners
5946 * trying to set an option to above their level get caught
5947 * by the CSMSG_BAD_SETLEVEL test above.
5949 if(value > uData->access)
5951 reply("CSMSG_BAD_SETTERS");
5958 cData->lvlOpts[option] = value;
5960 reply(levelOptions[option].format_name, cData->lvlOpts[option]);
5964 static MODCMD_FUNC(chan_opt_enfops)
5966 return channel_level_option(lvlEnfOps, CSFUNC_ARGS);
5969 static MODCMD_FUNC(chan_opt_giveops)
5971 return channel_level_option(lvlGiveOps, CSFUNC_ARGS);
5974 static MODCMD_FUNC(chan_opt_enfmodes)
5976 return channel_level_option(lvlEnfModes, CSFUNC_ARGS);
5979 static MODCMD_FUNC(chan_opt_enftopic)
5981 return channel_level_option(lvlEnfTopic, CSFUNC_ARGS);
5984 static MODCMD_FUNC(chan_opt_pubcmd)
5986 return channel_level_option(lvlPubCmd, CSFUNC_ARGS);
5989 static MODCMD_FUNC(chan_opt_setters)
5991 return channel_level_option(lvlSetters, CSFUNC_ARGS);
5994 static MODCMD_FUNC(chan_opt_ctcpusers)
5996 return channel_level_option(lvlCTCPUsers, CSFUNC_ARGS);
5999 static MODCMD_FUNC(chan_opt_userinfo)
6001 return channel_level_option(lvlUserInfo, CSFUNC_ARGS);
6004 static MODCMD_FUNC(chan_opt_givevoice)
6006 return channel_level_option(lvlGiveVoice, CSFUNC_ARGS);
6009 static MODCMD_FUNC(chan_opt_topicsnarf)
6011 return channel_level_option(lvlTopicSnarf, CSFUNC_ARGS);
6014 static MODCMD_FUNC(chan_opt_vote)
6016 return channel_level_option(lvlVote, CSFUNC_ARGS);
6019 static MODCMD_FUNC(chan_opt_inviteme)
6021 return channel_level_option(lvlInviteMe, CSFUNC_ARGS);
6025 channel_multiple_option(enum charOption option, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6027 struct chanData *cData = channel->channel_info;
6028 int count = charOptions[option].count, idx;
6032 idx = atoi(argv[1]);
6034 if(!isdigit(argv[1][0]) || (idx < 0) || (idx >= count))
6036 reply("CSMSG_INVALID_NUMERIC", idx);
6037 /* Show possible values. */
6038 for(idx = 0; idx < count; idx++)
6039 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6043 cData->chOpts[option] = charOptions[option].values[idx].value;
6047 /* Find current option value. */
6050 (idx < count) && (cData->chOpts[option] != charOptions[option].values[idx].value);
6054 /* Somehow, the option value is corrupt; reset it to the default. */
6055 cData->chOpts[option] = charOptions[option].default_value;
6060 reply(charOptions[option].format_name, idx, user_find_message(user, charOptions[option].values[idx].format_name));
6064 static MODCMD_FUNC(chan_opt_protect)
6066 return channel_multiple_option(chProtect, CSFUNC_ARGS);
6069 static MODCMD_FUNC(chan_opt_toys)
6071 return channel_multiple_option(chToys, CSFUNC_ARGS);
6074 static MODCMD_FUNC(chan_opt_ctcpreaction)
6076 return channel_multiple_option(chCTCPReaction, CSFUNC_ARGS);
6079 static MODCMD_FUNC(chan_opt_topicrefresh)
6081 return channel_multiple_option(chTopicRefresh, CSFUNC_ARGS);
6084 static struct svccmd_list set_shows_list;
6087 handle_svccmd_unbind(struct svccmd *target) {
6089 for(ii=0; ii<set_shows_list.used; ++ii)
6090 if(target == set_shows_list.list[ii])
6091 set_shows_list.used = 0;
6094 static CHANSERV_FUNC(cmd_set)
6096 struct svccmd *subcmd;
6100 /* Check if we need to (re-)initialize set_shows_list. */
6101 if(!set_shows_list.used)
6103 if(!set_shows_list.size)
6105 set_shows_list.size = chanserv_conf.set_shows->used;
6106 set_shows_list.list = calloc(set_shows_list.size, sizeof(set_shows_list.list[0]));
6108 for(ii = 0; ii < chanserv_conf.set_shows->used; ii++)
6110 const char *name = chanserv_conf.set_shows->list[ii];
6111 sprintf(buf, "%s %s", argv[0], name);
6112 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6115 log_module(CS_LOG, LOG_ERROR, "Unable to find set option \"%s\".", name);
6118 svccmd_list_append(&set_shows_list, subcmd);
6124 reply("CSMSG_CHANNEL_OPTIONS");
6125 for(ii = 0; ii < set_shows_list.used; ii++)
6127 subcmd = set_shows_list.list[ii];
6128 subcmd->command->func(user, channel, 1, argv+1, subcmd);
6133 sprintf(buf, "%s %s", argv[0], argv[1]);
6134 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6137 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6140 if((argc > 2) && !check_user_level(channel, user, lvlSetters, 1, 0))
6142 reply("CSMSG_NO_ACCESS");
6148 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6152 user_binary_option(char *name, unsigned long mask, struct userNode *user, struct chanNode *channel, int argc, char *argv[], struct svccmd *cmd)
6154 struct userData *uData;
6156 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6159 reply("CSMSG_NOT_USER", channel->name);
6165 /* Just show current option value. */
6167 else if(enabled_string(argv[1]))
6169 uData->flags |= mask;
6171 else if(disabled_string(argv[1]))
6173 uData->flags &= ~mask;
6177 reply("MSG_INVALID_BINARY", argv[1]);
6181 reply(name, user_find_message(user, (uData->flags & mask) ? "MSG_ON" : "MSG_OFF"));
6185 static MODCMD_FUNC(user_opt_noautoop)
6187 struct userData *uData;
6189 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6192 reply("CSMSG_NOT_USER", channel->name);
6195 if(uData->access < channel->channel_info->lvlOpts[lvlGiveOps])
6196 return user_binary_option("CSMSG_USET_NOAUTOVOICE", USER_AUTO_OP, CSFUNC_ARGS);
6198 return user_binary_option("CSMSG_USET_NOAUTOOP", USER_AUTO_OP, CSFUNC_ARGS);
6201 static MODCMD_FUNC(user_opt_autoinvite)
6203 if((argc > 1) && !check_user_level(channel, user, lvlInviteMe, 1, 0))
6205 reply("CSMSG_LOW_CHANNEL_ACCESS", channel->name);
6207 return user_binary_option("CSMSG_USET_AUTOINVITE", USER_AUTO_INVITE, CSFUNC_ARGS);
6210 static MODCMD_FUNC(user_opt_info)
6212 struct userData *uData;
6215 uData = GetChannelAccess(channel->channel_info, user->handle_info);
6219 /* If they got past the command restrictions (which require access)
6220 * but fail this test, we have some fool with security override on.
6222 reply("CSMSG_NOT_USER", channel->name);
6229 infoline = unsplit_string(argv + 1, argc - 1, NULL);
6230 if(strlen(infoline) > chanserv_conf.max_userinfo_length)
6232 reply("CSMSG_INFOLINE_TOO_LONG", chanserv_conf.max_userinfo_length);
6235 bp = strcspn(infoline, "\001");
6238 reply("CSMSG_BAD_INFOLINE", infoline[bp]);
6243 if(infoline[0] == '*' && infoline[1] == 0)
6246 uData->info = strdup(infoline);
6249 reply("CSMSG_USET_INFO", uData->info);
6251 reply("CSMSG_USET_INFO", user_find_message(user, "MSG_NONE"));
6255 struct svccmd_list uset_shows_list;
6257 static CHANSERV_FUNC(cmd_uset)
6259 struct svccmd *subcmd;
6263 /* Check if we need to (re-)initialize uset_shows_list. */
6264 if(!uset_shows_list.used)
6268 "NoAutoOp", "AutoInvite", "Info"
6271 if(!uset_shows_list.size)
6273 uset_shows_list.size = ArrayLength(options);
6274 uset_shows_list.list = calloc(uset_shows_list.size, sizeof(uset_shows_list.list[0]));
6276 for(ii = 0; ii < ArrayLength(options); ii++)
6278 const char *name = options[ii];
6279 sprintf(buf, "%s %s", argv[0], name);
6280 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6283 log_module(CS_LOG, LOG_ERROR, "Unable to find uset option %s.", name);
6286 svccmd_list_append(&uset_shows_list, subcmd);
6292 /* Do this so options are presented in a consistent order. */
6293 reply("CSMSG_USER_OPTIONS");
6294 for(ii = 0; ii < uset_shows_list.used; ii++)
6295 uset_shows_list.list[ii]->command->func(user, channel, 1, argv+1, uset_shows_list.list[ii]);
6299 sprintf(buf, "%s %s", argv[0], argv[1]);
6300 subcmd = dict_find(cmd->parent->commands, buf, NULL);
6303 reply("CSMSG_INVALID_OPTION", argv[1], argv[0]);
6307 return subcmd->command->func(user, channel, argc - 1, argv + 1, subcmd);
6310 static CHANSERV_FUNC(cmd_giveownership)
6312 struct handle_info *new_owner_hi;
6313 struct userData *new_owner;
6314 struct userData *curr_user;
6315 struct userData *invoker;
6316 struct chanData *cData = channel->channel_info;
6317 struct do_not_register *dnr;
6318 const char *confirm;
6320 unsigned short co_access;
6321 char reason[MAXLEN];
6324 curr_user = GetChannelAccess(cData, user->handle_info);
6325 force = IsHelping(user) && (argc > 2) && !irccasecmp(argv[2], "force");
6326 if(!curr_user || (curr_user->access != UL_OWNER))
6328 struct userData *owner = NULL;
6329 for(curr_user = channel->channel_info->users;
6331 curr_user = curr_user->next)
6333 if(curr_user->access != UL_OWNER)
6337 reply("CSMSG_MULTIPLE_OWNERS", channel->name);
6344 else if(!force && (now < cData->ownerTransfer + chanserv_conf.giveownership_period))
6346 char delay[INTERVALLEN];
6347 intervalString(delay, cData->ownerTransfer + chanserv_conf.giveownership_period - now, user->handle_info);
6348 reply("CSMSG_TRANSFER_WAIT", delay, channel->name);
6351 if(!(new_owner_hi = modcmd_get_handle_info(user, argv[1])))
6353 if(new_owner_hi == user->handle_info)
6355 reply("CSMSG_NO_TRANSFER_SELF");
6358 new_owner = GetChannelAccess(cData, new_owner_hi);
6363 new_owner = add_channel_user(cData, new_owner_hi, UL_OWNER - 1, 0, NULL);
6367 reply("CSMSG_NO_CHAN_USER", new_owner_hi->handle, channel->name);
6371 if((chanserv_get_owned_count(new_owner_hi) >= chanserv_conf.max_owned) && !force)
6373 reply("CSMSG_OWN_TOO_MANY", new_owner_hi->handle, chanserv_conf.max_owned);
6376 if((dnr = chanserv_is_dnr(NULL, new_owner_hi)) && !force) {
6377 if(!IsHelping(user))
6378 reply("CSMSG_DNR_ACCOUNT", new_owner_hi->handle);
6380 chanserv_show_dnrs(user, cmd, NULL, new_owner_hi->handle);
6383 invoker = GetChannelUser(cData, user->handle_info);
6384 if(invoker->access <= UL_OWNER)
6386 confirm = make_confirmation_string(curr_user);
6387 if((argc < 3) || strcmp(argv[2], confirm))
6389 reply("CSMSG_CONFIRM_GIVEOWNERSHIP", new_owner_hi->handle, confirm);
6393 if(new_owner->access >= UL_COOWNER)
6394 co_access = new_owner->access;
6396 co_access = UL_COOWNER;
6397 new_owner->access = UL_OWNER;
6399 curr_user->access = co_access;
6400 cData->ownerTransfer = now;
6401 reply("CSMSG_OWNERSHIP_GIVEN", channel->name, new_owner_hi->handle);
6402 sprintf(reason, "%s ownership transferred to %s by %s.", channel->name, new_owner_hi->handle, user->handle_info->handle);
6403 global_message(MESSAGE_RECIPIENT_OPERS | MESSAGE_RECIPIENT_HELPERS, reason);
6407 static CHANSERV_FUNC(cmd_suspend)
6409 struct handle_info *hi;
6410 struct userData *actor, *real_actor, *target;
6411 unsigned int override = 0;
6414 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6415 actor = GetChannelUser(channel->channel_info, user->handle_info);
6416 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6417 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6419 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6422 if(target->access >= actor->access)
6424 reply("MSG_USER_OUTRANKED", hi->handle);
6427 if(target->flags & USER_SUSPENDED)
6429 reply("CSMSG_ALREADY_SUSPENDED", hi->handle);
6434 target->present = 0;
6437 if(!real_actor || target->access >= real_actor->access)
6438 override = CMD_LOG_OVERRIDE;
6439 target->flags |= USER_SUSPENDED;
6440 reply("CSMSG_USER_SUSPENDED", hi->handle, channel->name);
6441 return 1 | override;
6444 static CHANSERV_FUNC(cmd_unsuspend)
6446 struct handle_info *hi;
6447 struct userData *actor, *real_actor, *target;
6448 unsigned int override = 0;
6451 if(!(hi = modcmd_get_handle_info(user, argv[1]))) return 0;
6452 actor = GetChannelUser(channel->channel_info, user->handle_info);
6453 real_actor = GetChannelAccess(channel->channel_info, user->handle_info);
6454 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6456 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6459 if(target->access >= actor->access)
6461 reply("MSG_USER_OUTRANKED", hi->handle);
6464 if(!(target->flags & USER_SUSPENDED))
6466 reply("CSMSG_NOT_SUSPENDED", hi->handle);
6469 if(!real_actor || target->access >= real_actor->access)
6470 override = CMD_LOG_OVERRIDE;
6471 target->flags &= ~USER_SUSPENDED;
6472 scan_user_presence(target, NULL);
6473 reply("CSMSG_USER_UNSUSPENDED", hi->handle, channel->name);
6474 return 1 | override;
6477 static MODCMD_FUNC(cmd_deleteme)
6479 struct handle_info *hi;
6480 struct userData *target;
6481 const char *confirm_string;
6482 unsigned short access_level;
6485 hi = user->handle_info;
6486 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6488 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6491 if(target->access == UL_OWNER)
6493 reply("CSMSG_NO_OWNER_DELETEME", channel->name);
6496 confirm_string = make_confirmation_string(target);
6497 if((argc < 2) || strcmp(argv[1], confirm_string))
6499 reply("CSMSG_CONFIRM_DELETEME", confirm_string);
6502 access_level = target->access;
6503 channel_name = strdup(channel->name);
6504 del_channel_user(target, 1);
6505 reply("CSMSG_DELETED_YOU", access_level, channel_name);
6510 static CHANSERV_FUNC(cmd_addvote)
6512 struct chanData *cData = channel->channel_info;
6513 struct userData *uData, *target;
6514 struct handle_info *hi;
6515 if (!cData) return 0;
6517 hi = user->handle_info;
6518 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6520 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6523 if(target->access < 300) {
6524 reply("CSMSG_NO_ACCESS");
6528 reply("CSMSG_ADDVOTE_FULL");
6532 msg = unsplit_string(argv + 1, argc - 1, NULL);
6533 cData->vote = strdup(msg);
6534 cData->vote_start=0;
6535 dict_delete(cData->vote_options);
6536 cData->vote_options = dict_new();
6537 dict_set_free_data(cData->vote_options, free_vote_options);
6538 for(uData = channel->channel_info->users; uData; uData = uData->next)
6543 reply("CSMSG_ADDVOTE_DONE");
6547 static CHANSERV_FUNC(cmd_delvote)
6549 struct chanData *cData = channel->channel_info;
6550 struct userData *target;
6551 struct handle_info *hi;
6552 if (!cData) return 0;
6553 hi = user->handle_info;
6554 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6556 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6559 if(target->access < 300) {
6560 reply("CSMSG_NO_ACCESS");
6564 reply("CSMSG_NO_VOTE");
6569 reply("CSMSG_DELVOTE_DONE");
6573 static CHANSERV_FUNC(cmd_addoption)
6575 struct chanData *cData = channel->channel_info;
6576 struct userData *target;
6577 struct handle_info *hi;
6578 if (!cData) return 0;
6580 hi = user->handle_info;
6581 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6583 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6586 if(target->access < 300) {
6587 reply("CSMSG_NO_ACCESS");
6591 reply("CSMSG_NO_VOTE");
6597 msg = unsplit_string(argv + 1, argc - 1, NULL);
6600 unsigned int lastid = 1;
6601 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6602 struct vote_option *cvOpt = iter_data(it);
6603 if(cvOpt->option_id > lastid)
6604 lastid = cvOpt->option_id;
6606 struct vote_option *vOpt;
6607 vOpt = calloc(1, sizeof(*vOpt));
6608 vOpt->name = strdup(msg);
6609 vOpt->option_id = (lastid + 1);
6611 sprintf(str,"%i",(lastid + 1));
6612 vOpt->option_str = strdup(str);
6614 dict_insert(cData->vote_options,vOpt->option_str,vOpt);
6616 reply("CSMSG_ADDOPTION_DONE",dict_size(cData->vote_options),lastid,(lastid + 1));
6620 static CHANSERV_FUNC(cmd_deloption)
6622 struct chanData *cData = channel->channel_info;
6623 struct userData *uData, *target;
6624 struct handle_info *hi;
6625 if (!cData) return 0;
6627 hi = user->handle_info;
6628 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6630 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6633 if(target->access < 300) {
6634 reply("CSMSG_NO_ACCESS");
6638 reply("CSMSG_NO_VOTE");
6641 if(cData->vote_start) {
6642 if(dict_size(cData->vote_options) < 3) {
6643 reply("CSMSG_VOTE_NEED_OPTIONS");
6648 int find_id = atoi(argv[1]);
6650 unsigned int found = 0;
6653 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6655 if (find_id == ii) {
6656 struct vote_option *vOpt = iter_data(it);
6657 found = vOpt->option_id;
6659 sprintf(str,"%i",vOpt->option_id);
6660 dict_remove(cData->vote_options, str);
6665 for(uData = channel->channel_info->users; uData; uData = uData->next) {
6666 if(uData->votefor == found) {
6671 reply("CSMSG_DELOPTION_DONE");
6674 reply("CSMSG_DELOPTION_NONE");
6679 static CHANSERV_FUNC(cmd_vote)
6681 struct chanData *cData = channel->channel_info;
6682 struct userData *target;
6683 struct handle_info *hi;
6684 unsigned int votedfor = 0;
6685 char *votedfor_str = NULL;
6687 if (!cData || !cData->vote) {
6688 reply("CSMSG_NO_VOTE");
6691 if(argc > 1 && cData->vote_start) {
6692 hi = user->handle_info;
6693 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6695 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6698 if(!check_user_level(channel, user, lvlVote, 1, 0)) {
6699 reply("CSMSG_NO_ACCESS");
6703 reply("CSMSG_VOTE_VOTED");
6706 int find_id = atoi(argv[1]);
6709 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6711 if (find_id == ii) {
6712 struct vote_option *vOpt = iter_data(it);
6715 target->votefor = vOpt->option_id;
6716 votedfor = vOpt->option_id;
6717 votedfor_str = vOpt->name;
6721 reply("CSMSG_VOTE_INVALID");
6725 if (!cData->vote_start) {
6726 reply("CSMSG_VOTE_NOT_STARTED");
6728 reply("CSMSG_VOTE_QUESTION",cData->vote);
6730 unsigned int voteid = 0;
6733 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6734 struct vote_option *vOpt = iter_data(it);
6736 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6738 if(argc > 1 && cData->vote_start && votedfor_str) {
6739 reply("CSMSG_VOTE_DONE",votedfor_str);
6744 static CHANSERV_FUNC(cmd_startvote)
6746 struct chanData *cData = channel->channel_info;
6747 struct userData *target;
6748 struct handle_info *hi;
6749 if (!cData) return 0;
6750 hi = user->handle_info;
6751 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6753 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6756 if(target->access < 300) {
6757 reply("CSMSG_NO_ACCESS");
6761 reply("CSMSG_NO_VOTE");
6764 if(cData->vote_start) {
6765 reply("CSMSG_STARTVOTE_RUNNING");
6768 if(dict_size(cData->vote_options) < 2) {
6769 reply("CSMSG_VOTE_NEED_OPTIONS");
6772 cData->vote_start = 1;
6773 char response[MAXLEN];
6774 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_TOP"), user->nick);
6775 irc_privmsg(cmd->parent->bot, channel->name, response);
6776 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_QUESTION"), cData->vote);
6777 irc_privmsg(cmd->parent->bot, channel->name, response);
6778 unsigned int voteid = 0;
6780 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6781 struct vote_option *vOpt = iter_data(it);
6783 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_OPTION"), voteid, vOpt->name);
6784 irc_privmsg(cmd->parent->bot, channel->name, response);
6786 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_ACCESS"), cData->lvlOpts[lvlVote]); //Todo
6787 irc_privmsg(cmd->parent->bot, channel->name, response);
6788 sprintf(response, user_find_message(user, "CSMSG_STARTVOTE_HOWTO")); //Todo
6789 irc_privmsg(cmd->parent->bot, channel->name, response);
6793 static CHANSERV_FUNC(cmd_endvote)
6795 struct chanData *cData = channel->channel_info;
6796 struct userData *target;
6797 struct handle_info *hi;
6798 if (!cData) return 0;
6799 hi = user->handle_info;
6800 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6802 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6805 if(target->access < 300) {
6806 reply("CSMSG_NO_ACCESS");
6810 reply("CSMSG_NO_VOTE");
6813 if(!cData->vote_start) {
6814 reply("CSMSG_ENDVOTE_STOPPED");
6817 cData->vote_start = 0;
6818 reply("CSMSG_ENDVOTE_DONE");
6822 static CHANSERV_FUNC(cmd_voteresults)
6824 struct chanData *cData = channel->channel_info;
6825 struct userData *target;
6826 struct handle_info *hi;
6827 if (!cData) return 0;
6829 reply("CSMSG_NO_VOTE");
6832 if (argc > 1 && !irccasecmp(argv[1], "*")) {
6833 hi = user->handle_info;
6834 if(!(target = GetTrueChannelAccess(channel->channel_info, hi)))
6836 reply("CSMSG_NO_CHAN_USER", hi->handle, channel->name);
6839 if(target->access < 300) {
6840 reply("CSMSG_NO_ACCESS");
6843 char response[MAXLEN];
6844 sprintf(response, user_find_message(user, "CSMSG_VOTERES_QUESTION"), cData->vote);
6845 irc_privmsg(cmd->parent->bot, channel->name, response);
6846 unsigned int voteid = 0;
6848 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6849 struct vote_option *vOpt = iter_data(it);
6851 sprintf(response, user_find_message(user, "CSMSG_VOTERES_OPTION"), voteid, vOpt->name, vOpt->voted);
6852 irc_privmsg(cmd->parent->bot, channel->name, response);
6855 reply("CSMSG_VOTE_QUESTION",cData->vote);
6856 unsigned int voteid = 0;
6858 for (it = dict_first(cData->vote_options); it; it = iter_next(it)) {
6859 struct vote_option *vOpt = iter_data(it);
6861 reply("CSMSG_VOTE_OPTION",voteid,vOpt->name,vOpt->voted);
6868 chanserv_refresh_topics(UNUSED_ARG(void *data))
6870 unsigned int refresh_num = (now - self->link_time) / chanserv_conf.refresh_period;
6871 struct chanData *cData;
6874 for(cData = channelList; cData; cData = cData->next)
6876 if(IsSuspended(cData))
6878 opt = cData->chOpts[chTopicRefresh];
6881 if((refresh_num - cData->last_refresh) < (unsigned int)(1 << (opt - '1')))
6884 SetChannelTopic(cData->channel, chanserv, cData->topic, 1);
6885 cData->last_refresh = refresh_num;
6887 timeq_add(now + chanserv_conf.refresh_period, chanserv_refresh_topics, NULL);
6890 static CHANSERV_FUNC(cmd_unf)
6894 char response[MAXLEN];
6895 const char *fmt = user_find_message(user, "CSMSG_UNF_RESPONSE");
6896 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6897 irc_privmsg(cmd->parent->bot, channel->name, response);
6900 reply("CSMSG_UNF_RESPONSE");
6904 static CHANSERV_FUNC(cmd_ping)
6908 char response[MAXLEN];
6909 const char *fmt = user_find_message(user, "CSMSG_PING_RESPONSE");
6910 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6911 irc_privmsg(cmd->parent->bot, channel->name, response);
6914 reply("CSMSG_PING_RESPONSE");
6918 static CHANSERV_FUNC(cmd_wut)
6922 char response[MAXLEN];
6923 const char *fmt = user_find_message(user, "CSMSG_WUT_RESPONSE");
6924 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, fmt);
6925 irc_privmsg(cmd->parent->bot, channel->name, response);
6928 reply("CSMSG_WUT_RESPONSE");
6932 static CHANSERV_FUNC(cmd_8ball)
6934 unsigned int i, j, accum;
6939 for(i=1; i<argc; i++)
6940 for(j=0; argv[i][j]; j++)
6941 accum = (accum << 5) - accum + toupper(argv[i][j]);
6942 resp = chanserv_conf.eightball->list[accum % chanserv_conf.eightball->used];
6945 char response[MAXLEN];
6946 sprintf(response, "
\ 2%s
\ 2: %s", user->nick, resp);
6947 irc_privmsg(cmd->parent->bot, channel->name, response);
6950 send_message_type(4, user, cmd->parent->bot, "%s", resp);
6954 static CHANSERV_FUNC(cmd_d)
6956 unsigned long sides, count, modifier, ii, total;
6957 char response[MAXLEN], *sep;
6961 if((count = strtoul(argv[1], &sep, 10)) < 1)
6971 else if(((sep[0] == 'd') || (sep[0] == 'D')) && isdigit(sep[1])
6972 && (sides = strtoul(sep+1, &sep, 10)) > 1)
6976 else if((sep[0] == '-') && isdigit(sep[1]))
6977 modifier = strtoul(sep, NULL, 10);
6978 else if((sep[0] == '+') && isdigit(sep[1]))
6979 modifier = strtoul(sep+1, NULL, 10);
6986 reply("CSMSG_BAD_DIE_FORMAT", argv[1]);
6991 reply("CSMSG_BAD_DICE_COUNT", count, 10);
6994 for(total = ii = 0; ii < count; ++ii)
6995 total += (rand() % sides) + 1;
6998 if((count > 1) || modifier)
7000 fmt = user_find_message(user, "CSMSG_DICE_ROLL");
7001 sprintf(response, fmt, total, count, sides, modifier);
7005 fmt = user_find_message(user, "CSMSG_DIE_ROLL");
7006 sprintf(response, fmt, total, sides);
7009 send_channel_message(channel, cmd->parent->bot, "$b%s$b: %s", user->nick, response);
7011 send_message_type(4, user, cmd->parent->bot, "%s", response);
7015 static CHANSERV_FUNC(cmd_huggle)
7017 /* CTCP must be via PRIVMSG, never notice */
7019 send_target_message(1, channel->name, cmd->parent->bot, "CSMSG_HUGGLES_HIM", user->nick);
7021 send_target_message(1, user->nick, cmd->parent->bot, "CSMSG_HUGGLES_YOU");
7026 chanserv_adjust_limit(void *data)
7028 struct mod_chanmode change;
7029 struct chanData *cData = data;
7030 struct chanNode *channel = cData->channel;
7033 if(IsSuspended(cData))
7036 cData->limitAdjusted = now;
7037 limit = channel->members.used + chanserv_conf.adjust_threshold + 5;
7038 if(cData->modes.modes_set & MODE_LIMIT)
7040 if(limit > cData->modes.new_limit)
7041 limit = cData->modes.new_limit;
7042 else if(limit == cData->modes.new_limit)
7046 mod_chanmode_init(&change);
7047 change.modes_set = MODE_LIMIT;
7048 change.new_limit = limit;
7049 mod_chanmode_announce(chanserv, channel, &change);
7053 handle_new_channel(struct chanNode *channel)
7055 struct chanData *cData;
7057 if(!(cData = channel->channel_info))
7060 if(cData->modes.modes_set || cData->modes.modes_clear)
7061 mod_chanmode_announce(chanserv, cData->channel, &cData->modes);
7063 if(self->uplink && !self->uplink->burst && channel->channel_info->topic)
7064 SetChannelTopic(channel, chanserv, channel->channel_info->topic, 1);
7067 void handle_new_channel_created(char *chan, struct userNode *user) {
7068 if(user->handle_info && chanserv_conf.new_channel_authed) {
7069 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_authed);
7070 } else if(!user->handle_info && chanserv_conf.new_channel_unauthed) {
7071 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_unauthed);
7073 if(chanserv_conf.new_channel_msg)
7074 send_target_message(5, chan, chanserv, "%s", chanserv_conf.new_channel_msg);
7077 /* Welcome to my worst nightmare. Warning: Read (or modify)
7078 the code below at your own risk. */
7080 handle_join(struct modeNode *mNode)
7082 struct mod_chanmode change;
7083 struct userNode *user = mNode->user;
7084 struct chanNode *channel = mNode->channel;
7085 struct chanData *cData;
7086 struct userData *uData = NULL;
7087 struct banData *bData;
7088 struct handle_info *handle;
7089 unsigned int modes = 0, info = 0;
7093 if(IsLocal(user) || !channel->channel_info || IsSuspended(channel->channel_info))
7096 cData = channel->channel_info;
7097 if(channel->members.used > cData->max) {
7098 cData->max = channel->members.used;
7099 cData->max_time = now;
7102 for(i = 0; i < channel->invited.used; i++)
7104 if(channel->invited.list[i] == user) {
7105 userList_remove(&channel->invited, user);
7109 /* Check for bans. If they're joining through a ban, one of two
7111 * 1: Join during a netburst, by riding the break. Kick them
7112 * unless they have ops or voice in the channel.
7113 * 2: They're allowed to join through the ban (an invite in
7114 * ircu2.10, or a +e on Hybrid, or something).
7115 * If they're not joining through a ban, and the banlist is not
7116 * full, see if they're on the banlist for the channel. If so,
7119 if(user->uplink->burst && !mNode->modes)
7122 for(ii = 0; ii < channel->banlist.used; ii++)
7124 if(user_matches_glob(user, channel->banlist.list[ii]->ban, MATCH_USENICK))
7126 /* Riding a netburst. Naughty. */
7127 KickChannelUser(user, channel, chanserv, "User from far side of netsplit should have been banned - bye.");
7133 mod_chanmode_init(&change);
7135 if(channel->banlist.used < MAXBANS)
7137 /* Not joining through a ban. */
7138 for(bData = cData->bans;
7139 bData && !user_matches_glob(user, bData->mask, MATCH_USENICK);
7140 bData = bData->next);
7144 char kick_reason[MAXLEN];
7145 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7147 bData->triggered = now;
7148 if(bData != cData->bans)
7150 /* Shuffle the ban to the head of the list. */
7152 bData->next->prev = bData->prev;
7154 bData->prev->next = bData->next;
7157 bData->next = cData->bans;
7160 cData->bans->prev = bData;
7161 cData->bans = bData;
7164 change.args[0].mode = MODE_BAN;
7165 change.args[0].u.hostmask = bData->mask;
7166 mod_chanmode_announce(chanserv, channel, &change);
7167 KickChannelUser(user, channel, chanserv, kick_reason);
7172 /* ChanServ will not modify the limits in join-flooded channels,
7173 or when there are enough slots left below the limit. */
7174 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7175 && !channel->join_flooded
7176 && (channel->limit - channel->members.used) < chanserv_conf.adjust_threshold)
7178 /* The user count has begun "bumping" into the channel limit,
7179 so set a timer to raise the limit a bit. Any previous
7180 timers are removed so three incoming users within the delay
7181 results in one limit change, not three. */
7183 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7184 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7187 if(channel->join_flooded)
7189 /* don't automatically give ops or voice during a join flood */
7191 else if(cData->lvlOpts[lvlGiveOps] == 0)
7192 modes |= MODE_CHANOP;
7193 else if(cData->lvlOpts[lvlGiveVoice] == 0)
7194 modes |= MODE_VOICE;
7196 greeting = cData->greeting;
7197 if(user->handle_info)
7199 handle = user->handle_info;
7201 if(IsHelper(user) && !IsHelping(user))
7204 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7206 if(channel == chanserv_conf.support_channels.list[ii])
7208 HANDLE_SET_FLAG(user->handle_info, HELPING);
7214 uData = GetTrueChannelAccess(cData, handle);
7215 if(uData && !IsUserSuspended(uData))
7217 /* Ops and above were handled by the above case. */
7218 if(IsUserAutoOp(uData))
7220 if(uData->access >= cData->lvlOpts[lvlGiveOps])
7221 modes |= MODE_CHANOP;
7222 else if(uData->access >= cData->lvlOpts[lvlGiveVoice])
7223 modes |= MODE_VOICE;
7225 if(uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7226 cData->visited = now;
7227 if(cData->user_greeting)
7228 greeting = cData->user_greeting;
7230 && (uData->access >= cData->lvlOpts[lvlUserInfo])
7231 && ((now - uData->seen) >= chanserv_conf.info_delay)
7239 /* If user joining normally (not during burst), apply op or voice,
7240 * and send greeting/userinfo as appropriate.
7242 if(!user->uplink->burst)
7246 if(modes & MODE_CHANOP)
7247 modes &= ~MODE_VOICE;
7248 change.args[0].mode = modes;
7249 change.args[0].u.member = mNode;
7250 mod_chanmode_announce(chanserv, channel, &change);
7253 send_message_type(4, user, chanserv, "(%s) %s", channel->name, greeting);
7254 if(uData && info && (modes || !(channel->modes & MODE_DELAYJOINS)))
7255 send_target_message(5, channel->name, chanserv, "[%s] %s", user->nick, uData->info);
7261 handle_auth(struct userNode *user, UNUSED_ARG(struct handle_info *old_handle))
7263 struct mod_chanmode change;
7264 struct userData *channel;
7265 unsigned int ii, jj;
7267 if(!user->handle_info)
7270 mod_chanmode_init(&change);
7272 for(channel = user->handle_info->channels; channel; channel = channel->u_next)
7274 struct chanNode *cn;
7275 struct modeNode *mn;
7276 if(IsUserSuspended(channel)
7277 || IsSuspended(channel->channel)
7278 || !(cn = channel->channel->channel))
7281 mn = GetUserMode(cn, user);
7284 if(!IsUserSuspended(channel)
7285 && IsUserAutoInvite(channel)
7286 && (channel->access >= channel->channel->lvlOpts[lvlInviteMe])
7288 && !user->uplink->burst)
7289 irc_invite(chanserv, user, cn);
7293 if(channel->access >= UL_PRESENT && !HANDLE_FLAGGED(channel->handle, BOT))
7294 channel->channel->visited = now;
7296 if(IsUserAutoOp(channel))
7298 if(channel->access >= cn->channel_info->lvlOpts[lvlGiveOps])
7299 change.args[0].mode = MODE_CHANOP;
7300 else if(channel->access >= cn->channel_info->lvlOpts[lvlGiveVoice])
7301 change.args[0].mode = MODE_VOICE;
7303 change.args[0].mode = 0;
7304 change.args[0].u.member = mn;
7305 if(change.args[0].mode)
7306 mod_chanmode_announce(chanserv, cn, &change);
7309 channel->seen = now;
7310 channel->present = 1;
7313 for(ii = 0; ii < user->channels.used; ++ii)
7315 struct chanNode *chan = user->channels.list[ii]->channel;
7316 struct banData *ban;
7318 if((user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7319 || !chan->channel_info
7320 || IsSuspended(chan->channel_info))
7322 for(jj = 0; jj < chan->banlist.used; ++jj)
7323 if(user_matches_glob(user, chan->banlist.list[jj]->ban, MATCH_USENICK))
7325 if(jj < chan->banlist.used)
7327 for(ban = chan->channel_info->bans; ban; ban = ban->next)
7329 char kick_reason[MAXLEN];
7330 if(!user_matches_glob(user, ban->mask, MATCH_USENICK | MATCH_VISIBLE))
7332 change.args[0].mode = MODE_BAN;
7333 change.args[0].u.hostmask = ban->mask;
7334 mod_chanmode_announce(chanserv, chan, &change);
7335 sprintf(kick_reason, "(%s) %s", ban->owner, ban->reason);
7336 KickChannelUser(user, chan, chanserv, kick_reason);
7337 ban->triggered = now;
7342 if(IsSupportHelper(user))
7344 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7346 if(GetUserMode(chanserv_conf.support_channels.list[ii], user))
7348 HANDLE_SET_FLAG(user->handle_info, HELPING);
7356 handle_part(struct modeNode *mn, UNUSED_ARG(const char *reason))
7358 struct chanData *cData;
7359 struct userData *uData;
7361 cData = mn->channel->channel_info;
7362 if(!cData || IsSuspended(cData) || IsLocal(mn->user))
7365 if((cData->flags & CHANNEL_DYNAMIC_LIMIT) && !mn->channel->join_flooded)
7367 /* Allow for a bit of padding so that the limit doesn't
7368 track the user count exactly, which could get annoying. */
7369 if((mn->channel->limit - mn->channel->members.used) > chanserv_conf.adjust_threshold + 5)
7371 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7372 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7376 if((uData = GetTrueChannelAccess(cData, mn->user->handle_info)))
7378 scan_user_presence(uData, mn->user);
7380 if (uData->access >= UL_PRESENT && !HANDLE_FLAGGED(uData->handle, BOT))
7381 cData->visited = now;
7384 if(IsHelping(mn->user) && IsSupportHelper(mn->user))
7387 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii) {
7388 struct chanNode *channel;
7389 struct userNode *exclude;
7390 /* When looking at the channel that is being /part'ed, we
7391 * have to skip over the client that is leaving. For
7392 * other channels, we must not do that.
7394 channel = chanserv_conf.support_channels.list[ii];
7395 exclude = (channel == mn->channel) ? mn->user : NULL;
7396 if(find_handle_in_channel(channel, mn->user->handle_info, exclude))
7399 if(ii == chanserv_conf.support_channels.used)
7400 HANDLE_CLEAR_FLAG(mn->user->handle_info, HELPING);
7405 handle_kick(struct userNode *kicker, struct userNode *victim, struct chanNode *channel)
7407 struct userData *uData;
7409 if(!channel->channel_info || !kicker || IsService(kicker)
7410 || (kicker == victim) || IsSuspended(channel->channel_info)
7411 || (kicker->handle_info && kicker->handle_info == victim->handle_info))
7414 if(protect_user(victim, kicker, channel->channel_info))
7416 const char *reason = user_find_message(kicker, "CSMSG_USER_PROTECTED_2");
7417 KickChannelUser(kicker, channel, chanserv, reason);
7420 if((uData = GetTrueChannelAccess(channel->channel_info, victim->handle_info)))
7425 handle_topic(struct userNode *user, struct chanNode *channel, const char *old_topic)
7427 struct chanData *cData;
7429 if(!channel->channel_info || !user || IsSuspended(channel->channel_info) || IsService(user))
7432 cData = channel->channel_info;
7433 if(bad_topic(channel, user, channel->topic))
7435 send_message(user, chanserv, "CSMSG_TOPIC_LOCKED", channel->name);
7436 if(cData->topic_mask && match_ircglob(old_topic, cData->topic_mask))
7437 SetChannelTopic(channel, chanserv, old_topic, 1);
7438 else if(cData->topic)
7439 SetChannelTopic(channel, chanserv, cData->topic, 1);
7442 /* With topicsnarf, grab the topic and save it as the default topic. */
7443 if(check_user_level(channel, user, lvlTopicSnarf, 0, 0))
7446 cData->topic = strdup(channel->topic);
7452 handle_mode(struct chanNode *channel, struct userNode *user, const struct mod_chanmode *change)
7454 struct mod_chanmode *bounce = NULL;
7455 unsigned int bnc, ii;
7458 if(!channel->channel_info || IsLocal(user) || IsSuspended(channel->channel_info) || IsService(user))
7461 if(!check_user_level(channel, user, lvlEnfModes, 1, 0)
7462 && mode_lock_violated(&channel->channel_info->modes, change))
7464 char correct[MAXLEN];
7465 bounce = mod_chanmode_dup(&channel->channel_info->modes, change->argc + 1);
7466 mod_chanmode_format(&channel->channel_info->modes, correct);
7467 send_message(user, chanserv, "CSMSG_MODE_LOCKED", correct, channel->name);
7469 for(ii = bnc = 0; ii < change->argc; ++ii)
7471 if((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP)) == (MODE_REMOVE|MODE_CHANOP))
7473 const struct userNode *victim = change->args[ii].u.member->user;
7474 if(!protect_user(victim, user, channel->channel_info))
7477 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7480 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7481 bounce->args[bnc].u.member = GetUserMode(channel, user);
7482 if(bounce->args[bnc].u.member)
7486 bounce->args[bnc].mode = MODE_CHANOP;
7487 bounce->args[bnc].u.member = change->args[ii].u.member;
7489 send_message(user, chanserv, "CSMSG_USER_PROTECTED", victim->nick);
7491 else if(change->args[ii].mode & MODE_CHANOP)
7493 const struct userNode *victim = change->args[ii].u.member->user;
7494 if(IsService(victim) || validate_op(user, channel, (struct userNode*)victim))
7497 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7498 bounce->args[bnc].mode = MODE_REMOVE | MODE_CHANOP;
7499 bounce->args[bnc].u.member = change->args[ii].u.member;
7502 else if((change->args[ii].mode & (MODE_REMOVE | MODE_BAN)) == MODE_BAN)
7504 const char *ban = change->args[ii].u.hostmask;
7505 if(!bad_channel_ban(channel, user, ban, NULL, NULL))
7508 bounce = mod_chanmode_alloc(change->argc + 1 - ii);
7509 bounce->args[bnc].mode = MODE_REMOVE | MODE_BAN;
7510 bounce->args[bnc].u.hostmask = strdup(ban);
7512 send_message(user, chanserv, "CSMSG_MASK_PROTECTED", ban);
7517 if((bounce->argc = bnc) || bounce->modes_set || bounce->modes_clear)
7518 mod_chanmode_announce(chanserv, channel, bounce);
7519 for(ii = 0; ii < change->argc; ++ii)
7520 if(bounce->args[ii].mode == (MODE_REMOVE | MODE_BAN))
7521 free((char*)bounce->args[ii].u.hostmask);
7522 mod_chanmode_free(bounce);
7527 handle_nick_change(struct userNode *user, UNUSED_ARG(const char *old_nick))
7529 struct chanNode *channel;
7530 struct banData *bData;
7531 struct mod_chanmode change;
7532 unsigned int ii, jj;
7533 char kick_reason[MAXLEN];
7535 mod_chanmode_init(&change);
7537 change.args[0].mode = MODE_BAN;
7538 for(ii = 0; ii < user->channels.used; ++ii)
7540 channel = user->channels.list[ii]->channel;
7541 /* Need not check for bans if they're opped or voiced. */
7542 if(user->channels.list[ii]->modes & (MODE_CHANOP|MODE_VOICE))
7544 /* Need not check for bans unless channel registration is active. */
7545 if(!channel->channel_info || IsSuspended(channel->channel_info))
7547 /* Look for a matching ban already on the channel. */
7548 for(jj = 0; jj < channel->banlist.used; ++jj)
7549 if(user_matches_glob(user, channel->banlist.list[jj]->ban, MATCH_USENICK))
7551 /* Need not act if we found one. */
7552 if(jj < channel->banlist.used)
7554 /* Look for a matching ban in this channel. */
7555 for(bData = channel->channel_info->bans; bData; bData = bData->next)
7557 if(!user_matches_glob(user, bData->mask, MATCH_USENICK | MATCH_VISIBLE))
7559 change.args[0].u.hostmask = bData->mask;
7560 mod_chanmode_announce(chanserv, channel, &change);
7561 sprintf(kick_reason, "(%s) %s", bData->owner, bData->reason);
7562 KickChannelUser(user, channel, chanserv, kick_reason);
7563 bData->triggered = now;
7564 break; /* we don't need to check any more bans in the channel */
7569 static void handle_rename(struct handle_info *handle, const char *old_handle)
7571 struct do_not_register *dnr = dict_find(handle_dnrs, old_handle, NULL);
7575 dict_remove2(handle_dnrs, old_handle, 1);
7576 safestrncpy(dnr->chan_name + 1, handle->handle, sizeof(dnr->chan_name) - 1);
7577 dict_insert(handle_dnrs, dnr->chan_name + 1, dnr);
7582 handle_unreg(UNUSED_ARG(struct userNode *user), struct handle_info *handle)
7584 struct userNode *h_user;
7586 if(handle->channels)
7588 for(h_user = handle->users; h_user; h_user = h_user->next_authed)
7589 send_message(h_user, chanserv, "CSMSG_HANDLE_UNREGISTERED");
7591 while(handle->channels)
7592 del_channel_user(handle->channels, 1);
7597 handle_server_link(UNUSED_ARG(struct server *server))
7599 struct chanData *cData;
7601 for(cData = channelList; cData; cData = cData->next)
7603 if(!IsSuspended(cData))
7604 cData->may_opchan = 1;
7605 if((cData->flags & CHANNEL_DYNAMIC_LIMIT)
7606 && !cData->channel->join_flooded
7607 && ((cData->channel->limit - cData->channel->members.used)
7608 < chanserv_conf.adjust_threshold))
7610 timeq_del(0, chanserv_adjust_limit, cData, TIMEQ_IGNORE_WHEN);
7611 timeq_add(now + chanserv_conf.adjust_delay, chanserv_adjust_limit, cData);
7617 chanserv_conf_read(void)
7621 char mode_line[MAXLEN], *modes[MAXNUMPARAMS];
7622 struct mod_chanmode *change;
7623 struct string_list *strlist;
7624 struct chanNode *chan;
7627 if(!(conf_node = conf_get_data(CHANSERV_CONF_NAME, RECDB_OBJECT)))
7629 log_module(CS_LOG, LOG_ERROR, "Invalid config node `%s'.", CHANSERV_CONF_NAME);
7632 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
7633 UnlockChannel(chanserv_conf.support_channels.list[ii]);
7634 chanserv_conf.support_channels.used = 0;
7635 if((strlist = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_STRING_LIST)))
7637 for(ii = 0; ii < strlist->used; ++ii)
7639 const char *str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7642 chan = AddChannel(strlist->list[ii], now, str2, NULL);
7644 channelList_append(&chanserv_conf.support_channels, chan);
7647 else if((str = database_get_data(conf_node, KEY_SUPPORT_CHANNEL, RECDB_QSTRING)))
7650 str2 = database_get_data(conf_node, KEY_SUPPORT_CHANNEL_MODES, RECDB_QSTRING);
7653 chan = AddChannel(str, now, str2, NULL);
7655 channelList_append(&chanserv_conf.support_channels, chan);
7657 str = database_get_data(conf_node, KEY_DB_BACKUP_FREQ, RECDB_QSTRING);
7658 chanserv_conf.db_backup_frequency = str ? ParseInterval(str) : 7200;
7659 str = database_get_data(conf_node, KEY_INFO_DELAY, RECDB_QSTRING);
7660 chanserv_conf.info_delay = str ? ParseInterval(str) : 180;
7661 str = database_get_data(conf_node, KEY_MAX_GREETLEN, RECDB_QSTRING);
7662 chanserv_conf.greeting_length = str ? atoi(str) : 200;
7663 str = database_get_data(conf_node, KEY_ADJUST_THRESHOLD, RECDB_QSTRING);
7664 chanserv_conf.adjust_threshold = str ? atoi(str) : 15;
7665 str = database_get_data(conf_node, KEY_ADJUST_DELAY, RECDB_QSTRING);
7666 chanserv_conf.adjust_delay = str ? ParseInterval(str) : 30;
7667 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_FREQ, RECDB_QSTRING);
7668 chanserv_conf.channel_expire_frequency = str ? ParseInterval(str) : 86400;
7669 str = database_get_data(conf_node, KEY_CHAN_EXPIRE_DELAY, RECDB_QSTRING);
7670 chanserv_conf.channel_expire_delay = str ? ParseInterval(str) : 86400*30;
7671 str = database_get_data(conf_node, KEY_DNR_EXPIRE_FREQ, RECDB_QSTRING);
7672 chanserv_conf.dnr_expire_frequency = str ? ParseInterval(str) : 3600;
7673 str = database_get_data(conf_node, KEY_INVITED_INTERVAL, RECDB_QSTRING);
7674 chanserv_conf.invited_timeout = str ? ParseInterval(str) : 600*2;
7675 str = database_get_data(conf_node, KEY_NODELETE_LEVEL, RECDB_QSTRING);
7676 chanserv_conf.nodelete_level = str ? atoi(str) : 1;
7677 str = database_get_data(conf_node, KEY_MAX_CHAN_USERS, RECDB_QSTRING);
7678 chanserv_conf.max_chan_users = str ? atoi(str) : 512;
7679 str = database_get_data(conf_node, KEY_MAX_CHAN_BANS, RECDB_QSTRING);
7680 chanserv_conf.max_chan_bans = str ? atoi(str) : 512;
7681 str = database_get_data(conf_node, KEY_MAX_USERINFO_LENGTH, RECDB_QSTRING);
7682 chanserv_conf.max_userinfo_length = str ? atoi(str) : 400;
7683 str = database_get_data(conf_node, KEY_NICK, RECDB_QSTRING);
7685 NickChange(chanserv, str, 0);
7686 str = database_get_data(conf_node, KEY_REFRESH_PERIOD, RECDB_QSTRING);
7687 chanserv_conf.refresh_period = str ? ParseInterval(str) : 3*60*60;
7688 str = database_get_data(conf_node, KEY_GIVEOWNERSHIP_PERIOD, RECDB_QSTRING);
7689 chanserv_conf.giveownership_period = str ? ParseInterval(str) : 0;
7690 str = database_get_data(conf_node, KEY_CTCP_SHORT_BAN_DURATION, RECDB_QSTRING);
7691 chanserv_conf.ctcp_short_ban_duration = str ? str : "3m";
7692 str = database_get_data(conf_node, KEY_CTCP_LONG_BAN_DURATION, RECDB_QSTRING);
7693 chanserv_conf.ctcp_long_ban_duration = str ? str : "1h";
7694 str = database_get_data(conf_node, KEY_MAX_OWNED, RECDB_QSTRING);
7695 chanserv_conf.max_owned = str ? atoi(str) : 5;
7696 str = database_get_data(conf_node, KEY_IRC_OPERATOR_EPITHET, RECDB_QSTRING);
7697 chanserv_conf.irc_operator_epithet = str ? str : "a megalomaniacal power hungry tyrant";
7698 str = database_get_data(conf_node, KEY_NETWORK_HELPER_EPITHET, RECDB_QSTRING);
7699 chanserv_conf.network_helper_epithet = str ? str : "a wannabe tyrant";
7700 str = database_get_data(conf_node, KEY_SUPPORT_HELPER_EPITHET, RECDB_QSTRING);
7701 chanserv_conf.support_helper_epithet = str ? str : "a wannabe tyrant";
7702 str = database_get_data(conf_node, KEY_NEW_CHANNEL_AUTHED, RECDB_QSTRING);
7703 chanserv_conf.new_channel_authed = str ? str : NULL;
7704 str = database_get_data(conf_node, KEY_NEW_CHANNEL_UNAUTHED, RECDB_QSTRING);
7705 chanserv_conf.new_channel_unauthed = str ? str : NULL;
7706 str = database_get_data(conf_node, KEY_NEW_CHANNEL_MSG, RECDB_QSTRING);
7707 chanserv_conf.new_channel_msg = str ? str : NULL;
7708 str = database_get_data(conf_node, "default_modes", RECDB_QSTRING);
7711 safestrncpy(mode_line, str, sizeof(mode_line));
7712 ii = split_line(mode_line, 0, ArrayLength(modes), modes);
7713 if((change = mod_chanmode_parse(NULL, NULL, modes, ii, MCP_KEY_FREE|MCP_NO_APASS, 0))
7714 && (change->argc < 2))
7716 chanserv_conf.default_modes = *change;
7717 mod_chanmode_free(change);
7719 free_string_list(chanserv_conf.set_shows);
7720 strlist = database_get_data(conf_node, "set_shows", RECDB_STRING_LIST);
7722 strlist = string_list_copy(strlist);
7725 static const char *list[] = {
7726 /* free form text */
7727 "DefaultTopic", "TopicMask", "Greeting", "UserGreeting", "Modes",
7728 /* options based on user level */
7729 "PubCmd", "InviteMe", "UserInfo", "GiveVoice", "GiveOps", "EnfOps",
7730 "EnfModes", "EnfTopic", "TopicSnarf", "Setters", "CtcpUsers",
7731 /* multiple choice options */
7732 "CtcpReaction", "Protect", "Toys", "TopicRefresh",
7733 /* binary options */
7734 "DynLimit", "NoDelete", "expire", "Vote",
7738 strlist = alloc_string_list(ArrayLength(list)-1);
7739 for(ii=0; list[ii]; ii++)
7740 string_list_append(strlist, strdup(list[ii]));
7742 chanserv_conf.set_shows = strlist;
7743 /* We don't look things up now, in case the list refers to options
7744 * defined by modules initialized after this point. Just mark the
7745 * function list as invalid, so it will be initialized.
7747 set_shows_list.used = 0;
7748 free_string_list(chanserv_conf.eightball);
7749 strlist = database_get_data(conf_node, KEY_8BALL_RESPONSES, RECDB_STRING_LIST);
7752 strlist = string_list_copy(strlist);
7756 strlist = alloc_string_list(4);
7757 string_list_append(strlist, strdup("Yes."));
7758 string_list_append(strlist, strdup("No."));
7759 string_list_append(strlist, strdup("Maybe so."));
7761 chanserv_conf.eightball = strlist;
7762 free_string_list(chanserv_conf.old_ban_names);
7763 strlist = database_get_data(conf_node, KEY_OLD_BAN_NAMES, RECDB_STRING_LIST);
7765 strlist = string_list_copy(strlist);
7767 strlist = alloc_string_list(2);
7768 chanserv_conf.old_ban_names = strlist;
7769 str = database_get_data(conf_node, "off_channel", RECDB_QSTRING);
7770 off_channel = str ? atoi(str) : 0;
7774 chanserv_note_type_read(const char *key, struct record_data *rd)
7777 struct note_type *ntype;
7780 if(!(obj = GET_RECORD_OBJECT(rd)))
7782 log_module(CS_LOG, LOG_ERROR, "Invalid note type %s.", key);
7785 if(!(ntype = chanserv_create_note_type(key)))
7787 log_module(CS_LOG, LOG_ERROR, "Memory allocation failed for note %s.", key);
7791 /* Figure out set access */
7792 if((str = database_get_data(obj, KEY_NOTE_OPSERV_ACCESS, RECDB_QSTRING)))
7794 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7795 ntype->set_access.min_opserv = strtoul(str, NULL, 0);
7797 else if((str = database_get_data(obj, KEY_NOTE_CHANNEL_ACCESS, RECDB_QSTRING)))
7799 ntype->set_access_type = NOTE_SET_CHANNEL_ACCESS;
7800 ntype->set_access.min_ulevel = strtoul(str, NULL, 0);
7802 else if((str = database_get_data(obj, KEY_NOTE_SETTER_ACCESS, RECDB_QSTRING)))
7804 ntype->set_access_type = NOTE_SET_CHANNEL_SETTER;
7808 log_module(CS_LOG, LOG_ERROR, "Could not find access type for note %s; defaulting to OpServ access level 0.", key);
7809 ntype->set_access_type = NOTE_SET_PRIVILEGED;
7810 ntype->set_access.min_opserv = 0;
7813 /* Figure out visibility */
7814 if(!(str = database_get_data(obj, KEY_NOTE_VISIBILITY, RECDB_QSTRING)))
7815 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7816 else if(!irccasecmp(str, KEY_NOTE_VIS_PRIVILEGED))
7817 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7818 else if(!irccasecmp(str, KEY_NOTE_VIS_CHANNEL_USERS))
7819 ntype->visible_type = NOTE_VIS_CHANNEL_USERS;
7820 else if(!irccasecmp(str, KEY_NOTE_VIS_ALL))
7821 ntype->visible_type = NOTE_VIS_ALL;
7823 ntype->visible_type = NOTE_VIS_PRIVILEGED;
7825 str = database_get_data(obj, KEY_NOTE_MAX_LENGTH, RECDB_QSTRING);
7826 ntype->max_length = str ? strtoul(str, NULL, 0) : 400;
7830 vote_option_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7832 struct vote_option *vOpt;
7835 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7837 log_module(CS_LOG, LOG_ERROR, "Invalid vote option in %s.", chan->channel->name);
7841 vOpt = calloc(1, sizeof(*vOpt));
7842 vOpt->name = strdup(database_get_data(rd->d.object, KEY_VOTE_OPTION_NAME, RECDB_QSTRING));
7843 str = database_get_data(rd->d.object, KEY_VOTE_OPTION_VOTED, RECDB_QSTRING);
7844 vOpt->voted = str ? atoi(str) : 0;
7845 vOpt->option_id = str ? atoi(key) : 0;
7846 vOpt->option_str = strdup(key);
7847 dict_insert(chan->vote_options,vOpt->option_str,vOpt);
7851 user_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7853 struct handle_info *handle;
7854 struct userData *uData;
7855 char *seen, *inf, *flags, *voted, *votefor;
7856 unsigned long last_seen;
7857 unsigned short access_level;
7859 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7861 log_module(CS_LOG, LOG_ERROR, "Invalid user in %s.", chan->channel->name);
7865 access_level = atoi(database_get_data(rd->d.object, KEY_LEVEL, RECDB_QSTRING));
7866 if(access_level > UL_OWNER)
7868 log_module(CS_LOG, LOG_ERROR, "Invalid access level for %s in %s.", key, chan->channel->name);
7872 inf = database_get_data(rd->d.object, KEY_INFO, RECDB_QSTRING);
7873 seen = database_get_data(rd->d.object, KEY_SEEN, RECDB_QSTRING);
7874 last_seen = seen ? strtoul(seen, NULL, 0) : now;
7875 flags = database_get_data(rd->d.object, KEY_FLAGS, RECDB_QSTRING);
7876 voted = database_get_data(rd->d.object, KEY_VOTE_VOTED, RECDB_QSTRING);
7877 votefor = database_get_data(rd->d.object, KEY_VOTE_VOTEDFOR, RECDB_QSTRING);
7878 handle = get_handle_info(key);
7881 log_module(CS_LOG, LOG_ERROR, "Nonexistent account %s in %s.", key, chan->channel->name);
7885 uData = add_channel_user(chan, handle, access_level, last_seen, inf);
7886 uData->flags = flags ? strtoul(flags, NULL, 0) : 0;
7888 uData->voted = voted ? strtoul(voted, NULL, 0) : 0;
7889 uData->votefor = votefor ? strtoul(votefor, NULL, 0) : 0;
7897 ban_read_helper(const char *key, struct record_data *rd, struct chanData *chan)
7899 struct banData *bData;
7900 char *set, *triggered, *s_duration, *s_expires, *reason, *owner;
7901 unsigned long set_time, triggered_time, expires_time;
7903 if(rd->type != RECDB_OBJECT || !dict_size(rd->d.object))
7905 log_module(CS_LOG, LOG_ERROR, "Invalid ban in %s.", chan->channel->name);
7909 set = database_get_data(rd->d.object, KEY_SET, RECDB_QSTRING);
7910 triggered = database_get_data(rd->d.object, KEY_TRIGGERED, RECDB_QSTRING);
7911 s_duration = database_get_data(rd->d.object, KEY_DURATION, RECDB_QSTRING);
7912 s_expires = database_get_data(rd->d.object, KEY_EXPIRES, RECDB_QSTRING);
7913 owner = database_get_data(rd->d.object, KEY_OWNER, RECDB_QSTRING);
7914 reason = database_get_data(rd->d.object, KEY_REASON, RECDB_QSTRING);
7915 if (!reason || !owner)
7918 set_time = set ? strtoul(set, NULL, 0) : now;
7919 triggered_time = triggered ? strtoul(triggered, NULL, 0) : 0;
7921 expires_time = strtoul(s_expires, NULL, 0);
7923 expires_time = set_time + atoi(s_duration);
7927 if(!reason || (expires_time && (expires_time < now)))
7930 bData = add_channel_ban(chan, key, owner, set_time, triggered_time, expires_time, reason);
7933 static struct suspended *
7934 chanserv_read_suspended(dict_t obj)
7936 struct suspended *suspended = calloc(1, sizeof(*suspended));
7940 str = database_get_data(obj, KEY_EXPIRES, RECDB_QSTRING);
7941 suspended->expires = str ? strtoul(str, NULL, 0) : 0;
7942 str = database_get_data(obj, KEY_REVOKED, RECDB_QSTRING);
7943 suspended->revoked = str ? strtoul(str, NULL, 0) : 0;
7944 str = database_get_data(obj, KEY_ISSUED, RECDB_QSTRING);
7945 suspended->issued = str ? strtoul(str, NULL, 0) : 0;
7946 suspended->suspender = strdup(database_get_data(obj, KEY_SUSPENDER, RECDB_QSTRING));
7947 suspended->reason = strdup(database_get_data(obj, KEY_REASON, RECDB_QSTRING));
7948 previous = database_get_data(obj, KEY_PREVIOUS, RECDB_OBJECT);
7949 suspended->previous = previous ? chanserv_read_suspended(previous) : NULL;
7954 chanserv_channel_read(const char *key, struct record_data *hir)
7956 struct suspended *suspended;
7957 struct mod_chanmode *modes;
7958 struct chanNode *cNode;
7959 struct chanData *cData;
7960 struct dict *channel, *obj;
7961 char *str, *argv[10];
7965 channel = hir->d.object;
7967 str = database_get_data(channel, KEY_REGISTRAR, RECDB_QSTRING);
7970 cNode = AddChannel(key, now, NULL, NULL);
7973 log_module(CS_LOG, LOG_ERROR, "Unable to create registered channel %s.", key);
7976 cData = register_channel(cNode, str);
7979 log_module(CS_LOG, LOG_ERROR, "Unable to register channel %s from database.", key);
7983 if((obj = database_get_data(channel, KEY_OPTIONS, RECDB_OBJECT)))
7985 enum levelOption lvlOpt;
7986 enum charOption chOpt;
7988 if((str = database_get_data(obj, KEY_FLAGS, RECDB_QSTRING)))
7989 cData->flags = atoi(str);
7991 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
7993 str = database_get_data(obj, levelOptions[lvlOpt].db_name, RECDB_QSTRING);
7995 cData->lvlOpts[lvlOpt] = user_level_from_name(str, UL_OWNER+1);
7996 else if(levelOptions[lvlOpt].old_flag)
7998 if(cData->flags & levelOptions[lvlOpt].old_flag)
7999 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].flag_value;
8001 cData->lvlOpts[lvlOpt] = levelOptions[lvlOpt].default_value;
8005 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8007 if(!(str = database_get_data(obj, charOptions[chOpt].db_name, RECDB_QSTRING)))
8009 cData->chOpts[chOpt] = str[0];
8012 else if((str = database_get_data(channel, KEY_FLAGS, RECDB_QSTRING)))
8014 enum levelOption lvlOpt;
8015 enum charOption chOpt;
8018 cData->flags = base64toint(str, 5);
8019 count = strlen(str += 5);
8020 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8023 if(levelOptions[lvlOpt].old_flag)
8025 if(cData->flags & levelOptions[lvlOpt].old_flag)
8026 lvl = levelOptions[lvlOpt].flag_value;
8028 lvl = levelOptions[lvlOpt].default_value;
8030 else switch(((count <= levelOptions[lvlOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[levelOptions[lvlOpt].old_idx])
8032 case 'c': lvl = UL_COOWNER; break;
8033 case 'm': lvl = UL_MASTER; break;
8034 case 'n': lvl = UL_OWNER+1; break;
8035 case 'o': lvl = UL_OP; break;
8036 case 'p': lvl = UL_PEON; break;
8037 case 'w': lvl = UL_OWNER; break;
8038 default: lvl = 0; break;
8040 cData->lvlOpts[lvlOpt] = lvl;
8042 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8043 cData->chOpts[chOpt] = ((count <= charOptions[chOpt].old_idx) ? str : CHANNEL_DEFAULT_OPTIONS)[charOptions[chOpt].old_idx];
8046 if((str = database_get_data(hir->d.object, KEY_EXPIRE, RECDB_QSTRING)))
8048 cData->expiry = atoi(str);
8049 if(cData->expiry > 0) {
8050 if(cData->expiry > now) {
8051 timeq_add(cData->expiry, chanserv_expire_channel, cData);
8053 timeq_add(1, chanserv_expire_channel, cData);
8060 if((obj = database_get_data(hir->d.object, KEY_SUSPENDED, RECDB_OBJECT)))
8062 suspended = chanserv_read_suspended(obj);
8063 cData->suspended = suspended;
8064 suspended->cData = cData;
8065 /* We could use suspended->expires and suspended->revoked to
8066 * set the CHANNEL_SUSPENDED flag, but we don't. */
8068 else if(IsSuspended(cData) && (str = database_get_data(hir->d.object, KEY_SUSPENDER, RECDB_QSTRING)))
8070 suspended = calloc(1, sizeof(*suspended));
8071 suspended->issued = 0;
8072 suspended->revoked = 0;
8073 suspended->suspender = strdup(str);
8074 str = database_get_data(hir->d.object, KEY_SUSPEND_EXPIRES, RECDB_QSTRING);
8075 suspended->expires = str ? atoi(str) : 0;
8076 str = database_get_data(hir->d.object, KEY_SUSPEND_REASON, RECDB_QSTRING);
8077 suspended->reason = strdup(str ? str : "No reason");
8078 suspended->previous = NULL;
8079 cData->suspended = suspended;
8080 suspended->cData = cData;
8084 cData->flags &= ~CHANNEL_SUSPENDED;
8085 suspended = NULL; /* to squelch a warning */
8088 if(IsSuspended(cData)) {
8089 if(suspended->expires > now)
8090 timeq_add(suspended->expires, chanserv_expire_suspension, suspended);
8091 else if(suspended->expires)
8092 cData->flags &= ~CHANNEL_SUSPENDED;
8095 if((!off_channel || !IsOffChannel(cData)) && !IsSuspended(cData)) {
8096 struct mod_chanmode change;
8097 mod_chanmode_init(&change);
8099 change.args[0].mode = MODE_CHANOP;
8100 change.args[0].u.member = AddChannelUser(chanserv, cNode);
8101 mod_chanmode_announce(chanserv, cNode, &change);
8104 str = database_get_data(channel, KEY_REGISTERED, RECDB_QSTRING);
8105 cData->registered = str ? strtoul(str, NULL, 0) : now;
8106 str = database_get_data(channel, KEY_VISITED, RECDB_QSTRING);
8107 cData->visited = str ? strtoul(str, NULL, 0) : now;
8108 str = database_get_data(channel, KEY_OWNER_TRANSFER, RECDB_QSTRING);
8109 cData->ownerTransfer = str ? strtoul(str, NULL, 0) : 0;
8110 str = database_get_data(channel, KEY_MAX, RECDB_QSTRING);
8111 cData->max = str ? atoi(str) : 0;
8112 str = database_get_data(channel, KEY_MAX_TIME, RECDB_QSTRING);
8113 cData->max_time = str ? atoi(str) : 0;
8114 str = database_get_data(channel, KEY_GREETING, RECDB_QSTRING);
8115 cData->greeting = str ? strdup(str) : NULL;
8116 str = database_get_data(channel, KEY_USER_GREETING, RECDB_QSTRING);
8117 cData->user_greeting = str ? strdup(str) : NULL;
8118 str = database_get_data(channel, KEY_TOPIC_MASK, RECDB_QSTRING);
8119 cData->topic_mask = str ? strdup(str) : NULL;
8120 str = database_get_data(channel, KEY_TOPIC, RECDB_QSTRING);
8121 cData->topic = str ? strdup(str) : NULL;
8123 str = database_get_data(channel, KEY_VOTE, RECDB_QSTRING);
8125 cData->vote = str ? strdup(str) : NULL;
8126 dict_delete(cData->vote_options);
8127 cData->vote_options = dict_new();
8128 dict_set_free_data(cData->vote_options, free_vote_options);
8129 str = database_get_data(channel, KEY_VOTE_START, RECDB_QSTRING);
8130 cData->vote_start = str ? atoi(str) : 0;
8131 obj = database_get_data(channel, KEY_VOTE_OPTIONS, RECDB_OBJECT);
8132 for(it = dict_first(obj); it; it = iter_next(it)) {
8133 vote_option_read_helper(iter_key(it), iter_data(it), cData);
8137 if(!IsSuspended(cData)
8138 && (str = database_get_data(channel, KEY_MODES, RECDB_QSTRING))
8139 && (argc = split_line(str, 0, ArrayLength(argv), argv))
8140 && (modes = mod_chanmode_parse(cNode, NULL, argv, argc, MCP_KEY_FREE|MCP_NO_APASS, 0))) {
8141 cData->modes = *modes;
8143 cData->modes.modes_set |= MODE_REGISTERED;
8144 if(cData->modes.argc > 1)
8145 cData->modes.argc = 1;
8146 mod_chanmode_announce(chanserv, cNode, &cData->modes);
8147 mod_chanmode_free(modes);
8150 obj = database_get_data(channel, KEY_USERS, RECDB_OBJECT);
8151 for(it = dict_first(obj); it; it = iter_next(it))
8152 user_read_helper(iter_key(it), iter_data(it), cData);
8154 if(!cData->users && !IsProtected(cData))
8156 log_module(CS_LOG, LOG_ERROR, "Channel %s had no users in database, unregistering it.", key);
8157 unregister_channel(cData, "has empty user list.");
8161 obj = database_get_data(channel, KEY_BANS, RECDB_OBJECT);
8162 for(it = dict_first(obj); it; it = iter_next(it))
8163 ban_read_helper(iter_key(it), iter_data(it), cData);
8165 obj = database_get_data(channel, KEY_NOTES, RECDB_OBJECT);
8166 for(it = dict_first(obj); it; it = iter_next(it))
8168 struct note_type *ntype = dict_find(note_types, iter_key(it), NULL);
8169 struct record_data *rd = iter_data(it);
8170 const char *note, *setter;
8172 if(rd->type != RECDB_OBJECT)
8174 log_module(CS_LOG, LOG_ERROR, "Bad record type for note %s in channel %s.", iter_key(it), key);
8178 log_module(CS_LOG, LOG_ERROR, "Bad note type name %s in channel %s.", iter_key(it), key);
8180 else if(!(note = database_get_data(rd->d.object, KEY_NOTE_NOTE, RECDB_QSTRING)))
8182 log_module(CS_LOG, LOG_ERROR, "Missing note text for note %s in channel %s.", iter_key(it), key);
8186 setter = database_get_data(rd->d.object, KEY_NOTE_SETTER, RECDB_QSTRING);
8187 if(!setter) setter = "<unknown>";
8188 chanserv_add_channel_note(cData, ntype, setter, note);
8196 chanserv_dnr_read(const char *key, struct record_data *hir)
8198 const char *setter, *reason, *str;
8199 struct do_not_register *dnr;
8200 unsigned long expiry;
8202 setter = database_get_data(hir->d.object, KEY_DNR_SETTER, RECDB_QSTRING);
8205 log_module(CS_LOG, LOG_ERROR, "Missing setter for DNR %s.", key);
8208 reason = database_get_data(hir->d.object, KEY_DNR_REASON, RECDB_QSTRING);
8211 log_module(CS_LOG, LOG_ERROR, "Missing reason for DNR %s.", key);
8214 str = database_get_data(hir->d.object, KEY_EXPIRES, RECDB_QSTRING);
8215 expiry = str ? strtoul(str, NULL, 0) : 0;
8216 if(expiry && expiry <= now)
8218 dnr = chanserv_add_dnr(key, setter, expiry, reason);
8221 str = database_get_data(hir->d.object, KEY_DNR_SET, RECDB_QSTRING);
8223 dnr->set = atoi(str);
8229 chanserv_saxdb_read(struct dict *database)
8231 struct dict *section;
8234 if((section = database_get_data(database, KEY_NOTE_TYPES, RECDB_OBJECT)))
8235 for(it = dict_first(section); it; it = iter_next(it))
8236 chanserv_note_type_read(iter_key(it), iter_data(it));
8238 if((section = database_get_data(database, KEY_CHANNELS, RECDB_OBJECT)))
8239 for(it = dict_first(section); it; it = iter_next(it))
8240 chanserv_channel_read(iter_key(it), iter_data(it));
8242 if((section = database_get_data(database, KEY_DNR, RECDB_OBJECT)))
8243 for(it = dict_first(section); it; it = iter_next(it))
8244 chanserv_dnr_read(iter_key(it), iter_data(it));
8250 chanserv_write_users(struct saxdb_context *ctx, struct userData *uData)
8252 int high_present = 0;
8253 saxdb_start_record(ctx, KEY_USERS, 1);
8254 for(; uData; uData = uData->next)
8256 if((uData->access >= UL_PRESENT) && uData->present && !HANDLE_FLAGGED(uData->handle, BOT))
8258 saxdb_start_record(ctx, uData->handle->handle, 0);
8259 saxdb_write_int(ctx, KEY_LEVEL, uData->access);
8260 saxdb_write_int(ctx, KEY_SEEN, uData->seen);
8262 saxdb_write_int(ctx, KEY_FLAGS, uData->flags);
8263 if(uData->channel->vote && uData->voted)
8264 saxdb_write_int(ctx, KEY_VOTE_VOTED, uData->voted);
8265 if(uData->channel->vote && uData->votefor)
8266 saxdb_write_int(ctx, KEY_VOTE_VOTEDFOR, uData->votefor);
8268 saxdb_write_string(ctx, KEY_INFO, uData->info);
8269 saxdb_end_record(ctx);
8271 saxdb_end_record(ctx);
8272 return high_present;
8276 chanserv_write_bans(struct saxdb_context *ctx, struct banData *bData)
8280 saxdb_start_record(ctx, KEY_BANS, 1);
8281 for(; bData; bData = bData->next)
8283 saxdb_start_record(ctx, bData->mask, 0);
8284 saxdb_write_int(ctx, KEY_SET, bData->set);
8285 if(bData->triggered)
8286 saxdb_write_int(ctx, KEY_TRIGGERED, bData->triggered);
8288 saxdb_write_int(ctx, KEY_EXPIRES, bData->expires);
8290 saxdb_write_string(ctx, KEY_OWNER, bData->owner);
8292 saxdb_write_string(ctx, KEY_REASON, bData->reason);
8293 saxdb_end_record(ctx);
8295 saxdb_end_record(ctx);
8299 chanserv_write_suspended(struct saxdb_context *ctx, const char *name, struct suspended *susp)
8301 saxdb_start_record(ctx, name, 0);
8302 saxdb_write_string(ctx, KEY_SUSPENDER, susp->suspender);
8303 saxdb_write_string(ctx, KEY_REASON, susp->reason);
8305 saxdb_write_int(ctx, KEY_ISSUED, susp->issued);
8307 saxdb_write_int(ctx, KEY_EXPIRES, susp->expires);
8309 saxdb_write_int(ctx, KEY_REVOKED, susp->revoked);
8311 chanserv_write_suspended(ctx, KEY_PREVIOUS, susp->previous);
8312 saxdb_end_record(ctx);
8316 chanserv_write_channel(struct saxdb_context *ctx, struct chanData *channel)
8320 enum levelOption lvlOpt;
8321 enum charOption chOpt;
8324 saxdb_start_record(ctx, channel->channel->name, 1);
8326 saxdb_write_int(ctx, KEY_REGISTERED, channel->registered);
8327 saxdb_write_int(ctx, KEY_MAX, channel->max);
8328 saxdb_write_int(ctx, KEY_MAX_TIME, channel->max_time);
8330 saxdb_write_string(ctx, KEY_TOPIC, channel->topic);
8331 if(channel->registrar)
8332 saxdb_write_string(ctx, KEY_REGISTRAR, channel->registrar);
8333 if(channel->greeting)
8334 saxdb_write_string(ctx, KEY_GREETING, channel->greeting);
8335 if(channel->user_greeting)
8336 saxdb_write_string(ctx, KEY_USER_GREETING, channel->user_greeting);
8337 if(channel->topic_mask)
8338 saxdb_write_string(ctx, KEY_TOPIC_MASK, channel->topic_mask);
8339 if(channel->suspended)
8340 chanserv_write_suspended(ctx, "suspended", channel->suspended);
8342 saxdb_write_int(ctx, KEY_EXPIRE, channel->expiry);
8345 saxdb_write_string(ctx, KEY_VOTE, channel->vote);
8346 if(channel->vote_start)
8347 saxdb_write_int(ctx, KEY_VOTE_START, channel->vote_start);
8348 if (dict_size(channel->vote_options)) {
8349 saxdb_start_record(ctx, KEY_VOTE_OPTIONS, 1);
8350 for (it = dict_first(channel->vote_options); it; it = iter_next(it)) {
8351 struct vote_option *vOpt = iter_data(it);
8353 sprintf(str,"%i",vOpt->option_id);
8354 saxdb_start_record(ctx, str, 0);
8356 saxdb_write_int(ctx, KEY_VOTE_OPTION_VOTED, vOpt->voted);
8358 saxdb_write_string(ctx, KEY_VOTE_OPTION_NAME, vOpt->name);
8359 saxdb_end_record(ctx);
8361 saxdb_end_record(ctx);
8365 saxdb_start_record(ctx, KEY_OPTIONS, 0);
8366 saxdb_write_int(ctx, KEY_FLAGS, channel->flags);
8367 for(lvlOpt = 0; lvlOpt < NUM_LEVEL_OPTIONS; ++lvlOpt)
8368 saxdb_write_int(ctx, levelOptions[lvlOpt].db_name, channel->lvlOpts[lvlOpt]);
8369 for(chOpt = 0; chOpt < NUM_CHAR_OPTIONS; ++chOpt)
8371 buf[0] = channel->chOpts[chOpt];
8373 saxdb_write_string(ctx, charOptions[chOpt].db_name, buf);
8375 saxdb_end_record(ctx);
8377 if(channel->modes.modes_set || channel->modes.modes_clear)
8379 mod_chanmode_format(&channel->modes, buf);
8380 saxdb_write_string(ctx, KEY_MODES, buf);
8383 high_present = chanserv_write_users(ctx, channel->users);
8384 chanserv_write_bans(ctx, channel->bans);
8386 if(dict_size(channel->notes))
8390 saxdb_start_record(ctx, KEY_NOTES, 1);
8391 for(it = dict_first(channel->notes); it; it = iter_next(it))
8393 struct note *note = iter_data(it);
8394 saxdb_start_record(ctx, iter_key(it), 0);
8395 saxdb_write_string(ctx, KEY_NOTE_SETTER, note->setter);
8396 saxdb_write_string(ctx, KEY_NOTE_NOTE, note->note);
8397 saxdb_end_record(ctx);
8399 saxdb_end_record(ctx);
8402 if(channel->ownerTransfer)
8403 saxdb_write_int(ctx, KEY_OWNER_TRANSFER, channel->ownerTransfer);
8404 saxdb_write_int(ctx, KEY_VISITED, high_present ? now : channel->visited);
8405 saxdb_end_record(ctx);
8409 chanserv_write_note_type(struct saxdb_context *ctx, struct note_type *ntype)
8413 saxdb_start_record(ctx, ntype->name, 0);
8414 switch(ntype->set_access_type)
8416 case NOTE_SET_CHANNEL_ACCESS:
8417 saxdb_write_int(ctx, KEY_NOTE_CHANNEL_ACCESS, ntype->set_access.min_ulevel);
8419 case NOTE_SET_CHANNEL_SETTER:
8420 saxdb_write_int(ctx, KEY_NOTE_SETTER_ACCESS, 1);
8422 case NOTE_SET_PRIVILEGED: default:
8423 saxdb_write_int(ctx, KEY_NOTE_OPSERV_ACCESS, ntype->set_access.min_opserv);
8426 switch(ntype->visible_type)
8428 case NOTE_VIS_ALL: str = KEY_NOTE_VIS_ALL; break;
8429 case NOTE_VIS_CHANNEL_USERS: str = KEY_NOTE_VIS_CHANNEL_USERS; break;
8430 case NOTE_VIS_PRIVILEGED: default: str = KEY_NOTE_VIS_PRIVILEGED; break;
8432 saxdb_write_string(ctx, KEY_NOTE_VISIBILITY, str);
8433 saxdb_write_int(ctx, KEY_NOTE_MAX_LENGTH, ntype->max_length);
8434 saxdb_end_record(ctx);
8438 write_dnrs_helper(struct saxdb_context *ctx, struct dict *dnrs)
8440 struct do_not_register *dnr;
8441 dict_iterator_t it, next;
8443 for(it = dict_first(dnrs); it; it = next)
8445 next = iter_next(it);
8446 dnr = iter_data(it);
8447 if(dnr->expires && dnr->expires <= now)
8449 dict_remove(dnrs, iter_key(it));
8452 saxdb_start_record(ctx, dnr->chan_name, 0);
8454 saxdb_write_int(ctx, KEY_DNR_SET, dnr->set);
8456 saxdb_write_int(ctx, KEY_EXPIRES, dnr->expires);
8457 saxdb_write_string(ctx, KEY_DNR_SETTER, dnr->setter);
8458 saxdb_write_string(ctx, KEY_DNR_REASON, dnr->reason);
8459 saxdb_end_record(ctx);
8464 chanserv_saxdb_write(struct saxdb_context *ctx)
8467 struct chanData *channel;
8470 saxdb_start_record(ctx, KEY_NOTE_TYPES, 1);
8471 for(it = dict_first(note_types); it; it = iter_next(it))
8472 chanserv_write_note_type(ctx, iter_data(it));
8473 saxdb_end_record(ctx);
8476 saxdb_start_record(ctx, KEY_DNR, 1);
8477 write_dnrs_helper(ctx, handle_dnrs);
8478 write_dnrs_helper(ctx, plain_dnrs);
8479 write_dnrs_helper(ctx, mask_dnrs);
8480 saxdb_end_record(ctx);
8483 saxdb_start_record(ctx, KEY_CHANNELS, 1);
8484 for(channel = channelList; channel; channel = channel->next)
8485 chanserv_write_channel(ctx, channel);
8486 saxdb_end_record(ctx);
8492 chanserv_db_cleanup(void) {
8494 unreg_part_func(handle_part);
8496 unregister_channel(channelList, "terminating.");
8497 for(ii = 0; ii < chanserv_conf.support_channels.used; ++ii)
8498 UnlockChannel(chanserv_conf.support_channels.list[ii]);
8499 free(chanserv_conf.support_channels.list);
8500 dict_delete(handle_dnrs);
8501 dict_delete(plain_dnrs);
8502 dict_delete(mask_dnrs);
8503 dict_delete(note_types);
8504 free_string_list(chanserv_conf.eightball);
8505 free_string_list(chanserv_conf.old_ban_names);
8506 free_string_list(chanserv_conf.set_shows);
8507 free(set_shows_list.list);
8508 free(uset_shows_list.list);
8511 struct userData *helper = helperList;
8512 helperList = helperList->next;
8517 #if defined(GCC_VARMACROS)
8518 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ARGS...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, ARGS)
8519 #elif defined(C99_VARMACROS)
8520 # define DEFINE_COMMAND(NAME, MIN_ARGC, FLAGS, ...) modcmd_register(chanserv_module, #NAME, cmd_##NAME, MIN_ARGC, FLAGS, __VA_ARGS__)
8522 #define DEFINE_CHANNEL_OPTION(NAME) modcmd_register(chanserv_module, "set "#NAME, chan_opt_##NAME, 1, 0, NULL)
8523 #define DEFINE_USER_OPTION(NAME) modcmd_register(chanserv_module, "uset "#NAME, user_opt_##NAME, 1, MODCMD_REQUIRE_REGCHAN, NULL)
8526 init_chanserv(const char *nick)
8528 CS_LOG = log_register_type("ChanServ", "file:chanserv.log");
8529 conf_register_reload(chanserv_conf_read);
8533 reg_server_link_func(handle_server_link);
8534 reg_new_channel_func(handle_new_channel);
8535 reg_join_func(handle_join);
8536 reg_part_func(handle_part);
8537 reg_kick_func(handle_kick);
8538 reg_topic_func(handle_topic);
8539 reg_mode_change_func(handle_mode);
8540 reg_nick_change_func(handle_nick_change);
8541 reg_auth_func(handle_auth);
8544 reg_handle_rename_func(handle_rename);
8545 reg_unreg_func(handle_unreg);
8547 handle_dnrs = dict_new();
8548 dict_set_free_data(handle_dnrs, free);
8549 plain_dnrs = dict_new();
8550 dict_set_free_data(plain_dnrs, free);
8551 mask_dnrs = dict_new();
8552 dict_set_free_data(mask_dnrs, free);
8554 reg_svccmd_unbind_func(handle_svccmd_unbind);
8555 chanserv_module = module_register("ChanServ", CS_LOG, "chanserv.help", chanserv_expand_variable);
8556 DEFINE_COMMAND(register, 1, MODCMD_REQUIRE_AUTHED, "flags", "+acceptchan,+helping", NULL);
8557 DEFINE_COMMAND(noregister, 1, MODCMD_REQUIRE_AUTHED, "flags", "+helping", NULL);
8558 DEFINE_COMMAND(allowregister, 2, 0, "template", "noregister", NULL);
8559 DEFINE_COMMAND(dnrsearch, 3, 0, "template", "noregister", NULL);
8560 modcmd_register(chanserv_module, "dnrsearch print", NULL, 0, 0, NULL);
8561 modcmd_register(chanserv_module, "dnrsearch remove", NULL, 0, 0, NULL);
8562 modcmd_register(chanserv_module, "dnrsearch count", NULL, 0, 0, NULL);
8563 DEFINE_COMMAND(move, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "template", "register", NULL);
8564 DEFINE_COMMAND(csuspend, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8565 DEFINE_COMMAND(cunsuspend, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+helping", NULL);
8566 DEFINE_COMMAND(createnote, 5, 0, "level", "800", NULL);
8567 DEFINE_COMMAND(removenote, 2, 0, "level", "800", NULL);
8569 DEFINE_COMMAND(unregister, 1, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "flags", "+loghostmask", NULL);
8570 DEFINE_COMMAND(merge, 2, MODCMD_REQUIRE_AUTHED|MODCMD_REQUIRE_REGCHAN, "access", "owner", NULL);
8572 DEFINE_COMMAND(adduser, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8573 DEFINE_COMMAND(deluser, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8574 DEFINE_COMMAND(suspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8575 DEFINE_COMMAND(unsuspend, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8576 DEFINE_COMMAND(deleteme, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8578 DEFINE_COMMAND(mdelowner, 2, MODCMD_REQUIRE_CHANUSER, "flags", "+helping", NULL);
8579 DEFINE_COMMAND(mdelcoowner, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", NULL);
8580 DEFINE_COMMAND(mdelmaster, 2, MODCMD_REQUIRE_CHANUSER, "access", "coowner", NULL);
8581 DEFINE_COMMAND(mdelop, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8582 DEFINE_COMMAND(mdelpeon, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8584 DEFINE_COMMAND(trim, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8585 DEFINE_COMMAND(opchan, 1, MODCMD_REQUIRE_REGCHAN|MODCMD_NEVER_CSUSPEND, "access", "1", NULL);
8586 DEFINE_COMMAND(clvl, 3, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8587 DEFINE_COMMAND(giveownership, 2, MODCMD_REQUIRE_CHANUSER, "access", "owner", "flags", "+loghostmask", NULL);
8589 DEFINE_COMMAND(up, 1, MODCMD_REQUIRE_CHANUSER, NULL);
8590 DEFINE_COMMAND(down, 1, MODCMD_REQUIRE_REGCHAN, NULL);
8591 DEFINE_COMMAND(upall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8592 DEFINE_COMMAND(downall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8593 DEFINE_COMMAND(op, 2, MODCMD_REQUIRE_CHANNEL, "access", "op", NULL);
8594 DEFINE_COMMAND(deop, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8595 DEFINE_COMMAND(voice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8596 DEFINE_COMMAND(devoice, 2, MODCMD_REQUIRE_CHANNEL, "template", "op", NULL);
8598 DEFINE_COMMAND(kickban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8599 DEFINE_COMMAND(kick, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8600 DEFINE_COMMAND(ban, 2, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8601 DEFINE_COMMAND(unban, 2, 0, "template", "op", NULL);
8602 DEFINE_COMMAND(unbanall, 1, 0, "template", "op", NULL);
8603 DEFINE_COMMAND(unbanme, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8604 DEFINE_COMMAND(open, 1, MODCMD_REQUIRE_CHANUSER, "template", "op", NULL);
8605 DEFINE_COMMAND(topic, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", "flags", "+never_csuspend", NULL);
8606 DEFINE_COMMAND(mode, 1, MODCMD_REQUIRE_REGCHAN, "template", "op", NULL);
8607 DEFINE_COMMAND(inviteme, 1, MODCMD_REQUIRE_CHANNEL, "access", "1", NULL);
8608 DEFINE_COMMAND(invitemeall, 1, MODCMD_REQUIRE_AUTHED, NULL);
8609 DEFINE_COMMAND(invite, 1, MODCMD_REQUIRE_CHANNEL, "access", "master", NULL);
8610 DEFINE_COMMAND(set, 1, MODCMD_REQUIRE_CHANUSER, "access", "op", NULL);
8611 DEFINE_COMMAND(wipeinfo, 2, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8612 DEFINE_COMMAND(resync, 1, MODCMD_REQUIRE_CHANUSER, "access", "master", NULL);
8614 DEFINE_COMMAND(events, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog", "access", "350", NULL);
8615 DEFINE_COMMAND(addban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8616 DEFINE_COMMAND(addtimedban, 3, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8617 DEFINE_COMMAND(delban, 2, MODCMD_REQUIRE_REGCHAN, "access", "250", NULL);
8618 DEFINE_COMMAND(uset, 1, MODCMD_REQUIRE_CHANUSER, "access", "1", NULL);
8620 DEFINE_COMMAND(bans, 1, MODCMD_REQUIRE_REGCHAN, "access", "1", "flags", "+nolog", NULL);
8621 DEFINE_COMMAND(peek, 1, MODCMD_REQUIRE_REGCHAN, "access", "op", "flags", "+nolog", NULL);
8623 DEFINE_COMMAND(myaccess, 1, MODCMD_REQUIRE_AUTHED, NULL);
8624 DEFINE_COMMAND(access, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8625 DEFINE_COMMAND(users, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8626 DEFINE_COMMAND(wlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8627 DEFINE_COMMAND(clist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8628 DEFINE_COMMAND(mlist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8629 DEFINE_COMMAND(olist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8630 DEFINE_COMMAND(plist, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8631 DEFINE_COMMAND(info, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8632 DEFINE_COMMAND(seen, 2, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8633 DEFINE_COMMAND(names, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+nolog,+joinable", NULL);
8635 DEFINE_COMMAND(note, 1, MODCMD_REQUIRE_REGCHAN, "flags", "+joinable,+acceptchan", NULL);
8636 DEFINE_COMMAND(delnote, 2, MODCMD_REQUIRE_CHANUSER, NULL);
8638 DEFINE_COMMAND(netinfo, 1, 0, "flags", "+nolog", NULL);
8639 DEFINE_COMMAND(ircops, 1, 0, "flags", "+nolog", NULL);
8640 DEFINE_COMMAND(helpers, 1, 0, "flags", "+nolog", NULL);
8641 DEFINE_COMMAND(staff, 1, 0, "flags", "+nolog", NULL);
8643 DEFINE_COMMAND(say, 2, 0, "flags", "+oper,+acceptchan", NULL);
8644 DEFINE_COMMAND(emote, 2, 0, "flags", "+oper,+acceptchan", NULL);
8645 DEFINE_COMMAND(expire, 1, 0, "flags", "+oper", NULL);
8646 DEFINE_COMMAND(search, 3, 0, "flags", "+nolog,+helping", NULL);
8647 DEFINE_COMMAND(unvisited, 1, 0, "flags", "+nolog,+helping", NULL);
8649 DEFINE_COMMAND(unf, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8650 DEFINE_COMMAND(ping, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8651 DEFINE_COMMAND(wut, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8652 DEFINE_COMMAND(8ball, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8653 DEFINE_COMMAND(d, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8654 DEFINE_COMMAND(huggle, 1, 0, "flags", "+nolog,+toy,+acceptchan", NULL);
8656 DEFINE_COMMAND(addvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8657 DEFINE_COMMAND(delvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8658 DEFINE_COMMAND(addoption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8659 DEFINE_COMMAND(deloption, 1, MODCMD_REQUIRE_AUTHED, NULL);
8660 DEFINE_COMMAND(vote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8661 DEFINE_COMMAND(startvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8662 DEFINE_COMMAND(endvote, 1, MODCMD_REQUIRE_AUTHED, NULL);
8663 DEFINE_COMMAND(voteresults, 1, MODCMD_REQUIRE_AUTHED, NULL);
8665 /* Channel options */
8666 DEFINE_CHANNEL_OPTION(defaulttopic);
8667 DEFINE_CHANNEL_OPTION(topicmask);
8668 DEFINE_CHANNEL_OPTION(greeting);
8669 DEFINE_CHANNEL_OPTION(usergreeting);
8670 DEFINE_CHANNEL_OPTION(modes);
8671 DEFINE_CHANNEL_OPTION(enfops);
8672 DEFINE_CHANNEL_OPTION(giveops);
8673 DEFINE_CHANNEL_OPTION(protect);
8674 DEFINE_CHANNEL_OPTION(enfmodes);
8675 DEFINE_CHANNEL_OPTION(enftopic);
8676 DEFINE_CHANNEL_OPTION(pubcmd);
8677 DEFINE_CHANNEL_OPTION(givevoice);
8678 DEFINE_CHANNEL_OPTION(userinfo);
8679 DEFINE_CHANNEL_OPTION(dynlimit);
8680 DEFINE_CHANNEL_OPTION(topicsnarf);
8681 DEFINE_CHANNEL_OPTION(vote);
8682 DEFINE_CHANNEL_OPTION(nodelete);
8683 DEFINE_CHANNEL_OPTION(toys);
8684 DEFINE_CHANNEL_OPTION(setters);
8685 DEFINE_CHANNEL_OPTION(topicrefresh);
8686 DEFINE_CHANNEL_OPTION(ctcpusers);
8687 DEFINE_CHANNEL_OPTION(ctcpreaction);
8688 DEFINE_CHANNEL_OPTION(inviteme);
8689 DEFINE_CHANNEL_OPTION(unreviewed);
8690 modcmd_register(chanserv_module, "set expire", chan_opt_expire, 1, 0, "flags", "+helping", NULL);
8691 modcmd_register(chanserv_module, "set unreviewed on", NULL, 0, 0, "flags", "+helping", NULL);
8692 modcmd_register(chanserv_module, "set unreviewed off", NULL, 0, 0, "flags", "+oper", NULL);
8694 DEFINE_CHANNEL_OPTION(offchannel);
8695 modcmd_register(chanserv_module, "set defaults", chan_opt_defaults, 1, 0, "access", "owner", NULL);
8697 /* Alias set topic to set defaulttopic for compatibility. */
8698 modcmd_register(chanserv_module, "set topic", chan_opt_defaulttopic, 1, 0, NULL);
8701 DEFINE_USER_OPTION(noautoop);
8702 DEFINE_USER_OPTION(autoinvite);
8703 DEFINE_USER_OPTION(info);
8705 /* Alias uset autovoice to uset autoop. */
8706 modcmd_register(chanserv_module, "uset noautovoice", user_opt_noautoop, 1, 0, NULL);
8708 note_types = dict_new();
8709 dict_set_free_data(note_types, chanserv_deref_note_type);
8712 const char *modes = conf_get_data("services/chanserv/modes", RECDB_QSTRING);
8713 chanserv = AddLocalUser(nick, nick, NULL, "Channel Services", modes);
8714 service_register(chanserv)->trigger = '!';
8715 reg_chanmsg_func('\001', chanserv, chanserv_ctcp_check);
8717 saxdb_register("ChanServ", chanserv_saxdb_read, chanserv_saxdb_write);
8719 if(chanserv_conf.channel_expire_frequency)
8720 timeq_add(now + chanserv_conf.channel_expire_frequency, expire_channels, NULL);
8722 if(chanserv_conf.dnr_expire_frequency)
8723 timeq_add(now + chanserv_conf.dnr_expire_frequency, expire_dnrs, NULL);
8725 if(chanserv_conf.refresh_period)
8727 unsigned long next_refresh;
8728 next_refresh = (now + chanserv_conf.refresh_period - 1) / chanserv_conf.refresh_period * chanserv_conf.refresh_period;
8729 timeq_add(next_refresh, chanserv_refresh_topics, NULL);
8732 reg_exit_func(chanserv_db_cleanup);
8733 message_register_table(msgtab);